Initial import of 'Serene5.28' into git repository.
authorRemco Rijnders <remco@webconquest.com>
Sun, 5 Jul 2009 05:02:48 +0000 (07:02 +0200)
committerRemco Rijnders <remco@webconquest.com>
Sun, 5 Jul 2009 05:02:48 +0000 (07:02 +0200)
73 files changed:
CHANGES [new file with mode: 0644]
Config [new file with mode: 0755]
INSTALL [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile.dist [new file with mode: 0755]
bsdinstall [new file with mode: 0755]
doc/CHANGES.old [new file with mode: 0644]
doc/INSTALL [new file with mode: 0644]
doc/conf.doc [new file with mode: 0644]
doc/example.conf [new file with mode: 0644]
doc/opermotd.doc [new file with mode: 0644]
include/channel.h [new file with mode: 0644]
include/cio.h [new file with mode: 0644]
include/ciofunc.h [new file with mode: 0644]
include/class.h [new file with mode: 0644]
include/common.h [new file with mode: 0644]
include/config.h [new file with mode: 0644]
include/dbuf.h [new file with mode: 0644]
include/h.h [new file with mode: 0644]
include/hash.h [new file with mode: 0644]
include/inet.h [new file with mode: 0644]
include/msg.h [new file with mode: 0644]
include/nameser.h [new file with mode: 0644]
include/numeric.h [new file with mode: 0644]
include/patchlevel.h [new file with mode: 0644]
include/res.h [new file with mode: 0644]
include/resolv.h [new file with mode: 0644]
include/sock.h [new file with mode: 0644]
include/struct.h [new file with mode: 0644]
include/sys.h [new file with mode: 0644]
include/userload.h [new file with mode: 0644]
include/whowas.h [new file with mode: 0644]
src/Makefile [new file with mode: 0644]
src/bsd.c [new file with mode: 0644]
src/buildm4 [new file with mode: 0755]
src/channel.c [new file with mode: 0644]
src/chkconf.c [new file with mode: 0644]
src/class.c [new file with mode: 0644]
src/crypt/Makefile [new file with mode: 0755]
src/crypt/README [new file with mode: 0644]
src/crypt/crypter [new file with mode: 0755]
src/crypt/mkpasswd.c [new file with mode: 0755]
src/dbuf.c [new file with mode: 0644]
src/hash.c [new file with mode: 0644]
src/ircd.c [new file with mode: 0644]
src/list.c [new file with mode: 0644]
src/list2.c [new file with mode: 0644]
src/masking.c [new file with mode: 0644]
src/match.c [new file with mode: 0644]
src/md5.c [new file with mode: 0644]
src/packet.c [new file with mode: 0644]
src/parse.c [new file with mode: 0644]
src/res.c [new file with mode: 0644]
src/res_comp.c [new file with mode: 0644]
src/res_init.c [new file with mode: 0644]
src/res_mkquery.c [new file with mode: 0644]
src/res_skipname.c [new file with mode: 0644]
src/s_bsd.c [new file with mode: 0644]
src/s_conf.c [new file with mode: 0644]
src/s_debug.c [new file with mode: 0644]
src/s_err.c [new file with mode: 0644]
src/s_misc.c [new file with mode: 0644]
src/s_numeric.c [new file with mode: 0644]
src/s_ping.c [new file with mode: 0644]
src/s_serv.c [new file with mode: 0644]
src/s_user.c [new file with mode: 0644]
src/s_zip.c [new file with mode: 0644]
src/send.c [new file with mode: 0644]
src/strtoul.c [new file with mode: 0644]
src/support.c [new file with mode: 0644]
src/userload.c [new file with mode: 0644]
src/version.c.SH [new file with mode: 0755]
src/whowas.c [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..55b3c7e
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,13 @@
+#########################################################################
+#      Changes in Serene5.27 (see doc/CHANGES.old for older changes)    #
+#########################################################################
+o: Changed StarChat references to Serene
+
+#########################################################################
+#      Changes in Serene5.28 (see doc/CHANGES.old for older changes)    #
+#########################################################################
+o: /whowas now shows the actual server to normal users.
+o: /whois shows the connection arguments the client used to connect to
+   IRC operators.
+o: Length for QUIT messages increased to 180 characters (up from 120).
+o: Forbid DCC SEND and CHAT functions for unregistered nicks.
diff --git a/Config b/Config
new file mode 100755 (executable)
index 0000000..45d1f51
--- /dev/null
+++ b/Config
@@ -0,0 +1,1368 @@
+#!/bin/sh
+
+# Copyright 1996,1997,1998 Michael Graff <explorer@flame.org>
+# You may distribute this file freely providing this notice
+# remains intact. 
+#
+# Modified August 1999 by GZ        <gz@starchat.net>
+#                         InnerFIRE <innerfire@starchat.net>
+
+IRCD_VERSION="Serene5.28"
+CONF_DATE=`date`
+LAST_VERSION="Serene5.27"
+#
+trap "" 13 14 15
+MV=mv
+RM=rm
+SETUP=include/setup.h
+OPTIONS_H=include/options.h
+OPTIONS=Options
+MAKEINST=.install
+AUTO_CONFIG=""
+#
+GLIBCCHECK=undef
+STDLIBH=undef
+STDDEFH=undef
+SYSSYSLOGH=undef
+PARAMH=undef
+UNISTDH=undef
+STRINGH=undef
+STRINGSH=undef
+STRCASECMP=undef
+RUSAGEH=undef
+HINDEX=undef
+STRERROR=undef
+STRTOKEN=define
+STRTOK=define
+INETADDR=define
+INETNTOA=define
+INETNETOF=define
+GETTIMEOFDAY=undef
+LRAND48=undef
+STRTOUL=undef
+LOCALD=define
+NEEDSKIPNAME=""
+CCPATH=''
+HAVE_BZERO=undef
+GETRUSAGE_2=undef
+TIMES_2=undef
+POSIX_NBLOCK=undef
+BSD_NBLOCK=undef
+POSIX_SIGNAL=undef
+BSD_SIGNAL=undef
+TMP=/tmp/.Configtmp$$.c
+EXEC=/tmp/.Configtmp$$
+PLATE=/tmp/.ConPlate$$
+c=''
+n=''
+#
+# Some reasonable defaults
+#
+DEFOPT="-O"
+DEFCFLAGS="$DEFOPT"
+DEFLIBS="none"
+OSNAME="Could not determine Operating System"
+#
+CONTACT_URL="http://www.serenity-irc.net/connect.html"
+CONTACT_EMAIL=""
+SERVICES_NAME="Services.Serenity-IRC.Net"
+KLINE_ADDRESS=""
+ZIP_LINKS="1"
+DPATH="$HOME/ircd"
+SPATH="$HOME/ircd/ircd"
+HUB=""
+CRYPT_OPER_PASSWORD="1"
+CRYPT_ILINE_PASSWORD=$CRYPT_OPER_PASSWORD
+MAXCONNECTIONS="1024"
+
+#
+# load $OPTIONS if present
+#
+if [ -r "$OPTIONS" ] ; then
+    . $OPTIONS
+fi
+
+#
+2>/dev/null
+if [ "`eval echo -n 'a'`" = "-n a" ] ; then
+       c='\c'
+else
+       n='-n'
+fi
+
+case $# in
+  1 )
+    case "z${1}" in
+        z--help | z-h )
+            echo "  -n          Use the Options file from a previous setup"
+            echo " "
+            echo "  --help      display this help and exit"
+            echo "  --version   output version information and exit"
+            echo " "
+            exit 0 ;;
+        z--version )
+            echo "Config $IRCD_VERSION"; exit 0 ;;
+        z-n )
+            if [ "$LAST_VERSION" != "$IRCD_VERSION" ] ; then
+                echo "You specified the no-questions-asked configure, but the version"
+                echo "of Config which created your Options file was LAST_VERSION,"
+                echo "And the current version is $IRCD_VERSION."
+                echo " "
+                echo "Please read the prompts carefully since some of them may have" 
+                echo "changed."
+                echo " "
+            else
+                AUTO_CONFIG=Yes
+            fi ;;
+        * ) ;;
+    esac
+    ;;
+  * ) ;;
+esac
+
+if [ "$LAST_VERSION" != "$IRCD_VERSION" ] ; then
+    if [ -r CHANGES ] ; then
+       more CHANGES
+       echo $n "[Enter to begin]"
+       read cc
+   fi
+fi
+
+clear
+
+#
+# Take a wild stab at the OS, and take reasonable defaults for each
+#
+# Include zlib as default for server compression, 
+# if they don't want it, they have a backspace key.
+# patched: 5/30/00 Paul de Regt [Ax0n] (deregt@accessnw.net)
+
+OS=`uname -a`
+case "$OS" in
+    *NetBSD*)
+       DEFCFLAGS="$DEFOPT"
+       DEFLIBS="-lcrypt -lz"
+       OSNAME="NetBSD"
+       ;;
+    *FreeBSD*)
+       DEFCFLAGS="$DEFOPT"
+       DEFLIBS="-lcrypt -lz"
+       OSNAME="FreeBSD"
+       ;;
+    *SCO_SV*)
+       DEFCFLAGS="$DEFOPT -DSCOUNIX"
+       DEFLIBS="-lsocket -lz"
+       OSNAME="SCO Openserver"
+       NEEDSKIPNAME="Yep"
+       ;;
+    *OSF1*alpha*)
+       DEFCFLAGS="$DEFOPT"
+       DEFLIBS="-lz"
+       OSNAME="OSF/1 or Digital Unix"
+       ;;
+    *SunOS*4.*)
+       DEFCFLAGS="$DEFOPT"
+       DEFLIBS="-lz"
+       OSNAME="SunOS 4.x"
+        LOCALD=undef
+       ;;
+    *SunOS*5.*)
+       DEFCFLAGS="$DEFOPT -DSOL20"
+       DEFLIBS="-lsocket -lnsl -lresolv -lz"
+       OSNAME="Solaris 2.x (or SunOS 5.x)"
+        LOCALD=undef
+       ;;
+    *Darwin*)
+       DEFCFLAGS="$DEFOPT"
+       DEFLIBS="-lz"
+       OSNAME="Darwin"
+       ;;
+    *Linux*)
+# New, cleaner detection code. -8/15/99 Sean-Paul Rees (sean@dreamfire.net)
+       cat > $TMP << __EOF__
+#include <stdio.h>
+main()
+{
+       #ifdef __GLIBC__
+       printf("%d_%d\n", __GLIBC__, __GLIBC_MINOR__);
+       #else
+       printf("0\n");
+       #endif
+
+       exit(0);
+}
+__EOF__
+#
+# Yes, I know we don't have $CCPATH and $XCFLAGS and such set up yet,
+# but below should work on just about any Linux system
+#
+# Glibc version now correctly detected (major.minor)
+# patched: 8/15/99 Sean-Paul Rees (sean@dreamfire.net)
+#
+       cc $TMP -o $EXEC >/dev/null 2>&1
+       GLIBCVERS=`$EXEC`
+
+       case $GLIBCVERS in
+           0)  # This is Linux without Glibc.
+               OSNAME="Linux"
+                DEFLIBS="-lz"
+                ;;
+            2*) # Linux with Glibc 2.x
+                OSNAME="Linux (Glibc $GLIBCVERS)"
+                DEFLIBS="-lcrypt -lresolv -lz"
+                ;;
+        esac   
+               
+       DEFCFLAGS="$DEFOPT"
+       ;;
+    *HP-UX*.09.*)
+       DEFCFLAGS="$DEFOPT"
+       DEFLIBS="-lz"
+       OSNAME="HPUX 9.x"
+       ;;
+    *HP-UX*.10.*)
+       DEFCFLAGS="$DEFOPT"
+       DEFLIBS="-lz"
+       OSNAME="HPUX 10.x"
+       ;;
+esac
+
+echo " "
+echo Operating System: $OSNAME
+echo " "
+       
+# Create Makefile if it doesn't exist...
+if [ ! -f Makefile ] ; then
+  cp Makefile.dist Makefile
+fi
+cat << __EOF__
+Welcome to autoconfigure for the Serenity-IRC server version $IRCD_VERSION.
+
+Config will generate a system-specific $SETUP file, a top
+level Makefile and a persistant options file named
+$OPTIONS
+
+Enter "none" at any prompt to effect a null entry.
+
+__EOF__
+
+runonce=""
+FOO=`egrep "^CC=" Makefile 2>/dev/null | sed -e 's/^[^=]*[     ]*=\(.*\)/\1/'`
+while [ -z "$CCPATH" ] ; do
+       MYP=`echo "$PATH" | sed -e 's/:/ /g'`
+       echo "Which compiler do you use, gcc or cc?"
+       echo $n "[$FOO] -> $c"
+       if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+           read cc
+       else
+           cc=""
+       fi
+       if [ -z "$cc" ] ; then
+               cc=$FOO
+               CCPATH=$FOO
+       elif [ -f $cc ] ; then
+               CCPATH=$cc
+       else
+               for i in $MYP; do
+                       if [ -f $i/$cc -a -z "$CCPATH" ] ; then
+                               CCPATH=$i/$cc
+                       fi
+               done
+       fi
+       if [ -z "$CCPATH" ]; then runonce="Yes"; fi
+done
+if [ "$FOO" != "$cc" ] ; then
+       MYP=`echo "$CCPATH" | sed -e 's@/@ @g'`
+       set $MYP
+       if [ $2 ] ; then
+               while [ $2 ] ; do
+                       shift
+               done
+       fi
+       if [ $1 = "gcc" ] ; then
+               CCPATH="$CCPATH"
+       fi
+fi
+echo "Compiler selected: $CCPATH"
+echo " "
+# Check it out
+cat > $TMP <<__EOF__
+main() {}
+__EOF__
+$CCPATH $TMP -o $EXEC >/dev/null 2>&1
+if [ ! -f $EXEC ] ; then
+        echo "You don't have $CCPATH or it's broken."
+        exit 1
+fi
+# Fix Makefile
+#
+$RM -f Makefile.tmp
+sed -e "s@^CC=\(.*\)@CC=$CCPATH@" Makefile > Makefile.tmp
+cp Makefile.tmp Makefile
+$RM -f Makefile.tmp
+#
+echo "Enter additional flags to give to $CCPATH"
+FOO=`egrep "^XCFLAGS=" Makefile 2>/dev/null | sed -e 's/^[^=]*=[        ]*\(.*\)/\1/'`
+if [ -z "$FOO" ] ; then
+    echo "I recommend $DEFCFLAGS"
+    FOO="$DEFCFLAGS"
+fi
+echo $n "[$FOO] -> $c"
+if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+    read cc
+else
+    cc=""
+fi
+if [ -z "$cc" ] ; then
+       cc="$FOO"
+fi
+if [ "$cc" = "none" ] ; then
+       cc=''
+fi
+XCFLAGS=$cc
+# Fix Makefile
+#
+$RM -f Makefile.tmp
+sed -e "s@^XCFLAGS=\(.*\)@XCFLAGS=$XCFLAGS@" Makefile > Makefile.tmp
+cp Makefile.tmp Makefile
+$RM -f Makefile.tmp
+#
+cat <<__EOF__
+
+If you need to use any extra libraries when compiling the server,
+please tell me now and please include all the -l and -L flags.
+
+You should use the recommended value unless you have a compelling reason
+not to...
+__EOF__
+LIBS=`egrep "^IRCDLIBS=" Makefile 2>/dev/null | sed -e 's/^[^=]*=\(.*\)/\1/' | tr -d "\012"`
+if [ -z "$LIBS" ] ; then
+    echo "I suggest: $DEFLIBS"
+    LIBS="$DEFLIBS"
+fi
+echo $n "[$LIBS] -> $c"
+if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+    read cc
+else
+    cc=""
+fi
+if [ -z "$cc" ] ; then
+       cc="$LIBS"
+fi
+if [ "$cc" = "none" ] ; then
+       cc=''
+fi
+LIBS=$cc
+# Fix Makefile
+#
+$RM -f Makefile.tmp
+sed -e "s@^IRCDLIBS=\(.*\)@IRCDLIBS=$LIBS@" Makefile > Makefile.tmp
+cp Makefile.tmp Makefile
+$RM -f Makefile.tmp
+#
+COMP="$CCPATH $XCFLAGS $TMP -o $EXEC $LIBS"
+#
+echo 'Checking out /usr/include'
+echo $n "Looking for /usr/include/stdlib.h...$c"
+if [ -r /usr/include/stdlib.h ] ; then
+       STDLIBH=define
+       echo  'found'
+else
+       echo 'not found'
+fi
+echo $n "Looking for stddef.h...$c"
+if [ -r /usr/include/stddef.h ] ; then
+        STDDEFH=define
+       echo  'found'
+else
+       echo 'not found'
+fi
+echo $n "Looking for /usr/include/sys/syslog.h...$c"
+if [ -r /usr/include/sys/syslog.h ] ; then
+       SYSSYSLOGH=define
+       echo  'found'
+else
+       echo 'not found'
+fi
+
+echo $n "Looking for /usr/include/sys/param.h...$c"
+if [ -r /usr/include/sys/param.h ] ; then
+       PARAMH=define
+       echo  'found'
+else
+       echo 'not found'
+fi
+echo $n "Looking for /usr/include/unistd.h...$c"
+if [ -r /usr/include/unistd.h ] ; then
+       UNISTDH=define
+       echo  'found'
+else
+       echo 'not found'
+fi
+echo $n "Looking for /usr/include/string.h...$c"
+if [ -r /usr/include/string.h ] ; then
+       STRINGH=define
+       echo  'found'
+else
+       echo 'not found'
+fi
+echo $n "Looking for /usr/include/strings.h...$c"
+if [ -r /usr/include/strings.h ] ; then
+       STRINGSH=define
+       echo  'found'
+else
+       echo 'not found'
+fi
+echo $n "Looking for /usr/include/sys/rusage.h...$c"
+if [ -r /usr/include/sys/rusage.h ] ; then
+       RUSAGEH=define
+       echo  'found'
+else
+       echo 'not found (good)'
+fi
+#
+# to b or not to b
+#
+echo $n "Searching for bcopy/bzero/bcmp...$c"
+cat > $TMP <<__EOF__
+main()
+{
+       char    a[3], b[3];
+       bzero(b,3);
+       bcopy(a,b,3);
+       (void)bcmp(a,b,3);
+       exit(0);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+       echo "located bcopy/bzero/bcmp"
+       HAVE_BZERO=define
+else
+       echo "No bcopy/bzero/bcmp found"
+       HAVE_BZERO=undef
+fi
+echo " "
+echo $n "Which one, gettimeofday, or lrand48..$c"
+cat > $TMP <<__EOF__
+#include <stdio.h>
+#include <sys/time.h>
+main()
+ {
+   struct timeval tv;
+   (void) gettimeofday(&tv, NULL);
+ }
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+       echo "gettimeofday found"
+       GETTIMEOFDAY=define
+else
+       echo "No gettimeofday. Trying lrand48."
+cat > $TMP <<__EOF__
+main()
+{
+   int a;
+   a=lrand48();
+}
+__EOF__
+       $COMP >/dev/null 2>&1
+       if [ $? -eq 0 ] ; then
+               echo "lrand48 found"
+               LRAND48=define
+       fi
+fi
+#
+# check for non-blocking fd style available..
+#
+echo 'Checking for POSIX/BSD/SYSV'
+if [ -f $TMP -o -d $TMP ] ; then
+       $RM -f $TMP
+fi
+cat > $PLATE <<__EOF__
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+alarmed()
+{
+       exit(1);
+}
+main()
+{
+       char b[12], x[32];
+       int f, l = sizeof(x);
+       f = socket(AF_INET, SOCK_DGRAM, 0);
+       if (f >= 0 && !(fcntl(f, F_SETFL, BLOCKING))) {
+               signal(SIGALRM, alarmed);
+               alarm(3);
+               recvfrom(f, b, 12, 0, x, &l);
+               alarm(0);
+               exit(0);
+       }
+       exit(1);
+}
+__EOF__
+sed -e 's/BLOCKING/O_NONBLOCK/' $PLATE > $TMP
+$COMP >/dev/null 2>&1
+if [ 0 -eq $? ] ; then
+       $EXEC
+fi
+if [ 0 -eq $? ] ; then
+       POSIX_NBLOCK=define
+else
+       echo 'O_NONBLOCK not present/working in fcntl.h or sys/ioctl.h'
+       if [ -f $TMP -o -d $TMP ] ; then
+               $RM -f $TMP $EXEC;
+       fi
+       sed -e 's/BLOCKING/O_NDELAY/' $PLATE > $TMP
+       $COMP >/dev/null 2>&1
+       if [ 0 -eq $? ] ; then
+               $EXEC
+       fi
+       if [ 0 -eq $? ] ; then
+               BSD_NBLOCK=define
+       else
+               echo 'O_NDELAY not present/working in fcntl.h or sys/ioctl.h'
+               if [ -f $TMP -o -d $TMP ] ; then
+                       $RM -f $TMP $EXEC;
+               fi
+               sed -e 's/BLOCKING/FIONBIO/' $PLATE > $TMP
+               $COMP >/dev/null 2>&1
+               if [ 0 -eq $? ] ; then
+                       echo 'ERROR: FIONBIO not found!  No option found!'
+               else
+                       echo "Using SysV blocking"
+               fi
+       fi
+fi
+$RM -f $TMP $PLATE $EXEC
+echo "Blocking selected: $BLOCK";
+#
+# reliable signals ?
+#
+echo 'Looking for reliable signals...'
+echo "Checking if you have 'action from POSIX..."
+cat > $TMP <<__EOF__
+#include <signal.h>
+
+main()
+{      /* poor replacement for NULL but who cares here ? */
+       sigaction(SIGTERM, (struct sigaction *)0L, (struct sigaction *)0L);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+       echo "'action from POSIX found"
+       POSIX_SIGNAL=define
+else
+       $RM -f $EXEC $TMP
+       cat > $TMP <<__EOF__
+#include <signal.h>
+int    calls = 0;
+void   handler()
+{
+       if (calls)
+               return;
+       calls++;
+       kill(getpid(), SIGTERM);
+       sleep(1);
+}
+main()
+{
+       signal(SIGTERM, handler);
+       kill(getpid(), SIGTERM);
+       exit (0);
+}
+__EOF__
+       echo $n "No, but you have...$c"
+       $COMP >/dev/null 2>&1
+       $EXEC
+       if [ $? -eq 0 ] ; then
+               echo 'reliable signals found'
+               BSD_SIGNAL=define
+       else
+               echo "unreliable SYSV detected"
+       fi
+fi
+$RM -f $EXEC $TMP
+#
+echo 'Now those strings libraries...'
+cat > $TMP <<__EOF__
+#$STRINGH STRINGH
+#$STRINGSH STRINGSH
+#ifdef STRINGH
+#include <string.h>
+#endif
+#ifdef STRINGSH
+#include <strings.h>
+#endif
+main()
+{
+       char *s = index("foo", 'o');
+       exit(0);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+       echo "you have index()!"
+        HINDEX=define
+else
+       echo "I guess there is a strchr() out there somewhere..."
+fi
+$RM -f $EXEC $TMP
+#
+# getrusage or times ?
+#
+echo $n "One for debugging, mainly, getrusage(2) or times(2)...$c"
+cat > $TMP <<__EOF__
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+main()
+{
+       struct  rusage  rus;
+       (void)getrusage(RUSAGE_SELF, &rus);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+       GETRUSAGE_2=define
+       echo "getrusage()"
+else
+       $RM -f $EXEC $TMP
+       cat > $TMP <<__EOF__
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/times.h>
+main()
+{
+       struct  tms     tmsbuf;
+       (void)times(&tmsbuf);
+}
+__EOF__
+       $COMP >/dev/null 2>&1
+       if [ $? -eq 0 ] ; then
+               TIMES_2=define
+               echo "times()"
+       else
+               echo "couldn't get either ?!"
+       fi
+fi
+#
+# what do we need that isn't here already ?
+#
+echo -n "Looking for strcasecmp..."
+cat > $TMP <<__EOF__
+#$STRINGH STRINGH
+#$STRINGSH STRINGSH
+#ifdef STRINGH
+#include <string.h>
+#endif
+#ifdef STRINGSH
+#include <strings.h>
+#endif
+main()
+{
+        char *s = strcasecmp('a', 'a');
+        exit(0);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+        echo "strcasecmp found"
+        STRCASECMP=define
+else
+        echo "No strcasecmp found"
+fi
+#$RM -f $EXEC $TMP
+echo "Checking for additional components..."
+echo $n "Searching...$c"
+cat > $TMP <<__EOF__
+main()
+{
+       unsigned long foo;
+
+       char  *s = strtoul("0x12345", &foo, 16);
+       exit(0);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+    echo $n " strtoul$c"
+    $RM -f Makefile.tmp
+    sed -e "s@^STRTOUL=\(.*\)@STRTOUL=strtoul.o@" Makefile > Makefile.tmp
+    cp Makefile.tmp Makefile
+    $RM -f Makefile.tmp
+else
+    $RM -f Makefile.tmp
+    sed -e "s@^STRTOUL=\(.*\)@STRTOUL=@" Makefile > Makefile.tmp
+    cp Makefile.tmp Makefile
+    $RM -f Makefile.tmp
+fi
+$RM -f $EXEC $TMP
+cat > $TMP <<__EOF__
+main()
+{
+       char  *s = strerror(0);
+       exit(0);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+       echo $n " strerror$c"
+else
+       STRERROR=define
+fi
+$RM -f $EXEC $TMP
+cat > $TMP <<__EOF__
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+main()
+{
+       dn_skipname("","");
+       exit(0);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+# dn_skipname passes the small test but won't resolve when ircd is linked.
+# Hmmm...
+if [ $? -ne 0 -o "$OSNAME" = "Linux RedHat 5.0" -o -n "$NEEDSKIPNAME" -o "$GLIBCCHECK" = "define" ] ; then
+    echo $n " dn_skipname$c"
+    $RM -f Makefile.tmp
+    sed -e "s@^RES=\(.*\)@RES=res_skipname.o@" Makefile > Makefile.tmp
+    cp Makefile.tmp Makefile
+    $RM -f Makefile.tmp
+else
+    $RM -f Makefile.tmp
+    sed -e "s@^RES=\(.*\)@RES=res_init.o@" Makefile > Makefile.tmp
+    cp Makefile.tmp Makefile
+    $RM -f Makefile.tmp
+fi
+$RM -f $EXEC $TMP
+cat > $TMP <<__EOF__
+#include <sys/types.h>
+main()
+{
+    u_int32_t foo;
+    exit(0);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+    echo $n " u_int32_t$c"
+    $RM -f Makefile.tmp
+    sed -e "s@^NEED_U_INT32_T=\(.*\)@NEED_U_INT32_T=-DNEED_U_INT32_T@" Makefile > Makefile.tmp
+    cp Makefile.tmp Makefile
+    $RM -f Makefile.tmp
+else
+    $RM -f Makefile.tmp
+    sed -e "s@^NEED_U_INT32_T=\(.*\)@NEED_U_INT32_T=@" Makefile > Makefile.tmp
+    cp Makefile.tmp Makefile
+    $RM -f Makefile.tmp
+fi
+$RM -f $EXEC $TMP
+cat > $TMP <<__EOF__
+#$STRINGH STRINGH
+#$STRINGSH STRINGSH
+#ifdef STRINGH
+#include <string.h>
+#endif
+#ifdef STRINGSH
+#include <strings.h>
+#endif
+main()
+{
+       char  t[] = "a", **p = NULL, *s = strtoken(&p, t, ",");
+       if (!strcmp(t, s))
+               exit(0);
+       exit(1);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+       echo $n " strtoken$c"
+       STRTOKEN=undef
+else
+       $EXEC
+       if [ $? -ne 0 ] ; then
+               echo $n " strtoken$c"
+               STRTOKEN=undef
+       fi
+fi
+$RM -f $EXEC $TMP
+cat > $TMP <<__EOF__
+#$STRINGH STRINGH
+#$STRINGSH STRINGSH
+#ifdef STRINGH
+#include <string.h>
+#endif
+#ifdef STRINGSH
+#include <strings.h>
+#endif
+main()
+{
+       char  t[] = "a", *s = strtok(t, ",");
+       if (!strcmp(t, s))
+               exit(0);
+       exit(1);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+       echo $n " strtok$c"
+       STRTOK=undef
+else
+       $EXEC
+       if [ $? -ne 0 ] ; then
+               echo $n " strtok$c"
+               STRTOK=undef
+       fi
+fi
+$RM -f $EXEC $TMP
+cat > $TMP << __EOF__
+#include <sys/types.h>
+#include <netinet/in.h>
+main()
+{
+       struct  in_addr in;
+       (void)inet_addr("1.2.3.4");
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+       echo $n " inet_addr$c"
+       INETADDR=undef
+fi
+$RM -f $EXEC $TMP
+cat > $TMP << __EOF__
+#include <sys/types.h>
+#include <netinet/in.h>
+main()
+{
+       struct  in_addr in;
+       in.s_addr = 0x12345678;
+       (void)inet_ntoa(in);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+       echo $n " inet_ntoa$c"
+fi
+$RM -f $EXEC $TMP
+cat > $TMP << __EOF__
+#include <sys/types.h>
+#include <netinet/in.h>
+main()
+{
+       struct  in_addr in;
+       in.s_addr = 0x87654321;
+       (void)inet_netof(in);
+}
+__EOF__
+$COMP >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+       echo $n " inet_netof$c"
+       INETNETOF=undef
+fi
+$RM -f $EXEC $TMP
+echo " "
+
+$RM -f $EXEC $TMP $PLATE
+cat > $SETUP  <<__EOF__
+#define IRCD_VERSION "$IRCD_VERSION"
+#$PARAMH       HAVE_PARAM_H
+#$UNISTDH      HAVE_UNISTD_H
+#$STRINGH      HAVE_STRING_H
+#$STRINGSH     HAVE_STRINGS_H
+#$STDLIBH      HAVE_STDLIB_H
+#$STDDEFH      HAVE_STDDEF_H
+#$SYSSYSLOGH   HAVE_SYSSYSLOG_H
+#$HINDEX       HAVE_INDEX
+#$STRERROR     HAVE_STRERROR
+#$STRTOKEN     HAVE_STRTOKEN
+#$STRTOK       HAVE_STRTOK
+#$INETADDR     HAVE_INET_ADDR
+#$INETNTOA     HAVE_INET_NTOA
+#$INETNETOF    HAVE_INET_NETOF
+#$GETTIMEOFDAY GETTIMEOFDAY
+#$LRAND48      HAVE_LRAND48
+#$STRCASECMP   HAVE_STRCASECMP
+#$HAVE_BZERO   HAVE_BZERO
+#$POSIX_NBLOCK HAVE_POSIX_NBLOCK
+#$BSD_NBLOCK   HAVE_BSD_NBLOCK
+#$POSIX_SIGNAL HAVE_POSIX_SIGNALS
+#$BSD_SIGNAL   HAVE_BSD_SIGNALS
+#$TIMES_2      HAVE_TIMES_2
+#$GETRUSAGE_2  HAVE_GETRUSAGE_2
+__EOF__
+
+if [ $? -ne 0 -o "$OSNAME" = "Darwin" ] ; then  
+echo "#define __Darwin__" >> $SETUP
+fi
+
+#
+# Now, get site specific options.
+#
+
+FOO=""
+runonce=""
+while [ -z "$FOO" ] ; do
+    FOO="$KLINE_ADDRESS"
+    echo " "
+    echo "What is the contact address for connect problems due to the"
+    echo "user being K:lined, shown to the user when they attempt to"
+    echo "connect?  This should be a valid email address."
+    echo " "
+    echo "For all servers, note that this message is displayed when"
+    echo "the user is affected by a local K:line or k:line.  With"
+    echo "Services-based autokills, the message is set up automatically"
+    echo "by Services to ask the user to email kline@serenity-irc.net.  It is"
+    echo "recommended that you set this up to give a valid email address"
+    echo "for the server's admin, not kline@serenity-irc.net."
+    echo $n "[$FOO] -> $c"
+    if [ -z "$AUTO_CONFIG" -o -n "$runonce" -o -z "$FOO" ] ; then
+       read cc
+    else
+       cc=""
+       runonce=Yes
+    fi
+    if [ -z "$cc" ] ; then
+       cc=$FOO
+    fi
+    case "$cc" in
+       *@*.*)
+           KLINE_ADDRESS=$cc
+           FOO="$KLINE_ADDRESS"
+           ;;
+       *)
+           echo " "
+           echo "Read the instructions and try again...  You did not enter a"
+           echo "proper email address (user@host.domain)."
+           FOO=""
+           ;;
+    esac
+done
+
+FOO=""
+runonce=""
+while [ -z "$FOO" ] ; do
+    FOO="$DPATH"
+    echo ""
+    echo "What directory are all the server configuration files in?"
+    echo $n "[$FOO] -> $c"
+    if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+       read cc
+    else
+       cc=""
+       runonce=Yes
+    fi
+    if [ -z "$cc" ] ; then
+       cc=$FOO
+    fi
+done
+DPATH=$cc
+
+FOO=""
+runonce=""
+while [ -z "$FOO" ] ; do
+    FOO="$DPATH/ircd"
+    echo ""
+    echo "What is the explicit path to where the ircd binary will be"
+    echo "installed?  This should point to a file, not a directory"
+    echo $n "[$FOO] -> $c"
+    if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+       read cc
+    else
+       cc=""
+       runonce=Yes
+    fi
+    if [ -z "$cc" ] ; then
+       cc=$FOO
+    fi
+done
+SPATH=$cc
+
+FOO=""
+runonce=""
+while [ -z "$FOO" ] ; do
+    echo ""
+    echo "What is your local domain name?  This is used for /stats w to"
+    echo "report local clients vs. remote clients."
+    if [ "$LOCALD" = "define" ] ; then
+      echo "Auto-probing local host..."
+      HOSTNAME=`hostname`
+      if [ $? -eq 0 ]; then
+          TEMPHOST="${HOSTNAME#*.}"
+        if [ "${#HOSTNAME}" -ne "${#TEMPHOST}" ]; then
+          FOO="${TEMPHOST}"
+        else 
+          echo ""
+          echo "This guess probably isn't right:"
+          FOO="${HOSTNAME}"
+        fi
+      else
+        FOO="$DOMAINNAME"
+      fi
+    else
+      echo "Skipping auto-probe. (Bad OS: $OSNAME)"
+      FOO="configure.this"
+    fi
+    echo $n "[$FOO] -> $c"
+    if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+       read cc
+    else
+       cc=""
+       runonce=Yes
+    fi
+    if [ -z "$cc" ] ; then
+       cc=$FOO
+    fi
+    FOO=$cc
+done
+DOMAINNAME=$cc
+   
+FOO=""
+runonce=""
+while [ -z "$FOO" ] ; do
+    if [ -n "$HUB" ] ; then
+       FOO="Yes"
+    else
+       FOO="No"
+    fi
+    echo ""
+    echo "Are you running as a HUB server?"
+    echo $n "[$FOO] -> $c"
+    if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+       read cc
+    else
+       cc=""
+       runonce=Yes
+    fi
+    if [ -z "$cc" ] ; then
+       cc=$FOO
+    fi
+    case "$cc" in
+       [Yy]*)
+           HUB="1"
+           ;;
+       [Nn]*)
+           HUB=""
+           ;;
+       *)
+           echo ""
+           echo "You need to enter either Yes or No here..."
+           echo ""
+           FOO=""
+           ;;
+    esac
+done
+
+FOO=""
+runonce=""
+while [ -z "$FOO" ] ; do
+    if [ -n "$CRYPT_OPER_PASSWORD" ] ; then
+       FOO="Yes"
+    else
+       FOO="No"
+    fi
+    echo ""
+    echo "Do you use encrypted operator passwords?"
+    echo $n "[$FOO] -> $c"
+    if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+       read cc
+    else
+       cc=""
+       runonce=Yes
+    fi
+    if [ -z "$cc" ] ; then
+       cc=$FOO
+    fi
+    case "$cc" in
+       [Yy]*)
+           CRYPT_OPER_PASSWORD="1"
+           ;;
+       [Nn]*)
+           CRYPT_OPER_PASSWORD=""
+           ;;
+       *)
+           echo ""
+           echo "You need to enter either Yes or No here..."
+           echo ""
+           FOO=""
+           ;;
+    esac
+done
+CRYPT_ILINE_PASSWORD=$CRYPT_OPER_PASSWORD
+
+FOO=""
+runonce=""
+while [ -z "$FOO" ] ; do
+    if [ -n "$ZIP_LINKS" ] ; then
+       FOO="Yes"
+    else
+       FOO="No"
+    fi
+    echo ""
+    echo "Do you want to enable server compression?"
+    echo $n "[$FOO] -> $c"
+    if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+       read cc
+    else
+       cc=""
+       runonce=Yes
+    fi
+    if [ -z "$cc" ] ; then
+       cc=$FOO
+    fi
+    case "$cc" in
+       [Yy]*)
+           ZIP_LINKS="1"
+           ;;
+       [Nn]*)
+           ZIP_LINKS=""
+           ;;
+       *)
+           echo ""
+           echo "You need to enter either Yes or No here..."
+           echo ""
+           FOO=""
+           ;;
+    esac
+done
+
+FOO=""
+runonce=""
+while [ -z "$FOO" ] ; do
+    FOO="$MAXCONNECTIONS"
+    echo ""
+    echo "How many file descriptors (or sockets) can the irc server use?"
+    echo $n "[$FOO] -> $c"
+    if [ -z "$AUTO_CONFIG" -o -n "$runonce" ] ; then
+       read cc
+    else
+       cc=""
+       runonce=Yes
+    fi
+    if [ -z "$cc" ] ; then
+       cc=$FOO
+    fi
+    case "$cc" in
+       [1-9][0-9][0-9]*)
+           MAXCONNECTIONS="$cc"
+           ;;
+       *)
+           echo ""
+           echo "You need to enter a number here, greater or equal to 100."
+           echo ""
+           FOO=""
+           ;;
+    esac
+done
+
+#
+# check FD_SETSIZE and override if needed.
+#
+
+cat > $TMP <<__EOF__
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifndef FD_SETSIZE
+#define FD_SETSIZE -1
+#endif
+
+/*
+ * Prints "notdef" if FD_SETSIZE is undefined,
+ *        "ok"     if FD_SETSIZE is at least as large as provided on the
+ *                 compile command line (-DMAXCONNECTIONS=1234)
+ *        "###"    if it is less.  (### is the FD_SETSIZE value)
+ */
+int
+main(int argc, char *argv[])
+{
+       if (FD_SETSIZE == -1)
+               printf("notdef\n");
+       else if (FD_SETSIZE >= MAXCONNECTIONS)
+               printf("ok\n");
+       else
+               printf("%d\n", FD_SETSIZE);
+
+       return 0;
+}
+__EOF__
+$COMP "-DMAXCONNECTIONS=$MAXCONNECTIONS" >/dev/null 2>&1
+if [ $? -ne 0 ] ; then
+    echo " "
+    echo "I could not derrive what your system allows for the maximum number"
+    echo "of connections becuase the test program did not compile."
+    echo " "
+    FD_SETSIZE=""
+else
+    fd_setsize_ok=`$EXEC`
+    case $fd_setsize_ok in
+       notdef)
+           echo " "
+           echo "I could not derrive what your system allows for the maximum"
+           echo "number of connections because the test program did not find"
+           echo "a system-supplied value for FD_SETSIZE.  Assuming it is"
+           echo "defined correctly but the test program cannot find it."
+           echo " "
+           FD_SETSIZE=""
+           ;;
+       ok)
+           echo " "
+           echo "Your system-supplied value for FD_SETSIZE is large enough"
+           echo "for ircd to leave it untouched."
+           echo " "
+           FD_SETSIZE=""
+           ;;
+       *)
+           echo " "
+           echo "Your system-supplied value for FD_SETSIZE is $fd_setsize_ok"
+           echo "but you requested $MAXCONNECTIONS for ircd.  FD_SETSIZE will"
+           echo "be overridden using -DFD_SETSIZE=$MAXCONNECTIONS when"
+           echo "compiling ircd."
+           echo " "
+           FD_SETSIZE=$MAXCONNECTIONS
+           ;;
+    esac
+fi
+
+if [ -n "$FD_SETSIZE" ] ; then
+    $RM -f Makefile.tmp
+    sed -e "s@^FD_SETSIZE=\(.*\)@FD_SETSIZE=-DFD_SETSIZE=$FD_SETSIZE@" Makefile > Makefile.tmp
+    cp Makefile.tmp Makefile
+    $RM -f Makefile.tmp
+else
+    $RM -f Makefile.tmp
+    sed -e "s@^FD_SETSIZE=\(.*\)@FD_SETSIZE=@" Makefile > Makefile.tmp
+    cp Makefile.tmp Makefile
+    $RM -f Makefile.tmp
+fi
+
+$RM -f $EXEC $TMP
+
+
+cat <<__EOF__
+
+The file "$OPTIONS" was either created or rewritten to contain your
+answers to the above questions.
+
+This file is automatically generated and will be updated each time you
+run Config.  You should retain a copy of this to help migrate to future
+versions of the server with ease.
+
+__EOF__
+
+#
+# continue the $SETUP file
+#
+cat >> $SETUP << __EOF__
+#define DPATH "$DPATH"
+#define SPATH "$SPATH"
+#define MAXCONNECTIONS $MAXCONNECTIONS
+__EOF__
+if [ -n "$HUB" ] ; then
+    echo "#define HUB" >> $SETUP 
+else
+    echo "#undef HUB" >> $SETUP
+fi
+if [ -n "$CRYPT_OPER_PASSWORD" ] ; then
+    echo "#define CRYPT_OPER_PASSWORD 1" >> $SETUP
+else
+    echo "#undef CRYPT_OPER_PASSWORD" >> $SETUP
+fi
+if [ -n "$CRYPT_ILINE_PASSWORD" ] ; then
+    echo "#define CRYPT_ILINE_PASSWORD 1" >> $SETUP
+else
+    echo "#undef CRYPT_ILINE_PASSWORD" >> $SETUP
+fi
+if [ -n "$CONTACT_URL" ] ; then
+    echo "#define CONTACT_URL \"$CONTACT_URL\"" >> $SETUP
+fi
+if [ -n "$CONTACT_EMAIL" ] ; then
+    echo "#define CONTACT_EMAIL \"$CONTACT_EMAIL\"" >> $SETUP
+fi
+if [ -n "$SERVICES_NAME" ] ; then
+    echo "#define SERVICES_NAME \"$SERVICES_NAME\"" >> $SETUP
+fi
+if [ -n "$KLINE_ADDRESS" ] ; then
+    echo "#define KLINE_ADDRESS \"$KLINE_ADDRESS\"" >> $SETUP
+fi
+if [ -n "$DOMAINNAME" ] ; then
+    echo "#define DOMAINNAME \"$DOMAINNAME\"" >> $SETUP
+else
+    echo "#undef DOMAINNAME" >> $SETUP
+fi
+if [ -n "$ZIP_LINKS" ] ; then
+    echo "#define ZIP_LINKS    1" >> $SETUP
+else 
+    echo "#undef ZIP_LINKS" >> $SETUP
+fi
+
+#
+# Create file for make install
+#
+rm -f $MAKEINST
+cat > $MAKEINST << __EOF__
+
+if [ ! -d $DPATH ] ; then
+    mkdir $DPATH
+fi
+
+if [ -f $SPATH ] ; then
+    cp $SPATH $SPATH.old
+fi
+
+if [ -f $DPATH/ircd.pid ] ; then
+    kill -9 \`cat $DPATH/ircd.pid \` 
+fi
+
+cp src/ircd $SPATH
+$SPATH
+__EOF__
+#
+chmod 711 $MAKEINST
+#
+# create the persistant file
+#
+rm -f $OPTIONS
+cat > $OPTIONS << __EOF__
+#
+# VERSION: $IRCD_VERSION
+# DATE:    $CONF_DATE
+#
+# This file is automatically generated and will be updated each time you
+# run Config.  You should retain a copy of this to help migrate to future
+# versions of the server with ease.
+#
+LAST_VERSION="$IRCD_VERSION"
+CONTACT_URL="$CONTACT_URL"
+CONTACT_EMAIL="$CONTACT_EMAIL"
+SERVICES_NAME="$SERVICES_NAME"
+KLINE_ADDRESS="$KLINE_ADDRESS"
+ZIP_LINKS="$ZIP_LINKS"
+DPATH="$DPATH"
+SPATH="$SPATH"
+HUB="$HUB"
+PARANOIA="$PARANOIA"
+CRYPT_OPER_PASSWORD="$CRYPT_OPER_PASSWORD"
+MAXCONNECTIONS="$MAXCONNECTIONS"
+DOMAINNAME="$DOMAINNAME"
+__EOF__
+
+cat <<__EOF__
+
+ Config is complete.
+
+ Before compiling, check the contents of include/config.h for any strange
+ things you usually set.  If you want them to be included in the future,
+ ask them to be moved to the setup.h file on remmy@serenity-irc.net.
+
+__EOF__
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..245fbff
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,169 @@
+Installation Procedures for the StarChat IRC Server:
+=======================================================================
+by Alexei Kosut (Lefler) <lefler@dal.net>
+with changes for dal4.3.6 by Mikko Hänninen (Wizzu) <wizzu@dal.net>
+for dal4.3.6
+with changes for dal4.4.11 by Cabal95 (cabal95@dal.net)
+with changes for Star4.10.Spire by GZ (gz@starchat.net)
+=======================================================================
+
+This document describes how to install ircd, the unix daemon that acts as 
+an IRC server. Specifically, for the StarChat IRC Network, though some 
+information may be applicable to other networks as well.
+
+For more information on StarChat or this server, please see our WWW page at 
+<http://www.starchat.net/>, or contact us at sc@starchat.net.
+
+For help with this ircd, try joining #ircd on StarChat. There you will find a friendly
+staff who can help you with most common problems affecting the ircd. If they can't help,
+they'll probably be able to find someone who will. :-)
+
+The StarChat server is available from ftp://utopia.starchat.net/pub/, along with the 
+latest copy of this document.
+
+
+=======================================================================
+This version of the StarChat IRC Server is known to compile on the following
+platforms, and with the following compilers.  If you wish to add to this
+list, send the relevant information to us.
+
+OS and Version      Compiler and Version     Comments
+------------------- ------------------------ -------------------------------
+NetBSD 1.2B         gcc 2.7.2
+
+FreeBSD 2.1.0       gcc 2.6.3                Do NOT use crypt at all...
+
+SunOS 4.1.4         gcc 2.7-96q1 (Cygnus)
+
+Solaris 2.4         gcc 2.7-96q1 (Cygnus)    (SunOS 5.4)
+
+Solaris 2.5         SunWorks Pro C           (SunOS 5.5.1)
+                    cc: SC4.0 18 Oct 1995 C 4.0
+
+Digital Unix 3.2    gcc 2.7-96q3 (Cygnus)
+
+Linux 2.0.24        gcc 2.7.2.1               should now also compile
+                                              under Red Hat 5.0
+
+HPUX 9.01           gcc 2.6.3
+
+HPUX 10.01          gcc 2.7-96q3 (Cygnus)
+
+=======================================================================
+Unpacking the Distribution
+
+If you are reading this, you have most likely already done this, but to 
+recap:
+
+The StarChat server <ftp:/utopia.starchat.net/pub/Star4.00.tar.gz>
+comes tarred and gzipped. To uncompress it and expand it, use the
+following command at the Unix prompt:
+
+tar -xzf Star4.10.tar.gz
+
+This will create a new directory called Star4.10, and unpack the source
+into it.
+
+=======================================================================
+Editing the Configuration Files
+
+In previous versions of the source code many files had to be edited to
+make things right for any given server.
+
+In this version you DO NOT NEED TO EDIT THE Makefile OR ANY FILES
+IN include!  That is, unless you set some strange options.  If you
+find the need to edit include/config.h, for example, mail us and
+tell us why ; Config can be made smarter, and that will make it so 
+you won't have to edit anything for the next version
+
+=======================================================================
+Compiling Your Server
+
+Windows users: You must compile the Config program first. To do this
+run the following command:  $CC src\Config.c
+where $CC is the name of your compiler.  for MSVC users that is 'cl'
+
+To build the server, simply run
+       % ./Config
+to start the configuraton program.  This program looks at your system
+and generate the include/setup.h, include/options.h, and Options files.
+You will be asked some questions.  Usually the default answers are the best
+ones and most correct.  To accept the default, just hit RETURN.
+
+Next, type 'make'. This will compile your server. Depending on your system,
+this may be a good time for a caffeine break. (MSVC users run 'nmake')
+
+Hopefully, the server will compile without incident. If it does not, and 
+you are not able to determine the error, please email coding@starchat.net
+and hopefully someone will be able to help you with the problem.  If you
+do need to fix something, mail there with a patch. Alternatively, try #ircd
+on StarChat.
+
+BE CERTAIN TO INCLUDE OPERATING SYSTEM INFORMATION (uname -a) AND COMPILER
+VERSION (gcc -v, for instance) IN ALL BUG REPORTS.
+
+Common problems
+===============
+
+If, when you try to compile, something like the following halts make:
+res.o: In function `proc_answer':
+/home/GZ/Star4.10/src/res.c:617: undefined reference to `dn_expand'
+/home/GZ/Star4.10/src/res.c:622: undefined reference to `_getshort'
+/home/GZ/Star4.10/src/res.c:624: undefined reference to `_getshort'
+
+you need to include a separate library. Rerun ./Config and in the space
+where it asks you what additional libraries you would like to include,
+type "-lresolv". If a similar problem occurs referring to 'crypt',
+include the "-lcrypt" library aswell.  
+=======================================================================
+Installing the Files
+
+'make install' does not work in this release -- it doesn't do anything
+except a compile if that is needed.
+
+The only files you need are the binary from src/ircd and and an ircd.conf
+(see below for copying doc/example.conf as your initial ircd.conf). You
+probably will want to create a MOTD file too, as well as place the man
+pages from the doc directory to appropriate place in your system.
+
+
+=======================================================================
+Configuring Your Server
+
+The previous step places a file named 'example.conf' into your irc 
+directory you specified to Config.
+
+To create an IRC configuration file, type:
+       cp example.conf ircd.conf
+
+Now edit this file to reflect your server. The file is mainly 
+self-explanatory. Note that if you plan to use your server on StarChat, 
+make sure that you have the following line:
+
+U:services.starchat.net:*:*
+
+If you need help configuring your file, please join StarChat, go to channel 
+#ircd, and ask.
+
+
+=======================================================================
+Starting Your Server
+
+Simply enter the complete path to the executable into the Unix command 
+line, then hit return. Your server is now operational, assuming that you 
+have completed all the steps described above.
+
+NOTE: If you get something like the following when running ircd:
+
+ircd fd table too big
+Hard Limit: 256 IRC max: 1024
+Fix MAXCONNECTIONS
+
+You need to change config.h and recompile. Find the line that says 
+"#define MAXCONNECTIONS 1024", and change it to the number given after 
+"Hard Limit" (most likely 256), then make the server again, following the 
+above instructions.
+
+Enjoy!
+
+- GZ
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..9a17037
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,249 @@
+
+                   GNU GENERAL PUBLIC LICENSE
+                    Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+                    675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The license agreements of most software companies try to keep users
+at the mercy of those companies.  By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must tell them their rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License.  The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications.  Each
+licensee is addressed as "you".
+
+  1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program.  You may charge a fee for the physical act of
+transferring a copy.
+
+  2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+    a) cause the modified files to carry prominent notices stating that
+    you changed the files and the date of any change; and
+
+    b) cause the whole of any work that you distribute or publish, that
+    in whole or in part contains the Program or any part thereof, either
+    with or without modifications, to be licensed at no charge to all
+    third parties under the terms of this General Public License (except
+    that you may choose to grant warranty protection to some or all
+    third parties, at your option).
+
+    c) If the modified program normally reads commands interactively when
+    run, you must cause it, when started running for such interactive use
+    in the simplest and most usual way, to print or display an
+    announcement including an appropriate copyright notice and a notice
+    that there is no warranty (or else, saying that you provide a
+    warranty) and that users may redistribute the program under these
+    conditions, and telling the user how to view a copy of this General
+    Public License.
+
+    d) You may charge a fee for the physical act of transferring a
+    copy, and you may at your option offer warranty protection in
+    exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+\f
+  3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+    a) accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    b) accompany it with a written offer, valid for at least three
+    years, to give any third party free (except for a nominal charge
+    for the cost of distribution) a complete machine-readable copy of the
+    corresponding source code, to be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    c) accompany it with the information you received as to where the
+    corresponding source code may be obtained.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it.  For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+  4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License.  However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+  5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions.  You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+\f
+  7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+  8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+  To do so, attach the following notices to the program.  It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 1, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19xx name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License.  Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  program `Gnomovision' (a program to direct compilers to make passes
+  at assemblers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile.dist b/Makefile.dist
new file mode 100755 (executable)
index 0000000..7a27634
--- /dev/null
@@ -0,0 +1,176 @@
+#/************************************************************************
+#*   IRC - Internet Relay Chat, Makefile
+#*   Copyright (C) 1990, Jarkko Oikarinen
+#*
+#*   This program is free software; you can redistribute it and/or modify
+#*   it under the terms of the GNU General Public License as published by
+#*   the Free Software Foundation; either version 1, or (at your option)
+#*   any later version.
+#*
+#*   This program is distributed in the hope that it will be useful,
+#*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#*   GNU General Public License for more details.
+#*
+#*   You should have received a copy of the GNU General Public License
+#*   along with this program; if not, write to the Free Software
+#*   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#*/
+
+CC=gcc
+INCLUDEDIR=../include
+
+# [CHANGEME]
+# Default flags:
+# Change XCFLAGS if you don't like what Config puts there.  Same with
+# IRCDLIBS.
+#
+# If you are configuring by hand, try "-O -g" for XCFLAGS, and leave
+# IRCDLIBS blank.  If that fails, try recomendations below.
+#
+XCFLAGS=
+IRCDLIBS=
+
+#
+# use the following on MIPS:
+#CFLAGS= -systype bsd43 -DSYSTYPE_BSD43 -I$(INCLUDEDIR)
+# For Irix 4.x (SGI), use the following:
+#CFLAGS= -g -cckr -I$(INCLUDEDIR)
+#
+# on NEXT use:
+#CFLAGS=-bsd -I$(INCLUDEDIR)
+#on NeXT other than 2.0:
+#IRCDLIBS=-lsys_s
+#
+# AIX 370 flags
+#CFLAGS=-D_BSD -Hxa -I$(INCLUDEDIR)
+#IRCDLIBS=-lbsd
+#
+# Dynix/ptx V2.0.x
+#CFLAGS= -I$(INCLUDEDIR) -O -Xo
+#IRCDLIBS= -lsocket -linet -lnsl -lseq
+# 
+# Dynix/ptx V1.x.x
+#IRCDLIBS= -lsocket -linet -lnsl -lseq
+#
+#use the following on SUN OS without nameserver libraries inside libc
+#IRCDLIBS=-lresolv
+#
+# Solaris 2
+#IRCDLIBS=-lsocket -lnsl -lresolv -L/usr/ucblib -R/usr/ucblib -lgen
+#
+# ESIX
+#CFLAGS=-O -I$(INCLUDEDIR) -I/usr/ucbinclude
+#IRCDLIBS=-L/usr/ucblib -L/usr/lib -lsocket -lucb -lns -lnsl
+#
+# LDFLAGS - flags to send the loader (ld). SunOS users may want to add
+# -Bstatic here.
+#
+#LDFLAGS=-Bstatic
+#
+#Dell SVR4
+#CC=gcc
+#CFLAGS= -I$(INCLUDEDIR) -O2
+#IRCDLIBS=-lsocket -lnsl -lucb
+
+# [CHANGEME]
+# IRCDMODE is the mode you want the binary to be.
+# The 4 at the front is important (allows for setuidness)
+#
+# WARNING: if you are making ircd SUID or SGID, check config.h to make sure
+#          you are not defining CMDLINE_CONFIG 
+IRCDMODE = 711
+
+# [CHANGEME]
+# IRCDDIR must be the same as DPATH in include/config.h
+#
+IRCDDIR=/usr/local/lib/ircd
+
+# [CHANGEME]
+# Some SunOS versions want this.  Try it without first.
+#RES=res_init.o res_comp.o res_mkquery.o
+# BSDI systems want this.
+#RES=res_skipname.o
+# Recent Linux systems seem to expect this:
+RES=res_init.o
+# The rest are perfectly content with this.
+#RES=
+
+# [CHANGEME]
+# If you get a compile-time error dealing with u_int32_t, comment out
+# this line.
+# NEED_U_INT32_T=      -DNEED_U_INT32_T
+NEED_U_INT32_T=
+
+# [CHANGEME]
+# If you get a link-time error dealing with strtoul, comment out
+# this line.
+# STRTOUL=     strtoul.o
+STRTOUL=
+
+# [CHANGEME]
+# If you get crashes around a specific number of clients, and that client
+# load comes close or a little over the system-defined value of FD_SETSIZE,
+# override it here and see what happens.
+FD_SETSIZE=
+
+CFLAGS=-I$(INCLUDEDIR) $(XCFLAGS) $(NEED_U_INT32_T) $(FD_SETSIZE)
+
+SHELL=/bin/sh
+SUBDIRS=src
+BINDIR=$(IRCDDIR)
+MANDIR=/usr/local/man
+INSTALL=/usr/bin/install
+RM=rm
+CP=cp
+TOUCH=touch
+
+all:   build
+
+MAKEARGS =     'CFLAGS=${CFLAGS}' 'CC=${CC}' 'IRCDLIBS=${IRCDLIBS}' \
+               'LDFLAGS=${LDFLAGS}' 'IRCDMODE=${IRCDMODE}' \
+               'BINDIR=${BINDIR}' 'INSTALL=${INSTALL}' \
+               'INCLUDEDIR=${INCLUDEDIR}' 'IRCDDIR=${IRCDDIR}' \
+               'MANDIR=${MANDIR}' 'RM=${RM}' 'CP=${CP}' 'TOUCH=${TOUCH}' \
+               'RES=${RES}' 'SHELL=${SHELL}' 'STRTOUL=${STRTOUL}'
+
+server:
+
+build:
+       -@if [ ! -f include/setup.h ] ; then \
+               echo "You have not run Config..."; \
+               echo "Doing so now.."; \
+               sh Config; \
+       fi
+       @for i in $(SUBDIRS); do \
+               echo "Building $$i";\
+               ( cd $$i; ${MAKE} ${MAKEARGS} build; ) ; \
+       done
+       @echo ''
+       @echo 'Serene IRCD compiled!'
+       @echo ''
+
+clean:
+       @echo 'Cleaning Serene IRCD'
+       @for i in $(SUBDIRS); do \
+               echo "Cleaning $$i";\
+               ( cd $$i; ${MAKE} ${MAKEARGS} clean; ) ; \
+       done
+       @echo ''
+       @echo 'Cleaning complete.'
+       @echo ''
+
+distclean: clean
+       rm -rf include/setup.h Makefile Options .install
+       @echo ''
+       @echo 'Distribution is now fully cleaned.'
+       @echo ''
+depend:
+       @for i in $(SUBDIRS); do \
+               echo "Making dependencies in $$i";\
+               ( cd $$i; ${MAKE} ${MAKEARGS} depend; ) ; \
+       done
+
+install: all
+       @echo "Copying ircd to the right location."
+       ./.install
diff --git a/bsdinstall b/bsdinstall
new file mode 100755 (executable)
index 0000000..12daca1
--- /dev/null
@@ -0,0 +1,83 @@
+#! /bin/sh
+
+#      @(#)install.sh  4.5     (Berkeley)      10/12/83
+#
+cmd=/bin/mv
+strip=""
+chmod="/bin/chmod 755"
+chown="chown -f root"
+chgrp="/bin/chgrp -f bin"
+while true ; do
+       case $1 in
+               -s )    strip="strip"
+                       shift
+                       ;;
+               -c )    cmd="/bin/cp"
+                       shift
+                       ;;
+               -m )    chmod="/bin/chmod $2"
+                       shift
+                       shift
+                       ;;
+               -o )    chown="/etc/chown -f $2"
+                       shift
+                       shift
+                       ;;
+               -g )    chgrp="/bin/chgrp -f $2"
+                       shift
+                       shift
+                       ;;
+               -d )    cmd="/bin/mkdir"
+                       shift
+                       ;;
+               * )     break
+                       ;;
+       esac
+done
+
+if [ ! ${2-""} ]
+then   echo "install: no destination specified"
+       exit 1
+fi
+if [ ${3-""} ]
+then   echo "install: too many files specified -> $*"
+       exit 1
+fi
+if [ $1 = $2 -o $2 = . ]
+then   echo "install: can't move $1 onto itself"
+       exit 1
+fi
+case $cmd in
+/bin/mkdir )
+       file=$2/$1
+       ;;
+* )
+       if [ '!' -f $1 ]
+       then    echo "install: can't open $1"
+               exit 1
+       fi
+       if [ -d $2 ]
+       then    file=$2/$1
+       else    file=$2
+       fi
+       /bin/rm -f $file
+       ;;
+esac
+
+case $cmd in
+/bin/mkdir )
+       if [ ! -d "$file" ]
+       then    $cmd $file
+       fi
+       ;;
+* )
+       $cmd $1 $file
+       if [ $strip ]
+       then    $strip $file
+       fi
+       ;;
+esac
+
+$chown $file
+$chgrp $file
+$chmod $file
diff --git a/doc/CHANGES.old b/doc/CHANGES.old
new file mode 100644 (file)
index 0000000..affd124
--- /dev/null
@@ -0,0 +1,102 @@
+#####################################
+#      Changes in Star5.18          #
+#####################################
+Remmy:
+o: Leaf servers no longer try to connect to other servers when they are
+   already connected to some other server.
+o: No more Q:Lined nick notices from U lined servers. (for real this
+   time!)
+o: Removed channel mode +p. It led to obscure bugs and no users were using 
+   it for legit reasons anyways. Dinosaur feature. 
+o: Removed R lines support from the ircd.conf. We have never used them
+   anyways.
+o: Added QUIT messages back to the ircd.
+o: Added support for svsjoin and svspart (/os push and /os part) to the ircd.
+o: The ircd had an incomplete list of supported channel modes it gave
+   out to clients upon connecting. Not sure it makes a difference as
+   it has been like this for over a year, but fixed it anyways.
+
+Siete: 
+o: Split up /stats reports for K and Z lines so they no longer should
+   flood us all off. /stats k for K lines, /stats Z for Z lines,
+   /stats K for both. Use /stats z for memory usage.
+
+#########################################################################
+#      Changes in Star5.19 (see doc/CHANGES.old for older changes)      #
+#########################################################################
+Remmy:
+o: Reading in an ircd.conf in DOS file format will no longer result in
+   strange and unexpected behaviour by the ircd.
+o: Removed pre Star5 support
+o: Some minor spelling fixes
+
+Siete:
+o: Added support for usermode 'j' (NetFounder)
+o: Removed /links and /map notices to IRC Operators
+
+#########################################################################
+#      Changes in Star5.20 (see doc/CHANGES.old for older changes)      #
+#########################################################################
+Remmy:
+o: Removed many compilation warnings when compiling with -Wall
+o: Channel mode +R was not properly transmitted between servers after a
+   split. Bad!
+      
+#########################################################################
+#      Changes in Star5.21 (see doc/CHANGES.old for older changes)      #
+#########################################################################
+Remmy:
+o: Users could set themselves mode +j (they shouldn't be allowed to).
+
+#########################################################################
+#      Changes in Star5.22 (see doc/CHANGES.old for older changes)      #
+#########################################################################
+Remmy:
+o: e line support. These can be used to exempt users otherwise caught in
+   a KLINE. Syntax is the same as that of a K line. Can also be used to
+   set exemption AKILLs using services.
+o: No more throttle notices! Now we can all sleep at night again.
+      
+#########################################################################
+#      Changes in Star5.23 (see doc/CHANGES.old for older changes)      #
+#########################################################################
+Remmy:
+o: Removed e line support ;). They interfere with AKILLs in the new setup 
+   and as such should not be used.
+o: Removed socks scan. These served little purpose anymore and caused 
+   significant delays for people behind a NAT network or firewall.
+o: Simplified the jinx handling in the ircd. This function is much 
+   smaller now and no longer supports the old hostjinxes (hardly used
+   anyways).
+                     
+#########################################################################
+#      Changes in Star5.24 (see doc/CHANGES.old for older changes)      #
+#########################################################################
+Remmy:
+o: People no longer can take on the nick WebServ when services are down.
+o: Introduced usermode 'w'. This one keeps track of who is and who isn't
+   a valid WebTV user. For use with Services version 0.6.0 and higher.
+o: /map, /links and /whois are available to all again and show servers.
+
+#########################################################################
+#      Changes in Star5.25 (see doc/CHANGES.old for older changes)      #
+#########################################################################
+o: Introduced channel mode H to require identifaction on certain
+   hostmasks prior to joining a channel. (Remmy)
+o: New and improved hostmasking for usermode +x to better differentiate
+   between numeric IP's. Also hardens the chance on someone finding the
+   real hostmask. (InnerFIRE)
+
+#########################################################################
+#      Changes in Star5.26 (see doc/CHANGES.old for older changes)      #
+#########################################################################
+o: Removed D,d and L lines support as they weren't being used. (Remmy)
+o: Removed identd support. We don't block users based on ident and not
+   checking may make things faster for firewalled / NAT users. (Remmy)
+o: Users can no longer /whois when jinxed. Quit messages can't be sent by
+   them. (Remmy)
+o: Removed support for the /SERVICES command. /IDENTIFY command no longer
+   forwards to ChanServ. (Remmy)
+o: Changed compilation flags and done some minor cleanups. (Remmy)
+
+
diff --git a/doc/INSTALL b/doc/INSTALL
new file mode 100644 (file)
index 0000000..157a3d9
--- /dev/null
@@ -0,0 +1,962 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, doc/INSTALL
+ *   Copyright (C) 1990,1991,1992, Jeff Trim, Mike Bolotski,
+ *   Jarkko Oikarinen and Darren Reed.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+            Installing IRC - The Internet Relay Chat Program
+
+
+Overview of this document:
+
+  1) The config.h file
+  2) Editing the Makefile
+  3) Compiling IRC
+  4) The ircd.conf file
+
+
+1) Edit the "config.h" file and make changes to the various #DEFINE's:
+   a) Copy the config.h.dist file to config.h before editing.
+  
+   b) Define what type of UNIX your machine uses.
+
+      Pick the machine type which best describes your machine and change
+      the #undef to #define (if needed).  Some flavours of Unix require no
+      #define and in such cases all others should be #undef'd.
+
+   c) DEBUGMODE
+
+       Define DEBUGMODE if you want to see the ircd debugging information
+      as the daemon is running. Normally this function will be undefined
+      as ircd produces a considerable amount of output.  DEBUGMODE must be
+      defined for either of -t or -x command line options to work.
+      
+   d) DPATH, SPATH, CPATH, MPATH, LPATH, PPATH
+
+        DPATH is provided so that the other pathnames (SPATH, CPATH, etc)
+      may be provided in just filename form.  When the server starts, it
+      chdir's to DPATH before chroot or any other file operation, making
+      it the "current directory" for the server.  This is where core files
+      will go if it core dumps.
+
+       Define SPATH to be the directory path to ircd.  This is usually
+      /usr/local/bin/ircd, unless you don't have installation permission
+      there. 
+
+      Define CPATH to be the directory path to the "irc.conf" file.
+      This path is usually /usr/local/lib/irc.conf. The format of this file 
+      will be discussed later.
+
+      The LPATH #define should be set to "/dev/null" unless you plan to 
+      debug the program.  Note that the logfile grows very quickly.
+
+      Define MPATH to be the path to the 'motd' (message of the day) file
+      for the server.  Keep in mind this is displayed whenever anyone
+      signs on to your server.
+
+      The PPATH is optional, but if defined, should point to a file which
+      either doesn't exist (but is creatable) or a previously used PPATH
+      file.  It is used for storing the server's PID so a ps(1) isn't
+      necessary.
+
+   e) CHROOTDIR
+
+       To use the CHROOTDIR feature, make sure it is #define'd and that
+      the server is being run as root.  The server will chroot to the
+      directory name provded by DPATH.
+
+   f) ENABLE_SUMMON, ENABLE_USERS
+
+       For security conscious server admins, they may wish to leave
+      ENABLE_USERS undefined, disabling the USERS command which can be used
+      to glean information the same as finger can.  ENABLE_SUMMON toggles
+      whether the server will attempt to summon local users to irc by
+      writing a message similar to that from talk(1) to a user's tty.
+
+   g) SHOW_INVISIBLE_LUSERS, NO_DEFAULT_INVISIBLE
+
+       On large IRC networks, the number of invisible users is likely to
+      be large and reporting that number cause no pain.  To aid and effect
+      this, SHOW_INVISIBLE_LUSERS is provided to cause the LUSERS command
+      to report the number of invisible users to all people and not just
+      operators.  The NO_DEFAULT_INVISIBLE define is used to toggle whether
+      clients are automatically made invisible when they register.
+
+   h) OPER_KILL, OPER_REHASH, OPER_RESTART, LOCAL_KILL_ONLY
+
+       The three operator only commands, KILL, REHASH and RESTART, may all
+      be disabled to ensure that an operator who does not have the correct
+      privilidges does not have the power to cause untoward things to occur.
+      To further curb the actions of guest operators, LOCAL_KILL_ONLY can
+      be defined to only allow locally connected clients to be KILLed.
+
+   i) The rest of the user changable #define's should be pretty much self
+      explanatory in the config.h file.  It is *NOT* recommended that any
+      of the file undef the line with "STOP STOP" in it be changed.
+
+3) Configure and compile the code.
+
+      Edit the root Makefile for the server, uncomment/comment the correct
+   CFLAGS/IRCDLIBS lines as appropriate for your system.
+      Change DESTDIR to be the same as the path for DPATH in config.h.
+   Type "make". This will compile the server, the client, and the services.
+   At the end of this step, the server directory will contain 'ircd',
+   and the client directory will contain 'irc'.  To get the server installed,
+   type "make install" which will build a default m4 file for preprocessing,
+   copy example.conf and put the server all in DESTDIR.  The irc client and
+   a copy of the server will also be placed in BINDIR and the modes set
+   accordingly.
+
+4) The ircd.conf file.
+
+   After installing the ircd and irc programs, edit the irc.conf file
+   as per the instructions in this section and  install it in the 
+   location you specified in the config.h file.  There is a sample
+   conf file called example.conf in the /doc directory.
+
+   Appendix A describes the differences between IP addresses and host
+   names.  If you are unfamiliar with this, you should probably scan 
+   through it before proceeding.
+
+   The irc.conf file contains various records that specify configuration
+   options.  The record types are as follows:
+   
+   1.  Server connections      (C,N)
+   2.  Machine information     (M)
+   3.  Client connections      (I)
+   4.  Default local server    (U)
+   5.  Operator priviliges     (O)
+   6.  Administrative info     (A)
+   7.  Excluded accounts       (K)
+   8.  Excluded machines        (Q)
+   9.  Connection Classes       (Y)
+  10.  Leaf connections         (L)
+  11.  Service connections      (S)
+  12.  Port connections                (P)
+  13.  Hub connections         (H)
+
+
+   1. SERVER CONNECTIONS:  How to connect to other servers
+                          How other servers can connect to you
+
+   WARNING:
+     The hostnames used as examples are really only examples and
+     not meant to be used (simply because they don't work) in real life.
+
+   Now you must decide WHICH hosts you want to connect to and WHAT ORDER you
+   want to connect to them in.  For my example let us assume I am on the
+   machine "rieska.oulu.fi" and I want to connect to irc daemons on 3 other
+   machines:
+
+         "garfield.mit.edu"        - Tertiary Connection
+         "irc.nada.kth.se"         - Secondary Connection
+         "nic.funet.fi"            - Primary Connection
+
+   And I prefer to connect to them in that order, meaning I first want to
+   try connecting to "nic.funet.fi", then to "irc.nada.kth.edu", and
+   finally to "garfield.mit.edu".  So if "nic.funet.fi" is down or
+   unreachable, the program will try to connect to "irc.nada.kth.se".
+   If irc.nada.kth.se is down it will try to connect to garfield and so forth.
+   PLEASE limit the number of hosts you will attempt to connect to down to 3.
+   This is because of two main reasons:
+     a) to save your server from causing extra load and delays
+        to users
+     b) to save internet from extra network traffic
+        (remember the old rwho program with traffic problems when
+        the number of machines increased).
+
+   The format for the CONNECT entry in the "irc.conf" is:
+
+       C:<TARGET Host Addr>:<Password>:<TARGET Host NAME>:<TARGET Host PORT>
+Field: 1        2              3                4               5
+
+   for example:
+   
+    C:nic.funet.fi:passwd:nic.funet.fi:6667 
+
+          - or -
+
+    C:128.214.6.100:passwd:nic.funet.fi:6667
+
+          - or -
+
+    C:root@nic.funet.fi:passwd:nic.funet.fi:6667
+
+
+    Explanation:
+
+    Each field is separated with a ":" charcter:
+
+    Field 1: Field 1 tells the IRC program which option is being configured.
+             "C" corresponds to a server Connect option.
+
+    Field 2: Specifies the host name or IP address of the machine to connect
+            to.  If "user@" prefixes the actual hostname or IP address
+            the server will require that the remote username returned by
+            the ident server be the same as the one given before the "@".
+
+    Field 3: The password of the other host.  A password must always be
+            present for the line to be recognized.
+
+    Field 4: The full hostname of the target machine. This is the name that 
+            the TARGET server will identify itself with when you connect 
+            to it.  If you were connecting to nic.funet.fi you would receive
+            "nic.funet.fi" and that is what you should place in 
+            this field.
+  
+    Field 5: The INTERNET Port that you want to connect to on the TARGET 
+            machine. Most of the time this will be set to "6667".  
+             If this field is left blank, then no connections will 
+             be attempted to the TARGET host, and your host will accept
+             connections FROM the TARGET host instead.
+
+   Some examples:
+
+            C:nic.funet.fi::nic.funet.fi:6667
+            This reads: Connect to host "nic.funet.fi", with no password
+            and expect this server to identify itself to you as
+            "nic.funet.fi". Your machine will connect to this host to
+            PORT 6667.
+
+            C:18.72.0.252:Jeff:garfield.mit.edu:6667
+
+            This reads: Connect to a host at address "18.72.0.252", using a
+            password of "Jeff".  The TARGET server should identify
+            itself as "garfield.mit.edu".  You will connect to Internet
+            Port 6667 on this host.
+
+            C:irc.nada.kth.se::irc.nada.kth.se
+
+            This reads: do not attempt to connect to "irc.nada.kth.se",
+                       but if "irc.nada.kth.se" requests a connection,
+                       allow it to connect.
+
+   Now back to our original problem, we wanted OUR server CONNECT to 3
+   hosts,  "nic.funet.fi", "irc.nada.kth.se" and "garfield.mit.edu" in
+   that order.  So as we enter these entries into the file they must be
+   done in REVERSE order of how we could want to connect to them.
+
+   Here's how it would look if we connected "nic.funet.fi" first:
+
+       C:garfield.mit.edu::garfield.mit.edu:6667
+       C:irc.nada.kth.se::irc.nada.kth.se:6667
+       C:nic.funet.fi::nic.funet.fi:6667
+
+   Ircd will attempt to connect to nic.funet.fi first, then to irc.nada
+   and finally to garfield.
+
+   Reciprocal entries:
+
+   Each "C" entry requires a corresponding 'N' entry that specifies
+   connection priviliges to other hosts.  The 'N' entry contains
+   the password, if any, that you require other hosts to have before
+   they can connect to you.  These entries are of the same format as
+   the "C" entries.
+    
+   Let us assume that "garfield.mit.edu" connects to your server
+   and you want to place password authorization authorization on garfield.
+   The "N" entry would be:
+   
+          N:garfield.mit.edu:golden:garfield.mit.edu
+  
+   This line says: expect a connection from host "garfield.mit.edu",
+   and expect a login password of "golden" 
+   and expect the host to identify itself as "garfield.mit.edu".
+     
+         N:18.72.0.252::garfield.mit.edu
+
+   This line says: expect a Connection from host "18.72.0.252", and 
+   don't expect login password.  The connecting host should identify itself
+   as "garfield.mit.edu". 
+
+  
+   Wildcards domains: 
+       To reduce the great amount of servers in IRCnet wildcard
+       DOMAINS were introduced in 2.6. To explain the usage of
+       wildcard domains we take an example of such:
+               *.de  - a domain name matching all machines
+                        in Germany.
+        Wildcard domains are useful in that ALL SERVERS in Germany
+        (or any other domain area) can be shown as one to the
+       rest of the world. Imagine 100 servers in Germany, it
+       would be incredible waste of netwotk bandwidth to broadcast
+       all of them to all servers around the world.
+
+       So wildcard domains are a great help, but how to use them ?
+       They can be defined in the N-line for a given connection,
+       in place of port number you write a magic number called
+       wildcard count.
+
+       Wildcard count tells you HOW MANY PARTS of your server's name
+       should be replaced by a wildcard. For example, your server's
+       name is "tolsun.oulu.fi" and you want to represent it as
+       "*.oulu.fi" to "nic.funet.fi". In this case the wildcard count
+       is 1, because only one word (tolsun) is replaced by a wildcard.
+       If the wildcard count would be 2, then the wildcard domain would
+       be "*.fi". Note that with wildcard name "*.fi" you could NOT
+       connect to "nic.funet.fi", because that would result in a server
+       name COLLISION (*.fi matches nic.funet.fi).
+
+       I advice you to not to use wildcard servers before you know
+       for sure how they are used, they are mostly beneficial for
+       backbones of countries and other large areas with common domain.
+
+
+   2. MACHINE INFORMATION
+                            
+   IRC needs to know a few things about your UNIX site, and the "M" command
+   specifies this information for IRC.  The fomat of this command is:
+
+           M:<YOUR Host NAME>:xxx:<Geographic Location>:<Internet Port>
+   Field:  1         2         3           4                 5
+
+   Explanation:
+
+      Field 1: "M" specifies a Machine description line
+
+      Field 2: The name of YOUR host adding any Internet DOMAINNAME that 
+               might also be present.  
+
+      Field 3: -- NOT USED --: Set to Value NULL (No spaces at ALL!).
+    
+      Field 4: Geographic Location is used to say WHERE YOUR SEVRER is,
+               and gives people in other parts of the world a good
+               idea of where you are!  If your server is in the USA, it is
+               usually best to say: <CITY> <STATE>, USA.  Like for Denver
+               I say: "Denver Colorado, USA".  Finnish sites (like
+               tolsun.oulu.fi generally say something like "Oulu, Finland".
+      Field 5: The Internet port your server will use.  Should be set to
+              the same value as in the config.h file.
+
+
+      Example:
+                M:tolsun.oulu.fi::Oulu, Finland:6667
+
+                This line reads: My Host's name is "tolsun.oulu.fi" and
+                my site is located in "Oulu, Finland".  My ircd will use
+               Internet Port 6667.
+
+
+                M:orion.cair.du.edu::Denver Colorado, USA:6667
+
+                This line reads: My Hosts name is "orion.cair.du.edu"
+                and my site is located in "Denver Colorado, USA".
+               I have defined Internet Port number "6667" to be used
+                as my IRCD Socket Port.
+
+
+   3. CLIENT CONNECTIONS -   How to let clients connect to your IRCD.
+
+   A client is a program that connects to the ircd daemon (ircd).  Currently
+   there are clients written in C and in GNU Emacs Lisp.  The "irc"
+   program is the C client.  Each person that talks via IRC is running
+   their own client.
+
+   The irc.conf files contains entries that specify which clients are allowed
+   to connect to your irc daemon.  Obviously you want to allow your cwn
+   machine's clients to connect.  You may want to allow clients from 
+   other sites to connect.  These remote clients will use your server
+   as a connection point.  All messages sent by these clients will pass
+   through your machine.
+
+   The format of this entry in the conf file is:
+
+          I:<TARGET Host Addr>:<Password>:<TARGET Hosts NAME>:<Internet Port>
+    Field:1         2               3             4                5
+
+
+      For example, if you were installing IRC on tolsun.oulu.fi and you wanted
+      to allow examples sake let us assume you were making this file for
+      tolsun and you wanted to let your own clients to connect to your
+      server, you would add this entry to the file:
+
+      I:128.214.5.6::tolsun.oulu.fi
+      or
+      I:tolsun.oulu.fi::tolsun.oulu.fi
+
+      If you wanted to let remote clients connect, you could add the
+      following lines:
+
+      I:*.du.edu::*.du.edu
+
+      Allow any clients from machines whose names end in "du.edu" to connect
+      with no password.
+
+      I:128.214.6.100::nic.funet.fi
+
+      Allow clients from a machine with that IP number and the name 
+      nic.funet.fi to connect.
+
+      I:*.tut.fi:secret:*.tut.fi
+
+      Allow clients from machines matching *.tut.fi to connect
+      with the password 'secret'.
+
+      I:*::*
+
+      Allow anyone from anywhere to connect your server.
+      This is the easiest way, but it also allows people to for example
+      dump files to your server, or connect 1000 (or how many open
+      sockets per process your OS allows) clients to your machine
+      and take your network ports. Of course the same things can be
+      done by simply telnetting to your machine's SMTP port (for example).
+
+   NEW!!!
+      As of the 2.7.2d version of the server, the server is able to accept
+      connections on multiple ports. I-lines are required for each P-line
+      to allow connections to be accepted. For unix sockets, this means
+      either adding I:/path/port::/path/port or some variation (wildcards
+      are recognised here). For internet ports, there must be an I-line
+      which allows the host access as normal, but the port field of the
+      I-line must match that of the port of the socket accepting the
+      connectiion. A port number of 0 is a wildcard (matches all ports).
+
+   4. DEFAULT HOSTS (for local clients)
+
+      This defines the default connection for the irc client.  If you are
+      running an ircd server on the same machine, you will want to define
+      this command to connect to your own host.  If your site is not running
+      a server then this command should contain the TARGET host's connection
+      information and password (if any).  The format for this command is:
+
+         U:<TARGET Host addr>:<Password>:<TARGET Host NAME>:<Internet Port>
+  Field: 1         2              3                4            5
+   
+
+      For example:
+
+           U:tolsun.oulu.fi::tolsun.oulu.fi:6667
+           U:128.214.5.6::tolsun.oulu.fi:6667
+           U:tolsun.oulu.fi::tolsun.oulu.fi
+
+      If the port number is omitted, irc will default to using 6667.
+
+  5. OPERATOR Privileges:  How to become the IRC administrator on your site
+
+   To become an IRC Administrator, IRC must know who is authorized to become
+   an operator and what their "Nickname" and "Password" is.  To add this
+   information, EDIT your "irc.conf" file and add the following command
+   line to it:
+
+          O:<TARGET Host NAME>:<password>:<nickname>:<port>:<class>
+  Field:  1          2             3          4        5       6
+
+  Explanation:
+     Field 1: Speficies Operator record. If you use capital letter ('O')
+              in it, it specifies a global operator. Small letter ('o')
+              specifies a local operator. Local operator has basically the
+              same rights except global operator with some restrictions.
+     Field 2: Tells IRC which host you have the privileges FROM.  This
+              means that you should be logged into this host when you
+              ask for the priviliges.  If you specify "tolsun.oulu.fi"
+              then IRC will expect your CLIENT to be connected at 
+              "tolsun.oulu.fi" - when you ask for OPERATOR privileges
+              from "tolsun.oulu.fi".  You cannot be logged in at any
+              other host and be able to use your OPERATOR privileges
+              at tolsun, only when you are connected at TOLSUN will this
+              work - this is a safeguard against unauthorized sites.
+
+
+     Field 3: If your AUTHORIZATION Password - this is the password that 
+              let's IRC know you are who you say you are!  Never tell anyone
+              your password and always keep the "irc.conf" file protected
+              from all of the other users.
+     Field 4: The Nickname you usually go by - but you can make this what
+              you want.  It is better to make this a NICKNAME that no one
+              else knows, but anything will do.  I usually use my own
+              loginname.
+
+     Field 5: Unused.
+
+     Field 6: The class field should refer to an existing class (preferably
+             having a lower number than that for the relevant I-line) and
+             determines the maximum number of simultaneous uses of the
+             O-line allowable through the max. links field in the Y-line.
+
+  Example:
+              O:orion.cair.du.edu:pyunxc:Jeff
+
+              There is an OPERATOR at "orion.cair.du.edu" that can get
+              Operator priviliges if he specifies a password of "pyunxc"
+              and uses a NICKNAME of "Jeff".
+
+
+
+  6. ADMINISTRATIVE INFORMATION
+
+  The "A" command is used for administrative information about a site.
+  The e-mail address of the person running the server should be included
+  here in case problems arise.
+
+  
+            A:<Your Name/Location>:<Your Electronic Mailing Addr>:<other> 
+  Field:    1        2                    3                         4
+
+  Explanation:
+
+       Field 1: "A" specifies an Admin record.
+
+
+       Field 2: Use this field to say tell your FULL NAME and where in the 
+                world your machine is.  Be sure to add your City, 
+               State/Province and Country.
+
+
+       Field 3: Use this field to specify your Electronic Mailing Address
+                preferably your Internet Mailing Address.  If you have
+                a UUCP or ARAPnet address - please add that as well.  Be
+                sure to add any extra DOMAIN information that is needed,
+                for example "mail jtrim@orion" probably won't work as a 
+                mail address to me if you happen to be in Alaska.  But 
+                "mail jtrim@orion.cair.du.edu" would work because you 
+                know that "orion" is part of the DOMAIN "cair.du.edu". 
+                So be sure to add your DOMAINNAMES to your mailing addresses.
+
+       Field 4: Is really an OTHER field - you can add what you want here,
+                
+
+  Examples (the line is just one line in the confuration file, here it
+            is cut into two lines to make it clearer to read):
+
+A:Jeff Trim -  Denver Colorado, USA:INET jtrim@orion.cair.du.edu UUCP {hao,
+isis}!udenva!jtrim:Terve! Heippa!  Have you said hello in Finnish today?;)
+
+  Would look like this when printed out with the /admin command:
+
+      Jeff Trim -  Denver Colorado, USA
+      INET jtrim@orion.cair.du.edu   UUCP {hao,isis}!udenva!jtrim
+      Terve! Hei! Heippa!  Have you said hello in Finnish today? ;)
+  Note that the A record cannot be split across multiple lines; it will 
+  typically be longer than 80 characters and will therefore wrap around
+  the screen.
+
+
+  7. REMOVING A USER FROM IRC  Remove an errant user from IRC on your site.
+
+  Obviously it is hoped that you wouldn't have to use this command.
+  Unfortunately sometimes a user can become unmanageable and this is your
+  only recourse - the KILL USER command.  THIS COMMAND ONLY AFFECTS YOUR
+  SERVER - If this user can connect to another SERVER somewhere else in
+  the IRC-Network then you would have to talk to the administrator on that
+  site to disable his access from that IRCD Server as well.
+
+  The format of this command is:
+
+         K:<Host Name>:<time interval(s)>:<User>
+  Field: 1     2               3            4
+
+  Explanation:
+
+       Field 1:  "K" tells the IRCD that you are making a KILL USER command
+                 entry.
+
+       Field 2:  In this field you specify the Hostname that the user is 
+                 connecting from.  If you wanted to REMOVE connects
+                 to IRC from "orion.cair.du.edu" then you would want to enter
+                 "orion.cair.du.edu".  If you want to REMOVE ALL HOSTS
+                 access you can use '*' (Wild Card notation) and no matter
+                 what host the USERNAME (specified in Field 4) connects from
+                 s/he will be denied access. Removing all hosts isn't
+                 very smart thing to do though, why would you run an ircd
+                 if you allow nobody to connect to it anyways ?
+
+       Field 3:  Either leave this field empty (no spaces), then then lines
+                is active continuously for the specified user/host machine.
+                You may also specify intervals during the line should be
+                active, see examples above.
+    
+       Field 4:  The USERNAME of the user you want removed from IRC.  For
+                 example 'root'.
+
+   
+   Some Examples:
+                   K:orion.cair.du.edu::jtrim
+               If user 'jtrim' connects to IRC from host "orion.cair.du.edu"
+               then IMMEDIATELY REMOVE HIM from my IRCD.
+
+                   K:*.cair.du.edu::root
+
+               If user 'root' connects to IRC from any host that has the 
+               suffix "cair.du.edu" - then IMMEDIATELY REMOVE THEM from
+               my IRCD.
+
+                   K:*::vijay
+
+               This line reads "I don't care WHAT HOST user 'vijay' is on,
+               I will NEVER allow username 'vijay' to login to my IRCD.
+
+                  K:*.oulu.fi:0800-1200,1400-1900:*
+
+              This disallows all users from hosts with enddomain 'oulu.fi'
+              access to your server between 8 and 12am, 2 and 7pm.
+              Users get kicked off if they're already signed on when the
+              line becomes active (they'll get a warning 5 minutes ago).
+
+  8. Disallowing SERVERS in your irc net.
+
+   In some cases people run into difficulties in net administration.
+   For one reason or another you do not want a certain server to be
+   in your net (for example because of the security holes it opens
+   for every server if it's not secured carefully). In that case
+   you should use Q-lines in your server. When you specify a server
+   name in Q-line, everytime some server link tries to introduce you
+   a server (remember, all server names are broadcast around the net),
+   that name is checked if it matches the Q-lines in your server.
+   If it matches, then your server disconnects the link. Note that
+   just placing Q-lines to your server probably results in your server
+   being left alone, unless other servers have agreed to have the
+   same Q-line in their ircd configuration files as well.
+
+   Example:
+                Q::of the security holes:foo.bar.baz
+
+   This command excludes a server named "foo.bar.baz", the reason
+   is given to be security holes (you should give a reason, it is
+   polite). The first field is unused, so leave it empty.
+
+ 9. Connection Classes.
+
+   To enable more efficient use of MAXIMUM_LINKS, connection classes
+   were implemented. To give a connection a class, add another field
+   (a sixth) to the C/N lines for a particular server.
+   Each line for a server should have the same number as the sixth
+   field.  If it is absent, the server deaults it to 0, using the
+   defaults from the config.h file.  To define a connection class,
+   you need to include a Y: line in the irc.conf file.  This enables
+   you to define the ping frequency, connection frequency and maximum
+   number of links that class should have.  Currently, the Y: line MUST
+   appear in the irc.conf file BEFORE it is used in any other way.
+
+   The format for the line is:
+
+       Y:<CLASS>:<PING FREQUENCY>:<CONNECT FREQUENCY>:<MAX LINKS>:<SENDQ>
+Field: 1    2                3                4                5     6
+
+   Field 2:  This is the class number which gains the following attributes
+   and should match that which is on the end of the C/N line.
+
+   Field 3:  This field defines how long the server will let the connection
+   remain "silent" before sending a PING message to make sure it is still
+   alive.  Unless you are sure of what you are doing, use the default value
+   which is in your config.h file.
+
+   Field 4:  By changing this number, you change how often your server
+   checks to see if it can connect to this server.  If you want to check
+   very occasionally, use a large value, but if it is an important
+   connection, you might want a smaller value so that you connect to it
+   as soon as possible.
+
+   Field 5:  This field defines the maximum number of links this class
+   will allow from automatic connections.  Using /CONNECT overrides this
+   feature.
+
+   Field 6:  This field defines the 'sendq' value for this class.  If this
+   field is not present, the default (from config.h) is assigned.
+
+   NOTE: leaving any of the fields out means their value is 0 (ZERO)!!
+
+   example:
+
+   Y:23:120:300:5
+
+   define class 23 to allow 5 auto-connections, which are checked every
+   300 seconds.  The connection is allowed to remain silent for 120
+   seconds before a PING is sent.  NOTE: fields 3 & 4 are in seconds.
+
+   You may also give I lines a class (again the sixth field to define
+   which class).  This is only usefull (currently) for redefining the
+   ping frequency.  It can also be useful as a diagnostic to see how
+   much each I line is used when combined with the TRACE output.
+
+   Another feature of connection class is the ability to do automatic
+   routing by using the class as a 'priority'.  If you are connected
+   to a server which has a class lower than one of the servers that is
+   'behind' it, the server will disconnect the lower class one and
+   schedule a 'new' connection for the higher class server.
+
+ 10. Leaf Connections.
+
+   To stop servers which should only act as leaves from hubs becoming
+   hubs accidently, the L line was introduced so that hubs can be aware
+   of which servers should and shouldnt be treated as leaves. A leaf
+   server is supposed to remain a node for the entirity of its life
+   whilst connected to the IRC server network.  It is quite easy, however
+   for a leaf server to be incorrectly setup and create problems by
+   becoming a node of 2 or more servers, ending its life as a leaf. The
+   L line enables the administrator of an IRC 'Hub server' to 'stop' a
+   server which is meant to act as a leaf trying to make itself a hub.
+   If, for example, the leaf server connects to another server which doesnt
+   have an L-line for it, the one which does will drop the connection, once
+   again making the server a leaf.
+
+       L:<SERVER MASK>:*:<SERVER NAME>:<MAX DEPTH>
+Field: 1       2       3       4            5
+
+   Field 2 is a mask of which servers the leaf-like attributes are used on
+   when the server receives SERVER messages.  The wildcards * and ? may be
+   used within this field for matching purposes.  If this field is empty,
+   it acts the same as if it were a single * (ie matches everything).
+
+   Field 4 is the the server connectted to you that for which you want to
+   enforce leaf-like attributes upon.
+
+   Field 5 is the maximum depth allowed on that leaf and if not specified,
+   a value of 1 is assumed.  The depth is checked each time a SERVER message
+   is received by the server, the hops to the server being the field checked
+   against this max depth and if greater, the connection to the server that
+   made its leaf too deep has its connection dropped.
+   For the L-line to come into effect, both fields, 2 and 4, must match up
+   with the new server being introduced and the server which is responsible
+   for introducing this new server.
+
+ 11. Service Connections (Not yet implemented)
+
+  Introduction.
+   The Service is a special kind of IRC client. It does not have the full
+   abilities of a normal user but can behave in a more active manner than
+   a normal client. Services as they stand now are not fully implemented.
+   The following line can be added to your ircd.conf file to enable a
+   service:
+  
+          S:<TARGET Host Mask>:<password>:<service_name>
+  Field:  1          2             3          4
+
+  Explanation:
+
+    Field 2:
+       The host mask should be set to match the hosts(s) from which the
+       service will be connecting from. This may be either an IP# or full
+       name (prefered).
+
+   Field 3:
+       This is the password which must be passed in the SERVICE command.
+   Field 4:
+       The 'service name' is only used for the purpose of finding the
+       right S-line from the ircd.conf file for password matching. The
+       actual service name used is that set by NICK commands prior to
+       SERVICE being sent.
+
+   To connect a service to your server, you must first create an S-line
+   entry in your ircd.conf file and get your server to read this in (ie
+   rehash or reboot). Once your server has updated itself, you can then
+   attempt to register your connection as a service.
+   Registering as a service is similar to registering as a normal user
+   except that you must send NICK first and then SERVICE. The service
+   command should look something like this:
+
+       SERVICE secretpassword referencename :Service information
+
+   A successfull registering of a service at the server will result in
+   a RPL_YOURESERVICE (383) being sent back to you. Any other reply as
+   a result of sending service indicates an error has occured.
+
+   A service is not a very useful sort of client, it cannot join channels
+   or issue certain commands although most are available to it. Services,
+   however, are not affected by flood control. It is therefore wise to
+   oversee the use of S-lines with some care.
+
+ 12. Port Connections
+
+  Introduction.
+   The port line adds flexibility to the server's ability to accept
+   connections. By use of this line in the ircd.conf file, it is easy
+   to setup both Unix Domain ports for the server to accept connections
+   on as well as extra internet ports.
+
+       P:<Internet IP# Mask>:<*>:<*>:<PORT>
+Field: 1        2             3   4     5
+
+or
+
+       P:<Directory>:<*>:<*>:<PORT>
+Field: 1        2             3   4     5
+
+  Explanation
+   Internet Ports
+    Field 1
+       The internet IP mask defines where connections may come from and
+       be accepted. The IP mask uses either *'s or 0's as wildcards. The
+       following two lines are the same:
+
+               P:128.2.*:::6664
+               P:128.2.0.0:::6664
+
+       The incoming isnt matched against the mask, rather the ip# string
+       is decoded and compared segment by segment. Thus
+               P:128.2*.1.2:::6664
+       will not match 128.20.1.2.
+
+     Field 5
+       The port number field tells the server which port number it should
+       listen on for incoming connections.
+
+    Unix Socket Ports.
+     Field 1
+       The path set in field 1 should be the directory name in which to
+       create the unix socket for later listening to. The server will
+       attempt to create the directory before creating the unix socket.
+
+     Field 5
+       The port field when used in combination with a pathname in a P-line
+       is the filename created in the directory set in Field 1.
+
+     Example:
+               P:/tmp/.ircd:::6667
+
+       Creates a unix socket in the /tmp/.ircd directory called "6667".
+       The unix socket (file) must be a numerical.
+
+13.  Hub Connections
+
+      In direct contrast to L-lines, the server also implements H-lines to
+   determine which servers may act as a hub and what they may 'hub for'.
+   If a server is only going to supply its own name (ie act as a solitary
+   leaf) then no H-line is required for, else a H-line must be added as
+   follows:
+
+       H:<SERVER MASK>:*:<SERVER NAME>
+Field: 1       2       3       4
+
+   Explanation:
+    Field 2
+      All servers that are allowed via this H-line must match the mask
+      given in this field.
+
+    Field 4
+      This field is used to match exactly against a server name, wildcards
+      being treated as literal characters.
+
+   Examples:
+
+               H:*.edu:*:*.bu.edu
+
+       Allows a server named "*.bu.edu" to introduce only servers that
+       match the "*.edu" name mask.
+
+               H:*:*:eff.org
+
+       Allow "eff.org" to introduce (and act as a hub for) any server.
+
+   Note:  It is possible to have and use multiple H-lines (or L-lines) for
+         the one server.  eg:
+
+               H:*.edu:*:*.bu.edu
+               H:*.au:*:*.bu.edu
+
+          is allowed as is
+
+               L:*.edu:*:*.au
+               L:*.com:*:*.au
+
+
+Appendix A: Difference between IP addresses and hostnames
+
+
+   There are 2 different types of INTERNET addresses, NAME addresses and
+   NUMERIC addresses.  NAME addresses look like ENGLISH words (and indeed
+   they are ENGLISH words that refer to a given host).  A NAME address looks
+   like "tolsun.oulu.fi" - and that particular address refers to the machine 
+   named TOLSUN in Finland.  It is a UNIQUE address because no other machine
+   in the world has its NAME address the same as "tolsun.oulu.fi".  Anytime
+   you say "telnet tolsun.oulu.fi" - you would always connect to TOLSUN in
+   Finland.  NUMERIC addresses refer to those addresses that are made up of
+   NUMBERS for example "128.214.5.6" is the NUMERIC address for TOLSUN.  This
+   address is also UNIQUE in that no other machine in the world will be use 
+   those NUMERIC numbers.  The NUMERIC address is usually more reliable than
+   the NAME address because not all sites can recognize and translate the
+   NAME address into it's numeric counterpart.  NUMERIC always seems to work
+   best, but use a NAME address when you can because it is easier to tell
+   what host you are connected to.
+
+
+   Every Unix machine has a file called "/etc/hosts" on it.  This file 
+   contains NAME and NUMERIC addresses.  When you supply IRC with a NAME 
+   address it will at first try to find it in /etc/hosts, and then (if it's 
+   really smart), use the local Domain Name Server (DNS) to find the NUMERIC
+   address for the host you want to connect to.  Thus if you plan to use NAME
+   addresses keep in mind that on SOME sites the entry for the TARGET machine
+   must be found in /etc/hosts or the NAME address will fail.  A typical 
+   entry in /etc/hosts looks like this:
+
+   130.253.1.15    orion.cair.du.edu orion.du.edu orion    # BSD 4.3
+
+   This particular example is the Host ORION at the University of Denver. 
+   Notice that on the far left is the NUMERIC Address for orion.  The
+   next few ENGLISH words are the NAME addresses that can be used for orion,
+   "orion.cair.du.edu", "orion.du.edu", "orion".  ALL of these NAME addresses
+   will return the NUMERIC address "130.253.1.15" which IRC will use to
+   connect to the TARGET UNIX. (when I say TARGET UNIX I am refering to the
+   UNIX you want to connect to for IRC). Any futher questions about
+   /etc/hosts should be directed to "man hosts".
+
+
+Appendix B: Enabling Summon Messages
+
+   +-----------------------------------------------------------------------+
+   |            E N A B L I N G    / S U M M O N    M E S S A G E S        |
+   +-----------------------------------------------------------------------+
+
+   *NOTE* You must have ROOT or special access to the GROUP tty ('/dev')
+   to do this. If you want to allow users around the world to summon
+   users at your site to irc, then you should make sure that summon works.
+
+   The "IRCD" program needs access to the GROUP of '/dev'.   This
+   directory is where user TTY's are stored (as UNIX treats each Terminal
+   as a FILE!)   IRCD needs GROUP ACCESS to /dev so that users can be
+   SUMMONED to the program by others users that are *in* the program.
+   This allows people from other Universities around the world to SUMMON
+   your users to IRC so that they can chat with them.  Berkeley, SUN, HP-UX
+   and most of the newer versions of UNIX check to see if a USER is
+   accepting MESSAGES via the GROUP access rights on their TTY listing
+   in the /dev directory. For example an entry in '/dev' looks like this:
+
+  (Unix Path on BSD 4.3 UNIX is:  /dev/ttyp0)
+
+        crw------- 1 jtrim     20,     0 Apr 29 10:35 ttyp0
+
+   You will note that 'jtrim' OWNS this terminal and can READ/WRITE to this
+   terminal as well (which makes sense because I am ENTERING DATA and
+   RECEIVEING DATA back from the UNIX).  I logged into this particular
+   UNIX on "April 29th" at "10:35am" and my TTY is "ttyp0".  But further
+   of *note* is that I do not have my MESSAGES ON! (mesg n)  -- This is
+   how my terminal would look with MESSAGES ON (mesg y):
+
+        crw--w---- 1 jtrim     20,     0 Apr 29 10:35 ttyp0
+
+   With my MESSAGES ON (mesg y) I can receive TALK(1) requests, use the 
+   UNIX WRITE(1) command and other commands that allow users to talk
+   to one another.  In IRC this would also allow me to get IRC /SUMMON
+   messages.  To set up the "IRCD" program to work with /SUMMON type
+   the following:  (using ROOT or an account that has access to '/dev').
+
+       % chgrp tty ircd
+       % chmod 6111 ircd 
+
+   The above commands read: "Give IRCD access to GROUP tty (which is /dev)
+   and then when ANYONE runs the IRCD allow SETUID and SETGID priviliges
+   so that they can use the /SUMMON command.
+
diff --git a/doc/conf.doc b/doc/conf.doc
new file mode 100644 (file)
index 0000000..ad243d7
--- /dev/null
@@ -0,0 +1,773 @@
+                    -- ircd.conf Programming --
+ (written for DALnet release ircd) v1.01 by Roddy Vagg -- <roddy@dal.net>
+                (Updated for StarIRCD 5.26 by Remmy)
+                        --------------------
+
+       1) ............................. Introduction
+       2) ............................. ircd.conf Basics
+       3) ............................. ircd.conf Lines
+        3.1) .......................... M Lines
+        3.2) .......................... A Lines
+        3.3) .......................... Y Lines
+        3.4) .......................... I Lines
+        3.5) .......................... O Lines
+        3.6) .......................... U Lines
+        3.7) .......................... C and N Lines
+        3.8) .......................... K Lines
+        3.9) .......................... Q Lines (server form)
+        3.10) ......................... Q Lines (nickname form)
+        3.11) ......................... H Lines
+        3.12) ......................... P Lines
+        3.13) ......................... X lines
+        3.14) ......................... Summary
+
+                        --------------------
+
+1) Introduction:
+
+  If you are running, or planning on running an IRC server for DALnet,
+ you will need to setup an ircd.conf, your ircd.conf must meet the
+ requirements of a linked DALnet server which means it must contain all
+ the standard DALnet lines, these will be listed at the bottom of this
+ document.
+
+                        --------------------
+
+2) ircd.conf Basics:
+
+  When you compile your server, you must specify the correct paths to
+ where you plan on keeping your ircd.conf, for simplicity it is recomended
+ that you keep it in the same diretory as your ircd binary and other ircd
+ files.
+   note: You need only supply full pathnames for DPATH and SPATH, the
+ other defines will only point to files under these directories so you
+ need not put full path names.
+  For security reasons, your ircd.conf should have permissions set to 600,
+ if other users on your system gain access to view the file they may be
+ able to breach the security of your server and compromise the whole
+ network.
+  When you have made your ircd.conf you may check it with the program
+ `chkconf', this program is supplied with the source code release and will
+ be installed into your ircd directory when you run `make install',
+ `chkconf' will check your ircd.conf for errors so is a usefull tool for
+ beginners to ircd.conf.
+  Your ircd.conf will be made up of a series of lines, each line is used
+ for a different purpose in the running of your server, some lines are
+ mandatory for ircd, so you must enter these lines or your server will not
+ start, these lines are listed below.
+  You may enter comments in your ircd.conf with the use of a hash mark (#)
+ at the beginning of a line, it is recommended that you make full use of
+ this to add comments to everything you put in your ircd.conf so you dont
+ have any problems later.
+   eg: Put a contact email address and the name/nick of the server admin
+       above each C/N line pair.
+  When ircd reads the ircd.conf file, it does it upside down, so lines with
+ higher prefrence should go lower in the file, this will be explained later.
+
+                        --------------------
+
+3) ircd.conf Lines:
+
+  Each type of line in this section will be given a rating of how needed
+ it is in the running of the server, the ratings are:
+
+     MANDATORY: you absolutely MUST have this line
+     NETWORKED: you must have this line if plan on connecting your server
+                to other servers. (note: you can run ircd stand alone)
+     SUGGESTED: it is highly suggested that you use this line
+      OPTIONAL: it's completely up to you whether to define this or not
+   DISCOURAGED: you really really should not use this line if at all
+                possible.
+
+ Note that "*" in a field indicates an "unused" field.
+
+                        --------------------
+
+3.1) M Lines: [MANDATORY]
+
+ This line sets your server's name, description, and port number.
+ If you are to be a part of DALnet you will be assigned 2 different
+ DNS entries for your ircd machine, the 1st is for general public use
+ and involves: <servername>.DAL.net
+ The second is for use between servers for identification, these take
+ the form of: <servername>.[<state>].<country>.DAL.net
+ If your server is located in the US or Australia, you will be given
+ a `state' field in your server's real name, otherwise your `state', or
+ `area' will not be included.
+ Most IRC networks default to port 6667 for their client connection's,
+ but DALnet uses port 7000 as its standard, you should compile ircd with
+ port 7000, not 6667, but you may open up port 6667 (it is recomended
+ that you do) with a P line (see later). Your M line's port number
+ should be the same as the number you defined in your config.h at compile
+ time.
+ Syntax:
+M:hostname:*:Description Of Your Server:7000
+ The 1st field should be the `real' name of your server, not the short
+ name.
+ The 2nd field is unused at the moment and should be left blank.
+ The 3rd field is your server's description, it is up to you what you
+ put in this field, but a short description of its geographic location
+ is recomended.
+ The 4th field is the port number you compiled ircd with. This should be
+ 7000 for DALnet.
+ Example:
+M:disney.us.dal.net::Walt's DALnet Server:7000
+
+                        --------------------
+
+3.2) A Lines: [MANDATORY]
+
+ This line sets your server's administrative information.
+ Whenever a user types /admin on your server (or /admin <servername>)
+ they will recieve the information you put here.
+ This line has no set information, so you may put arbitrary text if you
+ like, but it is recomended that you at least put your nick and email
+ address so users may contact you if need be.
+
+ Syntax:
+A:A little info about your server:Admin's nick/real name:contact address
+ There is no fixed standard, so you may put whatever you like in each
+ field, but you should put enough information for users to contact someone
+ responsible for the server.
+
+ Example:
+A:Disney's DALnet IRC Server:Admin - Walt Disney:walt@RIP.org
+
+                        --------------------
+
+3.3) Y Lines: [SUGGESTED]
+
+ These lines define connection classes. They allow you to fine-tune
+ your incomming and outgoing connections, both server and client types.
+ These classes are for use with C, N, I and O lines, more on this in later
+ sections. DALnet has a set of Y lines that each server must use for their
+ server connections, these are listed below and again at the bottom of this
+ document. Client connection classes are your responsibility, you must
+ make up your own set of Y lines for client connections based on your own
+ situation (netwise location, machine, etc).
+ Connection classes define a number of parameters for connections, these
+ include:
+  o Ping frequency of a silent connection.
+  o Connect frequency (for server connections only!).
+  o Maximum number of links allowed on the specific connection class.
+  o Maximum sendq allowed for the connection before it is dropped.
+ Your Y line numbers are not arbitraty. For server connection classes, the
+ higher the class number, the higher the priority the connection's are given
+ when auto-connecting, (see C/N lines below).
+ - Ping frequency: When a connection is silent for this period of time
+ the server will send a PING to the connection, if the client/server
+ on the connection does not reply after a set period of time, the
+ connection will be dropped. A value in this field will override the
+ ping frequency defined at compile time in your config.h. For server
+ connection classes, you should have the same ping frequency on both ends
+ of the link, so you should stick with the standard DALnet classes.
+ - Connect frequency: Since clients connect to servers and NOT the other
+ way around, only server connection classes need to have a connect
+ frequency. Client classes should have this field set to 0. When a server
+ listed in the server's ircd.conf (see C/N lines) is missing and belongs
+ on a conenction class that is holding less connections that defined by
+ the max links field, the server will keep on trying to connect to
+ the missing server. The ammount time between connection attempts is what
+ you define in this field.
+  example:
+   server1 and server2 are listed in server0's ircd.conf but the only
+   visible server to server0 is server1, both server1 and server2 are
+   in server0's ircd.conf on the same connection class that allows for `2'
+   links, server0 will go looking for server2 and try to connect to
+   it each `connect frequency' seconds until the server becomes visible
+   again, either by direct connection to server0, or by connection to server1
+ - Maximum number of links: Each Y line should have a restriction on the number
+ of connections allowed on the class. For client connections, when the limit
+ is reached on a particular class, connecting clients trying to connect
+ through this class are rejected. A server connecting on a `full' connection
+ class will be allowed as this number on server connection classes is used for
+ auto-connect purposes. As shown in the above example, when a missing server
+ is listed for a particular connection class, and the class is not `full',
+ your server will try and connect to this server untill it becomes visible
+ again. Servers being connected manually on a `full' connection class via the
+ /connect command will be allowed as long as you compiled with MAXIMUM_LINKS
+ high enough to accomidate all of your server connections. (you must compile
+ as a HUB if you wish to hold more than one server connection, also see H
+ lines later in this document).
+ - Maximum sendq: SendQ defines the `que' of data waiting to be sent to the
+ client/server on the other end of the connection. SendQ's will build up if
+ the client requests more data than the link can handle, say if they issue the
+ /list command on a network with a lot of channels and they are only on a
+ 14.4K link, their sendq on the server will build up as all the data cannot
+ be sent at once, the sendq size will decrease as the data is sent, and
+ increase as more data is requested. Clients will normally sit with a sendq of
+ 0, it is `abnormal' for a sendq to be high for a client for a long period
+ of time. When 2 servers connect, they must send their own data to
+ eachother, this data includes: all the users on the server and already
+ connected servers, channels, user modes, channel modes, topics (DALnet only)
+ etc. When there are many clients on a particular side of the connection, a
+ sendq will build up, especially if the link is slow, or already congested
+ (example: the link from Australia to the US). When the sendq built up reaches
+ the max sendq defined in the connection class for the particular
+ client/server, the connection will be dropped. If max sendq's are
+ particularly high, it will allow clients/server to take up excess memory on
+ the ircd machine so a limit should be placed, especially on client connection
+ classes. (IMPORTANT!) If any value of max sendq defined in a connection
+ class exceeds the value defined at compile time in your config.h, the sendq
+ value will default back to the compile time sendq. If your sendq field in
+ a Y line is empty, the class will use the default defined in your config.h
+ SendQ's for all connections on your server can be viewed with the
+  /stats l <servername>
+ command, this will show information for all your server's current links.
+ You should have a set of standard server connection classes, at least one
+ client connection class, and an Operator class. (see relevant parts of this
+ document for notes on each of these)
+
+ Syntax:
+Y:Class #:Ping frequency:Connect frequency:Max links:Max sendq
+
+ Examples:
+Y:1:90:0:20:10000
+ In this case, connect-frequency is 0 indicating that this is a client
+ class (servers never connect to clients, it is the other way around).
+ Clients may only idle for 90 seconds before being pinged by the server.
+ The number of clients allowed to use this class is 20.
+ Clients may only build up a sendq on the server of 10000 bits.
+
+ These are the standard server Y lines used on DALnet:
+
+# Connecting a hub to a hub
+Y:20:10:300:1:3000000
+# Connecting a US hub to a US leaf
+Y:30:45:0:0:2000000
+# Connecting a US leaf to a US hub
+Y:35:45:20:1:2000000
+# Connecting a US hub to an EU leaf
+Y:40:60:0:0:2200000
+# Connecting an EU leaf to a US hub
+Y:45:60:20:1:2200000
+# Connecting a US hub to an AU leaf
+Y:42:240:0:0:2200000
+# Connecting an AU leaf to a US hub
+Y:43:240:60:1:2200000
+
+                        --------------------
+
+3.4) I Lines: [MANDATORY]
+
+ These lines are the ones initially responsible for letting clients connect
+ to your server. So called `client-authorization' lines, they define who
+ may connect, and which connection class they will connect through.
+ I lines, like C, N and O lines refer back to Y lines, as they allow
+ connections, and each connection to ircd needs to be assigned to a
+ connection class. If you dont provide a connection class, the connection
+ will be governed by the defaults set at compile time in your config.h.
+ When a client connects to the server, it gives its own information,
+ this information includes username, nick and can include a password, the
+ server then goes through its client-authorization rules (I lines) to see
+ if the client fits any of the connection criteria.
+ The rules for connection on the I lines are read from right to left, so
+ if a connection is made, it is made on the right most rule it matches on
+ the line. Also, since the ircd.conf is read upside down, the server will
+ put the client on the lowest I line matching the client information. This
+ means that if the 1st rule the client can connect on matches a connection
+ class (Y line) that is `full' (see above), the client will be rejected,
+ even if there is a line further up in the file that the client matches on
+ that uses a connection class that has room for more clients. This means
+ that I lines may be used in much the same fashion as K lines (see later)
+ to block certain clients. It also means that you may place certain clients
+ on many different connection classes. (examples later)
+ Syntax:
+I:IP-address-mask:optional password:host/domain-mask::connection class (opt)
+ Wildcards (`*') may be used in the mask fields (1 and 3) to allow for
+ very broad connection rules. Ident (for more information on this, see
+ rfc1413) can also be used by placing an `@' in the mask fields in the
+ appropriate positions. If you don't want to use ident, only give the
+ host/IP part of the connecting addresses, if you add a @ (usually used
+ as *@), ircd will try and use ident to check the real username of the
+ client, any connecting clients on host's that are running ident that
+ give usernames that dont match those found by ircd will be rejected by
+ the server. If the host is not running ident, a `~' will be placed in
+ front of the username of the connecting client to show that the its
+ host isnt running ident.
+
+ Examples:
+
+I:*@*:foobar:*@*::1
+ This line will allow anyone from any host that uses the password
+ "foobar" to connect through connection class 1 (Y line 1), the server
+ will also try and use ident to verify the username of the client.
+ Placed at the top of the I lines in your ircd.conf, this line may serve
+ as a fall-through for connecting clients, any client that does not match
+ any other I line but gives the password "foobar" will be able to connect
+ through this line (If Y line 1 has space).
+
+I:205.133.*::*.toledolink.com::1
+ This is a standard vanilla I: line which will permit anyone with an IP
+ address starting with 205.133 OR with a hostname ending in
+ .toledolink.com to connect to the server. remember, ircd uses the
+ right-most match, so if I connect as rmiller@glass.toledolink.com
+ (which is rmiller@205.133.127.8) I will show up on irc as
+ rmiller@glass.toledolink.com since that is the first match it found.
+ (Even though the second match is valid). Any clients comming through
+ on this line will use connection class 1.
+
+I:*@205.133.*::*@*.toledolink.com::1
+ Same as above, but the server will use ident. You may even specify
+ certain usernames with ident I lines, but they will only match if their
+ host is running ident.
+
+I:NOMATCH::rmiller@glass.toledolink.com::1
+ Putting NOMATCH in the first field will stop the ircd from matching
+ automatically against the IP address and it will force the server to
+ match against the hostname. (the "NOMATCH" string is not mandatory, you
+ can use any arbitrary text in the first field).
+
+ Bulk example:
+I:NOMATCH::*@*::1
+I:NOMATCH::*@*.fr::2
+I:NOMATCH::*@*.de::3
+I:NOMATCH::*@*.se::4
+I:NOMATCH::*@*.au::5
+I:129.180.*::*.une.edu.au::6
+ In this example, conencting clients will 1st be matched against the mask
+ *.une.edu.au, if they match they will be placed on connection class 6
+ (note: if 6 is full, they will be rejected, they wont be passed on to the
+ next I line), then tried against the IP 129.180.*, if they match, they will
+ be placed on class 6. If the client dosen't match either of these masks, they
+ will be tried against the mask *.au, so if they are from Australia, but are
+ not from *.une.edu.au they will be placed on class 5. This goes on through
+ the other lines, being placed on the various connection classes if they match
+ any of the indicated host masks, if the client is not from the IP 129.180.*,
+ Australia, Sweden, Germany or France, they will be connected through the
+ final (top) I line as it serves as a fall-through, so these clients will be
+ put on class 1.
+
+                        --------------------
+
+3.5) O Lines: [OPTIONAL]
+
+ These lines provide rules as to who may gain Operator status on your server.
+ O lines are much like I lines in their operation and syntax.
+ Servers need not have any Operators as ircd, given well defined connection's
+ can perform all of its functions automatically. Server admins have the
+ ability to `kill -HUP' the server's PID to rehash the config file, removing
+ the need to use the /rehash command. However, a well running network such as
+ DALnet needs operators to oversee the users of the server, and make sure
+ users actually enjoy their time on IRC without being continually harrased
+ etc by troublematers.
+ O lines come in two forms, normal `O' lines, and Local Operator `o' lines.
+ Normal O lines give users power over the whole network, to use commands
+ such as /kill, local Operators only have power on their local server, that
+ is, the server where they can use the /oper command to make themselves +o.
+ Abilities of Operators and Local Operators can be defined in your config.h.
+ When a user issues the /oper command to the server, the server will search
+ through all listed O lines for a match of the user's mask, much the same way
+ as I lines. As with I lines, you may specify the use of ident by placing an
+ `@' in the appropriate positions.
+
+ DALnet's O:lines have an added field, the "operflag" field. Whereas before,
+ you simply had control over whether an oper was global or local, now it is
+ possible to specify almost all the access an oper has, specifically, by
+ choosing the operflags in their O:line. For more information, read the
+ example.conf located in this directory.
+
+ Syntax:
+
+O:hostname:password:nickname:operflags:class
+ See I lines for rules about the hostname and using ident.
+ If you use ident, a client matching the hostname must have ident running on
+ their host to be able to +o themselves.
+ If you compiled defining oper passwords to be crypted, you must 1st crypt
+ the plaintext using mkpasswd, a program supplied with the ircd distribution.
+ See src/crypt/README for more information on this.
+ The nickname is the nickname they must pass with the /oper command
+  ie:
+   /oper <nickname> <password>
+ The class is the connection class to be used when the user /oper's using
+ the O line, they connect using the standard I -- Y lines, but when they
+ /oper succusfully they are passed across to the new Y line.
+
+ Examples:
+
+O:RIP.org:waltspass:Walt:O:10
+ This line will allow anyone on the host RIP.org (running ident or not) to
+ issue the command `/oper Walt waltspass', at which point they will be moved
+ over to class 10 and be made usermode +o.
+
+o:*@*:GiJ.E\hGyjhaW:Anyone:o:10
+ This line allows anyone who is running ident to issue the command:
+ `/oper Anyone <password>'
+ This line comes from an ircd that uses encrypted passwords, so the user must
+ have the plaintext password.
+ When this line is used with success, the user will be a Local Operator since
+ the line only has a small `o'.
+
+                        --------------------
+
+3.6) U Lines: [OPTIONAL]
+
+ These lines define which server(s) on the network your server is connected
+ to will be able to `hack' channel modes.
+ On DALnet, services.dal.net is given this power, this allows the server
+ to change modes on channels without being a channel operator, the
+ commonly used form is ChanServ changing channel modes while not in the
+ channels.
+ If you are connected to a network such as DALnet that requires you to have
+ certain U lines and you don't have them, your server will cause problems
+ to the other servers when the server(s) that require U lines attempt to
+ change channel modes.
+ U lined servers also have the capability to add Akill's to your server,
+ Akill's are much the same as the /kline command except that they show up
+ as A: lines on /stats k.
+
+ Syntax:
+U:servername:*:*
+ The last 2 fields are currently unused so you only need to give the U
+ lined server's name.
+
+ Example:
+U:services.dal.net::
+U:services2.dal.net::
+ Both these lines are required on all DALnet server's, they allow servers
+ with the name's `services.dal.net' and `services2.dal.net' to hack channel
+ modes.
+
+                        --------------------
+
+3.7) C and N Lines [NETWORKED]
+
+ These lines are always used in pairs, one will not work without the other.
+ C lines define who your server may connect to, while N lines define what
+ servers may connect to you.
+ When two servers connect, they both send eachother the `SERVER' command,
+ this command contains the server name and server info (set by M lines)
+ along with this command is sent a password with the PASS command, C and N
+ lines provide a set of rules governing the connection between servers
+ given the details of the server and pass command's.
+ When one a server initiates the connection, the other server will check
+ the details of the incomming server against its N lines, if a match is
+ found, the server will return the server and pass command's to the
+ initiating server, which will also check its N lines for a match.
+ For a server to initiate a connection, it must have a C line. C lines
+ tell the server where to go to make the connection and what to send for
+ the pass command.
+ What this all means is that for two servers to make a complete connection,
+ they must have both C and N lines to refer to for the other server.
+
+ Syntax:
+C:remote server's hostname/IP:password:remote server's name:port:class
+N:remote server's hostname/IP:password:remote server's name:host mask:class
+ The remote server's hostname/IP should be the location on the internet that
+ the server can be found. IP addresses are prefered as they are more secure,
+ and can be a little quicker for the server. As with I and O lines, ident
+ can be used with this 1st field to specify the username the ircd on the
+ remote server is running from (if the remote server is running ident), to
+ use ident with C/N lines, place the username with an @ before the hostname.
+ The password should be crypted if you compile ircd specifying that link
+ passwords should be crypted. Your link passwords should be very secure, as
+ they provide more power, if hacked, than Operator passwords do. However
+ crypted link passwords can be very akward to keep track of.
+ Your C line password is the password used in the pass command, while your
+ N line password will be used to check against the pass command used by
+ incomming servers. So, your C line password should match the listed
+ server's N line password, and your N line password should match their C
+ line password.
+ If you compile your ircd specifying crypted link passwords, you only need
+ to crypt your N line passwords, use the same method as with O line
+ passwords. If you crypt your C line passwords, your link will not work!
+ Crypted passwords are a one sided affair, because one server crypts its
+ N line passwords does not mean the connecting servers must crypt their
+ C line passwords for that server.
+ For the 3rd field, the remote servers `name' should be used, this name is
+ the one given in that servers M line (see above). This name will be sent
+ with the SERVER command, so it must match the one given. The C and N line
+ pair should have the same name for this field.
+ The 4th field of C lines may contain the remote servers connection port.
+ Even though DALnet runs all its servers with a standard port 7000 open,
+ server -- server connections should be taken place through port 7325. It is
+ not mandatory that you place a port number in this field. If you don't give
+ a port number, the server will not try and autoconnect to the listed
+ server. If you do give a port number, the server will only try and
+ autoconnect to the listed server if there is enough room on the connection
+ class listed at the end of the C line (connection classes are covered in
+ more detail above, under Y lines), and the listed server is not visible
+ (ie: it is not connected to the network). If you don't give a port number,
+ any /connect commands for this C line will use the default port specified
+ in your config.h unless a port is given with the command. If you do put a
+ port number, any /connect command's will use this port unless another port
+ number is given with the command.
+ The 4th field of N lines is called the `host mask', this defined how many
+ parts of your hostname the incomming server will mask to. So, if your
+ server's name is disney.us.dal.net, and you want the connecting server to
+ see you as *.us.dal.net you will give a host mask of 1 in your N line. This
+ field should normally be left blank.
+ The 5th (last) field of both C and N lines gives the connection class to
+ place the connection on. If your C line has a 42 in this field, and your
+ server initiates a connection through this line, the connection will be
+ placed on class 42, however, if You have a 42 in your C line and a 43 in
+ your N line and an incomming server initiates a connection via this N
+ line, the server connection will be placed on class 43.
+ Examples: 
+C:143.53.233.32:mypass:somewhere.fr.dal.net:7325:35
+N:143.53.233.32:yourpass:somewhere.fr.dal.net::35
+ This set will allow a server named somewhere.fr.dal.net to connect to your
+ server if it has the IP address of 143.53.233.32 and gives a password of
+ `yourpass'. This connection will be governed by connection class 35.
+ If your server recieves the command /connect somewhere.*, it will try and
+ connect to the IP 143.53.233.32 through port 7325 and give the password
+ `mypass'.
+
+C:143.53.233.32:mypass:somewhere.fr.dal.net:7325:35
+N:143.53.233.32:yourpass:somewhere.fr.dal.net::35
+C:ircd@176.43.652.31:apass:elsewhere.jp.dal.net:7235:35
+N:ircd@176.43.652.31:THEpass:elsewhere.jp.dal.net::33
+ Both these set's will work as explained above, but if your Y line defining
+ class 35 has `max links' set to 1, and one of these servers is connected to
+ your server, your server will not try and autoconnect to the other since
+ the Y line is `full', but it will accept any incomming connections from the
+ other server and any /connect commands given for this server. If your Y
+ line allows for more connections but your C lines do not have port numbers,
+ your server will not try and autoconnect.
+ Since the second set in this example has a username, ident will be used to
+ authenticate any connections made to this server. If the listed server does
+ not run ident, or the incomming connection comes from another username, the
+ connection will be rejected.
+ If a connection is made via the second set by your server, the connection
+ will be ruled by connection class 35, if the other server initiates the
+ connection, the connection will use class 33.
+ Autoconnect C/N line pairs can be given prefrence over other pairs by placing
+ them lower in your ircd.conf, the lower the line, the higher the priority
+ when autoconnecting.
+ Connection classes and C/N line set's allow you to refine your autoconnects
+ to a very high degree, with practice you can have your server running so
+ it does not need any help.
+
+                        --------------------
+
+3.8) K Lines [OPTIONAL]
+
+ These lines restric access to certain users to your server based on
+ user@host matches and time of the day.
+ K lines can come in 3 forms, only one of which you can specify in your
+ ircd.conf, this type will show up as K on /stats k, the other other types
+ are `AutoKill' which will show up as A on /stats k, and `kline' which will
+ show up as k on /stats k. AutoKill's are set by U lined servers (see
+ above), they act in the same way as K lines except that they are set
+ remotly and are usually set on all servers, they dissapear when you
+ /rehash or restart your server. klines are set via the /kline command,
+ they operate more like AutoKill's than K lines because they also dissapear
+ when you /rehash, or restart the server. The /kline command can be used on
+ nicknames that appear on IRC, or you can use a user@host mask. If the
+ /kline is done on an existing nickname, a kline will be set with that users
+ mask and they will be killed off the server.
+
+ Syntax:
+K:hostmask:time/comment:username
+ The hostmask is the host that the user will have on IRC, this may be an
+ IP address or a standard host name. The time/comment field may either
+ contain nothing, a set of times, or a comment. This field should not
+ contain spaces, if you place a comment in the field, you should try and
+ be creative in your avoidance of spaces. The stntax of time specification
+ is:
+  from-to[,from-to[,from-to]]....
+ Again, you should not use spaces in this field, but you may specify as
+ many time periods as you want/need. 24 hour time should be used, AM and PM
+ will not work.
+ The username will be the username that shows up on IRC.
+ Wildcards (`*', `?') may be used with K lines in both the hostmask and
+ username fields.
+
+ Examples:
+K:RIP.org::walt
+ This will reject any user who appears as `walt@RIP.org'.
+
+K:*.edu:0800-1200,1300-1700:*
+ This will reject any user from any host with a top level `edu', In other
+ words, anyone appearing as *@*.edu are banned from the server.
+ This ban is only present during the hours of 8AM to 12AM, and again from
+ 1PM to 5PM, at times other than this, the K line will not be active.
+
+K:*::*rad
+ This K line will reject anyone with the username `rad', or anything ending
+ in `rad'. This ban will dissalow anyone using `rad' running ident or not.
+ You must always take into account the ident character (`~') that is placed
+ infront of usernames when their host is not running ident. If you place a
+ K line on a username `rad' the user will be banned only if they are running
+ ident, but if this user can turn off ident they can appear as ~rad, this
+ will allow them to bypass any ban of username `rad'. So, wildcards should
+ be used with usernames to take into account the ability to turn ident on
+ and off. (The ability to change usernames can only be tackled with a `*'
+ in the username field)
+
+                        --------------------
+
+3.9) Q Lines (server form) [DISCOURAGED]
+
+ Server form Q lines on DALnet servers are used to dissalow operators on
+ certain servers to use commands such as remote /kill's, and remote
+ /connect's, this will effectivly restrict the operators on the server to
+ local operator priveleges. These lines are usually only used for `test'
+ server situations. If a server isn't officially part of DALnet, they may
+ be temporarily linked and Q lined, this means the server can be tested
+ while not posing a threat to the rest of DALnet. Q lines need only be
+ placed on the hub connecting the `test' server.
+
+ Syntax:
+Q:*:*:servername
+ The 1st 2 fields are currently unused. A Q line placed on a hub connected
+ to the named server will dissalow operators on the server to affect other
+ DALnet users/servers.
+
+ Example:
+Q:::test-server.my.dal.net
+ Q line a server with the name `test-server.my.dal.net'.
+
+                        --------------------
+
+3.10) Q lines (nickname form) [OPTIONAL]
+
+ Nickname form Q lines have the ability to deny certain nicknames to users.
+ If a nickname is Q lined, the only people allowed to use those nicknames
+ are Operators. Q lines, like most other things in your ircd.conf, are local
+ only, for a nickname to be Q lined on a whole network all servers must have
+ a Q line for that nick. Q lines may also contain comments, these comments
+ are given to the user when they attempt to use the nickname and are asked
+ to choose another.
+
+ Syntax:
+Q:*:reason why nick is quarantined:nickname
+ The 1st field is currently unused. The 2nd field is the comment sent to any
+ user attempting to use the nickname. Unlike K lines, you may use spaces.
+ The last field is the nickname to be quarantined, this nickname may contain
+ wildcards.
+
+ Examples:
+Q::No nicknames on MY server!:*
+ This Q line will dissalow any nicknames on the server giving the reason:
+  No nicknames on MY server!
+ Only Operators will be allowed to use any nicknames, but since you must be
+ a user before you can be +o, you will effectivly ban everyone from your
+ server.
+
+Q::Do not use the Lords name in vain!:God
+ Anyone attempting to use the nickname `God' on your server will be told
+ that they must find a new nickname and will be given the reason:
+  Do not use the Lords name in vain!
+
+ DALnet has a set of standard Q lines that should be in place on all
+ server's. They are as follows:
+
+Q::Reserved for services:*Chan*S*rv*
+Q::Reserved for services:*Nick*S*rv*
+Q::Reserved for services:*Memo*S*rv
+Q::Reserved for services:*Oper*S*rv*
+Q::Reserved for services:*Help*S*rv*
+Q::Reserved for operators:DALnet
+Q::Reserved for operators:IRC*op*
+Q::Causes problems with mIRC:Status
+
+                        --------------------
+
+3.11) H Lines [OPTIONAL/NETWORKED]
+
+ These lines are similar to L lines, except that they define what servers
+ may act as a hub while connected to you. That is, which servers may
+ introduce other servers behind them.
+ You may limit what servers may be connected behind the H lined server.
+
+ Syntax:
+H:servers which are allowed behind the hub:*:hub servername
+ The 1st field defines what servernames the H lined server is allowed to
+ introduce. Wildcards are allowed.
+ The 2nd field is currently unused and should be left blank.
+ The 3rd field should be the exact name of the server allowed to be a hub
+ while connected to you. You may not use wildcards with this field unless
+ the server's name includes a `*' (See N lines for host masking).
+
+ Examples:
+H:*::dal-hub.us.dal.net
+ This line will allow the server with the name `dal-hub.us.dal.net' to act
+ as a hub server while you are connected to it, there are no restrictions
+ on the names of the servers it may introduce.
+
+H:*.us.dal.net::usa-hub.us.dal.net
+ This line will allow the server named `usa-hub.us.dal.net' to act as a hub
+ while your server is connected to it, but it is limited to introducing
+ servers with names matching `*.us.dal.net', so any servers trying to
+ connect to `usa-hub.us.dal.net' with a name such as `bad-link.nz.dal.net'
+ will be rejected by your server.
+
+                        --------------------
+
+3.12) P lines [OPTIONAL]
+
+ These lines will open up ports other than the port you specified in your
+ config.h when you compiled your ircd.
+ Using internet domain ports below 1024 mean that you must run ircd from
+ inetd. ircd can listen to ports in the UNIX domain as well as the internet
+ domain. With UNIX domain ports you must give a unix socket file, you must
+ also compile ircd with UNIXPORT defined in your config.h.
+ You may limit usage of ports in the internet domain to certain hostmasks.
+ You do not need to provide a P line for the default port you defined in
+ your config.h, only extra ports you wish to open. You should compile ircd
+ to run from port 7000, but it is recomended that you add a P line for port
+ 6667 as most IRC clients default to this port when connecting. If you are
+ connected to DALnet, you should have a P line for port 7325, this is the
+ standard server connection port for all DALnet servers.
+
+ Syntax:
+P:hostmask or UNIX socket file:*:*:port number
+ The 1st field should either specify a path to a UNIX socket file, or give
+ a hostmask to match against connecting clients on this port. Clients not
+ matching this mask will be rejected.
+ The 2nd and 3rd field's are currently unused, and should be left blank.
+ The last field is the port number to open up and listen to for connections.
+
+ Examples:
+P:*:::7325
+ This will open up the DALnet server connection port and wait for
+ connections. This line is mandatory if you run a server connected to DALnet
+
+P:*.net:::6665
+ This line will open up port 6665 and wait for connections, connections from
+ hosts not matching `*.net' will be rejected.
+
+P:/tmp/.ircd:*:*:6666
+ This line will open up the port 6666 in the UNIX domain, with a socket file
+ of: /tmp/.ircd.
+
+                        --------------------
+
+3.13) X lines [SUGGESTED]
+
+ The /die and /restart commands can occasionally be misused or accidentally
+ typed, causing a lot of trouble as hundreds of users are immediately
+ disconnected and the server is thrown into chaos, sometimes unrestartable
+ for hours. Furthermore, it can be maliciously used (in the case of O:line
+ hacking, it's just about as much damage as you can cause). The X line tries
+ to reduce the risk of this happening by providing passwords for the /die &
+ /restart commands. The format of the X:line is simply:
+
+ Syntax:
+X:password_for_die:password_for_restart
+
+ Example:
+X:foo:bar
+
+                        --------------------
+
+3.14) Summary:
+
+ Well, thats it for the lines you may use in your ircd.conf. Remember that
+ircd.conf is an art, just like any other type of programming. Some parts
+are particularly easy, but other's, like Y lines, can take a while to get
+used to. Given a little time experementing with lines on a network of
+servers, you will become well versed in ircd.conf programming.
+
+Good luck!
+                        --------------------
+
+[ Some text stolen from original example.conf modified for DALnet ]
+
+ -- Roddy Vagg / <roddy@dal.net>
+
diff --git a/doc/example.conf b/doc/example.conf
new file mode 100644 (file)
index 0000000..94cb57e
--- /dev/null
@@ -0,0 +1,322 @@
+# IRC - Internet Relay Chat, doc/example.conf
+# Copyright (C) 2000, Gerhard Mack
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#
+# Example ircd.conf file for Star4.00.Velocity or newer.
+#
+#
+# In this file I will explain what the different lines in the ircd.conf file
+# do. You may want to test config changes with chkconf before you try them
+# on your server to prevent disruption to your users due to errors.
+#
+# In this file I use the term "ircd" to designate an irc server (daemon).
+#
+# The following terms can be used to determine if you need a line or not:
+# MANDATORY:   The ircd requires the line to operate.
+# NETWORKED:   The ircd needs the line when it is linked to one or more other
+#              servers
+# SUGGESTED:   It would most likely be a good idea if you used this.
+# OPTIONAL:    It's up to the person doing the configuration.
+# DISCOURAGED: Probably not a good idea, except in limited circumstances.
+# OBSOLETE:    Old, out of date, don't use this
+#
+# NOTE: Many networks will require certain configuration lines to allow 
+#       their networks to run more smoothly. If you are given lines by
+#       the network you are linking to then they should be implemented 
+#       regardless of this file's recommendation.
+#
+# This file goes through the lines in the order they should be placed in 
+# a functional ircd.conf file.
+#
+# NOTE: Keep in mind that this file is read in REVERSE, that means the
+#       bottom line is the first one read and the top line is the last. 
+#       Remember this when you are doing anything that needs to be in 
+#       a specific order such as I-lines.
+#
+#
+#
+# M: MANDATORY. This line specifies the basic information the server needs
+# about itself to operate.
+#
+# M:server name:IP address:Server description:port
+# Server name is the name the server shows to clients and other servers on
+# the network.
+# IP address tells the server what address on the machine to bind to, if 
+# you fill the field with a "*", or leave it empty, it will bind itself to 
+# all addresses it finds on the machine.
+# Server description is the description of the server shown to clients and
+# other servers on the network.
+# Port is the default port the server will attach itself to.
+# 
+# This line names the server server1.starchat.net and attaches itself to
+# all addresses attached to the machine on port 7000:
+#
+M:Server1.starchat.net:*:Cool new server:7000
+#
+#
+#
+# A: MANDATORY. This is your administration info that is shown when someone
+# on your server types "/admin" This is a good place to keep the information
+# users need to contact someone in charge.
+#
+# ":" Designates a new line, other than that it's pretty much free form.
+#
+# Here is a format often used:
+# A:server's provider:admin: admin's email : coadmin: coadmin's email 
+#
+A:Lame1 colocations:admin John Random: email JRandom@lame1.com
+#
+#
+#
+# Y: SUGGESTED. Defines connection class types and behavior.
+# Class is the number representing the Y line, Ping frequency defines the
+# duration between pings. Auto-connect frequency defines how often the server
+# will try to connect to the server defined with the corresponding connection
+# class: max connection defines the maximum number of connections the server
+# will allow using this connection class.  
+#
+# Sendq is the maximum space the server will use to buffer outgoing
+# messages.  If the sendq is filled, the affected connection is dropped.
+# I reccomend setting this low for clients (prevents flooding) and high 
+# for servers. 
+#
+# NOTE: The max connections for all Y-lines should add up to the total 
+# number of connections the the server can handle MINUS the number of
+# file desrcriptors that are needed for things like wingate checking and one
+# for the config file. ( 5-10 should do)
+#
+# For example: A server compiled for 256 max filedescriptors should have all
+# Y-lines add up to 250 (remove 6 for server internal use)
+#
+#
+# Y:class:ping frequency:auto-connect frequency:max connections:max sendq
+#
+# This is a typical client Y-line no auto-connect, max 20 connections.
+Y:1:60:0:20:100000
+#
+# This is an example of a Y-line that could be used on your primary hub
+# 
+Y:40:120:60:1:3000000
+# This is an example of a Y-line that could be used for your secondary hubs
+Y:50:120:0:5:3000000
+#
+#
+#
+# I: MANDATORY.  These lines define who can connect to a server, without
+# them no one is able to connect to the server.
+#
+# I:IP mask:password:domain mask::Y-Line (optional)
+#
+# IP Mask is the numeric ip mask (ex 192.168.*) if Arbitrary text is 
+# entered such as NOMATCH the I-line will only match based on the domain 
+# name and will disallow clients from addresses that lack reverse lookup 
+# The password is optional, and is needed to connect using that I line.
+#
+# The domain name is the name returned from a reverse DNS lookup, the Y-line 
+# of course is the Y-line that you want to correspond to the connection.
+# NOTE: I-lines are read starting from the bottom passing though each one
+#       until a line is found that matches the client's connection if none
+#       is found access to the server is denied.  
+#
+# This is a I-line that will allow anyone at all to connect.
+I:*@*::*@*::1
+# 
+# 
+#
+# P: SUGGESTED. Allows you to add ports and addresses/domain sockets other 
+# then the one specified in the M-line.
+#
+# P:address:*:*:port
+#
+# To have the server bind all addresses available on a given port leave the
+# leave the field blank.
+# NOTE: if a given port is already in use the server will simply fail to
+# load.
+#
+# This line would have the server look for connections to port 7000 on
+# the address 127.0.0.1 
+#
+P:127.0.0.1:*:*:7000
+#
+#
+#
+# O: SUGGESTED. O-lines define who has Oper access on the server.  How
+# necessary these line are depend on how well your site is configured and the
+# philosophy of the network you are linked to.
+#
+# O:address:password:nick:flags:Y-line
+#
+# If the server is compiled to use encrypted Oper passwords then you must 
+# use mkpasswd to convert the password from plaintext.
+#
+# NOTE: Addresses using the numeric IP address will work even if it
+#       resolves.
+#
+# flags:
+# a = can set +a Designates a services oper
+# A = can set +A to set server admin
+# b = can set temp klines using /kline
+# B = can remove temp klines using /unkline
+# c = can use /squit and /connect on locally connected servers
+# C = can use /squit and /connect on any server 
+# D = can shut down the server using /die
+# f = can see server flood kills by setting /umode +f
+# g = can send globop messages
+# h = can see helpop requests by setting /umode +h 
+# k = can kill clients connected locally
+# K = can kill any clients on the network
+# l = can send locop messages
+# n = can send local server messages (/msg $servername message to users) 
+# N = can send global server messages (/msg $*.net message to users)
+# o = designates a local oper includes flags: bBcfghklnruw
+# O = designates a global oper includes flags: CKNo
+# r = can /rehash the server
+# R = can /restart the server
+# u = can see local connects and disconnects using /umode +c
+# w = can send wallops (obsolete)
+#
+# NOTE: Access to everything is: AaDOR
+#
+O:192.168.2.*:notarealpassword:AnOper:OR:10
+#
+#
+#
+# X: SUGGESTED. Sets a password for the /die and /restart commands.
+#
+# X:Die password:Restart password
+#
+X:killme:restartme
+#
+#
+# 
+# C: NETWORKED. Connect lines, this line defines who your server can 
+# connect to. C-lines must be paired with an N line to work.
+# 
+# C:remote address:password:remote server name:port to auto-connect:Y-line
+# The address is the machine's Internet address, the server name is
+# the server's name on irc network as defined by the machine's M-line.  
+# They do not have to be the same.
+# NOTE: Using the numeric ip address for the machine's address is slightly 
+#       faster and more secure then using it's domain name. 
+#
+C:192.168.2.1:notarealpassword:hub.starchat.net:7325:45
+#
+#
+#
+# N: NETWORKED. This line defines what servers your server can connect 
+# to yours. N-lines must be paired with an C line to work correctly.
+#
+# N:remote address:password:remote server name::Y-line
+# The address is the machine's address on the Internet, the server name is
+# the server's name on irc network as defined by the machine's M-line.
+# They do not have to be the same.
+#
+# NOTE: Using the numeric IP address for the machine's address is slightly
+#      faster and more secure then using it's domain name.
+#
+N:192.168.2.1:notarealpassword:hub.starchat.net::40
+#
+#
+#
+# U: NETWORKED. Determines what servers may make changes to things like
+# channel modes without the server sounding alarms or attempting to stop
+# them.
+#
+# U:Server-Name:*:*
+#
+# This line allows services to have the access needed to do it's job.
+#
+U:Services.StarChat.Net:*:*
+#
+#
+#
+# H: NETWORKED. Hub lines. These define what servers a given hub is permitted 
+# to connect to the network.
+# H:servers the hub is allowed connect::name
+#
+# NOTE: The name is the name defined in the hub's M-line it might not be the
+#       same as it's address.
+#
+# This would allow hub.starchat.net to hub any servers while being directly
+# connected to your server.
+H:*::hub.starchat.net
+#
+# This line would only allow hub2 to have Mexican servers as leaves.
+H:*.mx.*:hub2.starchat.net
+#
+#
+#
+# Q: OPTIONAL. Nick quarantine.  This line prevents non opers from using
+# nicks covered by the Q-line.
+#
+# Q::reason:nick
+#
+Q::users may not use services nicks:*serv*
+#
+#
+#
+# q: OBSOLETE. Server quarantine. This line quarantines the specified
+# server, the line must be on ALL SERVERS connected to the network or net
+# spits will occur.
+#
+# q::reason:server
+#
+q::I have no idea why:unknown.starchat.net
+#
+#
+#
+# K: OPTIONAL. Kill Line. K-lines are a means of denying access to 
+# certain clients. 
+#
+# K-lines work best for addresses that are likely to be banned long term 
+# and therefore a temporary kline or akill won't do.
+#
+# K:address:reason:ident
+#
+# Address is the address or mask of the client you wish to ban.
+# NOTE: If you ban an IP address and reverse DNS is successful the 
+#       domain name returned will be checked and NOT the IP address, any bans 
+#       on the IP address will be IGNORED.  This applies to both Klines
+#       and A-kills.
+#
+# The reason field is what is shown to the client on the receiving end 
+# of the kline.
+#
+# The ident is checked against what is returned by the users, keep in mind
+# that this is easily changeable, and not useful if you're trying to keep a
+# particular user off the network, although it is a lot more logical for temp 
+# K-lines or A-kills since they are more readily changed.
+# This will ban any user from lame.com and tell them it was for mass
+# advertising:
+K:*.lame.com:Mass advertising:*
+#
+# This will ban anyone using the ident Sphere and tell them to get 
+# a new script.
+K:*:Get a new script:Sphere
+#
+#
+# Z: DISCOURAGED. Disconnects the client at the earliest stage of connection.
+# useful for preventing annoying server messages by persistent attempted 
+# server connections, or getting rid of people ban evading using vhosts.
+#
+# NOTE: 1 The address MUST be a numeric IP or it won't work.
+# NOTE: 2 The last field must be a * or strange things may happen to the
+#         server.
+#
+# Z:address:reason:*
+# 
+Z:192.168.2.2:not a server:*
diff --git a/doc/opermotd.doc b/doc/opermotd.doc
new file mode 100644 (file)
index 0000000..c550658
--- /dev/null
@@ -0,0 +1,7 @@
+OperMOTD.DOC - How to use OperMOTD. (15/06/99)
+
+If you want to use the OperMOTD feature. You will have to copy a file
+named ircd.opermotd to the same location you copied your ircd.conf to.
+Any text you put in that file will be showed when somebody succesfully
+opers up.
+                               - GZ
diff --git a/include/channel.h b/include/channel.h
new file mode 100644 (file)
index 0000000..ea113ca
--- /dev/null
@@ -0,0 +1,42 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/channel.h
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef        __channel_include__
+#define __channel_include__
+#define CREATE 1       /* whether a channel should be
+                          created or just tested for existance */
+
+#define        MODEBUFLEN      200
+
+#define NullChn        ((aChannel *)0)
+
+#define ChannelExists(n)       (find_channel(n, NullChn) != NullChn)
+
+#define IsULine(cptr,sptr)     (sptr->flags & FLAGS_ULINE)
+
+/* NOTE: Timestamps will be added to MODE-commands, so never make
+ * RESYNCMODES and MODEPARAMS higher than MAXPARA-3.'
+ */
+
+#include "msg.h"
+#define        MAXMODEPARAMS   (MAXPARA-2)     /* Maximum modes processed */
+#define RESYNCMODES    6               /* Max modes per MODE in resync */
+#define MODEPARAMS     6               /* Max modes from user */
+
+#endif
diff --git a/include/cio.h b/include/cio.h
new file mode 100644 (file)
index 0000000..22e29cd
--- /dev/null
@@ -0,0 +1,27 @@
+
+#include <windows.h>
+
+
+#define CIOCLASS    "CioClass"
+
+#ifndef CIO
+#define CIO
+
+typedef struct tag_CioLine
+{
+       BYTE    *Data;
+       WORD    Len;
+       struct tag_CioLine      *Prev, *Next;
+} CioLine;
+
+typedef struct tag_CioWndInfo
+{
+       CioLine *FirstLine, *CurLine;
+       int     Lines, Scroll;
+       int     Width, Height, XChar, YChar, YJunk, ScrollMe;
+       HFONT   hFont;
+       BYTE    FR, FG, FB;
+} CioWndInfo;
+
+#endif
+
diff --git a/include/ciofunc.h b/include/ciofunc.h
new file mode 100644 (file)
index 0000000..175df7c
--- /dev/null
@@ -0,0 +1,20 @@
+
+#include "Cio.h"
+
+#define GWL_USER        0
+#define CIO_ADDSTRING   WM_USER
+#define CIO_CLEAR       WM_USER+1
+
+// Cio_Init.c
+BOOL Cio_Init(HINSTANCE hInstance);
+
+// Cio_Main.c
+LRESULT CALLBACK Cio_WndProc(HWND, UINT, WPARAM, LPARAM);
+HWND Cio_Create(HINSTANCE hInstance, HWND hParent, DWORD Style, int X, int Y, int W, int H);
+BOOL Cio_WndCreate(HWND hWnd);
+BOOL Cio_WndPaint(HWND hWnd);
+BOOL Cio_WndDestroy(HWND hWnd);
+BOOL Cio_WndAddString(HWND hWnd, int Len, char *Buffer);
+BOOL Cio_WndSize(HWND hWnd, LPARAM lParam);
+void Cio_Scroll(HWND hWnd, CioWndInfo *CWI, int Scroll);
+BOOL Cio_PrintF(HWND hWnd, char *InBuf, ...);
diff --git a/include/class.h b/include/class.h
new file mode 100644 (file)
index 0000000..3adf622
--- /dev/null
@@ -0,0 +1,69 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/class.h
+ *   Copyright (C) 1990 Darren Reed
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef        __class_include__
+#define __class_include__
+
+#ifndef PROTO
+#if __STDC__
+#       define PROTO(x) x
+#else
+#       define PROTO(x) ()
+#endif
+#endif
+
+typedef struct Class {
+       int     class;
+       int     conFreq;
+       int     pingFreq;
+       int     maxLinks;
+       long    maxSendq;
+       int     links;
+       struct Class *next;
+} aClass;
+
+#define        Class(x)        ((x)->class)
+#define        ConFreq(x)      ((x)->conFreq)
+#define        PingFreq(x)     ((x)->pingFreq)
+#define        MaxLinks(x)     ((x)->maxLinks)
+#define        MaxSendq(x)     ((x)->maxSendq)
+#define        Links(x)        ((x)->links)
+
+#define        ConfLinks(x)    (Class(x)->links)
+#define        ConfMaxLinks(x) (Class(x)->maxLinks)
+#define        ConfClass(x)    (Class(x)->class)
+#define        ConfConFreq(x)  (Class(x)->conFreq)
+#define        ConfPingFreq(x) (Class(x)->pingFreq)
+#define        ConfSendq(x)    (Class(x)->maxSendq)
+
+#define        FirstClass()    classes
+#define        NextClass(x)    ((x)->next)
+
+extern aClass  *classes;
+
+extern aClass  *find_class PROTO((int));
+extern int     get_conf_class PROTO((aConfItem *));
+extern int     get_client_class PROTO((aClient *));
+extern int     get_client_ping PROTO((aClient *));
+extern int     get_con_freq PROTO((aClass *));
+extern void    add_class PROTO((int, int, int, int, long));
+extern void    check_class PROTO((void));
+extern void    initclass PROTO((void));
+
+#endif /* __class_include__ */
diff --git a/include/common.h b/include/common.h
new file mode 100644 (file)
index 0000000..efbd8ea
--- /dev/null
@@ -0,0 +1,177 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/common.h
+ *   Copyright (C) 1990 Armin Gruner
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef        __common_include__
+#define __common_include__
+
+#include <time.h>
+
+#ifdef HAVE_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifndef PROTO
+#if __STDC__
+#      define PROTO(x) x
+#else
+#      define PROTO(x) ()
+#endif
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
+#ifdef FALSE
+#undef FALSE
+#endif
+
+#define FALSE (0)
+#define TRUE  (!FALSE)
+
+#ifndef UNSURE
+#define UNSURE (2)
+#endif
+
+extern int     match PROTO((char *, char *));
+extern  int    mycmp (char *, char *);
+
+#ifdef NEED_STRTOK
+extern char    *strtok PROTO((char *, char *));
+#endif
+#ifndef HAVE_STRTOKEN
+extern char    *strtoken PROTO((char **, char *, char *));
+#endif
+#ifndef HAVE_INET_ADDR
+extern unsigned long inet_addr PROTO((char *));
+#endif
+
+#if !defined(HAVE_INET_NTOA) || !defined(HAVE_INET_NETOF) && !defined(SCOUNIX)
+#include <netinet/in.h>
+#endif
+
+#ifndef SCOUNIX /* SCO Openserver has these, but the header files are broken */
+#ifndef HAVE_INET_NTOA
+extern char *inet_ntoa PROTO((struct in_addr));
+#endif
+
+#ifndef HAVE_INET_NETOF
+extern int inet_netof PROTO((struct in_addr));
+#endif
+#endif /* SCOUNIX */
+
+int global_count, max_global_count;
+extern char *myctime PROTO((time_t));
+extern char *strtoken PROTO((char **, char *, char *));
+
+#ifndef MAX
+#define MAX(a, b)      ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b)      ((a) < (b) ? (a) : (b))
+#endif
+
+#define DupString(x,y) do{x=MyMalloc(strlen(y)+1);(void)strcpy(x,y);(void)strip_trailing_spaces(x);}while(0)
+
+#ifndef SCOUNIX
+extern u_char tolowertab[], touppertab[];
+#else
+extern unsigned char tolowertab[], touppertab[]; 
+       /* Typedef for u_char exists and is wrong. */
+#endif
+
+#undef tolower
+#define tolower(c) (tolowertab[(u_char)(c)])
+
+#undef toupper
+#define toupper(c) (touppertab[(u_char)(c)])
+
+#undef isalpha
+#undef isdigit
+#undef isxdigit
+#undef isalnum
+#undef isprint
+#undef isascii
+#undef isgraph
+#undef ispunct
+#undef islower
+#undef isupper
+#undef isspace
+#undef iscntrl
+
+extern unsigned char char_atribs[];
+
+#define PRINT 1
+#define CNTRL 2
+#define ALPHA 4
+#define PUNCT 8
+#define DIGIT 16
+#define SPACE 32
+#define ALLOW 64
+
+#ifndef KLINE_TEMP
+#define KLINE_PERM 0
+#define KLINE_TEMP 1
+#define KLINE_AKILL 2
+#endif
+
+#define        iscntrl(c) (char_atribs[(u_char)(c)]&CNTRL)
+#define isallowed(c) (char_atribs[(u_char)(c)]&ALLOW)
+#define isalpha(c) (char_atribs[(u_char)(c)]&ALPHA)
+#define isspace(c) (char_atribs[(u_char)(c)]&SPACE)
+#define islower(c) ((char_atribs[(u_char)(c)]&ALPHA) && ((u_char)(c) > 0x5f))
+#define isupper(c) ((char_atribs[(u_char)(c)]&ALPHA) && ((u_char)(c) < 0x60))
+#define isdigit(c) (char_atribs[(u_char)(c)]&DIGIT)
+#define        isxdigit(c) (isdigit(c) || 'a' <= (c) && (c) <= 'f' || \
+                    'A' <= (c) && (c) <= 'F')
+#define isalnum(c) (char_atribs[(u_char)(c)]&(DIGIT|ALPHA))
+#define isprint(c) (char_atribs[(u_char)(c)]&PRINT)
+#define isascii(c) ((u_char)(c) >= 0 && (u_char)(c) <= 0x7f)
+#define isgraph(c) ((char_atribs[(u_char)(c)]&PRINT) && ((u_char)(c) != 0x32))
+#define ispunct(c) (!(char_atribs[(u_char)(c)]&(CNTRL|ALPHA|DIGIT)))
+
+extern char *MyMalloc();
+extern void flush_connections();
+extern struct SLink *find_user_link(/* struct SLink *, struct Client * */);
+extern char *get_mode_str() ;
+extern int do_snick() ;
+extern int mode_snick() ;
+
+/*
+ * Protocol support text.  DO NO CHANGE THIS unless you know what
+ * you are doing.
+ */
+#define PROTOCTL_SUPPORTED "NOQUIT TOKEN WATCH=128 SAFELIST NETWORK=Serenity-IRC CHANMODES=beH,k,l,imnstR MODES=6 MAXCHANNELS=10 NICKLEN=30 MAXBANS=60 EXCEPTS TOPICLEN=307 CHANNELLEN=32 CHARSET=ascii STD=i-d"
+
+#ifdef __FreeBSD__
+extern char *malloc_options;
+#endif
+
+extern int lu_noninv, lu_inv, lu_serv, lu_oper, 
+           lu_unknown, lu_channel, lu_lu, lu_lulocal, lu_lserv, 
+           lu_clu, lu_mlu, lu_cglobalu, lu_mglobalu;
+           
+
+time_t now;
+
+#endif /* __common_include__ */
diff --git a/include/config.h b/include/config.h
new file mode 100644 (file)
index 0000000..24bf776
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ *   IRC - Internet Relay Chat, include/config.h
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef        __config_include__
+#define        __config_include__
+
+#include "setup.h"
+
+/*
+ *
+ *   NOTICE
+ *
+ * Under normal conditions, you should not have to edit this file.  Run
+ * the Config script in the root directory instead!
+ *
+ */
+
+/*
+ *  Compression level.  1 is fastest and 9 is slowest.
+ * Do not use a setting above 5 or the ircd will use a lot more
+ * cpu.
+ */
+#ifdef ZIP_LINKS
+#define ZIP_LEVEL 2
+#endif
+
+/*
+  What network are we linked to? Define netwide policy here.
+*/
+
+#define irc_network "Serenity-IRC"
+#define admin_chan  "#help"
+#define random_serv "irc.serenity-irc.net"
+#define network_www "http://www.serenity-irc.net"
+#define network_aup "http://www.serenity-irc.net/aup/"
+#define netwide_kline "kline@serenity-irc.net"
+
+
+#define CLIENT_MASKING
+#define MASK_ON_CONNECT
+#define OPER_MASKS
+
+/* undef this to fix *\*!*@* ban bug */
+#undef ESCAPED_MATCHING
+
+#ifdef CLIENT_MASKING
+#define userspace_mask_prefix "Serene"
+#endif
+
+#define USERSPACE_X
+#define userspace_x_prefix "Serene"
+
+/* This will be the maximum length of a quit-message */
+
+#define QUITLEN 180
+
+/* High Traffic Command protection code settings (-GZ)
+ * 
+ * HTCTIME     Minimum time between sending commands.
+ * HTCTRIGGER  Triggers and sends locops when reached.
+ */ 
+
+#define HTCTIME 5
+#define HTCTRIGGER 15
+
+/* Oper Masks for the hide code */
+#if defined(CLIENT_MASKING) && defined(OPER_MASKS)
+#define ircop_host      "IRCop.Serenity-IRC.Net"
+#define admin_host      "Admin.Serenity-IRC.Net"
+#define locop_host      "Local.Serenity-IRC.Net"
+#define sadmin_host     "ServOp.Serenity-IRC.Net"
+#define sroot_host     "SRA.Serenity-IRC.Net"
+#define netadmin_host  "NetAdmin.Serenity-IRC.Net"
+#endif
+
+/* Additional flags to give FreeBSD's malloc, only play with this if you
+ * know what you're doing.
+ */
+#define MALLOC_FLAGS_EXTRA ""
+
+/* Many older Operating Systems are running with insecure TCP stacks.
+ * This allows IP spoofing attacks, which are very difficult for
+ * operators to track down and ban."
+ *
+ * If you have such an old OS you can enable it here but keep in mind that
+ * this will be the least of your security problems. 
+ *
+ * Enable this option for any windows version older than 2000
+ */
+
+#undef NOSPOOF
+       
+
+/* KLINE_ADDRESS
+ *
+ * This is the email address displayed to the user when they are K:lined
+ * so that they can email someone in the server's administration about it.
+ * It is usually set up by the Config script.
+ *
+ * It should be a valid email address for the users to contact.
+ *
+ * For StarChat servers, note that this message is displayed when the user
+ * is affected by a local K:line or k:line.  For Services-based autokills,
+ * the message is set up automatically by Services to ask to email
+ * kline@starchat.net.  It is recommended that you set this up to give a valid
+ * email address for the server's admin, not kline@starchat.net.
+ */
+#ifndef KLINE_ADDRESS
+#define KLINE_ADDRESS "clueless-admin@poorly.configured.server"
+#endif
+
+/*
+ * SEEUSERSTATS - Sends a notice to opers set +t that the client is doing
+ *               /stats, /admin or /links, #undef'ing this disables it.  -ns
+ */
+#define SEEUSERSTATS
+
+/*
+ * Max size arguments to stats/admin/links can be before truncation. 
+ * Only applies to notices sent to opers (see above).  -Ben
+ */
+#define USERSTATMAX    120
+
+/*
+ * NOTE: It is important to set this to the correct "domain" for your server.
+ * Define this for the correct "domain" that your server is in.  This
+ * is important for certain stats.  -mlv
+ */
+#ifndef DOMAINNAME
+#define DOMAINNAME "serenity-irc.net"
+#endif
+
+/*
+ * Define this if you wish to output a *file* to a K lined client rather
+ * than the K line comment (the comment field is treated as a filename)
+ */
+#undef COMMENT_IS_FILE
+
+
+/* Do these work? I dunno... */
+
+/* #undef      PCS             *//* PCS Cadmus MUNIX, use with BSD flag! */
+
+/*
+ * NOTE: On some systems, valloc() causes many problems.
+ */
+#undef VALLOC                  /* Define this if you have valloc(3) */
+
+/*
+ * read/write are restarted after signals defining this 1, gets
+ * siginterrupt call compiled, which attempts to remove this
+ * behaviour (apollo sr10.1/bsd4.3 needs this)
+ */
+#ifdef APOLLO
+#define        RESTARTING_SYSTEMCALLS
+#endif
+
+/* #undef      DEBUGMODE       *//* define DEBUGMODE to enable debugging mode.*/
+//#define DEBUGMODE
+
+/*
+ * Defining FORCE_CORE will automatically "unlimit core", forcing the
+ * server to dump a core file whenever it has a fatal error.  -mlv
+ * Make sure to check that your shell allows core files.
+ */
+#define FORCE_CORE
+
+/*
+ * Full pathnames and defaults of irc system's support files. Please note that
+ * these are only the recommened names and paths. Change as needed.
+ * You must define these to something, even if you don't really want them.
+ */
+#ifndef DPATH
+#define        DPATH   "/usr/local/lib/ircd"   *//* dir where all ircd stuff is */
+#endif
+#ifndef SPATH
+#define        SPATH   "/usr/local/bin/ircd"   *//* path to server executeable */
+#endif
+#define        CPATH   "ircd.conf"     /* server configuration file */
+#define        MPATH   "ircd.motd"     /* server MOTD file */
+#define OMPATH  "ircd.opermotd" /* server IRCop MOTD file */
+#define LUPATH  "ircd.lusers"   /* server lusers file */
+#define        LPATH   "debug.log"     /* Where the debug file lives, if DEBUGMODE */
+#define        PPATH   "ircd.pid"      /* file for server pid */
+
+/*
+ * Define this filename to maintain a list of persons who log
+ * into this server. Logging will stop when the file does not exist.
+ * Logging will be disable also if you do not define this.
+ * FNAME_USERLOG just logs user connections, FNAME_OPERLOG logs every
+ * successful use of /oper.  These are either full paths or files within DPATH.
+ */
+/* Define this only if you are actually logging -Studded
+#define FNAME_USERLOG "users.log"
+#define FNAME_OPERLOG "opers.log"
+*/
+
+/* CHROOTDIR
+ *
+ * Define for value added security if you are a rooter.
+ *
+ * All files you access must be in the directory you define as DPATH.
+ * (This may effect the PATH locations above, though you can symlink it)
+ *
+ * You may want to define IRC_UID and IRC_GID
+ */
+/* #define CHROOTDIR */
+
+/* SHOW_INVISIBLE_LUSERS
+ *
+ * As defined this will show the correct invisible count for anyone who does
+ * LUSERS on your server. On a large net this doesnt mean much, but on a
+ * small net it might be an advantage to undefine it.
+ * (This will get defined for you if you're using userload (stats w).  -mlv)
+ */
+#define        SHOW_INVISIBLE_LUSERS
+
+/* NO_DEFAULT_INVISIBLE and NO_DEFAULT_HOSTHIDDEN 
+ *
+ * When defined, your users will not automatically be attributed with user
+ * mode "i" (i == invisible). Invisibility means people dont showup in
+ * WHO or NAMES unless they are on the same channel as you.
+ * Added the ability to hide a users hostname also as with mode z.
+ * By default this mode needs to be off.
+ */
+
+#define        NO_DEFAULT_INVISIBLE
+
+/* NO_DEFAULT_HIDE
+ *
+ * When defined, your users will not automatically be attributed with user
+ * mode "x" (x == hidden host). Hide means people dont show their full
+ * IP when somone wants to see their IP or DNS. buhbye nukes.
+ */
+#ifdef USERSPACE_X
+#undef NO_DEFAULT_HIDE
+#endif
+
+/* If you want your server to save and restore the local/max client count */
+
+#define SAVE_MAXCLIENT_STATS
+
+
+/*  What listen() backlog value do you wish to use?  Some servers
+ *  have problems with more than 5, others work fine with many, many
+ *  more.
+ */
+
+#define LISTEN_SIZE 5
+
+/* Define your maximum sendq here */
+
+#define MAXSENDQLENGTH 3000000
+
+/* Define the size of a bufferpool (total of ALL sendq's in use) */
+  
+#define BUFFERPOOL (9 * MAXSENDQLENGTH)
+
+/* Define the size of your nickname history for /whowas */
+
+#define NICKNAMEHISTORYLENGTH 500        
+
+/* OPER_* defines
+ *
+ * See ./docs/example.conf for examples of how to restrict access for
+ * your IRC Operators
+ */
+
+/* MAXIMUM LINKS
+ *
+ * This define is useful for leaf nodes and gateways. It keeps you from
+ * connecting to too many places. It works by keeping you from
+ * connecting to more than "n" nodes which you have C:blah::blah:6667
+ * lines for.
+ *
+ * Note that any number of nodes can still connect to you. This only
+ * limits the number that you actively reach out to connect to.
+ *
+ * Leaf nodes are nodes which are on the edge of the tree. If you want
+ * to have a backup link, then sometimes you end up connected to both
+ * your primary and backup, routing traffic between them. To prevent
+ * this, #define MAXIMUM_LINKS 1 and set up both primary and
+ * secondary with C:blah::blah:6667 lines. THEY SHOULD NOT TRY TO
+ * CONNECT TO YOU, YOU SHOULD CONNECT TO THEM.
+ *
+ * Gateways such as the server which connects Australia to the US can
+ * do a similar thing. Put the American nodes you want to connect to
+ * in with C:blah::blah:6667 lines, and the Australian nodes with
+ * C:blah::blah lines. Have the Americans put you in with C:blah::blah
+ * lines. Then you will only connect to one of the Americans.
+ *
+ * This value is only used if you don't have server classes defined, and
+ * a server is in class 0 (the default class if none is set).
+ *
+ */
+#define MAXIMUM_LINKS 1
+
+/*
+ * If your server is running as a a HUB Server then define this.
+ * A HUB Server has many servers connect to it at the same as opposed
+ * to a leaf which just has 1 server (typically the uplink). Define this
+ * correctly for performance reasons.
+ */
+/* #define     HUB */
+
+/*
+ * NOTE: defining CMDLINE_CONFIG and installing ircd SUID or SGID is a MAJOR
+ *       security problem - they can use the "-f" option to read any files
+ *       that the 'new' access lets them. Note also that defining this is
+ *       a major security hole if your ircd goes down and some other user
+ *       starts up the server with a new conf file that has some extra
+ *       O-lines. So don't use this unless you're debugging.
+ */
+#undef CMDLINE_CONFIG /* allow conf-file to be specified on command line */
+
+/*
+ * To use m4 as a preprocessor on the ircd.conf file, define M4_PREPROC.
+ * The server will then call m4 each time it reads the ircd.conf file,
+ * reading m4 output as the server's ircd.conf file.
+ */
+#undef M4_PREPROC
+
+/*
+ * If you wish to have the server send 'vital' messages about server
+ * through syslog, define USE_SYSLOG. Only system errors and events critical
+ * to the server are logged although if this is defined with FNAME_USERLOG,
+ * syslog() is used instead of the above file. It is not recommended that
+ * this option is used unless you tell the system administrator beforehand
+ * and obtain their permission to send messages to the system log files.
+ */
+#undef USE_SYSLOG
+
+#ifdef USE_SYSLOG
+/*
+ * If you use syslog above, you may want to turn some (none) of the
+ * spurious log messages for KILL/SQUIT off.
+ */
+#undef SYSLOG_KILL     /* log all operator kills to syslog */
+#undef SYSLOG_SQUIT    /* log all remote squits for all servers to syslog */
+#undef SYSLOG_CONNECT  /* log remote connect messages for other all servs */
+#undef SYSLOG_USERS    /* send userlog stuff to syslog */
+#undef SYSLOG_OPER     /* log all users who successfully become an Op */
+
+/*
+ * If you want to log to a different facility than DAEMON, change
+ * this define.
+ */
+#define LOG_FACILITY LOG_DAEMON
+#endif /* USE_SYSLOG */
+
+/*
+ * IDLE_FROM_MSG
+ *
+ * Idle-time nullified only from privmsg, if undefined idle-time
+ * is nullified from everything except ping/pong.
+ * Added 3.8.1992, kny@cs.hut.fi (nam)
+ */
+#define IDLE_FROM_MSG
+
+/* 
+ * Size of the LISTEN request.  Some machines handle this large
+ * without problem, but not all.  It defaults to 5, but can be
+ * raised if you know your machine handles it.
+ */
+#ifndef LISTEN_SIZE
+#define LISTEN_SIZE 5
+#endif
+
+/*
+ * Max amount of internal send buffering when socket is stuck (bytes)
+ */
+#ifndef MAXSENDQLENGTH
+#define MAXSENDQLENGTH 3000000
+#endif
+/*
+ *  BUFFERPOOL is the maximum size of the total of all sendq's.
+ *  Recommended value is 2 * MAXSENDQLENGTH, for hubs, 5 *.
+ */
+#ifndef BUFFERPOOL
+#define        BUFFERPOOL     (9 * MAXSENDQLENGTH)
+#endif
+
+/*
+ * IRC_UID
+ *
+ * If you start the server as root but wish to have it run as another user,
+ * define IRC_UID to that UID.  This should only be defined if you are running
+ * as root and even then perhaps not.
+ */
+/* #undef      IRC_UID */
+/* #undef      IRC_GID */
+
+/*
+ * CLIENT_FLOOD
+ *
+ * this controls the number of bytes the server will allow a client to
+ * send to the server without processing before disconnecting the client for
+ * flooding it.  Values greater than 8000 make no difference to the server.
+ */
+#define        CLIENT_FLOOD    6000
+
+/*
+ * How many seconds in between simultaneous nick changes?
+ */
+#define NICK_CHANGE_DELAY      30
+
+/*
+ * How many open targets can one nick have for messaging nicks and
+ * inviting them?
+ */
+
+#define MAXTARGETS             20
+#define TARGET_DELAY           120
+
+/* Define default Z:line time for SOCKS   -taz */
+#define ZLINE_TIME     300
+
+/*
+ * StarChat Mandatory section
+ *
+ * It is VERY important that you DO NOT change any of the settings
+ * in this section for a server that is or will be running on StarChat.
+ * If you feel the need to make a change to one of these settings
+ * please write to coding@starchat.net FIRST to discuss your reasons.
+ */
+
+/* Client connection throttling
+ *
+ * This is based on the ircu version, modified by nikb.
+ * StarChat servers MUST use throttling. The values can
+ * be changed, but shouldn't be without good reason.
+ *
+ * CHECK_CLONE_LIMIT is the number of clients from the
+ * same IP that will be allowed. The CHECK_CLONE_LIMIT + 1'th
+ * client in CHECK_CLONE_PERIOD seconds will be throttled,
+ * and a temp z-line put in place for CHECK_CLONE_DELAY seconds. 
+ */
+#define THROTTLE
+#define CHECK_CLONE_LIMIT 3
+#define CHECK_CLONE_PERIOD 15
+#define CHECK_CLONE_DELAY 300
+
+/*
+ * Define this to prevent mixed case userids that clonebots use. However
+ * this affects the servers running telclients WLD* FIN*  etc.
+ */
+/*
+#undef DISALLOW_MIXED_CASE
+*/
+
+/*
+ * Define this if you wish to ignore the case of the first character of
+ * the user id when disallowing mixed case. This allows PC users to
+ * enter the more intuitive first name with the first letter capitalised
+ */
+#define        IGNORE_CASE_FIRST_CHAR
+
+/* FAILOPER_WARN
+ *
+ * When defined, warns users on a failed oper attempt that it was/is logged
+ * Only works when FNAME_OPERLOG is defined, and a logfile exists.
+ * NOTE: Failed oper attempts are logged regardless.
+ */
+#define FAILOPER_WARN
+
+/* 
+ * Define your network service names here.
+ */
+#define ChanServ "ChanServ"
+#define MemoServ "MemoServ"
+#define NickServ "NickServ"
+#define OperServ "OperServ"
+#define WebServ  "WebServ"
+
+/*
+ * End of StarChat Mandatory section
+ */
+
+/*   STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP STOP  */
+
+/* You shouldn't change anything below this line, unless absolutely needed. */
+
+/*
+ * Port where ircd resides. NOTE: This *MUST* be greater than 1024 if you
+ * plan to run ircd under any other uid than root.
+ */
+#define PORTNUM 7000           /* 7000 for StarChat */
+
+/*
+ * Maximum number of network connections your server will allow.  This should
+ * never exceed max. number of open file descrpitors and wont increase this.
+ * Should remain LOW as possible. Most sites will usually have under 30 or so
+ * connections. A busy hub or server may need this to be as high as 50 or 60.
+ * Making it over 100 decreases any performance boost gained from it being low.
+ * if you have a lot of server connections, it may be worth splitting the load
+ * over 2 or more servers.
+ * 1 server = 1 connection, 1 user = 1 connection.
+ * This should be at *least* 3: 1 listen port, 1 dns port + 1 client
+ *
+ * Note: this figure will be too high for most systems. If you get an 
+ * fd-related error on compile, change this to 256.
+ *
+ * Windows users: This should be a fairly high number.  Some operations
+ * will slow down because of this, but it is _required_ because of the way
+ * windows NT(and possibly 95) allocate fd handles. A good number is 16384.
+ */
+#ifndef MAXCONNECTIONS
+#define MAXCONNECTIONS 1024
+#endif
+
+/*
+ * this defines the length of the nickname history.  each time a user changes
+ * nickname or signs off, their old nickname is added to the top of the list.
+ * The following sizes are recommended:
+ * 8MB or less  core memory : 500      (at least 1/4 of max users)
+ * 8MB-16MB     core memory : 500-750  (1/4 -> 1/2 of max users)
+ * 16MB-32MB    core memory : 750-1000 (1/2 -> 3/4 of max users)
+ * 32MB or more core memory : 1000+    (> 3/4 if max users)
+ * where max users is the expected maximum number of users.
+ * (100 nicks/users ~ 25k)
+ * NOTE: this is directly related to the amount of memory ircd will use whilst
+ *      resident and running - it hardly ever gets swapped to disk! You can
+ *      ignore these recommendations- they only are meant to serve as a guide
+ * NOTE: But the *Minimum* ammount should be 100, in order to make nick
+ *       chasing possible for mode and kick.
+ */
+#ifndef NICKNAMEHISTORYLENGTH
+#define NICKNAMEHISTORYLENGTH 2000 
+#endif
+
+/*
+ * Time interval to wait and if no messages have been received, then check for
+ * PINGFREQUENCY and CONNECTFREQUENCY 
+ */
+#define TIMESEC  60            /* Recommended value: 60 */
+
+/*
+ * If daemon doesn't receive anything from any of its links within
+ * PINGFREQUENCY seconds, then the server will attempt to check for
+ * an active link with a PING message. If no reply is received within
+ * (PINGFREQUENCY * 2) seconds, then the connection will be closed.
+ */
+#define PINGFREQUENCY    120   /* Recommended value: 120 */
+
+/*
+ * If the connection to to uphost is down, then attempt to reconnect every 
+ * CONNECTFREQUENCY  seconds.
+ */
+#define CONNECTFREQUENCY 600   /* Recommended value: 600 */
+
+/*
+ * Often net breaks for a short time and it's useful to try to
+ * establishing the same connection again faster than CONNECTFREQUENCY
+ * would allow. But, to keep trying on bad connection, we require
+ * that connection has been open for certain minimum time
+ * (HANGONGOODLINK) and we give the net few seconds to steady
+ * (HANGONRETRYDELAY). This latter has to be long enough that the
+ * other end of the connection has time to notice it broke too.
+ */
+#define HANGONRETRYDELAY 240   /* Recommended value: 4 minutes */
+#define HANGONGOODLINK 3600    /* Recommended value: 1 hour */
+
+/*
+ * Number of seconds to wait for a connect(2) call to complete.
+ * NOTE: this must be at *LEAST* 10.  When a client connects, it has
+ * CONNECTTIMEOUT - 10 seconds for its host to respond to an ident lookup
+ * query and for a DNS answer to be retrieved.
+ */
+#define        CONNECTTIMEOUT  60      /* Recommended value: 60 */
+
+/*
+ * Max time from the nickname change that still causes KILL
+ * automaticly to switch for the current nick of that user. (seconds)
+ */
+#define KILLCHASETIMELIMIT 90   /* Recommended value: 90 */
+
+/*
+ * Max number of channels a user is allowed to join.
+ */
+#define MAXCHANNELSPERUSER  10 /* Recommended value: 10 */
+
+#endif /* __config_include__ */
+
diff --git a/include/dbuf.h b/include/dbuf.h
new file mode 100644 (file)
index 0000000..c7b1077
--- /dev/null
@@ -0,0 +1,163 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/dbuf.h
+ *   Copyright (C) 1990 Markku Savela
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __dbuf_include__
+#define __dbuf_include__
+
+#ifndef PROTO
+#ifdef __STDC__
+#      define PROTO(x) x
+#else
+#      define PROTO(x) ()
+#endif /* __STDC__ */
+#endif /* ! PROTO */
+
+/*
+** dbuf is a collection of functions which can be used to
+** maintain a dynamic buffering of a byte stream.
+** Functions allocate and release memory dynamically as
+** required [Actually, there is nothing that prevents
+** this package maintaining the buffer on disk, either]
+*/
+
+/*
+** These structure definitions are only here to be used
+** as a whole, *DO NOT EVER REFER TO THESE FIELDS INSIDE
+** THE STRUCTURES*! It must be possible to change the internal
+** implementation of this package without changing the
+** interface.
+*/
+#if !defined(_SEQUENT_)
+typedef struct dbuf
+    {
+       u_int   length; /* Current number of bytes stored */
+       u_int   offset; /* Offset to the first byte */
+       struct  dbufbuf *head;  /* First data buffer, if length > 0 */
+       /* added by mnystrom@mit.edu: */
+       struct  dbufbuf *tail; /* last data buffer, if length > 0 */
+    } dbuf;
+#else
+typedef struct dbuf
+    {
+        uint   length; /* Current number of bytes stored */
+        uint   offset; /* Offset to the first byte */
+        struct  dbufbuf *head;  /* First data buffer, if length > 0 */
+       /* added by mnystrom@mit.edu: */
+       struct  dbufbuf *tail; /* last data buffer, if length > 0 */
+    } dbuf;
+#endif
+/*
+** And this 'dbufbuf' should never be referenced outside the
+** implementation of 'dbuf'--would be "hidden" if C had such
+** keyword...
+** If it was possible, this would compile to be exactly 1 memory
+** page in size. 2048 bytes seems to be the most common size, so
+** as long as a pointer is 4 bytes, we get 2032 bytes for buffer
+** data after we take away a bit for malloc to play with. -avalon
+*/
+typedef struct dbufbuf
+    {
+       struct  dbufbuf *next;  /* Next data buffer, NULL if this is last */
+       char    data[2032];     /* Actual data stored here */
+    } dbufbuf;
+
+/*
+** dbuf_put
+**     Append the number of bytes to the buffer, allocating more
+**     memory as needed. Bytes are copied into internal buffers
+**     from users buffer.
+**
+**     returns > 0, if operation successfull
+**             < 0, if failed (due memory allocation problem)
+*/
+int    dbuf_put PROTO((dbuf *, char *, int));
+                                       /* Dynamic buffer header */
+                                       /* Pointer to data to be stored */
+                                       /* Number of bytes to store */
+
+/*
+** dbuf_get
+**     Remove number of bytes from the buffer, releasing dynamic
+**     memory, if applicaple. Bytes are copied from internal buffers
+**     to users buffer.
+**
+**     returns the number of bytes actually copied to users buffer,
+**             if >= 0, any value less than the size of the users
+**             buffer indicates the dbuf became empty by this operation.
+**
+**             Return 0 indicates that buffer was already empty.
+**
+**             Negative return values indicate some unspecified
+**             error condition, rather fatal...
+*/
+int    dbuf_get PROTO(( dbuf *, char *, int));
+                               /* Dynamic buffer header */
+                               /* Pointer to buffer to receive the data */
+                               /* Max amount of bytes that can be received */
+
+/*
+** dbuf_map, dbuf_delete
+**     These functions are meant to be used in pairs and offer
+**     a more efficient way of emptying the buffer than the
+**     normal 'dbuf_get' would allow--less copying needed.
+**
+**     map     returns a pointer to a largest contiguous section
+**             of bytes in front of the buffer, the length of the
+**             section is placed into the indicated "long int"
+**             variable. Returns NULL *and* zero length, if the
+**             buffer is empty.
+**
+**     delete  removes the specified number of bytes from the
+**             front of the buffer releasing any memory used for them.
+**
+**     Example use (ignoring empty condition here ;)
+**
+**             buf = dbuf_map(&dyn, &count);
+**             <process N bytes (N <= count) of data pointed by 'buf'>
+**             dbuf_delete(&dyn, N);
+**
+**     Note:   delete can be used alone, there is no real binding
+**             between map and delete functions...
+*/
+char *dbuf_map PROTO((dbuf *, int *));
+                                       /* Dynamic buffer header */
+                                       /* Return number of bytes accessible */
+
+int dbuf_delete PROTO((dbuf *, int));
+                                       /* Dynamic buffer header */
+                                       /* Number of bytes to delete */
+
+/*
+** DBufLength
+**     Return the current number of bytes stored into the buffer.
+**     (One should use this instead of referencing the internal
+**     length field explicitly...)
+*/
+#define DBufLength(dyn) ((dyn)->length)
+
+/*
+** DBufClear
+**     Scratch the current content of the buffer. Release all
+**     allocated buffers and make it empty.
+*/
+#define DBufClear(dyn) dbuf_delete((dyn),DBufLength(dyn))
+
+extern int     dbuf_getmsg PROTO((dbuf *, char *, int));
+
+#endif /* __dbuf_include__ */
diff --git a/include/h.h b/include/h.h
new file mode 100644 (file)
index 0000000..3b6287b
--- /dev/null
@@ -0,0 +1,326 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/h.h
+ *   Copyright (C) 1992 Darren Reed
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * "h.h". - Headers file.
+ *
+ * Most of the externs and prototypes thrown in here to 'cleanup' things.
+ * -avalon
+ */
+
+extern time_t  nextconnect, nextdnscheck, nextping;
+extern aClient *client, me, *local[];
+extern aChannel *channel;
+extern struct  stats   *ircstp;
+extern int     bootopt;
+/* Prototype added to force errors -- Barubary */
+extern time_t  check_pings(time_t now, int check_kills);
+
+/* moved here to allow the server to generate new seeds on startup
+       InnerFIRE       */
+
+#ifdef NOSPOOF
+extern u_int32_t NOSPOOF_SEED01, NOSPOOF_SEED02 ;
+#endif /*  NOSPOOF*/
+
+#define BREPORT_DO_DNS "NOTICE AUTH :*** Looking up your hostname...\r\n"
+#define BREPORT_FIN_DNS "NOTICE AUTH :*** Found your hostname\r\n"
+#define BREPORT_FIN_DNSC "NOTICE AUTH :*** Found your hostname (cached)\r\n"
+#define BREPORT_FAIL_DNS "NOTICE AUTH :*** Couldn't resolve your hostname; using IP address instead\r\n"
+#define BREPORT_DO_ID "NOTICE AUTH :*** Checking ident...\r\n"
+#define BREPORT_FIN_ID "NOTICE AUTH :*** Received ident response\r\n"
+#define BREPORT_FAIL_ID        "NOTICE AUTH :*** No ident response; username prefixed with ~\r\n"
+
+extern char REPORT_DO_DNS[128], REPORT_FIN_DNS[128], REPORT_FIN_DNSC[128],
+       REPORT_FAIL_DNS[128], REPORT_DO_ID[128], REPORT_FIN_ID[128],
+       REPORT_FAIL_ID[128];
+
+extern int R_do_dns, R_fin_dns, R_fin_dnsc, R_fail_dns,
+               R_do_id, R_fin_id, R_fail_id;
+
+extern aChannel *find_channel PROTO((char *, aChannel *));
+extern void    remove_user_from_channel PROTO((aClient *, aChannel *));
+extern void    del_invite PROTO((aClient *, aChannel *));
+extern int     del_silence PROTO((aClient *, char *));
+extern void    send_user_joins PROTO((aClient *, aClient *));
+extern void    clean_channelname PROTO((char *));
+extern int     do_nick_name PROTO((char *));
+/* We're going to pass the msg text to can_send() for +c channelmode -Defiant */
+extern int     can_send PROTO((aClient *, aChannel *, char *));
+extern int     is_chan_op PROTO((aClient *, aChannel *));
+extern int     has_voice PROTO((aClient *, aChannel *));
+extern int     count_channels PROTO((aClient *));
+extern  Ban    *is_banned PROTO((aClient *, aChannel *));
+extern  Ban     *is_banexception PROTO((aClient *, aChannel *)) ;
+extern aClient *find_client PROTO((char *, aClient *));
+extern aClient *find_name PROTO((char *, aClient *));
+extern aClient *find_nickserv PROTO((char *, aClient *));
+extern aClient *find_person PROTO((char *, aClient *));
+extern aClient *find_server PROTO((char *, aClient *));
+extern aClient *find_service PROTO((char *, aClient *));
+
+extern int     attach_conf PROTO((aClient *, aConfItem *));
+extern aConfItem *attach_confs PROTO((aClient*, char *, int));
+extern aConfItem *attach_confs_host PROTO((aClient*, char *, int));
+extern int     attach_Iline PROTO((aClient *, struct hostent *, char *));
+extern aConfItem *conf, *find_me PROTO(()), *find_admin PROTO(());
+extern aConfItem *count_cnlines PROTO((Link *));
+extern  aSqlineItem *sqline;
+extern void    det_confs_butmask PROTO((aClient *, int));
+extern int     detach_conf PROTO((aClient *, aConfItem *));
+extern  aSqlineItem *find_sqline_nick PROTO((char *));
+extern aSqlineItem *find_sqline_match PROTO((char *));
+extern  aJinxItem *jinx;
+extern  aJinxItem *find_jinx_userhost PROTO((char *));
+extern  aJinxItem *find_jinx_match PROTO((char *, char *));
+extern aConfItem *det_confs_butone PROTO((aClient *, aConfItem *));
+extern  char *find_diepass();
+extern  char *find_restartpass();
+extern aConfItem *find_conf PROTO((Link *, char*, int));
+extern aConfItem *find_conf_exact PROTO((char *, char *, char *, int));
+extern aConfItem *find_conf_host PROTO((Link *, char *, int));
+extern aConfItem *find_conf_ip PROTO((Link *, char *, char *, int));
+extern aConfItem *find_conf_name PROTO((char *, int));
+extern  aConfItem *find_temp_conf_entry PROTO((aConfItem *, u_int));
+extern  aConfItem *find_conf_servern PROTO((char *));
+extern int     find_kill PROTO((aClient *));
+extern char    *find_zap PROTO((aClient *, int));
+extern  int     find_socksexception PROTO((aClient *));
+extern  int     find_dccblock PROTO((char *));
+extern int     find_restrict PROTO((aClient *));
+extern int     rehash PROTO((aClient *, aClient *, int));
+// fixme: this is used 2 diffrent ways depending on the file ?
+extern int     initconf PROTO((int));
+// extern  aConfItem *initconf();
+extern void    add_temp_conf();
+extern void    inittoken PROTO(());
+extern  int     do_channel_synch(char *);
+int    synchmode;
+int    SVSNOOP;
+extern void calc_mask(aClient *) ;
+char *return_host(aClient *, aClient *) ;
+char *return_oper_host(aClient *, aClient *) ;
+char *return_oper_mask (aClient *) ;
+
+/* Related to the z-line timer for SOCKS -taz */
+extern  int    socks_zline_time;
+extern  aEvent  *EventList;
+extern  aEvent  *make_event();
+extern aSynchList *SJSynchList;
+extern  aSynchList *make_synchlist();
+extern  void    free_synchlist(aSynchList *);
+extern  void    free_event(aEvent *);
+extern  void    CheckEvents();
+extern  void    AddEvent();
+extern  void    RemoveZLine(char *);
+
+extern         char    *MyMalloc PROTO(()), *MyRealloc PROTO(()) ;
+extern char    *debugmode, *configfile, *sbrk0;
+extern char    *getfield PROTO((char *));
+extern void    get_sockhost PROTO((aClient *, char *));
+extern char    *rpl_str PROTO((int)), *err_str PROTO((int));
+extern char    *strerror PROTO((int));
+extern int     dgets PROTO((int, char *, int));
+extern char    *inetntoa PROTO((char *));
+
+extern int     dbufalloc, dbufblocks, debuglevel, errno, h_errno;
+extern int     highest_fd, debuglevel, portnum, debugtty, maxusersperchannel;
+extern int     readcalls, udpfd, resfd;
+extern aClient *add_connection PROTO((aClient *, int));
+extern int     add_listener PROTO((aConfItem *));
+extern void    add_local_domain PROTO((char *, int));
+extern int     check_client PROTO((aClient *));
+extern int     check_server PROTO((aClient *, struct hostent *, \
+                                   aConfItem *, aConfItem *, int));
+extern int     check_server_init PROTO((aClient *));
+extern void    close_connection PROTO((aClient *));
+extern void    close_listeners PROTO(());
+extern int connect_server PROTO((aConfItem *, aClient *, struct hostent *));
+extern void    get_my_name PROTO((aClient *, char *, int));
+extern int     get_sockerr PROTO((aClient *));
+extern int     inetport PROTO((aClient *, char *, int));
+extern void    init_sys PROTO(());
+extern int     read_message PROTO((time_t));
+extern void    report_error PROTO((char *, aClient *));
+extern void    set_non_blocking PROTO((int, aClient *));
+extern int     setup_ping PROTO(());
+extern  int     ping_server(aClient *, struct hostent *);
+extern  void    read_ping(aClient *);
+extern  void    send_ping(aClient *);
+extern  void    cancel_ping(aClient *, aClient *);
+extern  void    end_ping(aClient *);
+extern int     unixport PROTO((aClient *, char *, int));
+extern int     utmp_open PROTO(());
+extern int     utmp_read PROTO((int, char *, char *, char *, int));
+extern int     utmp_close PROTO((int));
+
+extern void    restart PROTO((char *));
+extern  void   send_channel_modes_sts PROTO((aClient *, aChannel *)); /* New StarChat STS1 resynch code */
+extern void    server_reboot PROTO((char *));
+extern void    terminate PROTO(()), write_pidfile PROTO(());
+
+extern int     send_queued PROTO((aClient *));
+extern void    sendto_one();
+extern void    sendto_channelops_butone();
+extern void    sendto_channelvoice_butone();
+extern void    sendto_serv_butone();
+extern void    sendto_SNICK_butone();
+extern void    sendto_serv_butone_quit();
+extern void    sendto_common_channels();
+extern void    sendto_channel_butserv();
+extern void    sendto_match_servs();
+extern void    sendto_match_butone();
+extern void    sendto_all_butone();
+extern void    sendto_ops();
+extern void    sendto_ops_butone();
+extern void    sendto_ops_butme();
+extern void    sendto_prefix_one();
+extern  void    sendto_failops_whoare_opers();
+extern  void    sendto_realops();
+extern  void    sendto_locfailops();
+extern  void    sendto_failops();
+extern  void    sendto_opers();
+extern void    sendto_umode();
+
+extern int     writecalls, writeb[];
+extern int     deliver_it PROTO((aClient *, char *, int));
+
+extern int     check_registered PROTO((aClient *));
+extern int     check_registered_user PROTO((aClient *));
+extern char    *get_client_name PROTO((aClient *, int));
+extern char    *get_client_host PROTO((aClient *));
+extern char    *my_name_for_link PROTO((char *, aConfItem *));
+extern char    *myctime PROTO((time_t)), *date PROTO((time_t));
+extern int     exit_client PROTO((aClient *, aClient *, aClient *, char *));
+extern void    initstats PROTO(()), tstats PROTO((aClient *, char *));
+extern char    *check_string PROTO((char *));
+extern char    *make_nick_user_host PROTO((char *, char *, char *));
+
+extern int     parse PROTO((aClient *, char *, char *, struct Message *));
+extern int     do_numeric PROTO((int, aClient *, aClient *, int, char **));
+extern int hunt_server PROTO((aClient *,aClient *,char *,int,int,char **));
+extern aClient *next_client PROTO((aClient *, char *));
+extern int     m_umode PROTO((aClient *, aClient *, int, char **));
+extern int     m_names PROTO((aClient *, aClient *, int, char **));
+extern int     m_server_estab PROTO((aClient *));
+extern void    send_umode PROTO((aClient *, aClient *, int, int, char *));
+extern void    send_umode_out PROTO((aClient*, aClient *, int));
+extern  char    *hideme PROTO((aClient *, aClient *));
+extern  char    *maskme PROTO((char *, int));
+extern  char    *maskmenow PROTO((aClient *, aClient*));
+
+extern void    free_client PROTO((aClient *));
+extern void    free_link PROTO((Link *));
+extern void    free_ban PROTO((Ban *));
+extern void    free_conf PROTO((aConfItem *));
+extern void    free_class PROTO((aClass *));
+extern void    free_user PROTO((anUser *, aClient *));
+extern int     find_str_match_link PROTO((Link **, char *));
+extern void    free_str_list PROTO ((Link *));
+extern Link    *make_link PROTO(());
+extern Ban     *make_ban PROTO(());
+extern anUser  *make_user PROTO((aClient *));
+extern  aSqlineItem *make_sqline PROTO(());
+//extern  aJinxItem *make_jinx PROTO(());
+extern aConfItem *make_conf PROTO(());
+extern aClass  *make_class PROTO(());
+extern aServer *make_server PROTO(());
+extern aClient *make_client PROTO((aClient *, aClient *));
+extern Link    *find_user_link PROTO((Link *, aClient *));
+extern int     IsMember PROTO((aClient *, aChannel *));
+extern char    *pretty_mask PROTO((char *));
+extern void    add_client_to_list PROTO((aClient *));
+extern void    checklist PROTO(());
+extern void    remove_client_from_list PROTO((aClient *));
+extern void    initlists PROTO(());
+
+extern void    add_class PROTO((int, int, int, int, long));
+extern void    fix_class PROTO((aConfItem *, aConfItem *));
+extern long    get_sendq PROTO((aClient *));
+extern int     get_con_freq PROTO((aClass *));
+extern int     get_client_ping PROTO((aClient *));
+extern int     get_client_class PROTO((aClient *));
+extern int     get_conf_class PROTO((aConfItem *));
+extern void    report_classes PROTO((aClient *));
+
+extern int     res_init();
+extern u_long  cres_mem(aClient *);
+extern struct  hostent *get_res PROTO((char *));
+extern struct  hostent *gethost_byaddr PROTO((char *, Link *));
+extern struct  hostent *gethost_byname PROTO((char *, Link *));
+extern void    flush_cache PROTO(());
+extern int     init_resolver PROTO((int));
+extern time_t  timeout_query_list PROTO((time_t));
+extern time_t  expire_cache PROTO((time_t));
+extern void    del_queries PROTO((char *));
+
+#ifdef ZIP_LINKS
+extern  int     zip_init (aClient *);
+extern  void    zip_free (aClient *);
+extern  char    *unzip_packet (aClient *, char *, int *);
+extern  char    *zip_buffer (aClient *, char *, int *, int);
+#endif
+
+
+extern void    clear_channel_hash_table PROTO(());
+extern void    clear_client_hash_table PROTO(());
+extern void    clear_notify_hash_table PROTO(());
+extern int     add_to_client_hash_table PROTO((char *, aClient *));
+extern int     del_from_client_hash_table PROTO((char *, aClient *));
+extern int     add_to_channel_hash_table PROTO((char *, aChannel *));
+extern int     del_from_channel_hash_table PROTO((char *, aChannel *));
+extern int     add_to_notify_hash_table PROTO((char *, aClient *));
+extern int     del_from_notify_hash_table PROTO((char *, aClient *));
+extern int     hash_check_notify PROTO((aClient *, int));
+extern int     hash_del_notify_list PROTO((aClient  *));
+extern void    count_watch_memory PROTO((int *, u_long *));
+extern aNotify *hash_get_notify PROTO((char *));
+extern aChannel *hash_get_chan_bucket PROTO((int));
+extern aChannel *hash_find_channel PROTO((char *, aChannel *));
+extern aClient *hash_find_client PROTO((char *, aClient *));
+extern aClient *hash_find_nickserver PROTO((char *, aClient *));
+extern aClient *hash_find_server PROTO((char *, aClient *));
+extern aClient *find_server_wildcard PROTO((char *));
+
+extern void    add_history PROTO((aClient *));
+extern aClient *get_history PROTO((char *, time_t));
+extern void    initwhowas PROTO(());
+extern void    off_history PROTO((aClient *));
+
+extern int     dopacket PROTO((aClient *, char *, int));
+
+void    count_memory(aClient *, char *);
+extern void    debug();
+#if defined(DEBUGMODE) 
+extern void    send_usage PROTO((aClient *, char *));
+extern void    send_listinfo PROTO((aClient *, char *));
+extern void    count_memory PROTO((aClient *, char *));
+#endif
+extern  void   get_max_users(void);
+extern         void    strip_trailing_spaces(char *) ;
+
+/* From channel.c */
+extern void    send_list(aClient *, int);
+
+extern void serv_info(aClient *, char *);    
+extern int str2array(char **, char *, char *);
+extern void CheckBandwidth(void);
+int max_connection_count, max_client_count;
+long max_sendqs,max_recvqs;
+extern int find_services(void);
diff --git a/include/hash.h b/include/hash.h
new file mode 100644 (file)
index 0000000..eac4e8a
--- /dev/null
@@ -0,0 +1,46 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/hash.h
+ *   Copyright (C) 1991 Darren Reed
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef        __hash_include__
+#define __hash_include__
+
+/* Ditch the stats if not running in debugmode */
+#ifdef DEBUGMODE
+typedef        struct  hashentry {
+       int     hits;
+       int     links;
+       void    *list;
+       } aHashEntry;
+#else /* DEBUGMODE */
+typedef        void    *aHashEntry;
+#endif /* DEBUGMODE */
+
+#ifndef        DEBUGMODE
+#define        HASHSIZE        32003   /* prime number */
+#define        CHANNELHASHSIZE 10007   /* prime number */
+#else
+extern int     HASHSIZE;
+extern int     CHANNELHASHSIZE;
+#endif
+
+#define NOTIFYHASHSIZE 10007   /* prime number  */
+
+#define NullChn        ((aChannel *)0)
+
+#endif /* __hash_include__ */
diff --git a/include/inet.h b/include/inet.h
new file mode 100644 (file)
index 0000000..955f907
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)inet.h      5.4 (Berkeley) 6/1/90
+ */
+
+/* External definitions for functions in inet(3) */
+#include "config.h" /* for system definitions */
+
+#ifdef __alpha
+#define        __u_l   unsigned int
+#else
+#define        __u_l   unsigned long
+#endif
+
+#ifdef __STDC__
+extern __u_l inet_addr(char *);
+extern char *inet_ntoa(char *);
+extern __u_l inet_netof (struct in_addr);
+extern __u_l inet_makeaddr(int , int);
+extern __u_l inet_network(char *);
+extern __u_l inet_lnaof(struct in_addr);
+#else
+extern __u_l inet_addr();
+extern char *inet_ntoa();
+#ifndef HPUX
+extern __u_l inet_makeaddr();
+#endif
+#endif
+#ifndef  HPUX
+extern __u_l inet_network();
+extern __u_l inet_lnaof();
+#endif
+#undef __u_l
diff --git a/include/msg.h b/include/msg.h
new file mode 100644 (file)
index 0000000..f304536
--- /dev/null
@@ -0,0 +1,353 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/msg.h
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef        __msg_include__
+#define __msg_include__
+
+/*
+ * The tokens are in the ascii character range of 33-127, and we start
+ * from 33 and just move up.  It would be nice to match then up so they
+ * are slightly related to their string counterpart, but that makes it
+ * too confusing when we want to add another one and need to make sure
+ * we're not using one already used. -Cabal95
+ *
+ * As long as the #defines are kept statically placed, it will be fine.
+ * We don't care/worry about the msgtab[] since it can be dynamic, but
+ * the tokens it uses will still be static according to the messages
+ * they represent.  In other words leave the #defines in order, if you're
+ * going to add something, PUT IT AT THE END.  Do not even look for an
+ * open spot somewhere, as that may lead to one type of message being
+ * sent by server A to server B, but server B thinks its something else.
+ * Remember, skip the : since its got a special use, and I skip the \ too
+ * since it _may_ cause problems, but not sure.  -Cabal95
+ * I'm skipping A and a as well, because some clients and scripts use
+ * these to test if the server has already processed whole queue.
+ * Since the client could request this protocol withhout the script
+ * knowing it, I'm considering that reserved, and TRACE/A is now 'b'.
+ * The normal msgtab should probably process this as special. -Donwulff
+ */
+
+  /* When tokens are all used we'll continue with AA etc. - GZ */
+
+#define MSG_PRIVATE    "PRIVMSG"       /* PRIV */
+#define TOK_PRIVATE    "!"             /* 33 */
+#define MSG_WHO                "WHO"           /* WHO  -> WHOC */
+#define TOK_WHO                "\""            /* 34 */
+#define MSG_WHOIS      "WHOIS"         /* WHOI */
+#define TOK_WHOIS      "#"             /* 35 */
+#define MSG_WHOWAS     "WHOWAS"        /* WHOW */
+#define TOK_WHOWAS     "$"             /* 36 */
+#define MSG_USER       "USER"          /* USER */
+#define TOK_USER       "%"             /* 37 */
+#define MSG_NICK       "NICK"          /* NICK */
+#define TOK_NICK       "&"             /* 38 */
+#define MSG_SERVER     "SERVER"        /* SERV */
+#define TOK_SERVER     "'"             /* 39 */
+#define MSG_LIST       "LIST"          /* LIST */
+#define TOK_LIST       "("             /* 40 */
+#define MSG_TOPIC      "TOPIC"         /* TOPI */
+#define TOK_TOPIC      ")"             /* 41 */
+#define MSG_INVITE     "INVITE"        /* INVI */
+#define TOK_INVITE     "*"             /* 42 */
+#define MSG_VERSION    "VERSION"       /* VERS */
+#define TOK_VERSION    "+"             /* 43 */
+#define MSG_QUIT       "QUIT"          /* QUIT */
+#define TOK_QUIT       ","             /* 44 */
+#define MSG_SQUIT      "SQUIT"         /* SQUI */
+#define TOK_SQUIT      "-"             /* 45 */
+#define MSG_KILL       "KILL"          /* KILL */
+#define TOK_KILL       "."             /* 46 */
+#define MSG_INFO       "INFO"          /* INFO */
+#define TOK_INFO       "/"             /* 47 */
+#define MSG_LINKS      "LINKS"         /* LINK */
+#define TOK_LINKS      "0"             /* 48 */
+#define MSG_STATS      "STATS"         /* STAT */
+#define TOK_STATS      "2"             /* 50 */
+#define MSG_HELP       "HELP"          /* HELP */
+#define TOK_HELP       "4"             /* 52 */
+#define MSG_ERROR      "ERROR"         /* ERRO */
+#define TOK_ERROR      "5"             /* 53 */
+#define MSG_AWAY       "AWAY"          /* AWAY */
+#define TOK_AWAY       "6"             /* 54 */
+#define MSG_CONNECT    "CONNECT"       /* CONN */
+#define TOK_CONNECT    "7"             /* 55 */
+#define MSG_PING       "PING"          /* PING */
+#define TOK_PING       "8"             /* 56 */
+#define MSG_PONG       "PONG"          /* PONG */
+#define TOK_PONG       "9"             /* 57 */
+#define MSG_OPER       "OPER"          /* OPER */
+#define TOK_OPER       ";"             /* 59 */
+#define MSG_PASS       "PASS"          /* PASS */
+#define TOK_PASS       "<"             /* 60 */
+#define MSG_TIME       "TIME"          /* TIME */
+#define TOK_TIME       ">"             /* 62 */
+#define MSG_NAMES      "NAMES"         /* NAME */
+#define TOK_NAMES      "?"             /* 63 */
+#define MSG_ADMIN      "ADMIN"         /* ADMI */
+#define TOK_ADMIN      "@"             /* 64 */
+#define MSG_NOTICE     "NOTICE"        /* NOTI */
+#define TOK_NOTICE     "B"             /* 66 */
+#define MSG_JOIN       "JOIN"          /* JOIN */
+#define TOK_JOIN       "C"             /* 67 */
+#define MSG_PART       "PART"          /* PART */
+#define TOK_PART       "D"             /* 68 */
+#define MSG_LUSERS     "LUSERS"        /* LUSE */
+#define TOK_LUSERS     "E"             /* 69 */
+#define MSG_MOTD       "MOTD"          /* MOTD */
+#define TOK_MOTD       "F"             /* 70 */
+#define MSG_MODE       "MODE"          /* MODE */
+#define TOK_MODE       "G"             /* 71 */
+#define MSG_KICK       "KICK"          /* KICK */
+#define TOK_KICK       "H"             /* 72 */
+#define MSG_USERHOST   "USERHOST"      /* USER -> USRH */
+#define TOK_USERHOST   "J"             /* 74 */
+#define MSG_ISON       "ISON"          /* ISON */
+#define TOK_ISON       "K"             /* 75 */
+#define        MSG_REHASH      "REHASH"        /* REHA */
+#define TOK_REHASH     "O"             /* 79 */
+#define        MSG_RESTART     "RESTART"       /* REST */
+#define TOK_RESTART    "P"             /* 80 */
+#define        MSG_CLOSE       "CLOSE"         /* CLOS */
+#define TOK_CLOSE      "Q"             /* 81 */
+#define        MSG_DIE         "DIE"           /* DIE */
+#define TOK_DIE                "R"             /* 82 */
+#define        MSG_HASH        "HASH"          /* HASH */
+#define TOK_HASH       "S"             /* 83 */
+#define        MSG_DNS         "DNS"           /* DNS  -> DNSS */
+#define TOK_DNS                "T"             /* 84 */
+#define MSG_SILENCE    "SILENCE"       /* SILE */
+#define TOK_SILENCE    "U"             /* 85 */
+#define MSG_AKILL      "AKILL"         /* AKILL */
+#define TOK_AKILL      "V"             /* 86 */
+#define MSG_KLINE      "KLINE"         /* KLINE */
+#define TOK_KLINE      "W"             /* 87 */
+#define MSG_UNKLINE    "UNKLINE"       /* UNKLINE */
+#define TOK_UNKLINE    "X"             /* 88 */
+#define MSG_RAKILL     "RAKILL"        /* RAKILL */
+#define TOK_RAKILL     "Y"             /* 89 */
+#define MSG_GNOTICE    "GNOTICE"       /* GNOTICE */
+#define TOK_GNOTICE    "Z"             /* 90 */
+#define MSG_GOPER      "GOPER"         /* GOPER */
+#define TOK_GOPER      "["             /* 91 */
+#define MSG_GLOBOPS    "GLOBOPS"       /* GLOBOPS */
+#define TOK_GLOBOPS    "]"             /* 93 */
+#define MSG_LOCOPS     "LOCOPS"        /* LOCOPS */
+#define TOK_LOCOPS     "^"             /* 94 */
+#define MSG_PROTOCTL   "PROTOCTL"      /* PROTOCTL */
+#define TOK_PROTOCTL   "_"             /* 95 */
+#define MSG_WATCH      "WATCH"         /* WATCH */
+#define TOK_WATCH      "`"             /* 96 */
+#define MSG_TRACE      "TRACE"         /* TRAC */
+#define TOK_TRACE      "b"             /* 97 */
+#define MSG_SQLINE     "SQLINE"        /* SQLINE */
+#define TOK_SQLINE     "c"             /* 98 */
+#define MSG_UNSQLINE   "UNSQLINE"      /* UNSQLINE */
+#define TOK_UNSQLINE   "d"             /* 99 */
+#define MSG_SVSNICK    "SVSNICK"       /* SVSNICK */
+#define TOK_SVSNICK    "e"             /* 100 */
+#define MSG_SVSNOOP    "SVSNOOP"       /* SVSNOOP */
+#define TOK_SVSNOOP    "f"             /* 101 */
+#define MSG_IDENTIFY   "IDENTIFY"      /* IDENTIFY */
+#define TOK_IDENTIFY   "g"             /* 102 */
+#define MSG_SVSKILL    "SVSKILL"       /* SVSKILL */
+#define TOK_SVSKILL    "h"             /* 103 */
+#define MSG_NICKSERV   "NICKSERV"      /* NICKSERV */
+#define MSG_NS         "NS"
+#define TOK_NICKSERV   "i"             /* 104 */
+#define MSG_CHANSERV   "CHANSERV"      /* CHANSERV */
+#define MSG_CS         "CS"
+#define TOK_CHANSERV   "j"             /* 105 */
+#define MSG_OPERSERV   "OPERSERV"      /* OPERSERV */
+#define MSG_OS         "OS"
+#define TOK_OPERSERV   "k"             /* 106 */
+#define MSG_MEMOSERV   "MEMOSERV"      /* MEMOSERV */
+#define MSG_MS         "MS"
+#define TOK_MEMOSERV   "l"             /* 107 */
+#define MSG_SVSMODE    "SVSMODE"       /* SVSMODE */
+#define TOK_SVSMODE    "n"             /* 109 */
+#define MSG_SAMODE     "SAMODE"        /* SAMODE */
+#define TOK_SAMODE     "o"             /* 110 */
+#define MSG_ZLINE      "ZLINE"         /* ZLINE */
+#define TOK_ZLINE      "q"             /* 112 */
+#define MSG_UNZLINE    "UNZLINE"       /* UNZLINE */                           
+#define TOK_UNZLINE    "r"             /* 113 */
+#define MSG_OPERMOTD    "OPERMOTD"      /* OPERMOTD */
+#define TOK_OPERMOTD    "s"             /* 114 */
+#define MSG_JINX        "JINX"          /* JINX */
+#define TOK_JINX        "t"             /* 115 */
+#define MSG_UNJINX      "UNJINX"        /* UNJINX */
+#define TOK_UNJINX      "u"             /* 116 */
+#define MSG_RPING      "RPING"         /* RPING */
+#define TOK_RPING       "v"            /* 117 */
+#define MSG_RPONG       "RPONG"         /* RPONG */
+#define TOK_RPONG       "w"            /* 118 */
+#define MSG_UPING       "UPING"         /* UPING */
+#define TOK_UPING       "x"             /* 119 */
+#define MSG_MAP         "MAP"           /* MAP */
+#define TOK_MAP         "{"             /* 122 */
+#define MSG_SJOIN       "SJOIN"         /* SJOIN */
+#define TOK_SJOIN       "|"             /* 123 */
+#define MSG_SNICK      "SNICK"         /* SNICK */
+#define TOK_SNICK      "}"             /* 124 */
+#define MSG_KNOCK       "KNOCK"         /* KNOCK */
+#define TOK_KNOCK       "~"             /* 125 */
+#define MSG_MKILL       "MKILL"         /* MKILL */
+#define TOK_MKILL       "="             /* 126 */
+#define MSG_GLINE       "GLINE"         /* GLINE */
+#define TOK_GLINE       "1"             /* 127 */
+/** We are running out of 8bit tokens. We'll switch to 16bit 
+ ** tokens from here. -GZ 
+ **/
+#define MSG_SVSJOIN     "SVSJOIN"       /* SVSJOIN */
+#define TOK_SVSJOIN     "!!"            /* 8481 */
+#define MSG_SVSPART     "SVSPART"       /* SVSPART */
+#define TOK_SVSPART     "!\""           /* 8482 */
+#define MSG_AKILLEX    "AKILLEX"       /* AKILLEX */
+#define TOK_AKILLEX    "!#"            /* 8483 */
+#define MSG_RAKILLEX   "RAKILLEX"      /* RAKILLEX */
+#define TOK_RAKILLEX   "!$"            /* 8484 */
+
+#define MAXPARA         15 
+
+extern int m_private(), m_topic(), m_join(), m_part(), m_mode(), m_svsmode();
+extern int m_ping(), m_pong(), m_kick(), m_svsnick();
+extern int m_nick(), m_error(), m_notice(), m_samode(), m_svsnoop();
+extern int m_invite(), m_quit(), m_kill(), m_svskill(), m_identify();
+extern int m_akill(), m_kline(), m_unkline(), m_rakill(), m_sqline();
+extern int m_akillex(), m_rakillex(), m_zline(), m_unzline();
+extern int m_gnotice(), m_goper(), m_globops(), m_locops(), m_unsqline();
+extern int m_protoctl();
+extern int m_motd(), m_who(), m_whois(), m_user(), m_list();
+extern int m_server(), m_info(), m_links(), m_stats();
+extern int m_version(), m_help();
+extern int m_nickserv(), m_operserv(), m_chanserv(), m_memoserv(), m_services(), m_identify();
+extern int m_squit(), m_away(), m_connect();
+extern int m_oper(), m_pass(), m_trace();
+extern int m_time(), m_names(), m_admin();
+extern int m_lusers(), m_umode(), m_close();
+extern int m_motd(), m_whowas(), m_silence();
+extern int m_userhost(), m_ison(), m_watch();
+extern int m_rehash(), m_restart(), m_die(), m_dns(), m_hash();
+extern int m_noshortn(),m_noshortc(),m_noshortm(),m_noshorto();
+extern int m_opermotd(),m_jinx(),m_unjinx();
+extern int m_rping(), m_rpong(), m_uping();
+extern int m_knock(), m_sjoin(), m_snick();
+extern int m_map(), m_mkill();
+extern int m_svsjoin(), m_svspart();
+
+#ifdef MSGTAB
+struct Message *msgmap[256];
+struct Message msgtab[] = {
+  { MSG_PRIVATE, m_private,  0, MAXPARA, 1, TOK_PRIVATE, 0L },
+  { MSG_NICK,    m_nick,     0, MAXPARA, 1, TOK_NICK,    0L },
+  { MSG_NOTICE,  m_notice,   0, MAXPARA, 1, TOK_NOTICE,  0L },
+  { MSG_JOIN,    m_join,     0, MAXPARA, 1, TOK_JOIN,    0L },
+  { MSG_MODE,    m_mode,     0, MAXPARA, 1, TOK_MODE,    0L },
+  { MSG_QUIT,    m_quit,     0, MAXPARA, 1, TOK_QUIT,    0L },
+  { MSG_PART,    m_part,     0, MAXPARA, 1, TOK_PART,    0L },
+  { MSG_TOPIC,   m_topic,    0, MAXPARA, 1, TOK_TOPIC,   0L },
+  { MSG_INVITE,  m_invite,   0, MAXPARA, 1, TOK_INVITE,  0L },
+  { MSG_KICK,    m_kick,     0, MAXPARA, 1, TOK_KICK,    0L },
+  { MSG_PING,    m_ping,     0, MAXPARA, 1, TOK_PING,    0L },
+  { MSG_PONG,    m_pong,     0, MAXPARA, 1, TOK_PONG,    0L },
+  { MSG_ERROR,   m_error,    0, MAXPARA, 1, TOK_ERROR,   0L },
+  { MSG_KILL,    m_kill,     0, MAXPARA, 1, TOK_KILL,    0L },
+  { MSG_PROTOCTL,m_protoctl, 0, MAXPARA, 1, TOK_PROTOCTL,0L },
+  { MSG_USER,    m_user,     0, MAXPARA, 1, TOK_USER,    0L },
+  { MSG_AWAY,    m_away,     0, MAXPARA, 1, TOK_AWAY,    0L },
+  { MSG_ISON,    m_ison,     0, 1,       1, TOK_ISON,    0L },
+  { MSG_WATCH,   m_watch,    0, 1,       1, TOK_WATCH,   0L }, 
+  { MSG_SERVER,  m_server,   0, MAXPARA, 1, TOK_SERVER,  0L },
+  { MSG_SQUIT,   m_squit,    0, MAXPARA, 1, TOK_SQUIT,   0L },
+  { MSG_WHOIS,   m_whois,    0, MAXPARA, 1, TOK_WHOIS,   0L },
+  { MSG_WHO,     m_who,      0, MAXPARA, 1, TOK_WHO,     0L },
+  { MSG_WHOWAS,  m_whowas,   0, MAXPARA, 1, TOK_WHOWAS,  0L },
+  { MSG_LIST,    m_list,     0, MAXPARA, 1, TOK_LIST,    0L },
+  { MSG_NAMES,   m_names,    0, MAXPARA, 1, TOK_NAMES,   0L },
+  { MSG_USERHOST,m_userhost, 0, 1,       1, TOK_USERHOST,0L },
+  { MSG_TRACE,   m_trace,    0, MAXPARA, 1, TOK_TRACE,   0L },
+  { MSG_PASS,    m_pass,     0, MAXPARA, 1, TOK_PASS,    0L },
+  { MSG_LUSERS,  m_lusers,   0, MAXPARA, 1, TOK_LUSERS,  0L },
+  { MSG_TIME,    m_time,     0, MAXPARA, 1, TOK_TIME,    0L },
+  { MSG_OPER,    m_oper,     0, MAXPARA, 1, TOK_OPER,    0L },
+  { MSG_CONNECT, m_connect,  0, MAXPARA, 1, TOK_CONNECT, 0L },
+  { MSG_VERSION, m_version,  0, MAXPARA, 1, TOK_VERSION, 0L },
+  { MSG_STATS,   m_stats,    0, MAXPARA, 1, TOK_STATS,   0L },
+  { MSG_LINKS,   m_links,    0, MAXPARA, 1, TOK_LINKS,   0L },
+  { MSG_ADMIN,   m_admin,    0, MAXPARA, 1, TOK_ADMIN,   0L },
+  { MSG_SVSMODE, m_svsmode,  0, MAXPARA, 1, TOK_SVSMODE, 0L },
+  { MSG_SAMODE,  m_samode,   0, MAXPARA, 1, TOK_SAMODE,  0L },
+  { MSG_SVSKILL, m_svskill,  0, MAXPARA, 1, TOK_SVSKILL, 0L },
+  { MSG_SVSNICK, m_svsnick,  0, MAXPARA, 1, TOK_SVSNICK, 0L },
+  { MSG_SVSNOOP, m_svsnoop,  0, MAXPARA, 1, TOK_SVSNOOP, 0L },
+  { MSG_CHANSERV,m_chanserv, 0, 1,       1, TOK_CHANSERV,0L },
+  { MSG_CS,     m_chanserv, 0, 1,       1, TOK_CHANSERV,0L },
+  { MSG_NICKSERV,m_nickserv, 0, 1,       1, TOK_NICKSERV,0L },
+  { MSG_NS,     m_nickserv, 0, 1,       1, TOK_NICKSERV,0L },
+  { MSG_OPERSERV,m_operserv, 0, 1,       1, TOK_OPERSERV,0L },
+  { MSG_OS,     m_operserv, 0, 1,       1, TOK_OPERSERV,0L },
+  { MSG_MEMOSERV,m_memoserv, 0, 1,       1, TOK_MEMOSERV,0L },
+  { MSG_MS,     m_memoserv, 0, 1,       1, TOK_MEMOSERV,0L },
+  { MSG_IDENTIFY,m_identify, 0, 1,       1, TOK_IDENTIFY,0L },
+  { MSG_HELP,    m_help,     0, 1,       1, TOK_HELP,    0L },
+  { MSG_INFO,    m_info,     0, MAXPARA, 1, TOK_INFO,    0L },
+  { MSG_MOTD,    m_motd,     0, MAXPARA, 1, TOK_MOTD,    0L },
+  { MSG_CLOSE,   m_close,    0, MAXPARA, 1, TOK_CLOSE,   0L },
+  { MSG_SILENCE, m_silence,  0, MAXPARA, 1, TOK_SILENCE, 0L },
+  { MSG_AKILL,   m_akill,    0, MAXPARA, 1, TOK_AKILL,   0L },
+  { MSG_SQLINE,  m_sqline,   0, MAXPARA, 1, TOK_SQLINE,  0L },
+  { MSG_UNSQLINE,m_unsqline, 0, MAXPARA, 1, TOK_UNSQLINE,0L },
+  { MSG_KLINE,   m_kline,    0, MAXPARA, 1, TOK_KLINE,   0L },
+  { MSG_UNKLINE, m_unkline,  0, MAXPARA, 1, TOK_UNKLINE, 0L },
+  { MSG_ZLINE,   m_zline,    0, MAXPARA, 1, TOK_ZLINE,   0L },
+  { MSG_UNZLINE, m_unzline,  0, MAXPARA, 1, TOK_UNZLINE, 0L },
+  { MSG_RAKILL,  m_rakill,   0, MAXPARA, 1, TOK_RAKILL,  0L },
+  { MSG_GNOTICE, m_gnotice,  0, MAXPARA, 1, TOK_GNOTICE, 0L },
+  { MSG_GOPER,   m_goper,    0, MAXPARA, 1, TOK_GOPER,   0L },
+  { MSG_GLOBOPS, m_globops,  0, MAXPARA, 1, TOK_GLOBOPS, 0L },
+  { MSG_LOCOPS,  m_locops,   0, 1,       1, TOK_LOCOPS,  0L },
+#undef USE_SERVICES
+  { MSG_HASH,    m_hash,     0, MAXPARA, 1, TOK_HASH,    0L },
+  { MSG_DNS,     m_dns,      0, MAXPARA, 1, TOK_DNS,     0L },
+  { MSG_REHASH,  m_rehash,   0, MAXPARA, 1, TOK_REHASH,  0L },
+  { MSG_RESTART, m_restart,  0, MAXPARA, 1, TOK_RESTART, 0L },
+  { MSG_DIE,     m_die,      0, MAXPARA, 1, TOK_DIE,     0L },
+  { MSG_OPERMOTD, m_opermotd, 0, MAXPARA, 1, TOK_OPERMOTD, 0L },
+  { MSG_JINX,   m_jinx,     0, MAXPARA, 1, TOK_JINX,    0L },
+  { MSG_UNJINX,  m_unjinx,   0, MAXPARA, 1, TOK_UNJINX,  0L },
+  { MSG_RPING,   m_rping,    0, MAXPARA, 1, TOK_RPING,   0L },
+  { MSG_RPONG,   m_rpong,    0, MAXPARA, 1, TOK_RPONG,   0L },
+  { MSG_UPING,   m_uping,    0, MAXPARA, 1, TOK_UPING,   0L },
+  { MSG_SJOIN,   m_sjoin,    0, MAXPARA, 1, TOK_SJOIN,   0L },
+  { MSG_SNICK,   m_snick,    0, MAXPARA, 1, TOK_SNICK,   0L },
+  { MSG_KNOCK,   m_knock,    0, MAXPARA, 1, TOK_KNOCK,   0L },
+  { MSG_MAP,     m_map,      0, MAXPARA, 1, TOK_MAP,     0L },
+  { MSG_MKILL,   m_mkill,    0, MAXPARA, 1, TOK_MKILL,   0L },
+  { MSG_SVSJOIN, m_svsjoin,  0, MAXPARA, 1, TOK_SVSJOIN, 0L },
+  { MSG_SVSPART, m_svspart,  0, MAXPARA, 1, TOK_SVSPART, 0L },
+  { MSG_AKILLEX, m_akillex,  0, MAXPARA, 1, TOK_AKILLEX, 0L },
+  { MSG_RAKILLEX, m_rakillex, 0, MAXPARA, 1, TOK_RAKILLEX, 0L },
+  { (char *) 0, (int (*)()) 0 , 0, 0, 0, {0}, 0L}
+};
+#else
+extern struct Message msgtab[];
+extern struct Message *msgmap[256];
+#endif
+#endif /* __msg_include__ */
diff --git a/include/nameser.h b/include/nameser.h
new file mode 100644 (file)
index 0000000..9e06b97
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1983, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)nameser.h   5.24 (Berkeley) 6/1/90
+ */
+
+/*
+ * Define constants based on rfc883
+ */
+#define PACKETSZ       512             /* maximum packet size */
+#define MAXDNAME       256             /* maximum domain name */
+#define MAXCDNAME      255             /* maximum compressed domain name */
+#define MAXLABEL       63              /* maximum length of domain label */
+       /* Number of bytes of fixed size data in query structure */
+#define QFIXEDSZ       4
+       /* number of bytes of fixed size data in resource record */
+#define RRFIXEDSZ      10
+
+/*
+ * Internet nameserver port number
+ */
+#define NAMESERVER_PORT        53
+
+/*
+ * Currently defined opcodes
+ */
+#define QUERY          0x0             /* standard query */
+#define IQUERY         0x1             /* inverse query */
+#define STATUS         0x2             /* nameserver status query */
+/*#define xxx          0x3              0x3 reserved */
+       /* non standard */
+#define UPDATEA                0x9             /* add resource record */
+#define UPDATED                0xa             /* delete a specific resource record */
+#define UPDATEDA       0xb             /* delete all nemed resource record */
+#define UPDATEM                0xc             /* modify a specific resource record */
+#define UPDATEMA       0xd             /* modify all named resource record */
+
+#define ZONEINIT       0xe             /* initial zone transfer */
+#define ZONEREF                0xf             /* incremental zone referesh */
+
+/*
+ * Currently defined response codes
+ */
+#ifdef NOERROR                         /* defined by solaris2 in */
+#undef NOERROR                         /* <sys/stream.h> to be -1 */
+#endif
+#define NOERROR                0               /* no error */
+#define FORMERR                1               /* format error */
+#define SERVFAIL       2               /* server failure */
+#define NXDOMAIN       3               /* non existent domain */
+#define NOTIMP         4               /* not implemented */
+#define REFUSED                5               /* query refused */
+       /* non standard */
+#define NOCHANGE       0xf             /* update failed to change db */
+
+/*
+ * Type values for resources and queries
+ */
+#define T_A            1               /* host address */
+#define T_NS           2               /* authoritative server */
+#define T_MD           3               /* mail destination */
+#define T_MF           4               /* mail forwarder */
+#define T_CNAME                5               /* connonical name */
+#define T_SOA          6               /* start of authority zone */
+#define T_MB           7               /* mailbox domain name */
+#define T_MG           8               /* mail group member */
+#define T_MR           9               /* mail rename name */
+#define T_NULL         10              /* null resource record */
+#define T_WKS          11              /* well known service */
+#define T_PTR          12              /* domain name pointer */
+#define T_HINFO                13              /* host information */
+#define T_MINFO                14              /* mailbox information */
+#define T_MX           15              /* mail routing information */
+#define T_TXT          16              /* text strings */
+       /* non standard */
+#define T_UINFO                100             /* user (finger) information */
+#define T_UID          101             /* user ID */
+#define T_GID          102             /* group ID */
+#define T_UNSPEC       103             /* Unspecified format (binary data) */
+       /* Query type values which do not appear in resource records */
+#define T_AXFR         252             /* transfer zone of authority */
+#define T_MAILB                253             /* transfer mailbox records */
+#define T_MAILA                254             /* transfer mail agent records */
+#define T_ANY          255             /* wildcard match */
+
+/*
+ * Values for class field
+ */
+
+#define C_IN           1               /* the arpa internet */
+#define C_CHAOS                3               /* for chaos net at MIT */
+#define C_HS           4               /* for Hesiod name server at MIT */
+       /* Query class values which do not appear in resource records */
+#define C_ANY          255             /* wildcard match */
+
+/*
+ * Status return codes for T_UNSPEC conversion routines
+ */
+#define CONV_SUCCESS 0
+#define CONV_OVERFLOW -1
+#define CONV_BADFMT -2
+#define CONV_BADCKSUM -3
+#define CONV_BADBUFLEN -4
+
+#ifndef BYTE_ORDER
+#define        LITTLE_ENDIAN   1234    /* least-significant byte first (vax) */
+#define        BIG_ENDIAN      4321    /* most-significant byte first (IBM, net) */
+#define        PDP_ENDIAN      3412    /* LSB first in word, MSW first in long (pdp) */
+
+#if defined(vax) || defined(ns32000) || defined(sun386) || defined(MIPSEL) || \
+    defined(BIT_ZERO_ON_RIGHT) || defined(sequent) || defined(i386) ||\
+    defined(___vax__) || defined(__ns32000__) || defined(__sun386__) ||\
+    defined(__alpha)
+#define BYTE_ORDER     LITTLE_ENDIAN
+
+#endif
+#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
+    defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
+    defined(MIPSEB) || defined(__hpux) || defined(__convex__) || \
+    defined(__pyr__) || defined(__mc68000__) || defined(__sparc__) ||\
+    defined(_IBMR2) || defined (BIT_ZERO_ON_LEFT)
+#define BYTE_ORDER     BIG_ENDIAN
+#endif
+#endif /* BYTE_ORDER */
+
+#ifndef BYTE_ORDER
+/* you must determine what the correct bit order is for your compiler */
+       UNDEFINED_BIT_ORDER;
+#endif
+/*
+ * Structure for query header, the order of the fields is machine and
+ * compiler dependent, in our case, the bits within a byte are assignd 
+ * least significant first, while the order of transmition is most 
+ * significant first.  This requires a somewhat confusing rearrangement.
+ */
+
+typedef struct {
+       u_short id;             /* query identification number */
+#if BYTE_ORDER == BIG_ENDIAN
+                       /* fields in third byte */
+       u_char  qr:1;           /* response flag */
+       u_char  opcode:4;       /* purpose of message */
+       u_char  aa:1;           /* authoritive answer */
+       u_char  tc:1;           /* truncated message */
+       u_char  rd:1;           /* recursion desired */
+                       /* fields in fourth byte */
+       u_char  ra:1;           /* recursion available */
+       u_char  pr:1;   /* primary server required (non standard) */
+       u_char  unused:2;       /* unused bits */
+       u_char  rcode:4;        /* response code */
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN || BYTE_ORDER == PDP_ENDIAN
+                       /* fields in third byte */
+       u_char  rd:1;           /* recursion desired */
+       u_char  tc:1;           /* truncated message */
+       u_char  aa:1;           /* authoritive answer */
+       u_char  opcode:4;       /* purpose of message */
+       u_char  qr:1;           /* response flag */
+                       /* fields in fourth byte */
+       u_char  rcode:4;        /* response code */
+       u_char  unused:2;       /* unused bits */
+       u_char  pr:1;   /* primary server required (non standard) */
+       u_char  ra:1;           /* recursion available */
+#endif
+                       /* remaining bytes */
+       u_short qdcount;        /* number of question entries */
+       u_short ancount;        /* number of answer entries */
+       u_short nscount;        /* number of authority entries */
+       u_short arcount;        /* number of resource entries */
+} HEADER;
+
+/*
+ * Defines for handling compressed domain names
+ */
+#define INDIR_MASK     0xc0
+
+/*
+ * Structure for passing resource records around.
+ */
+struct rrec {
+       short   r_zone;                 /* zone number */
+       short   r_class;                /* class number */
+       short   r_type;                 /* type number */
+#ifdef __alpha
+       u_int   r_ttl;                  /* time to live */
+#else
+       u_long  r_ttl;                  /* time to live */
+#endif
+       int     r_size;                 /* size of data area */
+       char    *r_data;                /* pointer to data */
+};
+
+extern u_short _getshort();
+#ifdef __alpha
+extern u_int   _getlong();
+#else
+extern u_long  _getlong();
+#endif
+
+/*
+ * Inline versions of get/put short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETSHORT(s, cp) { \
+       (s) = *(cp)++ << 8; \
+       (s) |= *(cp)++; \
+}
+
+#define GETLONG(l, cp) { \
+       (l) = *(cp)++ << 8; \
+       (l) |= *(cp)++; (l) <<= 8; \
+       (l) |= *(cp)++; (l) <<= 8; \
+       (l) |= *(cp)++; \
+}
+
+
+#define PUTSHORT(s, cp) { \
+       *(cp)++ = (s) >> 8; \
+       *(cp)++ = (s); \
+}
+
+/*
+ * Warning: PUTLONG destroys its first argument.
+ */
+#define PUTLONG(l, cp) { \
+       (cp)[3] = l; \
+       (cp)[2] = (l >>= 8); \
+       (cp)[1] = (l >>= 8); \
+       (cp)[0] = l >> 8; \
+       (cp) += sizeof(u_long); \
+}
diff --git a/include/numeric.h b/include/numeric.h
new file mode 100644 (file)
index 0000000..ba7dd7d
--- /dev/null
@@ -0,0 +1,292 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/numeric.h
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Reserve numerics 000-099 for server-client connections where the client
+ * is local to the server. If any server is passed a numeric in this range
+ * from another server then it is remapped to 100-199. -avalon
+ */
+#define        RPL_WELCOME          001
+#define        RPL_YOURHOST         002
+#define        RPL_CREATED          003
+#define        RPL_MYINFO           004
+#define RPL_PROTOCTL        005
+#define RPL_MAP                     006
+#define RPL_MAPEND           007
+
+/*
+ * Errors are in the range from 400-599 currently and are grouped by what
+ * commands they come from.
+ */
+#define ERR_NOSUCHNICK       401
+#define ERR_NOSUCHSERVER     402
+#define ERR_NOSUCHCHANNEL    403
+#define ERR_CANNOTSENDTOCHAN 404
+#define ERR_TOOMANYCHANNELS  405
+#define ERR_WASNOSUCHNICK    406
+#define ERR_TOOMANYTARGETS   407
+#define ERR_NOSUCHSERVICE    408
+#define        ERR_NOORIGIN         409
+#define ERR_CANNOTKNOCK      410
+#define ERR_NORECIPIENT      411
+#define ERR_NOTEXTTOSEND     412
+#define ERR_NOTOPLEVEL       413
+#define ERR_WILDTOPLEVEL     414
+#define ERR_SERVICESUP       415
+
+#define ERR_UNKNOWNCOMMAND   421
+#define        ERR_NOMOTD           422
+#define        ERR_NOADMININFO      423
+#define        ERR_FILEERROR        424
+
+#define ERR_NONICKNAMEGIVEN  431
+#define ERR_ERRONEUSNICKNAME 432
+#define ERR_NICKNAMEINUSE    433
+#define ERR_SERVICENAMEINUSE 434
+#define ERR_SERVICECONFUSED  435
+#define        ERR_NICKCOLLISION    436
+#define ERR_BANNICKCHANGE    437
+#define ERR_NCHANGETOOFAST   438
+#define ERR_TARGETTOOFAST    439
+#define ERR_SERVICESDOWN     440
+
+#define ERR_USERNOTINCHANNEL 441
+#define ERR_NOTONCHANNEL     442
+#define        ERR_USERONCHANNEL    443
+#define ERR_NOLOGIN          444
+#define        ERR_SUMMONDISABLED   445
+#define ERR_USERSDISABLED    446
+
+#define ERR_NOTREGISTERED    451
+
+#define ERR_HOSTILENAME      455
+
+#define ERR_NEEDMOREPARAMS   461
+#define ERR_ALREADYREGISTRED 462
+#define ERR_NOPERMFORHOST    463
+#define ERR_PASSWDMISMATCH   464
+#define ERR_YOUREBANNEDCREEP 465
+#define ERR_YOUWILLBEBANNED  466
+#define        ERR_KEYSET           467
+#define ERR_ONLYSERVERSCANCHANGE 468
+
+#define ERR_CHANNELISFULL    471
+#define ERR_UNKNOWNMODE      472
+#define ERR_INVITEONLYCHAN   473
+#define ERR_BANNEDFROMCHAN   474
+#define        ERR_BADCHANNELKEY    475
+#define        ERR_BADCHANMASK      476
+#define ERR_NEEDREGGEDNICK   477
+#define ERR_BANLISTFULL      478
+#define ERR_NEEDREGGEDNICKTOMSG  479
+#define ERR_NOPRIVILEGES     481
+#define ERR_CHANOPRIVSNEEDED 482
+#define        ERR_CANTKILLSERVER   483
+#define ERR_ISROOT           484       /* Undernet extension was ERR_ISCHANSERVICE */
+
+
+#define ERR_NOOPERHOST       491
+#define ERR_NOSERVICEHOST    492
+
+
+#define ERR_UMODEUNKNOWNFLAG 501
+#define ERR_USERSDONTMATCH   502
+
+#define ERR_SILELISTFULL     511
+#define ERR_TOOMANYWATCH     512
+#define ERR_NEEDPONG         513
+
+#define ERR_LISTSYNTAX       521
+
+/*
+ * Numberic replies from server commands.
+ * These are currently in the range 200-399.
+ */
+#define        RPL_NONE             300
+#define RPL_AWAY             301
+#define RPL_USERHOST         302
+#define RPL_ISON             303
+#define RPL_TEXT             304
+#define        RPL_UNAWAY           305
+#define        RPL_NOWAWAY          306
+#define RPL_WHOISREGNICK     307
+#define RPL_WHOISADMIN      308 /* redundant due to dreamforge changes in /whois reply */
+#define RPL_WHOISSADMIN      309 /* shall we remove these? They're not doing anything */
+#define RPL_WHOISUSER        311
+#define RPL_WHOISSERVER      312
+#define RPL_WHOISOPERATOR    313
+
+#define RPL_WHOWASUSER       314
+/* rpl_endofwho below (315) */
+#define        RPL_ENDOFWHOWAS      369
+
+#define RPL_WHOISCHANOP      316 /* redundant and not needed but reserved */
+#define RPL_WHOISIDLE        317
+
+#define RPL_ENDOFWHOIS       318
+#define RPL_WHOISCHANNELS    319
+#define RPL_WHOISCONNECTION  320
+
+#define RPL_LISTSTART        321
+#define RPL_LIST             322
+#define RPL_LISTEND          323
+#define RPL_CHANNELMODEIS    324
+#define RPL_CREATIONTIME     329
+
+#define RPL_NOTOPIC          331
+#define RPL_TOPIC            332
+#define RPL_TOPICWHOTIME     333
+
+#define RPL_LISTSYNTAX       334
+
+#define RPL_INVITING         341
+#define        RPL_SUMMONING        342
+
+#define RPL_VERSION          351
+
+#define RPL_WHOREPLY         352
+#define RPL_ENDOFWHO         315
+#define RPL_NAMREPLY         353
+#define RPL_ENDOFNAMES       366
+
+#define RPL_KILLDONE         361
+#define        RPL_CLOSING          362
+#define RPL_CLOSEEND         363
+#define RPL_LINKS            364
+#define RPL_ENDOFLINKS       365
+/* rpl_endofnames above (366) */
+#define RPL_BANLIST          367
+#define RPL_ENDOFBANLIST     368
+/* rpl_endofwhowas above (369) */
+
+#define        RPL_INFO             371
+#define        RPL_MOTD             372
+#define        RPL_INFOSTART        373
+#define        RPL_ENDOFINFO        374
+#define        RPL_MOTDSTART        375
+#define        RPL_ENDOFMOTD        376
+#define RPL_WHOISHOST        378
+
+#define RPL_EXBANLIST        379
+#define RPL_EXBANLISTEND     380
+
+#define RPL_YOUREOPER        381
+#define RPL_REHASHING        382
+#define RPL_YOURESERVICE     383
+#define RPL_MYPORTIS         384
+#define RPL_NOTOPERANYMORE   385
+
+#define RPL_HOSTRESTRICTLIST    386
+#define RPL_HOSTRESTRICTLISTEND 387
+
+#define RPL_TIME             391
+#define        RPL_USERSSTART       392
+#define        RPL_USERS            393
+#define        RPL_ENDOFUSERS       394
+#define        RPL_NOUSERS          395
+
+#define RPL_TRACELINK        200
+#define RPL_TRACECONNECTING  201
+#define RPL_TRACEHANDSHAKE   202
+#define RPL_TRACEUNKNOWN     203
+#define RPL_TRACEOPERATOR    204
+#define RPL_TRACEUSER        205
+#define RPL_TRACESERVER      206
+#define RPL_TRACESERVICE     207
+#define RPL_TRACENEWTYPE     208
+#define RPL_TRACECLASS       209
+
+#define RPL_STATSLINKINFO    211
+#define RPL_STATSCOMMANDS    212
+#define RPL_STATSCLINE       213
+#define RPL_STATSNLINE       214
+#define RPL_STATSILINE       215
+#define RPL_STATSKLINE       216
+#define RPL_STATSQLINE       217
+#define RPL_STATSYLINE       218
+#define RPL_ENDOFSTATS       219
+#define RPL_STATSBLINE      220
+
+#define RPL_UMODEIS          221
+#define RPL_SQLINE_NICK      222
+/* Related to SOCKS zline timer -taz */
+#define RPL_STATSFLINE       223
+
+#define RPL_SERVICEINFO      231
+#define RPL_ENDOFSERVICES    232
+#define        RPL_SERVICE          233
+#define RPL_SERVLIST         234
+#define RPL_SERVLISTEND      235
+#define RPL_JINX            236
+#define RPL_STATSJINX        237
+#define        RPL_STATSLLINE       241
+#define        RPL_STATSUPTIME      242
+#define        RPL_STATSOLINE       243
+#define        RPL_STATSHLINE       244
+#define        RPL_STATSSLINE       245
+#define RPL_STATSXLINE      247
+#define RPL_STATSULINE       248       
+#define        RPL_STATSDEBUG       249
+#define RPL_STATSCONN        250
+
+#define        RPL_LUSERCLIENT      251
+#define RPL_LUSEROP          252
+#define        RPL_LUSERUNKNOWN     253
+#define        RPL_LUSERCHANNELS    254
+#define        RPL_LUSERME          255
+#define        RPL_ADMINME          256
+#define        RPL_ADMINLOC1        257
+#define        RPL_ADMINLOC2        258
+#define        RPL_ADMINEMAIL       259
+
+#define        RPL_TRACELOG         261
+
+#define RPL_LOCALUSERS       265
+#define RPL_GLOBALUSERS      266
+
+#define RPL_SILELIST         271
+#define RPL_ENDOFSILELIST    272
+
+#define RPL_STATSDLINE       275
+
+/*
+ * Numberic replies from server commands.
+ * These are also in the range 600-799.
+ */
+
+#define RPL_LOGON            600
+#define RPL_LOGOFF           601
+#define RPL_WATCHOFF         602
+#define RPL_WATCHSTAT        603
+#define RPL_NOWON            604
+#define RPL_NOWOFF           605
+#define RPL_WATCHLIST        606
+#define RPL_ENDOFWATCHLIST   607
+#define RPL_WATCHCLEAR       608
+
+#define RPL_OMOTDSTART       609
+#define RPL_OMOTD            610
+#define RPL_ENDOFOMOTD       611
+#define RPL_WHOWASIP         612
+#define RPL_STATSGZLINE      613
+#define RPL_WHOISMODES       614
+#define RPL_MAPMORE         615
+#define RPL_POLICY          616
+#define ERR_HTCTOOFAST      617
+#define ERR_NEEDIDTODCC      618
diff --git a/include/patchlevel.h b/include/patchlevel.h
new file mode 100644 (file)
index 0000000..013e164
--- /dev/null
@@ -0,0 +1,127 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/patchlevel.h
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * PATCHes
+ *
+ * Only put here ADDED special stuff, for instance: ".mu3" or ".ban"
+ * Please start the patchlevel with a '.'
+ *
+ * IMPORTANT: We've copied the patch-scheme from u2.9, note the new
+ * format - it isn't neccessary to edit this manually !!!
+ * If you do, be sure you know what you are doing!
+ * 
+ * For patch developers:
+ * To make a diff of your patch, edit any of the below lines containing
+ * a "" (an EMPTY string). Your patch will then succeed, with only an
+ * offset, on the first empty place in the users patchlevel.h.
+ * Do not change anyother line, the '\' are to make sure that the 'fuzz'
+ * will stay 0. --Run
+ *
+ */
+
+#define PATCH1 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH2 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH3 \
+               \
+               \
+               \
+               ""      
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH4 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH5 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH6 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH7 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH8 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#ifdef DEBUGMODE 
+#define PATCH9 ".debug"
+#else
+#define PATCH9 ""
+#endif
+
+/*
+ * Deliberate empty lines
+ */
+
diff --git a/include/res.h b/include/res.h
new file mode 100644 (file)
index 0000000..3d384fa
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * irc2.7.2/ircd/res.h (C)opyright 1992 Darren Reed.
+ */
+#ifndef        __res_include__
+#define        __res_include__
+
+#define        RES_INITLIST    1
+#define        RES_CALLINIT    2
+#define RES_INITSOCK   4
+#define RES_INITDEBG   8
+#define RES_INITCACH    16
+
+#define MAXPACKET      1024
+#define MAXALIASES     35
+#define MAXADDRS       35
+
+#define        AR_TTL          600     /* TTL in seconds for dns cache entries */
+
+struct hent {
+       char    *h_name;        /* official name of host */
+       char    *h_aliases[MAXALIASES]; /* alias list */
+       int     h_addrtype;     /* host address type */
+       int     h_length;       /* length of address */
+       /* list of addresses from name server */
+       struct  in_addr h_addr_list[MAXADDRS];
+#define        h_addr  h_addr_list[0]  /* address, for backward compatiblity */
+};
+
+typedef        struct  reslist {
+       int     id;
+       int     sent;   /* number of requests sent */
+       int     srch;
+#ifdef  __alpha
+        u_int   ttl;                  /* time to live */
+#else
+        u_long  ttl;                  /* time to live */
+#endif
+       char    type;
+       char    retries; /* retry counter */
+       char    sends;  /* number of sends (>1 means resent) */
+       char    resend; /* send flag. 0 == dont resend */
+       time_t  sentat;
+       time_t  timeout;
+       struct  in_addr addr;
+       char    *name;
+       struct  reslist *next;
+       Link    cinfo;
+       struct  hent he;
+       } ResRQ;
+
+typedef        struct  cache {
+       time_t  expireat;
+       time_t  ttl;
+       struct  hostent he;
+       struct  cache   *hname_next, *hnum_next, *list_next;
+       } aCache;
+
+typedef struct cachetable {
+       aCache  *num_list;
+       aCache  *name_list;
+       } CacheTable;
+
+#define ARES_CACSIZE   101
+
+#define        MAXCACHED       81
+
+#endif /* __res_include__ */
diff --git a/include/resolv.h b/include/resolv.h
new file mode 100644 (file)
index 0000000..d9feb76
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1983, 1987, 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)resolv.h    5.10.1 (Berkeley) 6/1/90
+ */
+
+/*
+ * Resolver configuration file.
+ * Normally not present, but may contain the address of the
+ * inital name server(s) to query and the domain search list.
+ */
+
+#ifndef        _PATH_RESCONF
+#define _PATH_RESCONF        "/etc/resolv.conf"
+#endif
+
+/*
+ * Global defines and variables for resolver stub.
+ */
+#define        MAXNS           3               /* max # name servers we'll track */
+#define        MAXDFLSRCH      3               /* # default domain levels to try */
+#define        MAXDNSRCH       6               /* max # domains in search path */
+#define        LOCALDOMAINPARTS 2              /* min levels in name that is "local" */
+#define MAXSERVICES    2               /* max # of services to search */
+
+#define        RES_TIMEOUT     5               /* min. seconds between retries */
+
+struct state {
+       int     retrans;                /* retransmition time interval */
+       int     retry;                  /* number of times to retransmit */
+       long    options;                /* option flags - see below. */
+       int     nscount;                /* number of name servers */
+       struct  sockaddr_in nsaddr_list[MAXNS]; /* address of name server */
+#define        nsaddr  nsaddr_list[0]          /* for backward compatibility */
+       unsigned short  id;                     /* current packet id */
+       char    defdname[MAXDNAME];     /* default domain */
+       char    *dnsrch[MAXDNSRCH+1];   /* components of domain to search */
+       unsigned short  order[MAXSERVICES+1];   /* search service order */
+};
+
+#define RES_SERVICE_NONE       0
+#define RES_SERVICE_BIND       1
+#define RES_SERVICE_LOCAL      2
+
+/*
+ * Resolver options
+ */
+#define RES_INIT       0x0001          /* address initialized */
+#define RES_DEBUG      0x0002          /* print debug messages */
+#define RES_AAONLY     0x0004          /* authoritative answers only */
+#define RES_USEVC      0x0008          /* use virtual circuit */
+#define RES_PRIMARY    0x0010          /* query primary server only */
+#define RES_IGNTC      0x0020          /* ignore trucation errors */
+#define RES_RECURSE    0x0040          /* recursion desired */
+#define RES_DEFNAMES   0x0080          /* use default domain name */
+#define RES_STAYOPEN   0x0100          /* Keep TCP socket open */
+#define RES_DNSRCH     0x0200          /* search up local domain tree */
+
+#define RES_DEFAULT    (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH)
+
+extern struct state _res;
+extern char *p_cdname(), *p_rr(), *p_type(), *p_class(), *p_time();
+
diff --git a/include/sock.h b/include/sock.h
new file mode 100644 (file)
index 0000000..dc0dbbf
--- /dev/null
@@ -0,0 +1,44 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/sock.h
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Revision 1.1.1.1  1997/08/22 17:23:01  donwulff
+ * Original import from the "deadlined" version.
+ *
+ * Revision 1.1.1.1  1996/11/18 07:53:41  explorer
+ * ircd 4.3.3 -- about time
+ *
+ * Revision 1.1.1.1.4.1  1996/09/16 02:45:38  donwulff
+ * *** empty log message ***
+ *
+ * Revision 6.1  1991/07/04  21:04:35  gruner
+ * Revision 2.6.1 [released]
+ *
+ * Revision 6.0  1991/07/04  18:05:04  gruner
+ * frozen beta revision 2.6.1
+ *
+ */
+
+#ifndef FD_ZERO
+#define FD_ZERO(set)      (((set)->fds_bits[0]) = 0)
+#define FD_SET(s1, set)   (((set)->fds_bits[0]) |= 1 << (s1))
+#define FD_ISSET(s1, set) (((set)->fds_bits[0]) & (1 << (s1)))
+#define FD_SETSIZE        30
+#endif
diff --git a/include/struct.h b/include/struct.h
new file mode 100644 (file)
index 0000000..444555e
--- /dev/null
@@ -0,0 +1,995 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/struct.h
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef        __struct_include__
+#define __struct_include__
+
+#include "config.h"    /* Get SEEUSERSTATS */
+#include "common.h"
+#include "sys.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#ifdef HAVE_STDDEF_H
+# include <stddef.h>
+#endif
+
+#ifdef USE_SYSLOG
+# include <syslog.h>
+#ifdef HAVE_SYSSYSLOG_H
+#  include <sys/syslog.h>
+# endif
+#endif
+#ifdef pyr
+#include <sys/time.h>
+#endif
+
+typedef struct  SqlineItem aSqlineItem;
+typedef struct  JinxItem aJinxItem;
+typedef        struct  ConfItem aConfItem;
+typedef struct Notify  aNotify;
+typedef        struct  Client  aClient;
+/* Related to SOCKS zline timer -taz */
+typedef struct Event   aEvent;
+typedef struct  SynchList aSynchList;
+typedef        struct  Channel aChannel;
+typedef        struct  User    anUser;
+typedef        struct  Server  aServer;
+typedef        struct  SLink   Link;
+typedef struct SBan    Ban;
+typedef        struct  SMode   Mode;
+typedef struct ListOptions     LOpts;
+
+typedef struct  CloneItem aClone;
+
+#ifdef ZIP_LINKS
+typedef struct  Zdata   aZdata;
+#include <zlib.h>
+#endif
+
+#ifdef NEED_U_INT32_T
+typedef unsigned int  u_int32_t; /* XXX Hope this works! */
+#endif
+
+#ifndef VMSP
+#include "class.h"
+#include "dbuf.h"      /* THIS REALLY SHOULDN'T BE HERE!!! --msa */
+#endif
+
+#define        ARGLEN          132     /* Argument length for events  -taz        */
+#define        HOSTLEN         63      /* Length of hostname.  Updated to         */
+                               /* comply with RFC1123                     */
+
+#define        NICKLEN         30      /* Necessary to put 9 here instead of 10
+                               ** if s_msg.c/m_nick has been corrected.
+                               ** This preserves compatibility with old
+                               ** servers --msa
+                               */
+#define        USERLEN         10
+#define        REALLEN         50
+#define        TOPICLEN        307
+/* DAL MADE ME PUT THIS IN THE FIEND:  --Russell
+ * This number will be expanded to 200 in the near future
+ */
+#define        CHANNELLEN      32
+#define        PASSWDLEN       32      /* orig. 20, changed to 32 for nickpasswords */
+#define        KEYLEN          23
+#define        BUFSIZE         512             /* WARNING: *DONT* CHANGE THIS!!!! */
+#define        MAXRECIPIENTS   20
+#define        MAXKILLS        20
+#define        MAXBANS         60
+#define        MAXBANLENGTH    1024
+#define        MAXSILES        5
+#define        MAXSILELENGTH   128
+
+#define READBUF_SIZE    32000
+
+/*
+ * Watch it - Don't change this unless you also change the ERR_TOOMANYWATCH
+ * and PROTOCOL_SUPPORTED settings.
+ */
+#define MAXWATCH       128
+
+#define        USERHOST_REPLYLEN       (NICKLEN+HOSTLEN+USERLEN+5)
+
+#ifdef USE_SERVICES
+#include "service.h"
+#endif
+
+/* NOTE: this must be down here so the stuff from struct.h IT uses works */
+#include "whowas.h"
+
+
+/*
+** 'offsetof' is defined in ANSI-C. The following definition
+** is not absolutely portable (I have been told), but so far
+** it has worked on all machines I have needed it. The type
+** should be size_t but...  --msa
+*/
+#ifndef offsetof
+#define        offsetof(t,m) (int)((&((t *)0L)->m))
+#endif
+
+#define        elementsof(x) (sizeof(x)/sizeof(x[0]))
+
+/*
+** flags for bootup options (command line flags)
+*/
+#define        BOOT_CONSOLE    1
+#define        BOOT_QUICK      2
+#define        BOOT_DEBUG      4
+#define        BOOT_INETD      8
+#define        BOOT_TTY        16
+#define        BOOT_OPER       32
+#define        BOOT_AUTODIE    64
+
+#define STAT_PING       -7     /* for UPING -GZ */
+#define        STAT_LOG        -6      /* logfile for -x */
+#define        STAT_MASTER     -5      /* Local ircd master before identification */
+#define        STAT_CONNECTING -4
+#define        STAT_HANDSHAKE  -3
+#define        STAT_ME         -2
+#define        STAT_UNKNOWN    -1
+#define        STAT_SERVER     0
+#define        STAT_CLIENT     1
+#define        STAT_SERVICE    2       /* Services not implemented yet */
+
+/* This is where we are defining server capabilities from now on -GZ */
+
+#define VERSION_STAR5II  0x00000001 
+/* When all servers >= Star5.19 this can be pulled */
+
+#define VERSION_ZIPLINKS 0x00000002
+
+#ifdef ZIP_LINKS
+#define VERSION_SEND (VERSION_STAR5II|VERSION_ZIPLINKS)
+#else
+#define VERSION_SEND (VERSION_STAR5II)
+#endif
+/*
+ * status macros.
+ */
+#define        IsRegisteredUser(x)     ((x)->status == STAT_CLIENT)
+#define        IsRegistered(x)         ((x)->status >= STAT_SERVER)
+#define        IsConnecting(x)         ((x)->status == STAT_CONNECTING)
+#define        IsHandshake(x)          ((x)->status == STAT_HANDSHAKE)
+#define        IsMe(x)                 ((x)->status == STAT_ME)
+#define        IsUnknown(x)            ((x)->status == STAT_UNKNOWN || \
+                                (x)->status == STAT_MASTER)
+#define        IsServer(x)             ((x)->status == STAT_SERVER)
+#define        IsClient(x)             ((x)->status == STAT_CLIENT)
+#define        IsLog(x)                ((x)->status == STAT_LOG)
+#define IsService(x)           0
+
+#define        SetMaster(x)            ((x)->status = STAT_MASTER)
+#define        SetConnecting(x)        ((x)->status = STAT_CONNECTING)
+#define        SetHandshake(x)         ((x)->status = STAT_HANDSHAKE)
+#define        SetMe(x)                ((x)->status = STAT_ME)
+#define        SetUnknown(x)           ((x)->status = STAT_UNKNOWN)
+#define        SetServer(x)            ((x)->status = STAT_SERVER)
+#define        SetClient(x)            ((x)->status = STAT_CLIENT)
+#define        SetLog(x)               ((x)->status = STAT_LOG)
+#define        SetService(x)           ((x)->status = STAT_SERVICE)
+
+#define        FLAGS_PINGSENT   0x0001         /* Unreplied ping sent */
+#define        FLAGS_DEADSOCKET 0x0002         /* Local socket is dead--Exiting soon */
+#define        FLAGS_KILLED     0x0004         /* Prevents "QUIT" from being sent for this */
+#define        FLAGS_BLOCKED    0x0008         /* socket is in a blocked condition */
+#define        FLAGS_UNIX       0x0010         /* socket is in the unix domain, not inet */
+#define        FLAGS_CLOSING    0x0020         /* set when closing to suppress errors */
+#define        FLAGS_LISTEN     0x0040         /* used to mark clients which we listen() on */
+#define        FLAGS_CHKACCESS  0x0080         /* ok to check clients access if set */
+#define        FLAGS_DOINGDNS   0x0100         /* client is waiting for a DNS response */
+#define        FLAGS_AUTH       0x0200         /* client is waiting on rfc931 response */
+#define        FLAGS_WRAUTH     0x0400         /* set if we havent writen to ident server */
+#define        FLAGS_LOCAL      0x0800         /* set for local clients */
+/* #define FLAGS_AVAILABLE 0x1000       */
+/* #define FLAGS_AVAILABLE 0x2000      */
+#define        FLAGS_NONL       0x4000         /* No \n in buffer */
+#define FLAGS_TS8       0x8000         /* Why do you want to know? */
+#define FLAGS_ULINE    0x10000         /* User/server is considered U-lined */
+#define FLAGS_SQUIT    0x20000         /* Server has been /squit by an oper */
+#define FLAGS_PROTOCTL 0x40000         /* Received a PROTOCTL message */
+#define FLAGS_PING     0x80000         /* UPING SENT */
+#define FLAGS_ASKEDPING 0x100000       /* UPONG GOT*/
+
+//#define FLAGS_SJOIN   0x1000000 /* have SJOIN */
+#define FLAGS_MAP     0x2000000 /* Show this entry in /map */
+#define FLAGS_JOINING 0x4000000 /* Prevent user from getting killed during JOIN */
+//#define FLAGS_SNICK   0x8000000 /* have SNICK */
+
+/* Need more flags - here's flags2 */
+
+#define FLAGS2_HTC     0x00000001 /* Protect a user from getting killed with dead socket */
+
+/* Dec 26th, 1997 - having a go at
+ * splitting flags into flags and umodes
+ * -DuffJ
+ */
+#define FLAGS2_ZIP       0x00000002
+#define FLAGS2_ZIPFIRST  0x00000004
+#define FLAGS2_CAPAB_ZIP 0x00000008
+#define FLAGS2_CBURST    0x00000010
+
+#define        UMODE_INVISIBLE  0x0001 /* makes user invisible */
+#define        UMODE_OPER       0x0002 /* Operator */
+#define UMODE_WEBTV      0x0004 /* WebTV user */
+#define UMODE_FAILOP     0x0008 /* Shows some global messages */
+/* mode 0x0010 available */
+#define UMODE_REGMSGONLY 0x0020 /* Only registered users may message */
+
+#define UMODE_SADMIN    0x0040 /* Services Admin */
+#define UMODE_ADMIN     0x0080 /* Admin */
+
+#define        UMODE_SERVNOTICE 0x0100 /* server notices such as kill */
+#define        UMODE_LOCOP      0x0200 /* Local operator -- SRB */
+#define UMODE_KILLS     0x0400 /* Show server-kills... */
+#define UMODE_CLIENT    0x0800 /* Show client information */
+#define UMODE_FLOOD     0x1000 /* Receive flood warnings */
+
+/* Related to SEEUSERSTATS -Studded */
+#define UMODE_STATS     0x4000 /* Receive notice when users do /stats, et al */
+#define UMODE_HIDE      0x8000 /* Admin,Oper usermask-hiding */
+#define UMODE_WHOIS     0x10000 /* See USERHOST,WHOIS */
+#define UMODE_NETADMIN 0x20000 /* Network Admin */
+#define UMODE_SROOT    0x40000 /* Services Root */
+#define UMODE_IDENTIFY  0x80000 /* Identified */
+#define        SEND_UMODES (UMODE_INVISIBLE|UMODE_OPER|UMODE_WEBTV|UMODE_FAILOP|UMODE_SADMIN|UMODE_ADMIN|UMODE_HIDE|UMODE_WHOIS|UMODE_NETADMIN|UMODE_SROOT|UMODE_IDENTIFY|UMODE_REGMSGONLY)
+#define        ALL_UMODES      (SEND_UMODES|UMODE_SERVNOTICE|UMODE_LOCOP|UMODE_KILLS|UMODE_CLIENT|UMODE_FLOOD|UMODE_STATS)
+#define        FLAGS_ID        (FLAGS_DOID|FLAGS_GOTID)
+
+#define PROTO_NOQUIT   0x1     /* Negotiated NOQUIT protocol */
+#define PROTO_TOKEN    0x2     /* Negotiated TOKEN protocol */
+
+/*
+ * flags macros.
+ */
+#ifdef SEEUSERSTATS
+#define IsStatsF(x)            ((x)->umodes & UMODE_STATS)
+#else
+#define IsStatsF(x)            (0)
+#endif
+#define IsKillsF(x)            ((x)->umodes & UMODE_KILLS)
+#define IsClientF(x)           ((x)->umodes & UMODE_CLIENT)
+#define IsFloodF(x)            ((x)->umodes & UMODE_FLOOD)
+#define IsAdmin(x)             ((x)->umodes & UMODE_ADMIN)
+#define IsSAdmin(x)            ((x)->umodes & UMODE_SADMIN)
+#define IsSRoot(x)             ((x)->umodes & UMODE_SROOT)
+#define IsNetAdmin(x)          ((x)->umodes & UMODE_NETADMIN)
+
+#define SendFailops(x)         ((x)->umodes & UMODE_FAILOP)
+#define IsHidden(x)            ((x)->umodes & UMODE_HIDE)
+#define AcceptsRegisteredOnly(x) ((x)->umodes & UMODE_REGMSGONLY)
+#define IsWmode(x)              ((x)->umodes & UMODE_WHOIS)
+#define        IsOper(x)               ((x)->umodes & UMODE_OPER)
+#define        IsLocOp(x)              ((x)->umodes & UMODE_LOCOP)
+#define        IsInvisible(x)          ((x)->umodes & UMODE_INVISIBLE)
+#define        IsAnOper(x)             ((x)->umodes & (UMODE_OPER|UMODE_LOCOP))
+#define IsIdentified(x)         ((x)->umodes & UMODE_IDENTIFY)
+#define        IsPerson(x)             ((x)->user && IsClient(x))
+#define        IsPrivileged(x)         (IsAnOper(x) || IsServer(x))
+#define        SendServNotice(x)       ((x)->umodes & UMODE_SERVNOTICE)
+#define        IsUnixSocket(x)         ((x)->flags & FLAGS_UNIX)
+#define        IsListening(x)          ((x)->flags & FLAGS_LISTEN)
+#define        DoAccess(x)             ((x)->flags & FLAGS_CHKACCESS)
+#define        IsLocal(x)              ((x)->flags & FLAGS_LOCAL)
+#define        IsDead(x)               ((x)->flags & FLAGS_DEADSOCKET)
+#define GotProtoctl(x)         ((x)->flags & FLAGS_PROTOCTL)
+#define IsBlocked(x)           ((x)->flags & FLAGS_BLOCKED)
+#define GotCapab(x)             ((x)->flags & FLAGS_GOTCAPAB)
+#ifdef NOSPOOF
+#define        IsNotSpoof(x)           ((x)->nospoof == 0)
+#else
+#define IsNotSpoof(x)           (1)
+#endif
+
+#ifdef SEEUSERSTATS
+#define SetStatsF(x)           ((x)->umodes |= UMODE_STATS)
+#endif
+#define SetKillsF(x)           ((x)->umodes |= UMODE_KILLS)
+#define SetClientF(x)          ((x)->umodes |= UMODE_CLIENT)
+#define SetFloodF(x)           ((x)->umodes |= UMODE_FLOOD)
+#define SetHidden(x)           ((x)->umodes |= UMODE_HIDE)
+#define SetIdentified(x)        ((x)->umodes |= UMODE_IDENTIFY)
+#define        SetOper(x)              ((x)->umodes |= UMODE_OPER)
+#define        SetLocOp(x)             ((x)->umodes |= UMODE_LOCOP)
+#define        SetInvisible(x)         ((x)->umodes |= UMODE_INVISIBLE)
+#define SetWmode(x)             ((x)->umodes |= UMODE_WHOIS)
+#define        SetUnixSock(x)          ((x)->flags |= FLAGS_UNIX)
+#define        SetDNS(x)               ((x)->flags |= FLAGS_DOINGDNS)
+#define        DoingDNS(x)             ((x)->flags & FLAGS_DOINGDNS)
+#define        SetAccess(x)            ((x)->flags |= FLAGS_CHKACCESS)
+#define SetBlocked(x)          ((x)->flags |= FLAGS_BLOCKED)
+#define SetHaveSJOIN(x)             ((x)->flags |= FLAGS_SJOIN)
+#define SetHaveSNICK(x)             ((x)->flags |= FLAGS_SNICK)
+#define SetCapab(x)             ((x)->flags |= FLAGS_GOTCAPAB)
+#define        DoingAuth(x)            ((x)->flags & FLAGS_AUTH)
+
+#define        NoNewLine(x)            ((x)->flags & FLAGS_NONL)
+
+#ifdef SEEUSERSTATS
+#define ClearStatsF(x)         ((x)->umodes &= ~UMODE_STATS)
+#endif
+#define ClearAdmin(x)           ((x)->umodes &= ~UMODE_ADMIN)
+#define ClearSAdmin(x)          ((x)->umodes &= ~UMODE_SADMIN)
+#define ClearSRoot(x)           ((x)->umodes &= ~UMODE_SROOT)
+#define ClearNetAdmin(x)        ((x)->umodes &= ~UMODE_NETADMIN)
+
+#define ClearKillsF(x)         ((x)->umodes &= ~UMODE_KILLS)
+#define ClearClientF(x)                ((x)->umodes &= ~UMODE_CLIENT)
+#define ClearFloodF(x)         ((x)->umodes &= ~UMODE_FLOOD)
+#define ClearFailops(x)                ((x)->umodes &= ~UMODE_FAILOP)
+#define ClearHidden(x)          ((x)->umodes &= ~UMODE_HIDE)
+#define ClearIdentified(x)      ((x)->umodes &= ~UMODE_IDENTIFY)
+#define ClearWmode(x)           ((x)->umodes &= ~UMODE_WHOIS)
+#define        ClearOper(x)            ((x)->umodes &= ~UMODE_OPER)
+#define        ClearInvisible(x)       ((x)->umodes &= ~UMODE_INVISIBLE)
+#define        ClearDNS(x)             ((x)->flags &= ~FLAGS_DOINGDNS)
+#define        ClearAuth(x)            ((x)->flags &= ~FLAGS_AUTH)
+#define ClearHaveSJOIN(x)       ((x)->flags &= ~FLAGS_SJOIN)
+#define ClearHaveSNICK(x)       ((x)->flags &= ~FLAGS_SNICK)
+#define ClearCapab(x)           ((x)->flags &= ~FLAGS_GOTCAPAB)
+
+#define        ClearAccess(x)          ((x)->flags &= ~FLAGS_CHKACCESS)
+#define ClearBlocked(x)                ((x)->flags &= ~FLAGS_BLOCKED)
+
+/*
+ * ProtoCtl options
+ */
+#define DontSendQuit(x)                ((x)->proto & PROTO_NOQUIT)
+#define IsToken(x)             ((x)->proto & PROTO_TOKEN)
+
+#define SetNoQuit(x)           ((x)->proto |= PROTO_NOQUIT)
+#define SetToken(x)            ((x)->proto |= PROTO_TOKEN)
+
+#define ClearNoQuit(x)         ((x)->proto &= ~PROTO_NOQUIT)
+#define ClearToken(x)          ((x)->proto &= ~PROTO_TOKEN)
+
+
+#define MaskHost(x)            ((x)->user->mask)
+#define IsJinxed(x)            ((x)->jinx)
+#define SetJinx(x)             ((x)->jinx = 1)
+#define ClearJinx(x)           ((x)->jinx = 0)
+/*
+ * defined operator access levels
+ */
+#define OFLAG_REHASH   0x00000001  /* Oper can /rehash server */
+#define OFLAG_DIE      0x00000002  /* Oper can /die the server */
+#define OFLAG_RESTART  0x00000004  /* Oper can /restart the server */
+
+/* 0x00000010 available */
+
+#define OFLAG_GLOBOP   0x00000020  /* Oper can send /GlobOps */
+#define OFLAG_LOCOP    0x00000080  /* Oper can send /LocOps */
+#define OFLAG_LROUTE   0x00000100  /* Oper can do local routing */
+#define OFLAG_GROUTE   0x00000200  /* Oper can do global routing */
+#define OFLAG_LKILL    0x00000400  /* Oper can do local kills */
+#define OFLAG_GKILL    0x00000800  /* Oper can do global kills */
+#define OFLAG_KLINE    0x00001000  /* Oper can /kline users */
+#define OFLAG_UNKLINE  0x00002000  /* Oper can /unkline users */
+#define OFLAG_LNOTICE  0x00004000  /* Oper can send local serv notices */
+#define OFLAG_GNOTICE  0x00008000  /* Oper can send global notices */
+#define OFLAG_ADMIN    0x00010000  /* Admin */
+#define OFLAG_UMODEC   0x00020000  /* Oper can set umode +c */
+#define OFLAG_UMODEF   0x00040000  /* Oper can set umode +f */
+#define OFLAG_SADMIN    0x00080000  /* Oper can be a services admin */
+#define OFLAG_ZLINE    0x00100000  /* Oper can use /zline and /unzline */
+
+#define OFLAG_LOCAL    (OFLAG_REHASH|OFLAG_GLOBOP|OFLAG_LOCOP|OFLAG_LROUTE|OFLAG_LKILL|OFLAG_KLINE|OFLAG_UNKLINE|OFLAG_LNOTICE|OFLAG_UMODEC|OFLAG_UMODEF)
+#define OFLAG_GLOBAL   (OFLAG_LOCAL|OFLAG_GROUTE|OFLAG_GKILL|OFLAG_GNOTICE)
+#define OFLAG_ISGLOBAL (OFLAG_GROUTE|OFLAG_GKILL|OFLAG_GNOTICE)
+
+#define OPCanRehash(x) ((x)->oflag & OFLAG_REHASH)
+#define OPCanDie(x)    ((x)->oflag & OFLAG_DIE)
+#define OPCanRestart(x)        ((x)->oflag & OFLAG_RESTART)
+#define OPCanGlobOps(x)        ((x)->oflag & OFLAG_GLOBOP)
+#define OPCanLocOps(x) ((x)->oflag & OFLAG_LOCOP)
+#define OPCanLRoute(x) ((x)->oflag & OFLAG_LROUTE)
+#define OPCanGRoute(x) ((x)->oflag & OFLAG_GROUTE)
+#define OPCanLKill(x)  ((x)->oflag & OFLAG_LKILL)
+#define OPCanGKill(x)  ((x)->oflag & OFLAG_GKILL)
+#define OPCanKline(x)  ((x)->oflag & OFLAG_KLINE)
+#define OPCanUnKline(x)        ((x)->oflag & OFLAG_UNKLINE)
+#define OPCanLNotice(x)        ((x)->oflag & OFLAG_LNOTICE)
+#define OPCanGNotice(x)        ((x)->oflag & OFLAG_GNOTICE)
+#define OPIsAdmin(x)   ((x)->oflag & OFLAG_ADMIN)
+#define OPIsSAdmin(x)  ((x)->oflag & OFLAG_SADMIN)
+#define OPCanUModeC(x) ((x)->oflag & OFLAG_UMODEC)
+#define OPCanUModeF(x) ((x)->oflag & OFLAG_UMODEF)
+
+#define OPSetRehash(x) ((x)->oflag |= OFLAG_REHASH)
+#define OPSetDie(x)    ((x)->oflag |= OFLAG_DIE)
+#define OPSetRestart(x)        ((x)->oflag |= OFLAG_RESTART)
+#define OPSetGlobOps(x)        ((x)->oflag |= OFLAG_GLOBOP)
+#define OPSetLocOps(x) ((x)->oflag |= OFLAG_LOCOP)
+#define OPSetLRoute(x) ((x)->oflag |= OFLAG_LROUTE)
+#define OPSetGRoute(x) ((x)->oflag |= OFLAG_GROUTE)
+#define OPSetLKill(x)  ((x)->oflag |= OFLAG_LKILL)
+#define OPSetGKill(x)  ((x)->oflag |= OFLAG_GKILL)
+#define OPSetKline(x)  ((x)->oflag |= OFLAG_KLINE)
+#define OPSetUnKline(x)        ((x)->oflag |= OFLAG_UNKLINE)
+#define OPSetLNotice(x)        ((x)->oflag |= OFLAG_LNOTICE)
+#define OPSetGNotice(x)        ((x)->oflag |= OFLAG_GNOTICE)
+#define OPSSetAdmin(x) ((x)->oflag |= OFLAG_ADMIN)
+#define OPSSetSAdmin(x) ((x)->oflag |= OFLAG_SADMIN)
+#define OPSetUModeC(x) ((x)->oflag |= OFLAG_UMODEC)
+#define OPSetUModeF(x) ((x)->oflag |= OFLAG_UMODEF)
+#define OPSetZLine(x)  ((x)->oflag |= OFLAG_ZLINE)
+
+#define OPClearRehash(x)       ((x)->oflag &= ~OFLAG_REHASH)
+#define OPClearDie(x)          ((x)->oflag &= ~OFLAG_DIE)  
+#define OPClearRestart(x)      ((x)->oflag &= ~OFLAG_RESTART)
+#define OPClearGlobOps(x)      ((x)->oflag &= ~OFLAG_GLOBOP)
+#define OPClearLocOps(x)       ((x)->oflag &= ~OFLAG_LOCOP)
+#define OPClearLRoute(x)       ((x)->oflag &= ~OFLAG_LROUTE)
+#define OPClearGRoute(x)       ((x)->oflag &= ~OFLAG_GROUTE)
+#define OPClearLKill(x)                ((x)->oflag &= ~OFLAG_LKILL)
+#define OPClearGKill(x)                ((x)->oflag &= ~OFLAG_GKILL)
+#define OPClearKline(x)                ((x)->oflag &= ~OFLAG_KLINE)
+#define OPClearUnKline(x)      ((x)->oflag &= ~OFLAG_UNKLINE)
+#define OPClearLNotice(x)      ((x)->oflag &= ~OFLAG_LNOTICE)
+#define OPClearGNotice(x)      ((x)->oflag &= ~OFLAG_GNOTICE)
+#define OPClearAdmin(x)                ((x)->oflag &= ~OFLAG_ADMIN)
+#define OPClearSAdmin(x)       ((x)->oflag &= ~OFLAG_SADMIN)
+#define OPClearUModeC(x)       ((x)->oflag &= ~OFLAG_UMODEC)
+#define OPClearUModeF(x)       ((x)->oflag &= ~OFLAG_UMODEF)
+#define OPClearZLine(x)                ((x)->oflag &= ~OFLAG_ZLINE)
+
+/*
+ * defined debugging levels
+ */
+#define        DEBUG_FATAL  0
+#define        DEBUG_ERROR  1  /* report_error() and other errors that are found */
+#define        DEBUG_NOTICE 3
+#define        DEBUG_DNS    4  /* used by all DNS related routines - a *lot* */
+#define        DEBUG_INFO   5  /* general usful info */
+#define        DEBUG_NUM    6  /* numerics */
+#define        DEBUG_SEND   7  /* everything that is sent out */
+#define        DEBUG_DEBUG  8  /* anything to do with debugging, ie unimportant :) */
+#define        DEBUG_MALLOC 9  /* malloc/free calls */
+#define        DEBUG_LIST  10  /* debug list use */
+
+/*
+ * UPING Start
+ */
+
+#define ClearPing(x)            ((x)->flags &= ~FLAGS_PING)
+#define ClearAskedPing(x)       ((x)->flags &= ~FLAGS_ASKEDPING)
+#define SetAskedPing(x)         ((x)->flags |= FLAGS_ASKEDPING)
+#define AskedPing(x)            ((x)->flags & FLAGS_ASKEDPING)
+#define DoPing(x)               ((x)->flags & FLAGS_PING)
+#define IsPing(x)               ((x)->status == STAT_PING)
+#define SetPing(x)              ((x)->status = STAT_PING)
+
+/*
+ * defines for curses in client
+ */
+#define        DUMMY_TERM      0
+#define        CURSES_TERM     1
+#define        TERMCAP_TERM    2
+
+struct  SqlineItem     {
+       unsigned int    status;
+       char *sqline;
+       char *reason;
+       struct  SqlineItem *next;
+};
+
+struct ConfItem        {
+       unsigned int    status; /* If CONF_ILLEGAL, delete when no clients */
+       int     clients;        /* Number of *LOCAL* clients using this */
+       struct  in_addr ipnum;  /* ip number of host field */
+       char    *host;
+       char    *passwd;
+       char    *name;
+       int     port;
+       time_t  hold;   /* Hold action until this time (calendar time) */
+       int     tmpconf;
+#ifndef VMSP
+       aClass  *class;  /* Class of connection */
+#endif
+       struct  ConfItem *next;
+};
+
+#define        CONF_ILLEGAL            0x80000000
+#define        CONF_MATCH              0x40000000
+#define        CONF_QUARANTINED_SERVER 0x0001
+#define        CONF_CLIENT             0x0002
+#define        CONF_CONNECT_SERVER     0x0004
+#define        CONF_NOCONNECT_SERVER   0x0008
+#define        CONF_LOCOP              0x0010
+#define        CONF_OPERATOR           0x0020
+#define        CONF_ME                 0x0040
+#define        CONF_KILL               0x0080
+#define        CONF_ADMIN              0x0100
+#define        CONF_CLASS              0x0400
+#define        CONF_SERVICE            0x0800
+/* #define CONF_AVAILABLE       0x1000 */
+#define        CONF_LISTEN_PORT        0x2000
+#define        CONF_HUB                0x4000
+#define        CONF_UWORLD             0x8000
+#define CONF_QUARANTINED_NICK  0x10000
+#define CONF_ZAP               0x20000
+#define CONF_CONFIG             0x100000
+/* #define CONF_AVAILABLE       0x200000 */
+/* #define CONF_AVAILABLE       0x400000 */
+#define CONF_MISSING           0x800000
+#define CONF_SADMIN            0x1000000
+#define CONF_DRPASS            0x2000000   /* DIE/RESTART pass - NikB */
+#define CONF_ZTIME             0x4000000   /* zline timer -taz */
+#define CONF_NZCONNECT_SERVER   0x8000000
+/* #define CONF_AVAILABLE       0x10000000 */
+#define CONF_DCCBLOCK          0x20000000   /* Blocking of specific DCC transfers */
+#define CONF_KILLEXEMPT                0x40000000   /* Exemption of K-Line */
+
+#define        CONF_OPS                (CONF_OPERATOR | CONF_LOCOP)
+#define        CONF_SERVER_MASK        (CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER| \
+                                 CONF_NZCONNECT_SERVER)
+#define        CONF_CLIENT_MASK        (CONF_CLIENT | CONF_SERVICE | CONF_OPS | \
+                                CONF_SERVER_MASK)
+#define CONF_QUARANTINE                (CONF_QUARANTINED_SERVER|CONF_QUARANTINED_NICK)
+
+#define        IsIllegal(x)    ((x)->status & CONF_ILLEGAL)
+#define IsTemp(x)      ((x)->tmpconf)
+
+/* jinx structures */
+struct JinxItem {
+        unsigned int status;
+        char *userhost;
+        char *reason;
+        struct JinxItem *next;  
+};
+
+#ifdef ZIP_LINKS
+
+#define ZIP_MINIMUM 4096
+#define ZIP_MAXIMUM 8192
+
+
+struct Zdata {
+        z_stream        *in;            /* input zip stream data */
+        z_stream        *out;           /* output zip stream data */
+        char            inbuf[ZIP_MAXIMUM]; /* incoming zipped buffer */
+        char            outbuf[ZIP_MAXIMUM]; /* outgoing (unzipped) buffer */
+        int             incount;        /* size of inbuf content */
+        int             outcount;       /* size of outbuf content */
+};
+#endif
+
+/*
+ * Client structures
+ */
+struct User    {
+       struct  User    *nextu;
+       Link    *channel;       /* chain of channel pointer blocks */
+       Link    *invited;       /* chain of invite pointer blocks */
+       Link    *silence;       /* chain of silence pointer blocks */
+       char    *away;          /* pointer to away message */
+       time_t  last;
+       u_int32_t servicestamp;  /* Services' time stamp variable */
+       int     refcnt;         /* Number of times this block is referenced */
+       int     joined;         /* number of channels joined */
+       char    username[USERLEN+1];
+       char    host[HOSTLEN+1];
+       char    mask[HOSTLEN+1];
+        char   server[HOSTLEN+1];
+//        char    hidden[5];      /* Used for /whowas masking -GZ */
+                               /*
+                               ** In a perfect world the 'server' name
+                               ** should not be needed, a pointer to the
+                               ** client describing the server is enough.
+                               ** Unfortunately, in reality, server may
+                               ** not yet be in links while USER is
+                               ** introduced... --msa
+                               ** No.  We are going to assume the server
+                               ** is there.  There is no good reason at
+                               ** all that we should get a NICK line
+                               ** before a SERVER line.   -- Aeto
+                               ** BUT, it is hiddenin aClient! -- Aeto
+                               */
+#ifdef LIST_DEBUG
+       aClient *bcptr;
+#endif
+};
+
+struct Server  {
+       struct  Server  *nexts;
+       anUser  *user;          /* who activated this connection */
+       char    up[HOSTLEN+1];  /* uplink for this server */
+       char    by[NICKLEN+1];
+       aConfItem *nline;       /* N-line pointer for this server */
+#ifdef LIST_DEBUG
+       aClient *bcptr;
+#endif
+};
+
+/* SJOIN synch structure */
+struct SynchList {
+       char            nick[NICKLEN];
+       int             deop;
+       int             devoice;
+       int             op;
+       int             voice;
+       aSynchList      *next, *prev;
+};
+
+/* Related to zline timer for SOCKS -taz */
+struct Event {
+       void            (*func)(char *);
+       char            arg[ARGLEN+1];
+               u_int32_t       exectime;
+        aEvent         *next, *prev;
+};
+
+struct Client  {
+       struct  Client *next, *prev, *hnext;
+       anUser  *user;          /* ...defined, if this is a User */
+       aServer *serv;          /* ...defined, if this is a server */
+       int     tag;
+#ifdef USE_SERVICES
+       aService *service;
+#endif
+       int     hashv;          /* raw hash value (according to ircu) -- was
+                                * added so that UPING would work. Dreamforge
+                                * 4.6.7b Not needed in b ? I dunno they said so.
+                                */
+       time_t  lasttime;       /* ...should be only LOCAL clients? --msa */
+       time_t  firsttime;      /* time client was created */
+       time_t  since;          /* last time we parsed something */
+       time_t  lastnick;       /* TimeStamp on nick */
+       time_t  nextnick;       /* Time the next nick change will be allowed */
+       time_t  nexttarget;     /* Time until a change in targets is allowed */
+       time_t  lasthtc;        /* Time the user used a high traffic command for the last time */
+       int     htccount;       /* Counter for HTC commands */
+       int     htcignore;      /* flag for HTC ignore on/off */
+       u_char targets[MAXTARGETS]; /* Hash values of current targets */        
+       long    flags;          /* client flags */
+       long    flags2;         /* more client flags */
+       long    umodes;         /* client usermodes */
+       aClient *from;          /* == self, if Local Client, *NEVER* NULL! */
+       int     fd;             /* >= 0, for local clients */
+       int     hopcount;       /* number of servers to this 0 = local */
+       short   status;         /* Client type */
+       char    name[HOSTLEN+1]; /* Unique name of the client, nick or host */
+       char    username[USERLEN+1]; /* username here now for auth stuff */
+       char    info[REALLEN+1]; /* Free form additional client information */
+       char    version[REALLEN+1]; /* version of the client (servers only) -GZ */
+        int     jinx;           /* Tag for jinx */
+       int     cc;             /* Cline client-check for connect -GZ */
+//        char    hidden[5];       /* Used for /whowas masking -GZ */
+       aClient *srvptr;        /* Server introducing this.  May be &me */
+       Link    *history;       /* Whowas linked list */
+       /*
+       ** The following fields are allocated only for local clients
+       ** (directly connected to *this* server with a socket.
+       ** The first of them *MUST* be the "count"--it is the field
+       ** to which the allocation is tied to! *Never* refer to
+       ** these fields, if (from != self).
+       */
+       int     count;          /* Amount of data in buffer */
+       char    buffer[BUFSIZE]; /* Incoming message buffer */
+       char    sup_server[HOSTLEN+1], sup_host[HOSTLEN+1];
+       short   lastsq;         /* # of 2k blocks when sendqueued called last*/
+       dbuf    sendQ;          /* Outgoing message queue--if socket full */
+       dbuf    recvQ;          /* Hold for data incoming yet to be parsed */
+#ifdef NOSPOOF
+       u_int32_t       nospoof;        /* Anti-spoofing random number */
+#endif
+       long    oflag;          /* Operator access flags -Cabal95 */
+       long    proto;          /* ProtoCtl options */
+       long    sendM;          /* Statistics: protocol messages send */
+       long    sendK;          /* Statistics: total k-bytes send */
+       long    receiveM;       /* Statistics: protocol messages received */
+       long    receiveK;       /* Statistics: total k-bytes received */
+        long    u_receiveK;     /* Statistics: Uncompressed lengths */
+       long    u_sendK;
+        u_short u_sendB;
+        u_short u_receiveB;
+       long    previousSQK;    /* Statistics: used for bandwith-meter      */
+       long    previousRQK;    /* Statistics: used for bandwith-meter      */
+       long    deltaSQK;       /* Statistics: used for bandwith-meter      */
+       long    deltaRQK;       /* Statistics: used for bandwith-meter - GZ */
+               u_short sendB;          /* counters to count upto 1-k lots of bytes */
+       long    connectQ;       /* The count of bytes sent during connect */
+       u_short receiveB;       /* sent and received. */
+       aClient *acpt;          /* listening client which we accepted from */
+       Link    *confs;         /* Configuration record associated */
+#ifdef ZIP_LINKS
+        aZdata  *zip;
+#endif
+       struct  in_addr ip;     /* keep real ip# too */
+       u_short port;           /* and the remote port# too :-) */
+       struct  hostent *hostp;
+       u_short notifies;       /* Keep track of count of notifies */
+       Link    *notify;        /* Links to clients notify-structures */
+       LOpts   *lopt;          /* Saved /list options */
+#ifdef pyr
+       struct  timeval lw;
+#endif
+       char    sockhost[HOSTLEN+1]; /* This is the host name from the socket
+                                    ** and after which the connection was
+                                    ** accepted.
+                                    */
+       char    passwd[PASSWDLEN+1];
+#ifdef DEBUGMODE
+       time_t  cputime;
+#endif
+};
+
+#define        CLIENT_LOCAL_SIZE sizeof(aClient)
+#define        CLIENT_REMOTE_SIZE offsetof(aClient,count)
+
+/*
+ * statistics structures
+ */
+struct stats {
+       unsigned int    is_cl;  /* number of client connections */
+       unsigned int    is_sv;  /* number of server connections */
+       unsigned int    is_ni;  /* connection but no idea who it was */
+       unsigned short  is_cbs; /* bytes sent to clients */
+       unsigned short  is_cbr; /* bytes received to clients */
+       unsigned short  is_sbs; /* bytes sent to servers */
+       unsigned short  is_sbr; /* bytes received to servers */
+       unsigned long   is_cks; /* k-bytes sent to clients */
+       unsigned long   is_ckr; /* k-bytes received to clients */
+       unsigned long   is_sks; /* k-bytes sent to servers */
+       unsigned long   is_skr; /* k-bytes received to servers */
+       time_t          is_cti; /* time spent connected by clients */
+       time_t          is_sti; /* time spent connected by servers */
+       unsigned int    is_ac;  /* connections accepted */
+       unsigned int    is_ref; /* accepts refused */
+       unsigned int    is_unco; /* unknown commands */
+       unsigned int    is_wrdi; /* command going in wrong direction */
+       unsigned int    is_unpf; /* unknown prefix */
+       unsigned int    is_empt; /* empty message */
+       unsigned int    is_num; /* numeric message */
+       unsigned int    is_kill; /* number of kills generated on collisions */
+       unsigned int    is_fake; /* MODE 'fakes' */
+       unsigned int    is_udp; /* packets recv'd on udp port */
+       unsigned int    is_loc; /* local connections made */
+};
+
+struct ListOptions {
+       LOpts   *next;
+       Link    *yeslist, *nolist;
+       int     starthash;
+       short int       showall;
+       unsigned short  usermin;
+       int     usermax;
+       time_t  currenttime;
+       time_t  chantimemin;
+       time_t  chantimemax;
+       time_t  topictimemin;
+       time_t  topictimemax;
+};
+
+/* mode structure for channels */
+
+struct SMode   {
+       unsigned int    mode;
+       int     limit;
+       char    key[KEYLEN+1];
+};
+
+/* Message table structure */
+
+struct Message {
+       char    *cmd;
+       int     (* func)();
+       unsigned int    count;
+       int     parameters;
+       char    flags;
+               /* bit 0 set means that this command is allowed to be used
+                * only on the average of once per 2 seconds -SRB */
+       u_char  token[2]; /* Cheat for tokenized value */
+       unsigned long   bytes;
+#ifdef DEBUGMODE
+       unsigned long   lticks;
+       unsigned long   rticks;
+#endif
+};
+
+/* Used for notify-hash buckets... -Donwulff */
+
+struct Notify {
+       aNotify *hnext;
+       time_t  lasttime;
+       Link    *notify;
+       char    nick[1];
+};
+
+/* general link structure used for chains */
+
+struct SLink   {
+       struct  SLink   *next;
+       int     flags;
+       union {
+               aClient *cptr;
+               aChannel *chptr;
+               aConfItem *aconf;
+               aNotify *nptr;
+               aName *whowas;
+               char    *cp;
+       } value;
+};
+
+struct SBan    {
+       struct  SBan    *next;
+       char *banstr;
+       char *who;
+       time_t when;
+};
+
+/* channel structure */
+
+struct Channel {
+       struct  Channel *nextch, *prevch, *hnextch;
+       Mode    mode;
+       time_t  creationtime;
+       char    topic[TOPICLEN+1];
+       char    topic_nick[NICKLEN+1];
+       time_t  topic_time;
+       int     users;
+       Link    *members;
+       Link    *invites;
+       Ban     *banlist;
+       Ban     *exbanlist;
+        Ban     *hostrestrictlist;
+       char    chname[1];
+};
+
+#define CAP_ZIP 0x0001
+
+#define IsCapable(x, cap)  ((x)->caps & (cap))
+#define SetCapable(x, cap) ((x)->caps |= (cap))
+#define ClearCap(x, cap)   ((x)->caps &= ~(cap))
+
+/*
+** Channel Related macros follow
+*/
+
+/* Channel related flags */
+
+#define        CHFL_CHANOP     0x0001 /* Channel operator */
+#define        CHFL_VOICE      0x0002 /* the power to speak */
+#define        CHFL_DEOPPED    0x0004 /* Is de-opped by a server */
+#define        CHFL_SERVOPOK   0x0008 /* Server op allowed */
+#define        CHFL_ZOMBIE     0x0010 /* Kicked from channel */
+/* Bans are stored in separate linked list, so phase this out? */
+#define        CHFL_BAN        0x0020 /* ban channel flag */
+#define        CHFL_OVERLAP    (CHFL_CHANOP|CHFL_VOICE)
+
+/* Channel Visibility macros */
+
+#define        MODE_CHANOP     CHFL_CHANOP
+#define        MODE_VOICE      CHFL_VOICE
+#define        MODE_SECRET             0x0008
+#define        MODE_MODERATED          0x0010
+#define        MODE_TOPICLIMIT         0x0020
+#define        MODE_INVITEONLY         0x0040
+#define        MODE_NOPRIVMSGS         0x0080
+#define        MODE_KEY                0x0100
+#define        MODE_BAN                0x0200
+#define        MODE_LIMIT              0x0400
+#define MODE_RGSTR             0x0800
+#define MODE_RGSTRONLY          0x1000
+/* Colour-removing mode -Defiant */
+#define MODE_NOCOLORS           0x2000
+#define MODE_HOSTRESTRICT       0x4000
+#define MODE_EXBAN              0x8000
+/*
+ * mode flags which take another parameter (With PARAmeterS)
+ */
+#define        MODE_WPARAS     (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_EXBAN|MODE_HOSTRESTRICT|MODE_KEY|MODE_LIMIT)
+/*
+ * Undefined here, these are used in conjunction with the above modes in
+ * the source.
+#define        MODE_DEL       0x40000000
+#define        MODE_ADD       0x80000000
+ */
+
+#define        HoldChannel(x)          (!(x))
+/* name invisible */
+#define        SecretChannel(x)        ((x) && ((x)->mode.mode & MODE_SECRET))
+/* channel visible */
+#define        ShowChannel(v,c)        (PubChannel(c) || IsSAdmin(v) || IsMember((v),(c)))
+#define        PubChannel(x)           ((!x) || ((x)->mode.mode &\
+                                (MODE_SECRET)) == 0)
+
+#define        IsChannelName(name) ((name) && ((*(name) == '#') || (*(name) == '&') || ((*(name) == '+') && (*(name + 1) != '#'))))
+#define IsModelessChannel(name) ((name) && (*(name) == '+'))
+
+/* Misc macros */
+
+#define        BadPtr(x) (!(x) || (*(x) == '\0'))
+
+#define        isvalid(c) (((c) >= 'A' && (c) <= '~') || isdigit(c) || (c) == '-')
+
+#define        MyConnect(x)                    ((x)->fd >= 0)
+#define        MyClient(x)                     (MyConnect(x) && IsClient(x))
+#define        MyOper(x)                       (MyConnect(x) && IsOper(x))
+
+/* Lifted somewhat from Undernet code --Rak */
+
+#define IsSendable(x)          (DBufLength(&x->sendQ) < 2048)
+#define DoList(x)              ((x)->lopt)
+
+/* String manipulation macros */
+
+/* strncopynt --> strncpyzt to avoid confusion, sematics changed
+   N must be now the number of bytes in the array --msa */
+#define        strncpyzt(x, y, N) do{(void)strncpy(x,y,N);x[N-1]='\0';}while(0)
+#define        StrEq(x,y)      (!strcmp((x),(y)))
+
+/* used in SetMode() in channel.c and m_umode() in s_msg.c */
+
+#define        MODE_NULL      0
+#define        MODE_ADD       0x40000000
+#define        MODE_DEL       0x20000000
+
+/* return values for hunt_server() */
+
+#define        HUNTED_NOSUCH   (-1)    /* if the hunted server is not found */
+#define        HUNTED_ISME     0       /* if this server should execute the command */
+#define        HUNTED_PASS     1       /* if message passed onwards successfully */
+
+/* used when sending to #mask or $mask */
+
+#define        MATCH_SERVER  1
+#define        MATCH_HOST    2
+
+/* used for async dns values */
+
+#define        ASYNC_NONE      (-1)
+#define        ASYNC_CLIENT    0
+#define        ASYNC_CONNECT   1
+#define        ASYNC_CONF      2
+#define        ASYNC_SERVER    3
+#define ASYNC_PING     4       /* for UPING */
+
+/* misc variable externs */
+
+extern char    *version, *infotext[];
+extern char    *generation, *creation;
+
+/* misc defines */
+
+#define        FLUSH_BUFFER    -2
+#define        UTMP            "/etc/utmp"
+#define        COMMA           ","
+
+/* IRC client structures */
+
+#define     UDPPORT         "7007"     /* For UPING */
+
+struct DSlink {
+  struct DSlink *next;
+  struct DSlink *prev;
+  union {
+    aClient *cptr;
+    struct Channel *chptr;
+    struct ConfItem *aconf;
+    char *cp;
+  } value;
+};
+
+#endif /* __struct_include__ */
diff --git a/include/sys.h b/include/sys.h
new file mode 100644 (file)
index 0000000..5519cad
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ *   IRC - Internet Relay Chat, include/sys.h
+ *   Copyright (C) 1990 University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef        __sys_include__
+#define __sys_include__
+#ifdef ISC202
+#include <net/errno.h>
+#else
+#include <sys/errno.h>
+#endif
+
+#include "setup.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+
+#ifndef HAVE_STRCASECMP
+#define        strcasecmp      mycmp
+#endif
+#ifndef HAVE_INDEX
+#define   index   strchr
+#define   rindex  strrchr
+/*
+extern char    *index PROTO((char *, char));
+extern char    *rindex PROTO((char *, char));
+*/
+#endif
+
+#ifndef HAVE_BZERO
+#define bzero(a,b)      memset(a,0,b)
+#define bcopy(a,b,c)    memcpy(b,a,c)
+#define bcmp    memcmp
+#endif
+
+#ifdef HAVE_GETRUSAGE_2
+#  define GETRUSAGE_2
+#else
+#  ifdef HAVE_TIMES_2
+#    define TIMES_2
+#   endif              /* HAVE_TIMES_2 */
+#endif                         /* GETRUSAGE_2 */
+
+#ifdef HAVE_POSIX_NBLOCK
+#  define NBLOCK_POSIX
+#else           /* HAVE_POSIX_NBLOCK */
+#  ifdef HAVE_BSD_NBLOCK 
+#    define NBLOCK_BSD
+#  else 
+#    define NBLOCK_SYSV
+#  endif               /* HAVE_BSD_NBLOCK */
+#endif                 /* HAVE_POSIX_NBLOCK */
+
+#ifdef HAVE_POSIX_SIGNALS
+#  define POSIX_SIGNALS
+#else
+#  ifdef HAVE_BSD_SIGNALS
+#    define BSD_RELIABLE_SIGNALS
+#  else
+#    define SYSV_UNRELIABLE_SIGNALS
+#  endif               /* HAVE_BSD_SIGNALS */
+#endif                 /* HAVE_POSIX_SIGNALS */
+
+#ifdef AIX
+#  include <sys/select.h>
+#  include <sys/machine.h>
+#  define BSD_INCLUDES
+#  if BYTE_ORDER == BIG_ENDIAN
+#  define BIT_ZERO_ON_LEFT
+# endif
+# if BYTE_ORDER == LITTLE_ENDIAN
+#  define BIT_ZERO_ON_RIGHT
+# endif
+#endif
+
+
+#ifdef  __hpux
+#define HPUX
+#endif
+
+
+#if defined(HPUX )|| defined(AIX)
+#include <time.h>
+#ifdef AIX
+#include <sys/time.h>
+#endif
+#else
+#include <sys/time.h>
+#endif
+
+#if !defined(DEBUGMODE)
+#  define MyFree(x)    if ((x) != NULL) free(x)
+#else
+#define        free(x)         MyFree(x)
+#endif
+
+#ifdef NEXT
+#define VOIDSIG int    /* whether signal() returns int of void */
+#else
+#define VOIDSIG void   /* whether signal() returns int of void */
+#endif
+
+#ifdef SOL20
+#define OPT_TYPE char  /* opt type for get/setsockopt */
+#else
+#define OPT_TYPE void
+#endif
+
+#ifdef  __osf__
+#  define OSF
+/* OSF defines BSD to be its version of BSD */
+#  undef BSD
+#  include <sys/param.h>
+#  ifndef BSD
+#    define BSD
+#  endif
+#endif
+
+
+#ifdef _SEQUENT_                /* Dynix 1.4 or 2.0 Generic Define.. */
+#undef BSD
+#define SYSV                    /* Also #define SYSV */
+#endif
+
+#ifdef  ultrix
+#define ULTRIX
+#endif
+
+#ifdef  sgi
+#define SGI
+#endif
+
+#if defined(mips) || defined(PCS)
+#undef SYSV
+#endif
+
+#ifdef MIPS
+#undef BSD
+#define BSD             1       /* mips only works in bsd43 environment */
+#endif
+
+#ifdef sequent                   /* Dynix (sequent OS) */
+#define SEQ_NOFILE    128        /* set to your current kernel impl, */
+#endif                           /* max number of socket connections */
+
+#ifdef _SEQUENT_
+#define DYNIXPTX
+#endif
+
+/*
+ * Different name on NetBSD, FreeBSD, BSDI, OpenBSD and Linux
+ */
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__bsdi__) || defined(__OpenBSD__) ||  defined(__linux__) || defined(SOL20) || defined(__Darwin__)
+#define dn_skipname  __dn_skipname
+#endif
+
+extern VOIDSIG dummy();
+
+#ifdef DYNIXPTX
+#define        NO_U_TYPES
+typedef unsigned short n_short;         /* short as received from the net */
+typedef unsigned long   n_long;         /* long as received from the net */
+typedef unsigned long   n_time;         /* ms since 00:00 GMT, byte rev */
+#define _NETINET_IN_SYSTM_INCLUDED
+#endif
+
+#ifdef NO_U_TYPES
+typedef        unsigned char   u_char;
+typedef        unsigned short  u_short;
+typedef        unsigned long   u_long;
+typedef        unsigned int    u_int;
+#endif
+
+#ifdef USE_VARARGS
+#include <varargs.h>
+#endif
+#define MOTD MPATH
+#define LUSERS LUPATH
+#define OMOTD OMPATH
+#define MYNAME SPATH
+#define CONFIGFILE CPATH
+#define IRCD_PIDFILE PPATH
+
+#ifndef KLINE_TEMP
+#define KLINE_PERM 0
+#define KLINE_TEMP 1
+#define KLINE_AKILL 2
+#endif
+
+#ifdef DEBUGMODE
+extern  void    debug();
+# define Debug(x) debug x
+# define LOGFILE LPATH
+#else
+# define Debug(x) ;
+# define LOGFILE "/dev/null"
+#endif
+
+/*
+ * safety margin so we can always have one spare fd, for motd or
+ * whatever else.  -4 allows "safety" margin of 1 and space reserved.
+ */
+
+#define MAXCLIENTS      (MAXCONNECTIONS-4)
+   
+#if defined(CLIENT_FLOOD)
+#  if   (CLIENT_FLOOD > 8000)
+#    define CLIENT_FLOOD 8000
+#  else
+#    if (CLIENT_FLOOD < 512)
+error CLIENT_FLOOD needs redefining.
+#    endif
+#  endif
+#else
+error CLIENT_FLOOD undefined
+#endif
+#if (NICKNAMEHISTORYLENGTH < 100)
+#  define NICKNAMEHISTORYLENGTH 100
+#endif
+   
+   
+#endif /* __sys_include__ */
diff --git a/include/userload.h b/include/userload.h
new file mode 100644 (file)
index 0000000..6166bf6
--- /dev/null
@@ -0,0 +1,52 @@
+/****************************************************************************
+ *  Userload module by Michael L. VanLoon (mlv) <michaelv@iastate.edu>
+ *  Written 2/93.  Originally grafted into irc2.7.2g 4/93.
+ *
+ *   IRC - Internet Relay Chat, ircd/userload.h
+ *   Copyright (C) 1990 University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ ****************************************************************************/
+
+/* This needs to be defined for the counts to be correct--it should be the
+ * default anyway, as opers shouldn't be superior to lusers except where
+ * absolutely necessary, and here it isn't necessary.                       */
+#ifndef SHOW_INVISIBLE_LUSERS
+#define SHOW_INVISIBLE_LUSERS
+#endif
+
+struct current_load_struct {
+  u_short client_count, local_count, conn_count;
+  u_long  entries;
+};  
+
+extern struct current_load_struct current_load_data;
+
+struct load_entry {
+  struct  load_entry *prev;
+  u_short client_count, local_count, conn_count;
+#ifdef DEBUGMODE
+  u_short cpu_usage;
+#endif
+  long    time_incr;
+};
+
+extern struct load_entry *load_list_head, *load_list_tail,
+                         *load_free_head, *load_free_tail;
+
+
+extern void initload PROTO ((void));
+extern void update_load PROTO ((void));
+extern void calc_load PROTO ((aClient *, char *));
diff --git a/include/whowas.h b/include/whowas.h
new file mode 100644 (file)
index 0000000..d0dbb7a
--- /dev/null
@@ -0,0 +1,97 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, include/whowas.h
+ *   Copyright (C) 1990  Markku Savela
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Revision 1.1.1.1  1997/08/22 17:23:01  donwulff
+ * Original import from the "deadlined" version.
+ *
+ * Revision 1.1.1.1  1996/11/18 07:53:42  explorer
+ * ircd 4.3.3 -- about time
+ *
+ * Revision 1.1.1.1.4.1  1996/09/16 02:45:41  donwulff
+ * *** empty log message ***
+ *
+ * Revision 6.1  1991/07/04  21:04:39  gruner
+ * Revision 2.6.1 [released]
+ *
+ * Revision 6.0  1991/07/04  18:05:08  gruner
+ * frozen beta revision 2.6.1
+ *
+ */
+
+#ifndef        __whowas_include__
+#define __whowas_include__
+
+#ifndef PROTO
+#if __STDC__
+#      define PROTO(x) x
+#else
+#      define PROTO(x) ()
+#endif /* __STDC__ */
+#endif /* ! PROTO */
+
+/*
+** WHOWAS structure moved here from whowas.c
+*/
+typedef struct aname {
+       anUser  *ww_user;
+       aClient *ww_online;
+       time_t  ww_logout;
+       char    ww_nick[NICKLEN+1];
+       char    ww_info[REALLEN+1];
+        char    ww_ip[35];
+        char    ww_mask[HOSTLEN+1];
+} aName;
+
+/*
+** add_history
+**     Add the currently defined name of the client to history.
+**     usually called before changing to a new name (nick).
+**     Client must be a fully registered user (specifically,
+**     the user structure must have been allocated).
+*/
+void   add_history PROTO((aClient *));
+
+/*
+** off_history
+**     This must be called when the client structure is about to
+**     be released. History mechanism keeps pointers to client
+**     structures and it must know when they cease to exist. This
+**     also implicitly calls AddHistory.
+*/
+void   off_history PROTO((aClient *));
+
+/*
+** get_history
+**     Return the current client that was using the given
+**     nickname within the timelimit. Returns NULL, if no
+**     one found...
+*/
+aClient        *get_history PROTO((char *, time_t));
+                                       /* Nick name */
+                                       /* Time limit in seconds */
+
+int    m_whowas PROTO((aClient *, aClient *, int, char *[]));
+
+/*
+** for debugging...counts related structures stored in whowas array.
+*/
+void   count_whowas_memory PROTO((int *, int *, u_long *));
+
+#endif /* __whowas_include__ */
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..5dcd0f0
--- /dev/null
@@ -0,0 +1,238 @@
+#************************************************************************
+#*   IRC - Internet Relay Chat, src/Makefile
+#*   Copyright (C) 1990 Jarkko Oikarinen
+#*
+#*   This program is free software; you can redistribute it and/or modify
+#*   it under the terms of the GNU General Public License as published by
+#*   the Free Software Foundation; either version 1, or (at your option)
+#*   any later version.
+#*
+#*   This program is distributed in the hope that it will be useful,
+#*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#*   GNU General Public License for more details.
+#*
+#*   You should have received a copy of the GNU General Public License
+#*   along with this program; if not, write to the Free Software
+#*   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#*/
+
+OBJS=bsd.o dbuf.o packet.o send.o match.o parse.o support.o channel.o \
+       class.o hash.o ircd.o list.o res.o masking.o s_bsd.o s_conf.o \
+       s_debug.o s_err.o s_misc.o s_numeric.o s_ping.o s_serv.o s_user.o s_zip.o \
+       whowas.o userload.o md5.o $(RES) $(STRTOUL)
+
+SRC=$(OBJS:%.o=%.c)
+
+MAKE = make 'CFLAGS=${CFLAGS}' 'CC=${CC}' 'IRCDLIBS=${IRCDLIBS}' \
+           'LDFLAGS=${LDFLAGS}' 'IRCDMODE=${IRCDMODE}'
+
+MKDIR=mkdir
+CHMOD=chmod
+CHOWN=chown
+CHGRP=chgrp
+DPATH=../
+IRCDMODE=0700
+IRCDOWN=irc
+IRCDGRP=irc
+MPATH=../ircd.motd
+INSTALL=../bsdinstall
+
+MV=mv
+GREP=grep
+SHELL=/bin/sh
+CC=__oops__
+
+all: build
+
+build: ircd chkconf
+
+ircd: parenttest $(OBJS) 
+       @${SHELL} version.c.SH
+       ${CC} ${CFLAGS} -c version.c;
+       $(CC) $(CFLAGS) $(OBJS) version.o $(LDFLAGS) $(IRCDLIBS) -o ircd
+       chmod $(IRCDMODE) ircd
+
+parenttest:
+       @if [ "${CC}" = "__oops__" ]; then \
+               echo ""; echo ""; echo "";\
+               echo "*** Run make from the parent directory ***"; \
+               echo ""; echo "";echo "";\
+               exit 1; \
+       fi
+
+chkconf: ../include/struct.h ../include/config.h  ../include/sys.h \
+        ../include/common.h chkconf.c
+       $(CC) $(CFLAGS) -DCR_CHKCONF -o chkmatch.o -c match.c
+       $(CC) $(CFLAGS) -o support.o -c support.c
+       $(CC) $(CFLAGS) chkconf.c chkmatch.o support.o \
+       $(LDFLAGS) $(IRCDLIBS) -o chkconf
+
+clean:
+       $(RM) -f *.o *~ *.sav *.save core ircd version.c chkconf
+
+cleandir: clean
+
+install: build
+       @if [ ! -d ${DPATH} -a ! -f ${DPATH} ]; then \
+         echo "Creating directory ${DPATH}"; \
+         ${MKDIR} ${DPATH}; \
+         ${CHMOD} 700 ${DPATH}; \
+         ${CHOWN} ${IRCDOWN} ${DPATH}; \
+         ${CHGRP} ${IRCDGRP} ${DPATH}; \
+       fi
+       @echo "Installing new ircd as ${BINDIR}/ircd :"
+       ${INSTALL} -m ${IRCDMODE} -o ${IRCDOWN} -g ${IRCDGRP} ircd ${BINDIR}/ircd
+       ${INSTALL} -s -m 700 -o ${IRCDOWN} -g ${IRCDGRP} chkconf ${BINDIR}
+       ${INSTALL} -m 600 -o ${IRCDOWN} -g ${IRCDGRP} ../doc/example.conf ${DPATH}
+       ( cd ${DPATH}; \
+         ${TOUCH} ${MPATH}; \
+         ${CHOWN} ${IRCDOWN} ${MPATH}; \
+         ${CHGRP} ${IRCDGRP} ${MPATH}; )
+       
+
+depend: parenttest
+       @if [ -f Makefile.bak ]; then \
+         echo "make depend: First remove src/Makefile.bak"; \
+       else \
+         ( ${MV} Makefile Makefile.bak; \
+           ${GREP} -A1 -B1000 '^# DO NOT DELETE THIS LINE' Makefile.bak > Makefile;\
+           ${CC} ${CFLAGS} -MM ${SRC} >> Makefile; ) \
+       fi
+
+.SUFFIXES: .c .o
+
+.c.o:
+       ${CC} ${CFLAGS} -c $< -o $@
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+hash.o:        hash.c
+bsd.o: bsd.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/h.h
+dbuf.o: dbuf.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h
+packet.o: packet.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/msg.h ../include/h.h
+send.o: send.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/h.h
+match.o: match.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h
+parse.o: parse.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/msg.h ../include/numeric.h \
+ ../include/h.h
+support.o: support.c ../include/config.h ../include/setup.h \
+  ../include/struct.h ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h
+channel.o: channel.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/channel.h \
+ ../include/msg.h ../include/hash.h ../include/h.h
+class.o: class.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/h.h
+hash.o: hash.c ../include/numeric.h ../include/struct.h \
+ ../include/config.h ../include/setup.h  \
+ ../include/common.h ../include/sys.h ../include/class.h \
+ ../include/dbuf.h ../include/whowas.h ../include/hash.h \
+ ../include/h.h
+ircd.o: ircd.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/userload.h \
+ ../include/h.h
+list.o: list.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/h.h ../include/numeric.h
+res.o: res.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/res.h ../include/numeric.h \
+ ../include/h.h ../include/nameser.h ../include/resolv.h \
+ ../include/inet.h
+masking.o: masking.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/msg.h \
+ ../include/channel.h ../include/userload.h ../include/h.h
+s_bsd.o: s_bsd.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/res.h ../include/numeric.h \
+ ../include/patchlevel.h ../include/inet.h ../include/nameser.h \
+ ../include/resolv.h ../include/sock.h ../include/h.h
+s_conf.o: s_conf.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/channel.h \
+ ../include/msg.h ../include/inet.h ../include/h.h
+s_debug.o: s_debug.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/hash.h \
+ ../include/h.h
+s_err.o: s_err.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h
+s_misc.o: s_misc.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/userload.h \
+ ../include/h.h
+s_numeric.o: s_numeric.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/h.h
+s_ping.o: s_ping.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/patchlevel.h \
+ ../include/sock.h ../include/h.h
+s_serv.o: s_serv.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/msg.h \
+ ../include/channel.h ../include/userload.h ../include/h.h
+s_user.o: s_user.c ../include/config.h ../include/setup.h \
+ ../include/struct.h ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/msg.h \
+ ../include/channel.h ../include/userload.h ../include/h.h
+s_zip.o: s_zip.c ../include/config.h ../include/setup.h \
+ ../include/struct.h ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/msg.h \
+ ../include/channel.h ../include/userload.h ../include/h.h
+whowas.o: whowas.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/h.h
+userload.o: userload.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/userload.h ../include/h.h
+md5.o: md5.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h
+map.o: map.c ../include/struct.h ../include/config.h \
+ ../include/setup.h  ../include/common.h \
+ ../include/sys.h ../include/class.h ../include/dbuf.h \
+ ../include/whowas.h ../include/h.h ../include/numeric.h
diff --git a/src/bsd.c b/src/bsd.c
new file mode 100644 (file)
index 0000000..2c2f2a9
--- /dev/null
+++ b/src/bsd.c
@@ -0,0 +1,179 @@
+
+
+/************************************************************************
+ *   IRC - Internet Relay Chat, common/bsd.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include <signal.h>
+#include <sys/socket.h>
+
+extern int errno;  /* ...seems that errno.h doesn't define this everywhere */
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__linux__) && !defined(__Darwin__)
+extern char *sys_errlist[];
+#endif
+
+#ifdef DEBUGMODE
+int writecalls = 0, writeb[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+VOIDSIG dummy ()
+{
+#ifndef HAVE_RELIABLE_SIGNALS
+    (void) signal (SIGALRM, dummy);
+    (void) signal (SIGPIPE, dummy);
+#ifndef HPUX                     /* Only 9k/800 series require this, but don't know how to.. */
+#ifdef SIGWINCH
+    (void) signal (SIGWINCH, dummy);
+#endif
+#endif
+#else
+#ifdef POSIX_SIGNALS
+    struct sigaction act;
+
+    act.sa_handler = dummy;
+    act.sa_flags = 0;
+    (void) sigemptyset (&act.sa_mask);
+    (void) sigaddset (&act.sa_mask, SIGALRM);
+    (void) sigaddset (&act.sa_mask, SIGPIPE);
+#ifdef SIGWINCH
+    (void) sigaddset (&act.sa_mask, SIGWINCH);
+#endif
+    (void) sigaction (SIGALRM, &act, (struct sigaction *) NULL);
+    (void) sigaction (SIGPIPE, &act, (struct sigaction *) NULL);
+#ifdef SIGWINCH
+    (void) sigaction (SIGWINCH, &act, (struct sigaction *) NULL);
+#endif
+#endif
+#endif
+}
+
+
+/*
+   ** deliver_it
+   **   Attempt to send a sequence of bytes to the connection.
+   **   Returns
+   **
+   **   < 0 Some fatal error occurred, (but not EWOULDBLOCK).
+   **       This return is a request to close the socket and
+   **       clean up the link.
+   **   
+   **   >= 0    No real error occurred, returns the number of
+   **       bytes actually transferred. EWOULDBLOCK and other
+   **       possibly similar conditions should be mapped to
+   **       zero return. Upper level routine will have to
+   **       decide what to do with those unwritten bytes...
+   **
+   **   *NOTE*  alarm calls have been preserved, so this should
+   **       work equally well whether blocking or non-blocking
+   **       mode is used...
+   **
+   **   *NOTE*  I nuked 'em.  At the load of current ircd servers
+   **       you can't run with stuff that blocks. And we don't.
+ */
+int deliver_it (cptr, str, len)
+     aClient *cptr;
+     int len;
+     char *str;
+{
+    int retval;
+    aClient *acpt = cptr->acpt;
+
+#ifdef DEBUGMODE
+    writecalls++;
+#endif
+    if (IsDead (cptr)
+       || (!IsServer (cptr) && !IsPerson (cptr) && !IsHandshake (cptr)
+           && !IsUnknown (cptr))) {
+       str[len] = '\0';
+       sendto_ops
+           ("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s",
+            cptr->name, cptr->status, IsDead (cptr) ? "DEAD" : "", str);
+       return -1;
+    }
+    retval = send (cptr->fd, str, len, 0);
+    /*
+       ** Convert WOULDBLOCK to a return of "0 bytes moved". This
+       ** should occur only if socket was non-blocking. Note, that
+       ** all is Ok, if the 'write' just returns '0' instead of an
+       ** error and errno=EWOULDBLOCK.
+       **
+       ** ...now, would this work on VMS too? --msa
+     */
+    if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN ||
+                      errno == ENOBUFS)) {
+       retval = 0;
+       SetBlocked (cptr);
+    }
+    else if (retval > 0) {
+#ifdef pyr
+       (void) gettimeofday (&cptr->lw, NULL);
+#endif
+       ClearBlocked (cptr);
+    }
+#ifdef DEBUGMODE
+    if (retval < 0) {
+       writeb[0]++;
+       Debug ((DEBUG_ERROR, "write error (%s) to %s",
+               sys_errlist[errno], cptr->name));
+
+    }
+    else if (retval == 0)
+       writeb[1]++;
+    else if (retval < 16)
+       writeb[2]++;
+    else if (retval < 32)
+       writeb[3]++;
+    else if (retval < 64)
+       writeb[4]++;
+    else if (retval < 128)
+       writeb[5]++;
+    else if (retval < 256)
+       writeb[6]++;
+    else if (retval < 512)
+       writeb[7]++;
+    else if (retval < 1024)
+       writeb[8]++;
+    else
+       writeb[9]++;
+#endif
+    if (retval > 0) {
+       cptr->sendB += retval;
+       me.sendB += retval;
+       if (cptr->sendB > 1023) {
+           cptr->sendK += (cptr->sendB >> 10);
+           cptr->sendB &= 0x03ff;      /* 2^10 = 1024, 3ff = 1023 */
+       }
+       if (acpt != &me) {
+           acpt->sendB += retval;
+           if (acpt->sendB > 1023) {
+               acpt->sendK += (acpt->sendB >> 10);
+               acpt->sendB &= 0x03ff;
+           }
+       }
+       else if (me.sendB > 1023) {
+           me.sendK += (me.sendB >> 10);
+           me.sendB &= 0x03ff;
+       }
+    }
+    return (retval);
+}
diff --git a/src/buildm4 b/src/buildm4
new file mode 100755 (executable)
index 0000000..5fd2a89
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+# DALnet RCS/CVS identification:
+# $Id: buildm4,v 1.1.1.1 2001/10/22 19:27:09 remmy Exp $
+
+# IRC - Internet Relay Chat, ircd/buildm4
+# Copyright (C) 1993 Darren Reed
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 1, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#
+# If only this was a perl script...*sigh*
+#
+IRCDDIR=$1
+M4=$IRCDDIR/ircd.m4
+/bin/rm -f $M4
+egrep "^#def[^P]*PATCHLEVEL" ../include/patchlevel.h | \
+sed -e 's/[^\"]*\"\([^\"]*\)\"/define(VERSION,\1)/' >>$M4
+DEBUG=`egrep "^#define[                ]*DEBUGMODE" ../include/config.h`
+if [ -n "$DEBUG" ] ; then
+       echo "define(DEBUGMODE,1)" >>$M4
+else
+       echo "undefine(DEBUGMODE)" >>$M4
+fi
+echo -n "define(HOSTNAME," >> $M4
+echo "`hostname`" | sed -e 's/\([a-zA-Z0-9\-]*\).*/\1)/' >> $M4
+echo "define(USER,$USER)" >>$M4
+echo -n 'define(PORT,' >>$M4
+PORT=`egrep '^#define[         ]*PORT[         ]*[0-9]*' ../include/config.h`
+echo "$PORT" | sed -e 's/[^0-9]*\([0-9]*\).*/\1)/' >> $M4
+echo -n 'define(PFREQ,' >>$M4
+PING=`egrep '^#define[         ]*PINGFREQUENCY[        ]*[0-9]*' ../include/config.h`
+echo "$PING" | sed -e 's/[^0-9]*\([0-9]*\).*/\1)/' >> $M4
+echo -n 'define(CFREQ,' >>$M4
+CONT=`egrep '^#define[         ]*CONNECTFREQUENCY[     ]*[0-9]*' ../include/config.h`
+echo "$CONT" | sed -e 's/[^0-9]*\([0-9]*\).*/\1)/' >> $M4
+echo -n "define(MAXSENDQ," >>$M4
+MAXQ=`egrep '^#define[         ]*MAXSENDQLENGTH[       ]*[0-9]*' ../include/config.h`
+echo "$MAXQ" | sed -e 's/[^0-9]*\([0-9]*\).*/\1)/' >> $M4
+echo -n "define(MAXLINKS," >>$M4
+MAXL=`egrep '^#define[         ]*MAXIMUM_LINKS[        ]*[0-9]* | head -1' \
+../include/config.h`
+echo "$MAXL" | sed -e 's/[^0-9]*\([0-9]*\).*/\1)/' >> $M4
+DOM=`egrep '^domain' /etc/resolv.conf | \
+sed -e 's/^domain[     ]*\([a-zA-Z\-\.0-9]*\).*/\1/'`
+echo "define(DOMAIN,$DOM)" >> $M4
+
+cat >>$M4 <<_EOF_
+define(CL,\`ifelse(len(\$1),0,0,\$1)')
+define(HOST,\$1)
+define(HOSTM,\$1)
+define(ID,*@\$1)
+define(PASS,\$1)
+define(PING,\`ifelse(len(\$1),0,PFREQ,\$1)')
+define(APORT,\`ifelse(len(\$1),0,PORT,\$1)')
+define(FREQ,\`ifelse(len(\$1),0,CFREQ,\$1)')
+define(SENDQ,\`ifelse(len(\$1),0,MAXSENDQ,\$1)')
+define(MAX,\`ifelse(len(\$1),0,MAXLINKS,\$1)')
+define(CPORT,\$1)
+define(SERV,\$1)
+define(ADMIN,A:\$1:\$2:\$3:\$4:\$5)
+define(ALLOW,N:\`HOST(\$1)':\`PASS(\$2)':\`SERV(\$3)':\`HOSTM(\$4)':\`CL(\$5)')
+define(BAN,K:\$1:\$2:\$3)
+define(CLASS,Y:\$1:\`PING(\$2)':\$3:\`MAX(\$4)':\`SENDQ(\$5)')
+define(CLIENT,I:\`HOST(\$1)':\`PASS(\$2)':\`ifelse(len(HOST(\$3)),0,\$1,\$3)':\
+\`APORT(\$4)':\`CL(\$5)')
+define(CONNECT,C:\`HOST(\$1)':\`PASS(\$2)':\`SERV(\$3)':\
+\`CPORT(\$4)':\`CL(\$5)')
+define(ME,M:\$1:\$2:\$3:\$4:\$5)
+define(HUB,H:\`ifelse(len(\$2),0,*,\$2)':*:\$1)
+define(LEAF,L:\`ifelse(len(\$2),0,*,\$2)':*:\$1:1)
+define(SERVER,\`
+CONNECT(\$1,\$2,\$3,\$5,\$6)
+ALLOW(\$1,\$2,\$3,\$4,\$6)
+')
+_EOF_
diff --git a/src/channel.c b/src/channel.c
new file mode 100644 (file)
index 0000000..167fe1c
--- /dev/null
@@ -0,0 +1,4405 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/channel.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Co Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "channel.h"
+#include "msg.h"                 /* For TOK_*** and MSG_*** strings  */
+#include "hash.h"                /* For CHANNELHASHSIZE */
+#include "h.h"
+
+aChannel *channel = NullChn;
+aSynchList *SJSynchList = NULL;
+
+static void add_invite PROTO ((aClient *, aChannel *));
+static int can_join PROTO ((aClient *, aChannel *, char *));
+static void channel_modes PROTO ((aClient *, char *, char *, aChannel *));
+static int check_channelmask PROTO ((aClient *, aClient *, char *));
+static int add_banid PROTO ((aClient *, aChannel *, char *));
+static int del_banid PROTO ((aChannel *, char *));
+static int find_banid PROTO ((aChannel *, char *));
+static int add_exbanid PROTO ((aClient *, aChannel *, char *));
+static int del_exbanid PROTO ((aChannel *, char *));
+static int find_exbanid PROTO ((aChannel *, char *));
+static int add_hostrestrictid PROTO ((aClient *, aChannel *, char *));
+static int del_hostrestrictid PROTO ((aChannel *, char *));
+static int find_hostrestrictid PROTO ((aChannel *, char *));
+static int have_ops PROTO ((aChannel *));
+static int set_mode PROTO ((aClient *, aClient *, aChannel *, int,
+                           char **, char *, char *, int *, int));
+static void sub1_from_channel PROTO ((aChannel *));
+
+void clean_channelname PROTO ((char *));
+void del_invite PROTO ((aClient *, aChannel *));
+int check_for_target_limit (aClient *, void *, const char *);  /* from s_user.c */
+
+int sendmodeto_one (aClient *, char *, char *, char *, char *, time_t);
+
+static char *PartFmt = ":%s PART %s";
+static char *PartFmt2 = ":%s PART %s :%s";
+
+/*
+ * some buffers for rebuilding channel/nick lists with ,'s
+ */
+static char nickbuf[BUFSIZE], buf[BUFSIZE];
+static char modebuf[MODEBUFLEN], parabuf[MODEBUFLEN];
+
+/*
+ * return the length (>=0) of a chain of links.
+ */
+static int list_length (lp)
+     Link *lp;
+{
+    int count = 0;
+
+    for (; lp; lp = lp->next)
+       count++;
+    return count;
+}
+
+/*
+   ** find_chasing
+   **   Find the client structure for a nick name (user) using history
+   **   mechanism if necessary. If the client is not found, an error
+   **   message (NO SUCH NICK) is generated. If the client was found
+   **   through the history, chasing will be 1 and otherwise 0.
+ */
+static aClient *find_chasing (sptr, user, chasing)
+     aClient *sptr;
+     char *user;
+     int *chasing;
+{
+    aClient *who = find_client (user, (aClient *) NULL);
+
+    if (chasing)
+       *chasing = 0;
+    if (who)
+       return who;
+    if (!(who = get_history (user, (long) KILLCHASETIMELIMIT))) {
+       sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name, sptr->name,
+                   user);
+       return NULL;
+    }
+    if (chasing)
+       *chasing = 1;
+    return who;
+}
+
+/*
+ * Ban functions to work with mode +b
+ */
+/* add_banid - add an id to be banned to the channel  (belongs to cptr) */
+
+static int add_banid (cptr, chptr, banid)
+     aClient *cptr;
+     aChannel *chptr;
+     char *banid;
+{
+    Ban *ban;
+    int cnt = 0, len = 0;
+
+    if (MyClient (cptr))
+       (void) collapse (banid);
+    for (ban = chptr->banlist; ban; ban = ban->next) {
+       len += strlen (ban->banstr);
+       if (MyClient (cptr)) {
+           if ((len > MAXBANLENGTH) || (++cnt >= MAXBANS)) {
+               sendto_one (cptr, err_str (ERR_BANLISTFULL),
+                           me.name, cptr->name, chptr->chname, banid);
+               return -1;
+           }
+           else {
+               if (!match (ban->banstr, banid)
+                   || !match (banid, ban->banstr))
+                   return -1;
+           }
+       }
+       else if (!mycmp (ban->banstr, banid))
+           return -1;
+
+    }
+    ban = make_ban ();
+    bzero ((char *) ban, sizeof (Ban));
+/*  ban->flags = CHFL_BAN;          They're all bans!! */
+    ban->next = chptr->banlist;
+    ban->banstr = (char *) MyMalloc (strlen (banid) + 1);
+    (void) strcpy (ban->banstr, banid);
+    ban->who = (char *) MyMalloc (strlen (cptr->name) + 1);
+    (void) strcpy (ban->who, cptr->name);
+    ban->when = time (NULL);
+    chptr->banlist = ban;
+    return 0;
+}
+
+/*
+ * del_banid - delete an id belonging to cptr
+ */
+static int del_banid (chptr, banid)
+     aChannel *chptr;
+     char *banid;
+{
+    Ban **ban;
+    Ban *tmp;
+
+    if (!banid)
+       return -1;
+    for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
+       if (mycmp (banid, (*ban)->banstr) == 0) {
+           tmp = *ban;
+           *ban = tmp->next;
+           MyFree (tmp->banstr);
+           MyFree (tmp->who);
+           free_ban (tmp);
+           return 0;
+       }
+    return -1;
+}
+
+/*
+ * find_banid - Find an exact match for a ban
+ */
+static int find_banid (chptr, banid)
+     aChannel *chptr;
+     char *banid;
+{
+    Ban **ban;
+
+    if (!banid)
+       return -1;
+    for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
+       if (!mycmp (banid, (*ban)->banstr))
+           return 1;
+    return 0;
+}
+
+/*
+ *  Exception Ban Functions (+e)
+ *  -Ax0n
+ */
+
+static int add_exbanid (cptr, chptr, banid)
+     aClient *cptr;
+     aChannel *chptr;
+     char *banid;
+{
+    Ban *ban;
+    int cnt = 0, len = 0;
+
+    if (MyClient (cptr))
+       (void) collapse (banid);
+    for (ban = chptr->exbanlist; ban; ban = ban->next) {
+       len += strlen (ban->banstr);
+       if (MyClient (cptr)) {
+           if ((len > MAXBANLENGTH) || (++cnt >= MAXBANS)) {
+               sendto_one (cptr, err_str (ERR_BANLISTFULL),
+                           me.name, cptr->name, chptr->chname, banid);
+               return -1;
+           }
+           else {
+               if (!match (ban->banstr, banid)
+                   || !match (banid, ban->banstr))
+                   return -1;
+           }
+       }
+       else if (!mycmp (ban->banstr, banid))
+           return -1;
+
+    }
+    ban = make_ban ();
+    bzero ((char *) ban, sizeof (Ban));
+/*  ban->flags = CHFL_BAN;          They're all bans!! */
+    ban->next = chptr->exbanlist;
+    ban->banstr = (char *) MyMalloc (strlen (banid) + 1);
+    (void) strcpy (ban->banstr, banid);
+    ban->who = (char *) MyMalloc (strlen (cptr->name) + 1);
+    (void) strcpy (ban->who, cptr->name);
+    ban->when = time (NULL);
+    chptr->exbanlist = ban;
+    return 0;
+}
+
+static int del_exbanid (chptr, banid)
+     aChannel *chptr;
+     char *banid;
+{
+    Ban **ban;
+    Ban *tmp;
+
+    if (!banid)
+       return -1;
+    for (ban = &(chptr->exbanlist); *ban; ban = &((*ban)->next))
+       if (mycmp (banid, (*ban)->banstr) == 0) {
+           tmp = *ban;
+           *ban = tmp->next;
+           MyFree (tmp->banstr);
+           MyFree (tmp->who);
+           free_ban (tmp);
+           return 0;
+       }
+    return -1;
+}
+
+static int find_exbanid (chptr, banid)
+     aChannel *chptr;
+     char *banid;
+{
+    Ban **ban;
+
+    if (!banid)
+       return -1;
+    for (ban = &(chptr->exbanlist); *ban; ban = &((*ban)->next))
+       if (!mycmp (banid, (*ban)->banstr))
+           return 1;
+    return 0;
+}
+
+/*
+ *  Host Restrict Functions (+H)
+ *  -Remmy
+ */
+
+static int add_hostrestrictid (cptr, chptr, banid)
+     aClient *cptr;
+     aChannel *chptr;
+     char *banid;
+{
+    Ban *ban;
+    int cnt = 0, len = 0;
+
+    if (MyClient (cptr))
+       (void) collapse (banid);
+    for (ban = chptr->hostrestrictlist; ban; ban = ban->next) {
+       len += strlen (ban->banstr);
+       if (MyClient (cptr)) {
+           if ((len > MAXBANLENGTH) || (++cnt >= MAXBANS)) {
+               sendto_one (cptr, err_str (ERR_BANLISTFULL),
+                           me.name, cptr->name, chptr->chname, banid);
+               return -1;
+           }
+           else {
+               if (!match (ban->banstr, banid)
+                   || !match (banid, ban->banstr))
+                   return -1;
+           }
+       }
+       else if (!mycmp (ban->banstr, banid))
+           return -1;
+
+    }
+    ban = make_ban ();
+    bzero ((char *) ban, sizeof (Ban));
+/*  ban->flags = CHFL_BAN;          They're all bans!! */
+    ban->next = chptr->hostrestrictlist;
+    ban->banstr = (char *) MyMalloc (strlen (banid) + 1);
+    (void) strcpy (ban->banstr, banid);
+    ban->who = (char *) MyMalloc (strlen (cptr->name) + 1);
+    (void) strcpy (ban->who, cptr->name);
+    ban->when = time (NULL);
+    chptr->hostrestrictlist = ban;
+    return 0;
+}
+
+static int del_hostrestrictid (chptr, banid)
+     aChannel *chptr;
+     char *banid;
+{
+    Ban **ban;
+    Ban *tmp;
+
+    if (!banid)
+       return -1;
+    for (ban = &(chptr->hostrestrictlist); *ban; ban = &((*ban)->next))
+       if (mycmp (banid, (*ban)->banstr) == 0) {
+           tmp = *ban;
+           *ban = tmp->next;
+           MyFree (tmp->banstr);
+           MyFree (tmp->who);
+           free_ban (tmp);
+           return 0;
+       }
+    return -1;
+}
+
+static int find_hostrestrictid (chptr, banid)
+     aChannel *chptr;
+     char *banid;
+{
+    Ban **ban;
+
+    if (!banid)
+       return -1;
+    for (ban = &(chptr->hostrestrictlist); *ban; ban = &((*ban)->next))
+       if (!mycmp (banid, (*ban)->banstr))
+           return 1;
+    return 0;
+}
+
+
+/*
+ * IsMember - returns 1 if a person is joined
+ */
+int IsMember (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+    Link *lp;
+    return (((lp = find_user_link (chptr->members, cptr)))? 1 : 0);
+}
+
+/*
+ * is_banned - returns a pointer to the ban structure if banned else NULL
+ */
+extern Ban *is_banned (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+    Ban *tmp;
+    char *s, realaddy[NICKLEN + USERLEN + HOSTLEN + 6],
+       fakeaddy[NICKLEN + USERLEN + HOSTLEN + 6];
+    int testem = 0;
+
+    if (!IsPerson (cptr))
+       return NULL;
+    if (strcmp (cptr->user->host, MaskHost (cptr)))
+       testem = 1;
+    s = make_nick_user_host (cptr->name, cptr->user->username,
+                            cptr->user->host);
+    strcpy (realaddy, s);
+    s = make_nick_user_host (cptr->name, cptr->user->username,
+                            MaskHost (cptr));
+    strcpy (fakeaddy, s);
+    for (tmp = chptr->banlist; tmp; tmp = tmp->next)
+       if ((match (tmp->banstr, realaddy) == 0) ||
+           (testem && (match (tmp->banstr, fakeaddy) == 0)))
+           break;
+    return (tmp);
+
+}
+
+/*
+ * is_banexception - see is_banned(), same diff.
+ */
+extern Ban *is_banexception (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+    Ban *tmp;
+    char *s, realaddy[NICKLEN + USERLEN + HOSTLEN + 6],
+       fakeaddy[NICKLEN + USERLEN + HOSTLEN + 6];
+    int testem = 0;
+
+    if (!IsPerson (cptr))
+       return NULL;
+    if (strcmp (cptr->user->host, MaskHost (cptr)))
+       testem = 1;
+    s = make_nick_user_host (cptr->name, cptr->user->username,
+                            cptr->user->host);
+    strcpy (realaddy, s);
+    s = make_nick_user_host (cptr->name, cptr->user->username,
+                            MaskHost (cptr));
+    strcpy (fakeaddy, s);
+    for (tmp = chptr->exbanlist; tmp; tmp = tmp->next)
+       if ((match (tmp->banstr, realaddy) == 0) ||
+           (testem && (match (tmp->banstr, fakeaddy) == 0)))
+           break;
+    return (tmp);
+
+}
+
+/*
+ * is_banexception - see is_banned(), same diff.
+ */
+extern Ban *is_host_restricted (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+    Ban *tmp;
+    char *s, realaddy[NICKLEN + USERLEN + HOSTLEN + 6],
+       fakeaddy[NICKLEN + USERLEN + HOSTLEN + 6];
+    int testem = 0;
+
+    if (!IsPerson (cptr))
+       return NULL;
+    if (strcmp (cptr->user->host, MaskHost (cptr)))
+       testem = 1;
+    s = make_nick_user_host (cptr->name, cptr->user->username,
+                            cptr->user->host);
+    strcpy (realaddy, s);
+    s = make_nick_user_host (cptr->name, cptr->user->username,
+                            MaskHost (cptr));
+    strcpy (fakeaddy, s);
+    for (tmp = chptr->hostrestrictlist; tmp; tmp = tmp->next)
+       if ((match (tmp->banstr, realaddy) == 0) ||
+           (testem && (match (tmp->banstr, fakeaddy) == 0)))
+           break;
+    return (tmp);
+
+}
+
+/*
+ * adds a user to a channel by adding another link to the channels member
+ * chain.
+ */
+static void add_user_to_channel (chptr, who, flags)
+     aChannel *chptr;
+     aClient *who;
+     int flags;
+{
+    Link *ptr;
+
+    if (who->user) {
+       ptr = make_link ();
+       ptr->value.cptr = who;
+       ptr->flags = flags;
+       ptr->next = chptr->members;
+       chptr->members = ptr;
+       chptr->users++;
+
+       ptr = make_link ();
+       ptr->value.chptr = chptr;
+       ptr->next = who->user->channel;
+       who->user->channel = ptr;
+       who->user->joined++;
+    }
+}
+
+void remove_user_from_channel (sptr, chptr)
+     aClient *sptr;
+     aChannel *chptr;
+{
+    Link **curr;
+    Link *tmp;
+    Link *lp = chptr->members;
+
+    for (; lp && (lp->flags & CHFL_ZOMBIE || lp->value.cptr == sptr);
+        lp = lp->next);
+    for (;;) {
+       for (curr = &chptr->members; (tmp = *curr); curr = &tmp->next)
+           if (tmp->value.cptr == sptr) {
+               *curr = tmp->next;
+               free_link (tmp);
+               break;
+           }
+       for (curr = &sptr->user->channel; (tmp = *curr); curr = &tmp->next)
+           if (tmp->value.chptr == chptr) {
+               *curr = tmp->next;
+               free_link (tmp);
+               break;
+           }
+       sptr->user->joined--;
+       if (lp)
+           break;
+       if (chptr->members)
+           sptr = chptr->members->value.cptr;
+       else
+           break;
+       sub1_from_channel (chptr);
+    }
+    sub1_from_channel (chptr);
+}
+
+
+static int have_ops (chptr)
+     aChannel *chptr;
+{
+    Link *lp;
+
+    if (chptr) {
+       lp = chptr->members;
+       while (lp) {
+           if (lp->flags & CHFL_CHANOP)
+               return (1);
+           lp = lp->next;
+       }
+    }
+    return 0;
+}
+
+int is_chan_op (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+    Link *lp;
+
+    if (chptr)
+       if ((lp = find_user_link (chptr->members, cptr)))
+           return (lp->flags & CHFL_CHANOP);
+
+    return 0;
+}
+
+int has_voice (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+    Link *lp;
+
+    if (chptr)
+       if ((lp = find_user_link (chptr->members, cptr)) &&
+           !(lp->flags & CHFL_ZOMBIE))
+           return (lp->flags & CHFL_VOICE);
+
+    return 0;
+}
+
+/* This is used in determining why the user can't send to a channel -Defiant */
+#define CANNOT_SEND_MODERATED 1
+#define CANNOT_SEND_NOPRIVMSGS 2
+#define CANNOT_SEND_NOCOLORS 3
+#define CANNOT_SEND_BAN 4
+
+int can_send (cptr, chptr, msgtext)
+     aClient *cptr;
+     aChannel *chptr;
+     char *msgtext;    /* for color checking -Defiant */
+{
+    Link *lp;
+    int member;
+
+    /* Moved check here, kinda faster.
+     * Note IsULine only uses the other parameter. -Donwulff */
+    if (IsULine (cptr, cptr) || IsServer (cptr) || IsSRoot (cptr))
+       return 0;
+
+    member = IsMember (cptr, chptr);
+    lp = find_user_link (chptr->members, cptr);
+
+    if (chptr->mode.mode & MODE_MODERATED &&
+       (!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE)) ||
+        (lp->flags & CHFL_ZOMBIE)))
+       return (CANNOT_SEND_MODERATED);
+
+    if (chptr->mode.mode & MODE_NOPRIVMSGS && !member)
+       return (CANNOT_SEND_NOPRIVMSGS);
+
+    if (chptr->mode.mode & MODE_NOCOLORS)
+       if (strchr (msgtext, 3) != (char) NULL ||
+           strchr (msgtext, 27) != (char) NULL)
+           return (CANNOT_SEND_NOCOLORS);
+
+    if ((!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE)) ||
+        (lp->flags & CHFL_ZOMBIE)) && MyClient (cptr) &&
+       is_banned (cptr, chptr)) {
+       if (!is_banexception (cptr, chptr))
+           return (CANNOT_SEND_BAN);
+    }
+    return 0;
+}
+
+aChannel *find_channel (chname, chptr)
+     char *chname;
+     aChannel *chptr;
+{
+    return hash_find_channel (chname, chptr);
+}
+
+/*
+ * write the "simple" list of channel modes for channel chptr onto buffer mbuf
+ * with the parameters in pbuf.
+ */
+static void channel_modes (cptr, mbuf, pbuf, chptr)
+     aClient *cptr;
+     char *mbuf, *pbuf;
+     aChannel *chptr;
+{
+    *mbuf++ = '+';
+    if (chptr->mode.mode & MODE_SECRET)
+       *mbuf++ = 's';
+    if (chptr->mode.mode & MODE_MODERATED)
+       *mbuf++ = 'm';
+    if (chptr->mode.mode & MODE_TOPICLIMIT)
+       *mbuf++ = 't';
+    if (chptr->mode.mode & MODE_NOCOLORS)
+       *mbuf++ = 'c';
+    if (chptr->mode.mode & MODE_INVITEONLY)
+       *mbuf++ = 'i';
+    if (chptr->mode.mode & MODE_NOPRIVMSGS)
+       *mbuf++ = 'n';
+    if (chptr->mode.mode & MODE_RGSTR)
+       *mbuf++ = 'r';
+    if (chptr->mode.mode & MODE_RGSTRONLY)
+       *mbuf++ = 'R';
+    if (chptr->mode.limit) {
+       *mbuf++ = 'l';
+       if (IsMember (cptr, chptr) || IsServer (cptr) || IsULine (cptr, cptr)
+           || IsSAdmin (cptr))
+           (void) sprintf (pbuf, "%d ", chptr->mode.limit);
+    }
+    if (*chptr->mode.key) {
+       *mbuf++ = 'k';
+       if (IsMember (cptr, chptr) || IsServer (cptr) || IsULine (cptr, cptr)
+           || IsSAdmin (cptr))
+           (void) strcat (pbuf, chptr->mode.key);
+    }
+    *mbuf++ = '\0';
+    return;
+}
+
+static int send_ban_list (cptr, chname, creationtime, top)
+     aClient *cptr;
+     Link *top;
+     char *chname;
+     time_t creationtime;
+{
+    Link *lp;
+    char *cp, *name;
+    int count = 0, send = 0, sent = 0;
+
+    cp = modebuf + strlen (modebuf);
+    if (*parabuf)                /* mode +l or +k xx */
+       count = 1;
+    for (lp = top; lp; lp = lp->next) {
+       name = ((Ban *) lp)->banstr;
+
+       if (strlen (parabuf) + strlen (name) + 11 < (size_t) MODEBUFLEN) {
+           if (*parabuf)
+               (void) strcat (parabuf, " ");
+           (void) strcat (parabuf, name);
+           count++;
+           *cp++ = 'b';
+           *cp = '\0';
+       }
+       else if (*parabuf)
+           send = 1;
+       if (count == RESYNCMODES)
+           send = 1;
+       if (send) {
+           /* cptr is always a server! So we send creationtimes */
+           sendmodeto_one (cptr, me.name, chname, modebuf, parabuf,
+                           creationtime);
+           sent = 1;
+           send = 0;
+           *parabuf = '\0';
+           cp = modebuf;
+           *cp++ = '+';
+           if (count != RESYNCMODES) {
+               (void) strcpy (parabuf, name);
+               *cp++ = 'b';
+           }
+           count = 0;
+           *cp = '\0';
+       }
+    }
+    return sent;
+}
+
+static int send_exban_list (cptr, chname, creationtime, top)
+     aClient *cptr;
+     Link *top;
+     char *chname;
+     time_t creationtime;
+{
+    Link *lp;
+    char *cp, *name;
+    int count = 0, send = 0, sent = 0;
+
+    cp = modebuf + strlen (modebuf);
+    if (*parabuf)                /* mode +l or +k xx */
+       count = 1;
+    for (lp = top; lp; lp = lp->next) {
+       name = ((Ban *) lp)->banstr;
+
+       if (strlen (parabuf) + strlen (name) + 11 < (size_t) MODEBUFLEN) {
+           if (*parabuf)
+               (void) strcat (parabuf, " ");
+           (void) strcat (parabuf, name);
+           count++;
+           *cp++ = 'e';
+           *cp = '\0';
+       }
+       else if (*parabuf)
+           send = 1;
+       if (count == RESYNCMODES)
+           send = 1;
+       if (send) {
+           /* cptr is always a server! So we send creationtimes */
+           sendmodeto_one (cptr, me.name, chname, modebuf, parabuf,
+                           creationtime);
+           sent = 1;
+           send = 0;
+           *parabuf = '\0';
+           cp = modebuf;
+           *cp++ = '+';
+           if (count != RESYNCMODES) {
+               (void) strcpy (parabuf, name);
+               *cp++ = 'e';
+           }
+           count = 0;
+           *cp = '\0';
+       }
+    }
+    return sent;
+}
+
+static int send_hostrestrict_list (cptr, chname, creationtime, top)
+     aClient *cptr;
+     Link *top;
+     char *chname;
+     time_t creationtime;
+{
+    Link *lp;
+    char *cp, *name;
+    int count = 0, send = 0, sent = 0;
+
+    cp = modebuf + strlen (modebuf);
+    if (*parabuf)                /* mode +l or +k xx */
+       count = 1;
+    for (lp = top; lp; lp = lp->next) {
+       name = ((Ban *) lp)->banstr;
+
+       if (strlen (parabuf) + strlen (name) + 11 < (size_t) MODEBUFLEN) {
+           if (*parabuf)
+               (void) strcat (parabuf, " ");
+           (void) strcat (parabuf, name);
+           count++;
+           *cp++ = 'H';
+           *cp = '\0';
+       }
+       else if (*parabuf)
+           send = 1;
+       if (count == RESYNCMODES)
+           send = 1;
+       if (send) {
+           /* cptr is always a server! So we send creationtimes */
+           sendmodeto_one (cptr, me.name, chname, modebuf, parabuf,
+                           creationtime);
+           sent = 1;
+           send = 0;
+           *parabuf = '\0';
+           cp = modebuf;
+           *cp++ = '+';
+           if (count != RESYNCMODES) {
+               (void) strcpy (parabuf, name);
+               *cp++ = 'H';
+           }
+           count = 0;
+           *cp = '\0';
+       }
+    }
+    return sent;
+}
+
+/* 
+ * This will send "cptr" a full list of the modes for channel chptr,
+ * using the new StarChat Time Stamp 1 protocol (based on EFnet TS3)
+ */
+
+void send_channel_modes_sts (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+
+    Link *members;
+    Link *lp;
+    char *name;
+    char *bufptr;
+
+    int n = 0;
+
+    if (*chptr->chname != '#')
+       return;
+
+    members = chptr->members;
+
+    /* First we'll send channel, channel modes and members and status */
+
+    *modebuf = *parabuf = '\0';
+    channel_modes (cptr, modebuf, parabuf, chptr);
+
+    if (*parabuf)
+       strcat (parabuf, " ");
+    else
+       strcpy (parabuf, "<none>");
+
+    sprintf (buf, ":%s SJOIN %ld %s %s %s :", me.name,
+            chptr->creationtime, chptr->chname, modebuf, parabuf);
+
+    bufptr = buf + strlen (buf);
+
+    for (lp = members; lp; lp = lp->next) {
+
+       if (lp->flags & MODE_CHANOP)
+           *bufptr++ = '@';
+
+       if (lp->flags & MODE_VOICE)
+           *bufptr++ = '+';
+
+       name = lp->value.cptr->name;
+
+       strcpy (bufptr, name);
+       bufptr += strlen (bufptr);
+       *bufptr++ = ' ';
+       n++;
+
+       if (bufptr - buf > BUFSIZE - 80) {
+           *bufptr++ = '\0';
+           if (bufptr[-1] == ' ')
+               bufptr[-1] = '\0';
+           sendto_one (cptr, "%s", buf);
+
+           sprintf (buf, ":%s SJOIN %ld %s %s %s :", me.name,
+                    chptr->creationtime, chptr->chname, modebuf, parabuf);
+           n = 0;
+
+           bufptr = buf + strlen (buf);
+       }
+    }
+
+    if (n) {
+       *bufptr++ = '\0';
+       if (bufptr[-1] == ' ')
+           bufptr[-1] = '\0';
+       sendto_one (cptr, "%s", buf);
+    }
+    /* Then we'll send the ban-list, ex-ban list and hostrestrict list */
+
+    *parabuf = '\0';
+    *modebuf = '+';
+    modebuf[1] = '\0';
+
+    send_ban_list (cptr, chptr->chname, chptr->creationtime, chptr->banlist);
+    send_exban_list (cptr, chptr->chname, chptr->creationtime,
+                    chptr->exbanlist);
+    send_hostrestrict_list (cptr, chptr->chname, chptr->creationtime, 
+                            chptr->hostrestrictlist);
+    
+    if (modebuf[1] || *parabuf)
+       sendto_one (cptr, ":%s MODE %s %s %s", me.name, chptr->chname,
+                   modebuf, parabuf);
+
+    return;
+}
+
+/*
+   ** m_knock
+   **   parv[0] - sender prefix
+   **   parv[1] - channel
+   **   parv[2] - reason
+   **
+ */
+int m_knock (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aChannel *chptr;
+
+    if (check_registered_user (sptr))
+       return 0;
+
+    if (parc < 3 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "KNOCK");
+       return -1;
+    }
+    if (MyConnect (sptr))
+       clean_channelname (parv[1]);
+
+    if (check_channelmask (sptr, cptr, parv[1]))
+       return 0;
+
+    if (*parv[1] != '#' && *parv[1] != '&') {
+       sendto_one (sptr, err_str (ERR_CANNOTKNOCK),
+                   me.name, sptr->name, parv[1], "No such channel");
+
+       return 0;
+    }
+    if (!(chptr = find_channel (parv[1], NullChn))) {
+       sendto_one (sptr, err_str (ERR_CANNOTKNOCK),
+                   me.name, sptr->name, parv[1], "No such channel");
+
+       return 0;
+    }
+    if (IsMember (sptr, chptr) == 1) {
+       sendto_one (sptr, err_str (ERR_CANNOTKNOCK),
+                   me.name, sptr->name, chptr->chname,
+                   "You're already in that channel");
+
+       return 0;
+    }
+    if (is_banned (sptr, chptr)) {
+       sendto_one (sptr, err_str (ERR_CANNOTKNOCK),
+                   me.name, sptr->name, chptr->chname, "You're banned");
+
+       return 0;
+    }
+    if (!(chptr->mode.mode & MODE_INVITEONLY)) {
+       sendto_one (sptr, err_str (ERR_CANNOTKNOCK),
+                   me.name, sptr->name, chptr->chname,
+                   "Channel is not invite only");
+
+       return 0;
+    }
+    sendto_channelops_butone (NULL, &me, chptr,
+                             ":%s NOTICE @%s :%s wants to join (%s).",
+                             me.name, chptr->chname, sptr->name, parv[2]);
+
+    sendto_one (sptr, ":%s NOTICE %s :Knocked on to %s", me.name, sptr->name,
+               chptr->chname);
+
+    return 0;
+}
+
+/*
+ * m_samode
+ * parv[0] = sender
+ * parv[1] = channel
+ * parv[2] = modes
+ * -taz
+ */
+int m_samode (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+/*  static char tmp[MODEBUFLEN]; Compiler says this is unused */
+    int badop, sendts;
+    aChannel *chptr;
+
+    if (check_registered (cptr))
+       return 0;
+
+    if (!IsPrivileged (cptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if (!IsSAdmin (cptr) || parc < 2)
+       return 0;
+
+    chptr = find_channel (parv[1], NullChn);
+    if (chptr == NullChn)
+       return 0;
+
+    sptr->flags &= ~FLAGS_TS8;
+
+    clean_channelname (parv[1]);
+
+    if (check_channelmask (sptr, cptr, parv[1]))
+       return 0;
+
+    sendts = set_mode (cptr, sptr, chptr, parc - 2, parv + 2,
+                      modebuf, parabuf, &badop, 1);
+
+    if (strlen (modebuf) > (size_t) 1 || sendts > 0) {
+       if (badop != 2 && strlen (modebuf) > (size_t) 1)
+           sendto_channel_butserv (chptr, sptr, ":%s MODE %s %s %s",
+                                   me.name, chptr->chname, modebuf, parabuf);
+
+       sendto_match_servs (chptr, cptr, ":%s MODE %s %s %s 1",
+                           parv[0], chptr->chname, modebuf, parabuf);
+
+       if (MyClient (sptr)) {
+           sendto_serv_butone (&me, ":%s GLOBOPS :%s used SAMODE (%s %s %s)",
+                               me.name, sptr->name, chptr->chname, modebuf,
+                               parabuf);
+           sendto_failops_whoare_opers ("from %s: %s used SAMODE (%s %s %s)",
+                                        me.name, sptr->name, chptr->chname,
+                                        modebuf, parabuf);
+       }
+    }
+    return 0;
+}
+
+/*
+ * m_mode
+ * parv[0] - sender
+ * parv[1] - channel
+ */
+int m_mode (cptr, sptr, parc, parv)
+     aClient *cptr;
+     aClient *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    Link *lp;
+
+    static char tmp[MODEBUFLEN];
+    int badop, sendts, listbans;
+    aChannel *chptr;
+
+/* Not needed re change to parse.c
+   if (check_registered(sptr))
+   return 0;
+ */
+    /* Now, try to find the channel in question */
+    if (parc > 1) {
+       chptr = find_channel (parv[1], NullChn);
+       if (chptr == NullChn)
+           return m_umode (cptr, sptr, parc, parv);
+    }
+    else {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "MODE");
+       return 0;
+    }
+
+    sptr->flags &= ~FLAGS_TS8;
+
+    if (MyConnect (sptr))
+       clean_channelname (parv[1]);
+    if (check_channelmask (sptr, cptr, parv[1]))
+       return 0;
+
+    if (parc < 3) {
+       *modebuf = *parabuf = '\0';
+       modebuf[1] = '\0';
+       channel_modes (sptr, modebuf, parabuf, chptr);
+       sendto_one (sptr, rpl_str (RPL_CHANNELMODEIS), me.name, parv[0],
+                   chptr->chname, modebuf, parabuf);
+       sendto_one (sptr, rpl_str (RPL_CREATIONTIME), me.name, parv[0],
+                   chptr->chname, chptr->creationtime);
+       return 0;
+    }
+    /* Some +v patches (Yes, this is not the right place...so what? }:) -GZ */
+
+    if (parc > 3) {
+
+       if (!strncmp (parv[2], "-v", 2)) {
+           if ((acptr = find_person (parv[3], NULL))) {
+               if (sptr == acptr) {
+                   lp = find_user_link (chptr->members, acptr);
+                   if (lp) {
+                       if (lp->flags & CHFL_VOICE) {
+                           lp->flags &= ~CHFL_VOICE;
+                           sendto_channel_butserv (chptr, cptr,
+                                                   ":%s MODE %s -v %s",
+                                                   parv[0], chptr->chname,
+                                                   acptr->name);
+                           sendto_match_servs (chptr, cptr,
+                                               ":%s MODE %s -v %s", parv[0],
+                                               chptr->chname, acptr->name);
+                           return 0;
+                       }
+                   }
+               }
+           }
+       }
+    }
+    /* is-not-a-channel-operator-bugfix. 
+     * and prevent non-opers from seeing bans from the outside.
+     */
+
+    listbans = 0;
+
+    if ((!(strncmp (parv[2], "+b", 2))) && (parc == 3)
+       && (IsMember (sptr, chptr) || IsAnOper (sptr)))
+       listbans = 1;
+
+    if ((!(strncmp (parv[2], "b", 1))) && (parc == 3)
+       && (IsMember (sptr, chptr) || IsAnOper (sptr)))
+       listbans = 1;
+
+    if ((!(strncmp (parv[2], "+e", 2))) && (parc == 3)
+       && (IsMember (sptr, chptr) || IsAnOper (sptr)))
+       listbans = 1;
+
+    if ((!(strncmp (parv[2], "e", 1))) && (parc == 3)
+       && (IsMember (sptr, chptr) || IsAnOper (sptr)))
+       listbans = 1;
+
+    if ((!(strncmp (parv[2], "+H", 2))) && (parc == 3)
+       && (IsMember (sptr, chptr) || IsAnOper (sptr)))
+       listbans = 1;
+
+    if ((!(strncmp (parv[2], "H", 1))) && (parc == 3)
+       && (IsMember (sptr, chptr) || IsAnOper (sptr)))
+       listbans = 1;
+
+    if (!IsServer (cptr) && !IsULine (cptr, sptr) && !is_chan_op (sptr, chptr)
+       && (listbans == 0)) {
+       sendto_one (sptr, err_str (ERR_CHANOPRIVSNEEDED),
+                   me.name, parv[0], chptr->chname);
+       return 0;
+    }
+    if (!(sendts = set_mode (cptr, sptr, chptr, parc - 2, parv + 2,
+                            modebuf, parabuf, &badop, 0))
+       && !IsULine (cptr, sptr)) {
+       sendto_one (sptr, err_str (ERR_CHANOPRIVSNEEDED),
+                   me.name, parv[0], chptr->chname);
+       return 0;
+    }
+    if ((badop >= 2) && !IsULine (cptr, sptr)) {
+       int i = 3;
+       *tmp = '\0';
+       while (i < parc) {
+           strcat (tmp, " ");
+           strcat (tmp, parv[i++]);
+       }
+    }
+    if (strlen (modebuf) > (size_t) 1 || sendts > 0) {
+       if (badop != 2 && strlen (modebuf) > (size_t) 1)
+           sendto_channel_butserv (chptr, sptr, ":%s MODE %s %s %s",
+                                   parv[0], chptr->chname, modebuf, parabuf);
+       /* We send a creationtime of 0, to mark it as a hack --Run */
+       if ((IsServer (sptr) && (badop == 2 || sendts > 0)) ||
+           IsULine (cptr, sptr)) {
+           if (*modebuf == '\0')
+               strcpy (modebuf, "+");
+           if (badop == 2)
+               badop = 2;
+           else
+               sendto_match_servs (chptr, cptr, ":%s MODE %s %s %s %lu",
+                                   parv[0], chptr->chname, modebuf, parabuf,
+                                   (badop ==
+                                    4) ? (time_t) 0 : chptr->creationtime);
+       }
+       else
+           sendto_match_servs (chptr, cptr, ":%s MODE %s %s %s",
+                               parv[0], chptr->chname, modebuf, parabuf);
+    }
+    return 0;
+}
+
+int DoesOp (modebuf)
+     char *modebuf;
+{
+    modebuf--;                   /* Is it possible that a mode starts with o and not +o ? */
+    while (*++modebuf)
+       if (*modebuf == 'o' || *modebuf == 'v')
+           return (1);
+    return 0;
+}
+
+int sendmodeto_one (cptr, from, name, mode, param, creationtime)
+     aClient *cptr;
+     char *from, *name, *mode, *param;
+     time_t creationtime;
+{
+    if ((IsServer (cptr) && DoesOp (mode) && creationtime) ||
+       IsULine (cptr, cptr))
+       sendto_one (cptr, ":%s %s %s %s %s %lu", from,
+                   (IsToken (cptr) ? TOK_MODE : MSG_MODE), name, mode, param,
+                   creationtime);
+    else
+       sendto_one (cptr, ":%s %s %s %s %s", from,
+                   (IsToken (cptr) ? TOK_MODE : MSG_MODE), name, mode,
+                   param);
+    return 0;
+}
+
+char *pretty_mask (mask)
+     char *mask;
+{
+    char *cp;
+    char *user;
+    char *host;
+
+    if ((user = index ((cp = mask), '!')))
+       *user++ = '\0';
+    if ((host = rindex (user ? user : cp, '@'))) {
+       *host++ = '\0';
+       if (!user)
+           return make_nick_user_host (NULL, cp, host);
+    }
+    else if (!user && index (cp, '.'))
+       return make_nick_user_host (NULL, NULL, cp);
+    return make_nick_user_host (cp, user, host);
+}
+
+/*
+ * Check and try to apply the channel modes passed in the parv array for
+ * the client ccptr to channel chptr.  The resultant changes are printed
+ * into mbuf and pbuf (if any) and applied to the channel.
+ */
+static int set_mode (cptr, sptr, chptr, parc, parv, mbuf, pbuf, badop, sadmin)
+     aClient *cptr, *sptr;
+     aChannel *chptr;
+     int parc, *badop, sadmin;
+     char *parv[], *mbuf, *pbuf;
+{
+    static Link chops[MAXMODEPARAMS];
+    static int flags[] = {
+       MODE_SECRET, 's',
+       MODE_MODERATED, 'm', MODE_NOPRIVMSGS, 'n',
+       MODE_TOPICLIMIT, 't', MODE_INVITEONLY, 'i',
+       MODE_VOICE, 'v', MODE_KEY, 'k',
+       MODE_RGSTR, 'r', MODE_RGSTRONLY, 'R',
+       MODE_NOCOLORS, 'c',
+       0x0, 0x0
+    };
+
+    Link *lp;
+    Ban *ban;
+    Ban *exban;
+    Ban *hrstrct;
+    char *curr = parv[0], *cp = NULL;
+    int *ip;
+    Link *member, *tmp = NULL;
+    u_int whatt = MODE_ADD, bwhatt = 0;
+    int limitset = 0, chasing = 0, bounce;
+    int nusers = 0, new, len, blen, keychange = 0, opcnt = 0, banlsent = 0;
+    int exbanlsent = 0, hostrestrictlsent = 0;
+    int doesdeop = 0, doesop = 0, hacknotice = 0, change, gotts = 0;
+    aClient *who;
+    Mode *mode, oldm;
+    static char bmodebuf[MODEBUFLEN], bparambuf[MODEBUFLEN], numeric[16];
+    char *bmbuf = bmodebuf, *bpbuf = bparambuf;
+    time_t newtime = (time_t) 0;
+    aConfItem *aconf;
+
+    *mbuf = *pbuf = *bmbuf = *bpbuf = '\0';
+    *badop = 0;
+    if (parc < 1)
+       return 0;
+    mode = &(chptr->mode);
+    bcopy ((char *) mode, (char *) &oldm, sizeof (Mode));
+    /* Mode is accepted when sptr is a channel operator
+       * but also when the mode is received from a server. --Run
+     */
+    new = mode->mode;
+
+    while (curr && *curr) {
+       switch (*curr) {
+       case '+':
+           whatt = MODE_ADD;
+           break;
+       case '-':
+           whatt = MODE_DEL;
+           break;
+       case 'o':
+       case 'v':
+           if (--parc <= 0)
+               break;
+           parv++;
+           *parv = check_string (*parv);
+           if (MyClient (sptr) && opcnt >= MODEPARAMS)
+               break;
+           if (opcnt >= MAXMODEPARAMS)
+               break;
+           /*
+            * Check for nickname changes and try to follow these
+            * to make sure the right client is affected by the
+            * mode change.
+            */
+           if (!(who = find_chasing (sptr, parv[0], &chasing)))
+               break;
+           if (!(member = find_user_link (chptr->members, who))) {
+               sendto_one (cptr, err_str (ERR_USERNOTINCHANNEL),
+                           me.name, cptr->name, parv[0], chptr->chname);
+               break;
+           }
+           if (whatt == MODE_ADD) {
+               lp = &chops[opcnt++];
+               lp->value.cptr = who;
+               if ((IsServer (sptr) &&
+                    (!(who->flags & FLAGS_TS8) ||
+                     ((*curr == 'o') && !(member->flags &
+                                          (CHFL_SERVOPOK | CHFL_CHANOP))) ||
+                     who->from != sptr->from)) ||
+                   IsULine (cptr, sptr) || sadmin == 1 ||
+                   (!MyClient (sptr) && IsSAdmin (sptr)))
+                   *badop = ((member->flags & CHFL_DEOPPED)
+                             && (*curr == 'o')) ? 2 : 3;
+               lp->flags = (*curr == 'o') ? MODE_CHANOP : MODE_VOICE;
+               lp->flags |= MODE_ADD;
+           }
+           else if (whatt == MODE_DEL) {
+               lp = &chops[opcnt++];
+               lp->value.cptr = who;
+               doesdeop = 1;     /* Also when -v */
+               lp->flags = (*curr == 'o') ? MODE_CHANOP : MODE_VOICE;
+               lp->flags |= MODE_DEL;
+           }
+           if (*curr == 'o')
+               doesop = 1;
+           break;
+       case 'k':
+           if (--parc <= 0)
+               break;
+           parv++;
+           /* check now so we eat the parameter if present */
+           if (keychange)
+               break;
+           *parv = check_string (*parv);
+           {
+               u_char *s1, *s2;
+
+               for (s1 = s2 = (u_char *) * parv; *s2; s2++)
+                   if ((*s1 = *s2 & 0x7f) > (u_char) 32 && *s1 != ':')
+                       s1++;
+               *s1 = '\0';
+           }
+           if (MyClient (sptr) && opcnt >= MODEPARAMS)
+               break;
+           if (opcnt >= MAXMODEPARAMS)
+               break;
+
+           if (whatt == MODE_ADD) {
+               if (*mode->key && !IsServer (cptr) &&
+                   !IsULine (cptr, sptr) && sadmin == 0 && !IsSAdmin (sptr))
+                   sendto_one (cptr, err_str (ERR_KEYSET),
+                               me.name, cptr->name, chptr->chname);
+               else if (!*mode->key || IsServer (cptr) ||
+                        IsULine (cptr, sptr) || sadmin == 1
+                        || (!MyClient (sptr) && IsSAdmin (sptr))) {
+                   lp = &chops[opcnt++];
+                   lp->value.cp = *parv;
+                   if (strlen (lp->value.cp) > (size_t) KEYLEN)
+                       lp->value.cp[KEYLEN] = '\0';
+                   lp->flags = MODE_KEY | MODE_ADD;
+                   keychange = 1;
+               }
+           }
+           else if (whatt == MODE_DEL) {
+               if (mycmp (mode->key, *parv) == 0 ||
+                   IsServer (cptr) || IsULine (cptr, sptr) ||
+                   sadmin == 1 || (!MyClient (sptr) && IsSAdmin (sptr))) {
+                   lp = &chops[opcnt++];
+                   lp->value.cp = mode->key;
+                   lp->flags = MODE_KEY | MODE_DEL;
+                   keychange = 1;
+               }
+           }
+           break;
+       case 'b':
+           if ((IsRegisteredUser (cptr)) && (!IsMember (cptr, chptr))
+               && (!IsOper (cptr))) {
+               sendto_one (cptr, err_str (ERR_NOTONCHANNEL), me.name,
+                           parv[0], chptr->chname);
+               break;
+           }
+           if (--parc <= 0) {
+               if (banlsent)     /* Only send it once */
+                   break;
+               for (ban = chptr->banlist; ban; ban = ban->next)
+                   sendto_one (cptr, rpl_str (RPL_BANLIST),
+                               me.name, cptr->name,
+                               chptr->chname, ban->banstr, ban->who,
+                               ban->when);
+               sendto_one (cptr, rpl_str (RPL_ENDOFBANLIST), me.name,
+                           cptr->name, chptr->chname);
+               banlsent = 1;
+               break;
+           }
+
+           parv++;
+           if (BadPtr (*parv))
+               break;
+           if (MyClient (sptr) && opcnt >= MODEPARAMS)
+               break;
+           if (opcnt >= MAXMODEPARAMS)
+               break;
+           /* check for malignantly formed ban */
+           if (strchr (*parv, ':'))
+               break;
+           if (whatt == MODE_ADD) {
+               lp = &chops[opcnt++];
+               lp->value.cp = *parv;
+               lp->flags = MODE_ADD | MODE_BAN;
+           }
+           else if (whatt == MODE_DEL) {
+               lp = &chops[opcnt++];
+               lp->value.cp = *parv;
+               lp->flags = MODE_DEL | MODE_BAN;
+           }
+           break;
+       case 'e':
+           if ((IsRegisteredUser (cptr)) && (!IsMember (cptr, chptr))
+               && (!IsOper (cptr))) {
+               sendto_one (cptr, err_str (ERR_NOTONCHANNEL), me.name,
+                           parv[0], chptr->chname);
+               break;
+           }
+           if (--parc <= 0) {
+               if (exbanlsent)   /* Only send it once */
+                   break;
+               for (exban = chptr->exbanlist; exban; exban = exban->next)
+                   sendto_one (cptr, rpl_str (RPL_EXBANLIST),
+                               me.name, cptr->name,
+                               chptr->chname, exban->banstr, exban->who,
+                               exban->when);
+               sendto_one (cptr, rpl_str (RPL_EXBANLISTEND), me.name,
+                           cptr->name, chptr->chname);
+               exbanlsent = 1;
+               break;
+           }
+           parv++;
+           if (BadPtr (*parv))
+               break;
+           if (MyClient (sptr) && opcnt >= MODEPARAMS)
+               break;
+           if (opcnt >= MAXMODEPARAMS)
+               break;
+           /* check for malignantly formed exban */
+           if (strchr (*parv, ':'))
+               break;
+           if (whatt == MODE_ADD) {
+               lp = &chops[opcnt++];
+               lp->value.cp = *parv;
+               lp->flags = MODE_ADD | MODE_EXBAN;
+           }
+           else if (whatt == MODE_DEL) {
+               lp = &chops[opcnt++];
+               lp->value.cp = *parv;
+               lp->flags = MODE_DEL | MODE_EXBAN;
+           }
+           break;
+       case 'H':
+           if ((IsRegisteredUser (cptr)) && (!IsMember (cptr, chptr))
+               && (!IsOper (cptr))) {
+               sendto_one (cptr, err_str (ERR_NOTONCHANNEL), me.name,
+                           parv[0], chptr->chname);
+               break;
+           }
+           if (--parc <= 0) {
+               if (hostrestrictlsent)    /* Only send it once */
+                   break;
+               for (hrstrct = chptr->hostrestrictlist; hrstrct; hrstrct = hrstrct->next)
+                   sendto_one (cptr, rpl_str (RPL_HOSTRESTRICTLIST),
+                               me.name, cptr->name,
+                               chptr->chname, hrstrct->banstr, hrstrct->who,
+                               hrstrct->when);
+               sendto_one (cptr, rpl_str (RPL_HOSTRESTRICTLISTEND), me.name,
+                           cptr->name, chptr->chname);
+               hostrestrictlsent = 1;
+               break;
+           }
+           parv++;
+           if (BadPtr (*parv))
+               break;
+           if (MyClient (sptr) && opcnt >= MODEPARAMS)
+               break;
+           if (opcnt >= MAXMODEPARAMS)
+               break;
+           /* check for malignantly formed exban */
+           if (strchr (*parv, ':'))
+               break;
+           if (whatt == MODE_ADD) {
+               lp = &chops[opcnt++];
+               lp->value.cp = *parv;
+               lp->flags = MODE_ADD | MODE_HOSTRESTRICT;
+           }
+           else if (whatt == MODE_DEL) {
+               lp = &chops[opcnt++];
+               lp->value.cp = *parv;
+               lp->flags = MODE_DEL | MODE_HOSTRESTRICT;
+           }
+           break;
+       case 'l':
+           /*
+            * limit 'l' to only *1* change per mode command but
+            * eat up others.
+            */
+           if (limitset) {
+               if (whatt == MODE_ADD && --parc > 0)
+                   parv++;
+               break;
+           }
+           if (whatt == MODE_DEL) {
+               limitset = 1;
+               nusers = 0;
+               break;
+           }
+           if (--parc > 0) {
+               if (BadPtr (*parv))
+                   break;
+               if (MyClient (sptr) && opcnt >= MODEPARAMS)
+                   break;
+               if (opcnt >= MAXMODEPARAMS)
+                   break;
+               if (!(nusers = atoi (*++parv)))
+                   continue;
+               lp = &chops[opcnt++];
+               lp->flags = MODE_ADD | MODE_LIMIT;
+               limitset = 1;
+               break;
+           }
+           sendto_one (cptr, err_str (ERR_NEEDMOREPARAMS),
+                       me.name, cptr->name, "MODE +l");
+           break;
+       case 'r':
+           if (IsServer (cptr)) {
+               if (whatt == MODE_DEL)
+                   new &= ~MODE_RGSTR;
+               else
+                   new |= MODE_RGSTR;
+           }
+           else
+               sendto_one (cptr, err_str (ERR_ONLYSERVERSCANCHANGE),
+                           me.name, cptr->name, chptr->chname);
+           break;
+       case 'i':                 /* falls through for default case */
+           if (whatt == MODE_DEL)
+               while ((lp = chptr->invites))
+                   del_invite (lp->value.cptr, chptr);
+       default:
+           for (ip = flags; *ip; ip += 2)
+               if (*(ip + 1) == *curr)
+                   break;
+
+           if (*ip) {
+               if (whatt == MODE_ADD)
+                   new |= *ip;
+               else
+                   new &= ~*ip;
+           }
+           else if (!IsServer (cptr) && !IsULine (cptr, sptr))
+               sendto_one (cptr, err_str (ERR_UNKNOWNMODE),
+                           me.name, cptr->name, *curr);
+           break;
+       }
+       curr++;
+       /*
+        * Make sure mode strings such as "+m +t +i" are parsed
+        * fully.
+        */
+       if (!*curr && parc > 0) {
+           curr = *++parv;
+           parc--;
+           /* If this was from a server, and it is the last
+            * parameter and it starts with a digit, it must
+            * be the creationtime.  --Run
+            */
+           if (IsServer (sptr) || IsULine (cptr, sptr)) {
+               if (parc == 1 && isdigit (*curr)) {
+                   newtime = atoi (curr);
+                   gotts = 1;
+                   if (newtime == 0) {
+                       *badop = 2;
+                       hacknotice = 1;
+                   }
+                   else if (newtime > chptr->creationtime) {   /* It is a net-break ride if we have ops.
+                                                                  * bounce modes if we have ops.
+                                                                  * --Run
+                                                                */
+                       if (doesdeop)
+                           *badop = 2;
+                       else if (chptr->creationtime == 0
+                                || !have_ops (chptr)) {
+                           if (chptr->creationtime && doesop);
+/*                    sendto_ops("NET.RIDE on opless %s from %s",
+   chptr->chname,sptr->name); */
+                           if (chptr->creationtime == 0 || doesop)
+                               chptr->creationtime = newtime;
+                           *badop = 0;
+                       }
+                       /* Bounce: */
+                       else
+                           *badop = 1;
+                   }
+                   else if (doesdeop && newtime < chptr->creationtime)
+                       *badop = 2;
+                   /* A legal *badop can occur when two
+                    * people join simultaneously a channel,
+                    * Allow for 10 min of lag (and thus hacking
+                    * on channels younger then 10 min) --Run
+                    */
+                   else if (*badop == 0 ||
+                            chptr->creationtime >
+                            (time (NULL) - (time_t) 600)) {
+                       if (doesop || !have_ops (chptr))
+                           chptr->creationtime = newtime;
+                       *badop = 0;
+                   }
+                   break;
+               }
+           }
+           else
+               *badop = 0;
+       }
+    }                            /* end of while loop for MODE processing */
+
+    if (doesop && newtime == 0 && (IsServer (sptr) || IsULine (cptr, sptr)
+                                  || sadmin == 1 || (!MyClient (sptr)
+                                                     && IsSAdmin (sptr))))
+       *badop = 2;
+
+    if (*badop >= 2 &&
+       (aconf = find_conf_host (cptr->confs, sptr->name, CONF_UWORLD)))
+       *badop = 4;
+    if (*badop >= 2 && (IsULine (cptr, sptr) || sadmin == 1 ||
+                       (!MyClient (sptr) && IsSAdmin (sptr))))
+       *badop = 4;
+
+    /* Fixes channel hacking.  Problem before was that it didn't bounce
+       unless user was deopped by server.  Now modes from *all*
+       non-chanops are bounced.  -- Barubary */
+    if (!IsServer (sptr) && !IsULine (cptr, sptr)
+       && !is_chan_op (sptr, chptr))
+       bounce = 1;
+    else
+       bounce = (*badop == 1 || *badop == 2) ? 1 : 0;
+    if (IsULine (cptr, sptr) || sadmin == 1
+       || (!MyClient (sptr) && IsSAdmin (sptr)))
+       bounce = 0;
+
+    whatt = 0;
+    for (ip = flags; *ip; ip += 2)
+       if ((*ip & new) && !(*ip & oldm.mode)) {
+           if (bounce) {
+               if (bwhatt != MODE_DEL) {
+                   *bmbuf++ = '-';
+                   bwhatt = MODE_DEL;
+               }
+               *bmbuf++ = *(ip + 1);
+           }
+           else {
+               if (whatt != MODE_ADD) {
+                   *mbuf++ = '+';
+                   whatt = MODE_ADD;
+               }
+               mode->mode |= *ip;
+               *mbuf++ = *(ip + 1);
+           }
+       }
+    for (ip = flags; *ip; ip += 2)
+       if ((*ip & oldm.mode) && !(*ip & new)) {
+           if (bounce) {
+               if (bwhatt != MODE_ADD) {
+                   *bmbuf++ = '+';
+                   bwhatt = MODE_ADD;
+               }
+               *bmbuf++ = *(ip + 1);
+           }
+           else {
+               if (whatt != MODE_DEL) {
+                   *mbuf++ = '-';
+                   whatt = MODE_DEL;
+               }
+               mode->mode &= ~*ip;
+               *mbuf++ = *(ip + 1);
+           }
+       }
+    blen = 0;
+    if (limitset && !nusers && mode->limit) {
+       if (bounce) {
+           if (bwhatt != MODE_ADD) {
+               *bmbuf++ = '+';
+               bwhatt = MODE_ADD;
+           }
+           *bmbuf++ = 'l';
+           (void) sprintf (numeric, "%-15d", mode->limit);
+           if ((cp = index (numeric, ' ')))
+               *cp = '\0';
+           (void) strcat (bpbuf, numeric);
+           blen += strlen (numeric);
+           (void) strcat (bpbuf, " ");
+       }
+       else {
+           if (whatt != MODE_DEL) {
+               *mbuf++ = '-';
+               whatt = MODE_DEL;
+           }
+           mode->mode &= ~MODE_LIMIT;
+           mode->limit = 0;
+           *mbuf++ = 'l';
+       }
+    }
+    /*
+     * Reconstruct "+bkov" chain.
+     */
+    if (opcnt) {
+       int i = 0;
+       char c = 0;
+       u_int prev_whatt = 0;
+
+       for (; i < opcnt; i++) {
+           lp = &chops[i];
+           /*
+            * make sure we have correct mode change sign
+            */
+           if (whatt != (lp->flags & (MODE_ADD | MODE_DEL))) {
+               if (lp->flags & MODE_ADD) {
+                   *mbuf++ = '+';
+                   prev_whatt = whatt;
+                   whatt = MODE_ADD;
+               }
+               else {
+                   *mbuf++ = '-';
+                   prev_whatt = whatt;
+                   whatt = MODE_DEL;
+               }
+           }
+           len = strlen (pbuf);
+           /*
+            * get c as the mode char and tmp as a pointer to
+            * the parameter for this mode change.
+            */
+           switch (lp->flags & MODE_WPARAS) {
+           case MODE_CHANOP:
+               c = 'o';
+               cp = lp->value.cptr->name;
+               break;
+           case MODE_VOICE:
+               c = 'v';
+               cp = lp->value.cptr->name;
+               break;
+           case MODE_BAN:
+               /* I made this a bit more user-friendly (tm):
+                  * nick = nick!*@*
+                  * nick!user = nick!user@*
+                  * user@host = *!user@host
+                  * host.name = *!*@host.name    --Run
+                */
+               c = 'b';
+               cp = pretty_mask (lp->value.cp);
+               break;
+           case MODE_EXBAN:
+               c = 'e';
+               cp = pretty_mask (lp->value.cp);
+               break;
+           case MODE_HOSTRESTRICT:
+               c = 'H';
+               cp = pretty_mask (lp->value.cp);
+               break;
+           case MODE_KEY:
+               c = 'k';
+               cp = lp->value.cp;
+               break;
+           case MODE_LIMIT:
+               c = 'l';
+               (void) sprintf (numeric, "%-15d", nusers);
+               if ((cp = index (numeric, ' ')))
+                   *cp = '\0';
+               cp = numeric;
+               break;
+           }
+
+           if (len + strlen (cp) + 12 > (size_t) MODEBUFLEN)
+               break;
+           switch (lp->flags & MODE_WPARAS) {
+           case MODE_KEY:
+               if (strlen (cp) > (size_t) KEYLEN)
+                   *(cp + KEYLEN) = '\0';
+               if ((whatt == MODE_ADD && (*mode->key == '\0' ||
+                                          mycmp (mode->key, cp) != 0)) ||
+                   (whatt == MODE_DEL && (*mode->key != '\0'))) {
+                   if (bounce) {
+                       if (*mode->key == '\0') {
+                           if (bwhatt != MODE_DEL) {
+                               *bmbuf++ = '-';
+                               bwhatt = MODE_DEL;
+                           }
+                           (void) strcat (bpbuf, cp);
+                           blen += strlen (cp);
+                           (void) strcat (bpbuf, " ");
+                           blen++;
+                       }
+                       else {
+                           if (bwhatt != MODE_ADD) {
+                               *bmbuf++ = '+';
+                               bwhatt = MODE_ADD;
+                           }
+                           (void) strcat (bpbuf, mode->key);
+                           blen += strlen (mode->key);
+                           (void) strcat (bpbuf, " ");
+                           blen++;
+                       }
+                       *bmbuf++ = c;
+                       mbuf--;
+                       if (*mbuf != '+' && *mbuf != '-')
+                           mbuf++;
+                       else
+                           whatt = prev_whatt;
+                   }
+                   else {
+                       *mbuf++ = c;
+                       (void) strcat (pbuf, cp);
+                       len += strlen (cp);
+                       (void) strcat (pbuf, " ");
+                       len++;
+                       if (whatt == MODE_ADD)
+                           strncpyzt (mode->key, cp, sizeof (mode->key));
+                       else
+                           *mode->key = '\0';
+                   }
+               }
+               break;
+           case MODE_LIMIT:
+               if (nusers && nusers != mode->limit) {
+                   if (bounce) {
+                       if (mode->limit == 0) {
+                           if (bwhatt != MODE_DEL) {
+                               *bmbuf++ = '-';
+                               bwhatt = MODE_DEL;
+                           }
+                       }
+                       else {
+                           if (bwhatt != MODE_ADD) {
+                               *bmbuf++ = '+';
+                               bwhatt = MODE_ADD;
+                           }
+                           (void) sprintf (numeric, "%-15d", mode->limit);
+                           if ((cp = index (numeric, ' ')))
+                               *cp = '\0';
+                           (void) strcat (bpbuf, numeric);
+                           blen += strlen (numeric);
+                           (void) strcat (bpbuf, " ");
+                           blen++;
+                       }
+                       *bmbuf++ = c;
+                       mbuf--;
+                       if (*mbuf != '+' && *mbuf != '-')
+                           mbuf++;
+                       else
+                           whatt = prev_whatt;
+                   }
+                   else {
+                       *mbuf++ = c;
+                       (void) strcat (pbuf, cp);
+                       len += strlen (cp);
+                       (void) strcat (pbuf, " ");
+                       len++;
+                       mode->limit = nusers;
+                   }
+               }
+               break;
+           case MODE_CHANOP:
+           case MODE_VOICE:
+               tmp = find_user_link (chptr->members, lp->value.cptr);
+               if (lp->flags & MODE_ADD) {
+                   change = (~tmp->flags) & CHFL_OVERLAP & lp->flags;
+                   if (change && bounce) {
+                       if (lp->flags & MODE_CHANOP)
+                           tmp->flags |= CHFL_DEOPPED;
+                       if (bwhatt != MODE_DEL) {
+                           *bmbuf++ = '-';
+                           bwhatt = MODE_DEL;
+                       }
+                       *bmbuf++ = c;
+                       (void) strcat (bpbuf, lp->value.cptr->name);
+                       blen += strlen (lp->value.cptr->name);
+                       (void) strcat (bpbuf, " ");
+                       blen++;
+                       change = 0;
+                   }
+                   else if (change) {
+                       tmp->flags |= lp->flags & CHFL_OVERLAP;
+                       if (lp->flags & MODE_CHANOP) {
+                           tmp->flags &= ~CHFL_DEOPPED;
+                           if (IsServer (sptr) || IsULine (cptr, sptr))
+                               tmp->flags &= ~CHFL_SERVOPOK;
+                       }
+                   }
+               }
+               else {
+                   change = tmp->flags & CHFL_OVERLAP & lp->flags;
+                   if (change && bounce) {
+                       if (lp->flags & MODE_CHANOP)
+                           tmp->flags &= ~CHFL_DEOPPED;
+                       if (bwhatt != MODE_ADD) {
+                           *bmbuf++ = '+';
+                           bwhatt = MODE_ADD;
+                       }
+                       *bmbuf++ = c;
+                       (void) strcat (bpbuf, lp->value.cptr->name);
+                       blen += strlen (lp->value.cptr->name);
+                       (void) strcat (bpbuf, " ");
+                       blen++;
+                       change = 0;
+                   }
+                   else {
+                       tmp->flags &= ~change;
+                       if ((change & MODE_CHANOP) &&
+                           (IsServer (sptr) || IsULine (cptr, sptr)
+                            || sadmin == 1 || (!MyClient (sptr)
+                                               && IsSAdmin (sptr))))
+                           tmp->flags |= CHFL_DEOPPED;
+                   }
+               }
+               if (change || *badop == 2 || *badop == 4) {
+                   *mbuf++ = c;
+                   (void) strcat (pbuf, cp);
+                   len += strlen (cp);
+                   (void) strcat (pbuf, " ");
+                   len++;
+               }
+               else {
+                   mbuf--;
+                   if (*mbuf != '+' && *mbuf != '-')
+                       mbuf++;
+                   else
+                       whatt = prev_whatt;
+               }
+               break;
+           case MODE_BAN:
+/* Only bans aren't bounced, it makes no sense to bounce last second
+ * bans while propagating bans done before the net.rejoin. The reason
+ * why I don't bounce net.rejoin bans is because it is too much
+ * work to take care of too long strings adding the necessary TS to
+ * net.burst bans -- RunLazy
+ * We do have to check for *badop==2 now, we don't want HACKs to take
+ * effect.
+ */
+/* Not anymore.  They're not bounced from servers/ulines, but they *are*
+   bounced if from a user without chanops. -- Barubary */
+               if (IsServer (sptr) || IsULine (cptr, sptr) || !bounce) {
+                   if (*badop != 2 &&
+                       (((whatt & MODE_ADD) &&
+                         !add_banid (sptr, chptr, cp)) ||
+                        ((whatt & MODE_DEL) && !del_banid (chptr, cp)))) {
+                       *mbuf++ = c;
+                       (void) strcat (pbuf, cp);
+                       len += strlen (cp);
+                       (void) strcat (pbuf, " ");
+                       len++;
+                   }
+               }
+               else {
+                   if (whatt & MODE_ADD) {
+                       if (!find_banid (chptr, cp)) {
+                           if (bwhatt != MODE_DEL)
+                               *bmbuf++ = '-';
+                           *bmbuf++ = c;
+                           strcat (bpbuf, cp);
+                           blen += strlen (cp);
+                           strcat (bpbuf, " ");
+                           blen++;
+                       }
+                   }
+                   else {
+                       if (find_banid (chptr, cp)) {
+                           if (bwhatt != MODE_ADD)
+                               *bmbuf++ = '+';
+                           *bmbuf++ = c;
+                           strcat (bpbuf, cp);
+                           blen += strlen (cp);
+                           strcat (bpbuf, " ");
+                           blen++;
+                       }
+                   }
+               }
+               break;
+           case MODE_EXBAN:
+               if (IsServer (sptr) || IsULine (cptr, sptr) || !bounce) {
+                   if (*badop != 2 &&
+                       (((whatt & MODE_ADD) &&
+                         !add_exbanid (sptr, chptr, cp)) ||
+                        ((whatt & MODE_DEL) && !del_exbanid (chptr, cp)))) {
+                       *mbuf++ = c;
+                       (void) strcat (pbuf, cp);
+                       len += strlen (cp);
+                       (void) strcat (pbuf, " ");
+                       len++;
+                   }
+               }
+               else {
+                   if (whatt & MODE_ADD) {
+                       if (!find_exbanid (chptr, cp)) {
+                           if (bwhatt != MODE_DEL)
+                               *bmbuf++ = '-';
+                           *bmbuf++ = c;
+                           strcat (bpbuf, cp);
+                           blen += strlen (cp);
+                           strcat (bpbuf, " ");
+                           blen++;
+                       }
+                   }
+                   else {
+                       if (find_exbanid (chptr, cp)) {
+                           if (bwhatt != MODE_ADD)
+                               *bmbuf++ = '+';
+                           *bmbuf++ = c;
+                           strcat (bpbuf, cp);
+                           blen += strlen (cp);
+                           strcat (bpbuf, " ");
+                           blen++;
+                       }
+                   }
+               }
+               break;
+           case MODE_HOSTRESTRICT:
+               if (IsServer (sptr) || IsULine (cptr, sptr) || !bounce) {
+                   if (*badop != 2 &&
+                       (((whatt & MODE_ADD) &&
+                         !add_hostrestrictid (sptr, chptr, cp)) ||
+                        ((whatt & MODE_DEL) && !del_hostrestrictid (chptr, cp)))) {
+                       *mbuf++ = c;
+                       (void) strcat (pbuf, cp);
+                       len += strlen (cp);
+                       (void) strcat (pbuf, " ");
+                       len++;
+                   }
+               }
+               else {
+                   if (whatt & MODE_ADD) {
+                       if (!find_hostrestrictid (chptr, cp)) {
+                           if (bwhatt != MODE_DEL)
+                               *bmbuf++ = '-';
+                           *bmbuf++ = c;
+                           strcat (bpbuf, cp);
+                           blen += strlen (cp);
+                           strcat (bpbuf, " ");
+                           blen++;
+                       }
+                   }
+                   else {
+                       if (find_hostrestrictid (chptr, cp)) {
+                           if (bwhatt != MODE_ADD)
+                               *bmbuf++ = '+';
+                           *bmbuf++ = c;
+                           strcat (bpbuf, cp);
+                           blen += strlen (cp);
+                           strcat (bpbuf, " ");
+                           blen++;
+                       }
+                   }
+               }
+               break;
+           }
+       }                         /* for (; i < opcnt; i++) */
+    }                            /* if (opcnt) */
+    *mbuf++ = '\0';
+    *bmbuf++ = '\0';
+
+
+/* Bounce here */
+    if (!hacknotice && *bmodebuf && chptr->creationtime)
+       sendto_one (cptr, ":%s %s %s %s %s %lu",
+                   me.name, (IsToken (cptr) ? TOK_MODE : MSG_MODE),
+                   chptr->chname, bmodebuf, bparambuf,
+                   *badop == 2 ? (time_t) 0 : chptr->creationtime);
+
+    return gotts ? 1 : -1;
+}
+
+/* Now let _invited_ people join thru bans, +i and +l.
+ * Checking if an invite exist could be done only if a block exists,
+ * but I'm not too fancy of the complicated structure that'd cause,
+ * when optimization will hopefully take care of it. Most of the time
+ * a user won't have invites on him anyway. -Donwulff
+ */
+
+static int can_join (sptr, chptr, key)
+     aClient *sptr;
+     aChannel *chptr;
+     char *key;
+{
+    Link *lp;
+
+    if (IsULine (sptr, sptr) || IsServer (sptr) || IsSRoot (sptr))
+       return 0;
+
+    if ((chptr->mode.mode & MODE_RGSTRONLY) && !IsIdentified (sptr))
+       return (ERR_NEEDREGGEDNICK);
+
+    if (*chptr->mode.key && (BadPtr (key) || mycmp (chptr->mode.key, key)))
+       return (ERR_BADCHANNELKEY);
+
+    for (lp = sptr->user->invited; lp; lp = lp->next)
+       if (lp->value.chptr == chptr)
+           break;
+
+    if ((chptr->mode.mode & MODE_INVITEONLY) && !lp)
+       return (ERR_INVITEONLYCHAN);
+
+    if ((chptr->mode.limit && chptr->users >= chptr->mode.limit) && !lp)
+       return (ERR_CHANNELISFULL);
+
+    if (is_host_restricted(sptr, chptr) && !IsIdentified (sptr))
+        return (ERR_NEEDREGGEDNICK);
+        
+    if (is_banned (sptr, chptr) && is_banexception (sptr, chptr) && !lp) {
+       return 0;
+    }
+    else if (is_banned (sptr, chptr) && !is_banexception (sptr, chptr) && !lp) {
+       return (ERR_BANNEDFROMCHAN);
+    }
+
+    return 0;
+}
+
+/*
+   ** Remove bells and commas from channel name
+ */
+
+void clean_channelname (cn)
+     char *cn;
+{
+    u_char *ch = (u_char *) cn;
+
+
+    for (; *ch; ch++)
+       /* Don't allow any control chars, the space, the comma,
+        * or the "non-breaking space" in channel names.
+        * Might later be changed to a system where the list of
+        * allowed/non-allowed chars for channels was a define
+        * or some such.
+        *   --Wizzu
+        */
+       if (*ch < 33 || *ch == ',' || *ch == 160) {
+           *ch = '\0';
+           return;
+       }
+}
+
+/*
+   ** Return -1 if mask is present and doesnt match our server name.
+ */
+static int check_channelmask (sptr, cptr, chname)
+     aClient *sptr, *cptr;
+     char *chname;
+{
+    char *s;
+
+    if (*chname == '&' && IsServer (cptr))
+       return -1;
+    s = rindex (chname, ':');
+    if (!s)
+       return 0;
+
+    s++;
+    if (match (s, me.name) || (IsServer (cptr) && match (s, cptr->name))) {
+       if (MyClient (sptr))
+           sendto_one (sptr, err_str (ERR_BADCHANMASK),
+                       me.name, sptr->name, chname);
+       return -1;
+    }
+    return 0;
+}
+
+/*
+   **  Get Channel block for i (and allocate a new channel
+   **  block, if it didn't exists before).
+ */
+static aChannel *get_channel (cptr, chname, flag)
+     aClient *cptr;
+     char *chname;
+     int flag;
+{
+    aChannel *chptr;
+    int len;
+
+    if (BadPtr (chname))
+       return NULL;
+
+    len = strlen (chname);
+    if (MyClient (cptr) && len > CHANNELLEN) {
+       len = CHANNELLEN;
+       *(chname + CHANNELLEN) = '\0';
+    }
+    if ((chptr = find_channel (chname, (aChannel *) NULL)))
+       return (chptr);
+    if (flag == CREATE) {
+       chptr = (aChannel *) MyMalloc (sizeof (aChannel) + len);
+       bzero ((char *) chptr, sizeof (aChannel));
+       strncpyzt (chptr->chname, chname, len + 1);
+       if (channel)
+           channel->prevch = chptr;
+       chptr->prevch = NULL;
+       chptr->nextch = channel;
+       chptr->creationtime = MyClient (cptr) ? time (NULL) : (time_t) 0;
+       channel = chptr;
+       (void) add_to_channel_hash_table (chname, chptr);
+    }
+    return chptr;
+}
+
+/*
+ * Slight changes in routine, now working somewhat symmetrical:
+ *   First try to remove the client & channel pair to avoid duplicates
+ *   Second check client & channel invite-list lengths and remove tail
+ *   Finally add new invite-links to both client and channel
+ * Should U-lined clients have higher limits?   -Donwulff
+ */
+
+static void add_invite (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+    Link *inv, *tmp;
+
+    del_invite (cptr, chptr);
+    /*
+     * delete last link in chain if the list is max length
+     */
+    if (list_length (cptr->user->invited) >= MAXCHANNELSPERUSER) {
+       for (tmp = cptr->user->invited; tmp->next; tmp = tmp->next);
+       del_invite (cptr, tmp->value.chptr);
+
+    }
+    /* We get pissy over too many invites per channel as well now,
+     * since otherwise mass-inviters could take up some major
+     * resources -Donwulff
+     */
+    if (list_length (chptr->invites) >= MAXCHANNELSPERUSER) {
+       for (tmp = chptr->invites; tmp->next; tmp = tmp->next);
+       del_invite (tmp->value.cptr, chptr);
+    }
+    /*
+     * add client to the beginning of the channel invite list
+     */
+    inv = make_link ();
+    inv->value.cptr = cptr;
+    inv->next = chptr->invites;
+    chptr->invites = inv;
+    /*
+     * add channel to the beginning of the client invite list
+     */
+    inv = make_link ();
+    inv->value.chptr = chptr;
+    inv->next = cptr->user->invited;
+    cptr->user->invited = inv;
+}
+
+/*
+ * Delete Invite block from channel invite list and client invite list
+ */
+void del_invite (cptr, chptr)
+     aClient *cptr;
+     aChannel *chptr;
+{
+    Link **inv, *tmp;
+
+    for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
+       if (tmp->value.cptr == cptr) {
+           *inv = tmp->next;
+           free_link (tmp);
+           break;
+       }
+    for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
+       if (tmp->value.chptr == chptr) {
+           *inv = tmp->next;
+           free_link (tmp);
+           break;
+       }
+}
+
+/*
+   **  Subtract one user from channel i (and free channel
+   **  block, if channel became empty).
+ */
+static void sub1_from_channel (chptr)
+     aChannel *chptr;
+{
+    Ban *ban;
+    Link *lp;
+
+    if (--chptr->users <= 0) {
+       /*
+        * Now, find all invite links from channel structure
+        */
+       while ((lp = chptr->invites))
+           del_invite (lp->value.cptr, chptr);
+
+       while (chptr->banlist) {
+           ban = chptr->banlist;
+           chptr->banlist = ban->next;
+           MyFree (ban->banstr);
+           MyFree (ban->who);
+           free_ban (ban);
+       }
+       if (chptr->prevch)
+           chptr->prevch->nextch = chptr->nextch;
+       else
+           channel = chptr->nextch;
+       if (chptr->nextch)
+           chptr->nextch->prevch = chptr->prevch;
+       (void) del_from_channel_hash_table (chptr->chname, chptr);
+       MyFree ((char *) chptr);
+    }
+}
+
+/* mode_just_do_it (should be set_mode_without_PMS really }:)
+
+ * No whining about badops, timestamps or ugly code 
+ * that has been hacked over and over, just DO IT!
+ * 
+ *                              - GZ
+ */
+
+void mode_just_do_it (cptr, chptr, modebuf2, parabuf2)
+     aClient *cptr;
+     aChannel *chptr;
+     char *modebuf2, *parabuf2;
+{
+
+    static char pbuf1[50];
+    static char pbuf2[50];
+    char *string, *ppbuf1, *ppbuf2;
+
+    int add, count, space, firstpar;
+
+    add = count = space = 0;
+    firstpar = 1;
+
+    string = parabuf2;
+    ppbuf1 = pbuf1;
+    ppbuf2 = pbuf2;
+
+    pbuf1[0] = '\0';
+    pbuf2[0] = '\0';
+
+    if (string[0] != '\0') {
+
+       while (*string)
+           switch (*(string++)) {
+           case ' ':
+               space = count;
+               break;
+           default:
+               count++;
+           }
+
+
+       string = parabuf2;
+
+       if (!space || (space == count)) {
+           strncpyzt (ppbuf1, string, count + 1);
+           pbuf2[0] = '\0';
+
+       }
+       else {
+           strncpyzt (ppbuf1, string, space + 1);
+           strncpyzt (ppbuf2, string + space, (count - space) + 2);
+       }
+
+    }
+    /* All modes there are: beHiklmnostvRrc */
+
+    string = modebuf2;
+
+    while (*string)
+       switch (*(string++)) {
+       case '+':
+           add = 1;
+           break;
+       case '-':
+           add = 0;
+           break;
+       case 't':
+           if (add)
+               chptr->mode.mode |= MODE_TOPICLIMIT;
+           else
+               chptr->mode.mode &= ~MODE_TOPICLIMIT;
+           break;
+       case 's':
+           if (add)
+               chptr->mode.mode |= MODE_SECRET;
+           else
+               chptr->mode.mode &= ~MODE_SECRET;
+           break;
+       case 'c':
+           if (add)
+               chptr->mode.mode |= MODE_NOCOLORS;
+           else
+               chptr->mode.mode &= ~MODE_NOCOLORS;
+           break;
+       case 'i':
+           if (add)
+               chptr->mode.mode |= MODE_INVITEONLY;
+           else
+               chptr->mode.mode &= ~MODE_INVITEONLY;
+           break;
+       case 'n':
+           if (add)
+               chptr->mode.mode |= MODE_NOPRIVMSGS;
+           else
+               chptr->mode.mode &= ~MODE_NOPRIVMSGS;
+           break;
+       case 'm':
+           if (add)
+               chptr->mode.mode |= MODE_MODERATED;
+           else
+               chptr->mode.mode &= ~MODE_MODERATED;
+           break;
+       case 'r':
+           if (add)
+               chptr->mode.mode |= MODE_RGSTR;
+           else
+               chptr->mode.mode &= ~MODE_RGSTR;
+           break;
+       case 'R':
+           if (add)
+               chptr->mode.mode |= MODE_RGSTRONLY;
+           else
+               chptr->mode.mode &= ~MODE_RGSTRONLY;
+           break;
+       case 'k':
+           if (add) {
+               chptr->mode.mode |= MODE_KEY;
+               if (firstpar)
+                   strncpyzt (chptr->mode.key, ppbuf1, 50);
+               else {
+                   firstpar = 0;
+                   strncpyzt (chptr->mode.key, ppbuf2, 50);
+               }
+           }
+           else {
+               chptr->mode.mode &= ~MODE_KEY;
+               memset (chptr->mode.key, '\0', sizeof (chptr->mode.key));
+           }
+           break;
+       case 'l':
+           if (add) {
+               chptr->mode.mode |= MODE_LIMIT;
+               if (firstpar) {
+                   firstpar = 0;
+                   chptr->mode.limit = atoi (pbuf1);
+               }
+               else {
+                   chptr->mode.limit = atoi (pbuf2);
+               }
+           }
+           else {
+               chptr->mode.mode &= ~MODE_LIMIT;
+               chptr->mode.limit = 0;
+           }
+           break;
+       }
+
+    return;
+}
+
+/*
+   **      m_sjoin
+   **
+   **   SJOIN will synch channels and channelmodes using the new STS1 protocol
+   **      that is based on the EFnet TS3 protocol.
+   **                           -GZ (gz@starchat.net)
+   **         
+   **      parv[0] = sender prefix
+   **      parv[1] = channel timestamp
+   **      parv[2] = channel name
+   **      parv[3] = channel modes
+   **      parv[4] = channel mode parameters (key/limit)
+   **      parv[5] = nick names + modes - all in one parameter 
+ */
+int m_sjoin (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+
+    aClient *acptr, *tempptr;
+    aChannel *chptr;
+    aSynchList *synchptr, *synchptr2, *synchptr3;
+
+    Link *lp, *lp2, *members;
+
+    static char nick[NICKLEN];
+
+    char *t, *bp;
+    int c, f, fl, ts, copy, merge, wipem, nopara;
+    int voiced, opped, tc, send, opvoice, susp_ts;
+
+    if (IsClient (sptr) || parc < 6 || !IsServer (sptr))       /* Double check redundant? yeah, I think so -GZ */
+       return 0;
+
+    if (!IsChannelName (parv[2]))
+       return 0;
+
+    nopara = 0;
+
+    if (!strncmp (parv[4], "<none>", 6)) {
+       nopara = 1;
+    }
+    send = 1;
+
+    /* If length of modebuf is 1, we dont have to do anything */
+
+    if (parv[3][1] == '\0')      /* ie if the 2nd character is NULL, its length is one (or zero, but thats not possible */
+       send = 0;
+
+    chptr = get_channel (cptr, parv[2], CREATE);
+
+    ts = atol (parv[1]);
+
+    copy = merge = tc = wipem = susp_ts = 0;
+
+    if (chptr->creationtime > ts) {
+       copy = wipem = 1;
+    }
+    else if (chptr->creationtime == ts) {
+       merge = 1;
+    }
+    /* If our timestamp is 0 this is a new channel, copy it. */
+
+    if (chptr->creationtime == 0 && ts > 0) {
+       copy = 1;
+    }
+    /* Hmmmmmm.....timestamp is below 750000, something fishy is going on
+     * let's just not take this SJOIN too seriously... -GZ
+     */
+
+    if (ts < 750000) {
+       if (ts != 0)
+           sendto_ops
+               ("Warning! Possible desynch: SJOIN for channel %s has a fishy timestamp (%ld)",
+                chptr->chname, ts);
+       susp_ts = 1;
+    }
+    t = parv[5];
+    bp = buf;
+
+    c = fl = opped = voiced = opvoice = 0;
+    f = 1;
+
+    /* Let's see what the channel-modes are first here */
+
+    *parabuf = '\0';
+    *modebuf = '+';
+    modebuf[1] = '\0';
+
+    channel_modes (cptr, modebuf, parabuf, chptr);
+
+    /* Let's clean our channel-modes if nescessary */
+
+    if (wipem && (modebuf[1] != '\0') && !susp_ts) {
+
+       modebuf[0] = '-';
+
+       mode_just_do_it (cptr, chptr, modebuf, parabuf);
+
+       sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s", me.name,
+                               chptr->chname, modebuf, parabuf);
+    }
+    /* Now let's apply the modes as required */
+
+    if ((copy || merge) && send && !susp_ts) {
+       *parabuf = '\0';
+       *modebuf = '+';
+       modebuf[1] = '\0';
+
+       if (parv[3]) {
+           strcpy (modebuf, parv[3]);
+           send = 1;
+       }
+       if (!nopara)
+           strcpy (parabuf, parv[4]);
+       else
+           parabuf[0] = '\0';
+
+       if (send) {
+           sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s", me.name,
+                                   chptr->chname, modebuf, parabuf);
+           mode_just_do_it (cptr, chptr, modebuf, parabuf);
+       }
+    }
+    send = 0;
+
+    /* First we better wipe our chanop list if our TS is older than SJOIN TS */
+
+    if (wipem && !susp_ts) {
+       members = chptr->members;
+       for (lp2 = members; lp2; lp2 = lp2->next) {
+
+           if ((lp2->flags & MODE_CHANOP)) {
+               lp2->flags &= ~MODE_CHANOP;
+               acptr = lp2->value.cptr;
+
+               sendto_channel_butserv (chptr, acptr, ":%s MODE %s -o %s",
+                                       me.name, chptr->chname,
+                                       lp2->value.cptr->name);
+           }
+           if ((lp2->flags & MODE_VOICE)) {
+               lp2->flags &= ~MODE_VOICE;
+
+               acptr = lp2->value.cptr;
+
+               sendto_channel_butserv (chptr, acptr, ":%s MODE %s -v %s",
+                                       me.name, chptr->chname, acptr->name);
+           }
+       }
+    }
+    /* Ok - it should now all be wiped so lets see what they have to offer us */
+    /* t points to parv[5], nick list 
+     * f is 1 to begin with.
+     * c is 0 to begin with.
+     * bp points to buf. 
+     */
+
+    while (*t != '\0') {
+       if (*t == ' ') {
+
+           if (f)
+               strncpyzt (bp, (t - c), (c + 1));       /* Put the nick in bp */
+
+           else
+               strncpyzt (bp, (t - (c - 1)), c);       /* Put the nick in bp */
+
+           if (bp[0] == '+') {
+               strncpyzt (nick, bp + 1, c);
+               if (!(acptr = find_client (nick, NULL))) {
+                   /* The client doesn't exist, probably Killed off for some reason, this is
+                    * ok, we will just ignore it 
+                    */
+                   /* horrible, horrible thing to do, but this while loop needs rewriting anyhow */
+                   goto bad_nick_jump;
+
+               }
+               /* ok, now, is this an evil nick (ie, one on our side), or a good one,
+                * ie, one on the other side?
+                */
+               /* sptr = server who is sending the SJOIN */
+               tempptr = acptr;
+
+               while (tempptr != &me && tempptr != sptr)
+                   tempptr = tempptr->srvptr;
+
+               if (tempptr == &me) {
+                   /* this nick is an evil nick, since its on my
+                    * side... ignore this nick.
+                    */
+                   /* horrible, horrible thing to do, but this while loop needs rewriting anyhow */
+                   goto bad_nick_jump;
+               }
+               if (!IsMember (acptr, chptr)) {
+                   /* Its not really possible for the user to be a member of this
+                    * channel already, unless something is broken, but its not
+                    * too bad checking for this, just in case 
+                    */
+                   add_user_to_channel (chptr, acptr, fl);
+                   sendto_channel_butserv (chptr, acptr, ":%s JOIN :%s",
+                                           nick, parv[2]);
+               }
+               lp = find_user_link (chptr->members, acptr);
+
+               if (!lp)
+                   sendto_ops ("WARNING! possible desynch from %s in %s",
+                               nick, chptr->chname);
+
+               if (lp->flags & CHFL_CHANOP)
+                   opped = 1;
+               if (lp->flags & CHFL_VOICE)
+                   voiced = 1;
+
+               /* Foreign clients are never opped/voiced */
+
+               if (!MyClient (sptr)) {
+
+                   if (opped)
+                       lp->flags &= ~CHFL_CHANOP;
+                   if (voiced)
+                       lp->flags &= ~CHFL_VOICE;
+                   opped = voiced = 0;
+               }
+               synchptr = make_synchlist ();
+
+               strncpyzt (synchptr->nick, nick, sizeof (synchptr->nick));
+
+               if ((copy) && !(voiced)) {
+                   lp->flags |= CHFL_VOICE;
+                   synchptr->voice = 1;
+               }
+               if ((merge) && (!opped) && (!voiced)) {
+                   lp->flags |= CHFL_VOICE;
+                   synchptr->voice = 1;
+               }
+               if ((merge) && (opped)) {
+                   if (voiced)
+                       lp->flags &= ~CHFL_VOICE;
+                   synchptr->op = 1;
+               }
+               if (SJSynchList == NULL)
+                   SJSynchList = synchptr;
+               else {
+                   SJSynchList->prev = synchptr;
+                   synchptr->next = SJSynchList;
+                   SJSynchList = synchptr;
+               }
+           }
+           else if (bp[0] == '@') {
+
+               if (bp[1] == '+') {
+                   strncpyzt (nick, bp + 2, (c - 1));
+                   opvoice = 1;
+               }
+               else {
+                   strncpyzt (nick, bp + 1, c);
+               }
+
+               if (!(acptr = find_client (nick, NULL))) {
+                   /* The client doesn't exist, probably Killed off for some reason, this is
+                    * ok, we will just ignore it 
+                    */
+                   /* horrible, horrible thing to do, but this while loop needs rewriting anyhow */
+                   goto bad_nick_jump;
+
+               }
+               /* ok, now, is this an evil nick (ie, one on our side), or a good one,
+                * ie, one on the other side?
+                */
+               /* sptr = server who is sending the SJOIN */
+               tempptr = acptr;
+
+               while (tempptr != &me && tempptr != sptr)
+                   tempptr = tempptr->srvptr;
+
+               if (tempptr == &me) {
+                   /* this nick is an evil nick, since its on my
+                    * side... ignore this nick.
+                    */
+                   /* horrible, horrible thing to do, but this while loop needs rewriting anyhow */
+                   goto bad_nick_jump;
+               }
+               if (!IsMember (acptr, chptr)) {
+                   add_user_to_channel (chptr, acptr, fl);
+                   sendto_channel_butserv (chptr, acptr, ":%s JOIN :%s",
+                                           nick, parv[2]);
+               }
+               lp = find_user_link (chptr->members, acptr);
+
+               if (!lp)
+                   sendto_ops ("WARNING! possible desynch from %s in %s",
+                               nick, chptr->chname);
+
+               if (lp->flags & CHFL_CHANOP) {
+                   opped = 1;
+               }
+               if (lp->flags & CHFL_VOICE) {
+                   voiced = 1;
+               }
+               /* Foreign clients are never opped/voiced */
+
+               if (!MyClient (acptr)) {
+
+                   if (opped)
+                       lp->flags &= ~CHFL_CHANOP;
+                   if (voiced)
+                       lp->flags &= ~CHFL_VOICE;
+                   opped = voiced = 0;
+               }
+               synchptr = make_synchlist ();
+
+               strncpyzt (synchptr->nick, nick, sizeof (synchptr->nick));
+
+               if (((copy) || (merge)) && !opped) {
+                   lp->flags |= CHFL_CHANOP;
+                   synchptr->op = 1;
+               }
+               if (((copy) || (merge)) && (voiced && !opvoice)) {
+                   lp->flags &= ~CHFL_VOICE;
+               }
+               if (((copy) || (merge)) && opvoice) {
+                   if (!voiced)
+                       lp->flags |= CHFL_VOICE;
+                   synchptr->voice = 1;
+               }
+               if (SJSynchList == NULL)
+                   SJSynchList = synchptr;
+               else {
+                   SJSynchList->prev = synchptr;
+                   synchptr->next = SJSynchList;
+                   SJSynchList = synchptr;
+               }
+
+           }
+           else {
+               strncpyzt (nick, bp, (c + 1));
+               if (!(acptr = find_client (nick, NULL))) {
+                   /* The client doesn't exist, probably Killed off for some reason, this is
+                    * ok, we will just ignore it 
+                    */
+                   /* horrible, horrible thing to do, but this while loop needs rewriting anyhow */
+                   goto bad_nick_jump;
+
+               }
+               /* ok, now, is this an evil nick (ie, one on our side), or a good one,
+                * ie, one on the other side?
+                */
+               /* sptr = server who is sending the SJOIN */
+               tempptr = acptr;
+
+               while (tempptr != &me && tempptr != sptr)
+                   tempptr = tempptr->srvptr;
+
+               if (tempptr == &me) {
+                   /* this nick is an evil nick, since its on my
+                    * side... ignore this nick.
+                    */
+                   /* horrible, horrible thing to do, but this while loop needs rewriting anyhow */
+                   goto bad_nick_jump;
+               }
+               if (!IsMember (acptr, chptr)) {
+                   add_user_to_channel (chptr, acptr, fl);
+                   sendto_channel_butserv (chptr, acptr, ":%s JOIN :%s",
+                                           nick, parv[2]);
+               }
+               lp = find_user_link (chptr->members, acptr);
+
+               if (!lp)
+                   sendto_ops ("WARNING! possible desynch from %s in %s",
+                               nick, chptr->chname);
+
+               if (lp->flags & CHFL_CHANOP)
+                   opped = 1;
+               if (lp->flags & CHFL_VOICE)
+                   voiced = 1;
+
+               /* Foreign clients are never opped/voiced */
+
+               if (!MyClient (acptr)) {
+
+                   if (opped)
+                       lp->flags &= ~CHFL_CHANOP;
+                   if (voiced)
+                       lp->flags &= ~CHFL_VOICE;
+                   opped = voiced = 0;
+               }
+           }
+         bad_nick_jump:
+           tc++;
+           c = f = fl = 0;
+       }
+       t++;
+       c++;
+    }
+
+    /* Ok, we parsed the nicks, we made them join. Time to set their modes -GZ */
+
+    /* Deopping and devoicing shouldnt be happening here anymore, but hey, you
+     * never know, always expect the unexpected and stuff. -GZ
+     */
+
+    *parabuf = 0;
+    *modebuf = 0;
+
+    modebuf[0] = '-';
+    modebuf[1] = 0;
+
+    send = 0;
+
+    if (!susp_ts) {
+
+       for (synchptr2 = SJSynchList; synchptr2; synchptr2 = synchptr3) {
+
+           synchptr3 = synchptr2->next;
+
+           if ((synchptr2->deop)) {
+               strcat (modebuf, "o");
+               strcat (parabuf, synchptr2->nick);
+               strcat (parabuf, " ");
+               send++;
+
+               if (send == 10) {
+
+                   sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s",
+                                           me.name, chptr->chname, modebuf,
+                                           parabuf);
+                   parabuf[0] = '\0';
+                   modebuf[1] = '\0';
+                   modebuf[0] = '-';
+                   send = 0;
+               }
+           }
+       }
+
+       if (send) {
+           sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s", me.name,
+                                   chptr->chname, modebuf, parabuf);
+       }
+       if (SJSynchList == NULL)
+           goto EndMode;
+
+       modebuf[0] = '-';
+       modebuf[1] = 0;
+
+       send = 0;
+
+       for (synchptr2 = SJSynchList; synchptr2; synchptr2 = synchptr3) {
+
+           synchptr3 = synchptr2->next;
+
+           if ((synchptr2->devoice)) {
+               strcat (modebuf, "v");
+               strcat (parabuf, synchptr2->nick);
+               strcat (parabuf, " ");
+               send++;
+
+               if (send == 10) {
+                   strcat (modebuf, "\0");
+                   strcat (parabuf, "\0");
+
+                   sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s",
+                                           me.name, chptr->chname, modebuf,
+                                           parabuf);
+                   parabuf[0] = '\0';
+                   modebuf[0] = '-';
+                   modebuf[1] = '\0';
+                   send = 0;
+               }
+           }
+       }
+
+       strcat (modebuf, "\0");
+       strcat (parabuf, "\0");
+       if (send) {
+           sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s", me.name,
+                                   chptr->chname, modebuf, parabuf);
+       }
+       if (SJSynchList == NULL)
+           goto EndMode;
+
+       modebuf[1] = '\0';
+       parabuf[0] = '\0';
+       modebuf[0] = '+';
+
+       send = 0;
+
+       for (synchptr2 = SJSynchList; synchptr2; synchptr2 = synchptr3) {
+
+           synchptr3 = synchptr2->next;
+
+           if ((synchptr2->op)) {
+               strcat (modebuf, "o");
+               strcat (parabuf, synchptr2->nick);
+               strcat (parabuf, " ");
+               send++;
+
+               if (send == 10) {
+                   strcat (modebuf, "\0");
+                   strcat (parabuf, "\0");
+
+                   sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s",
+                                           me.name, chptr->chname, modebuf,
+                                           parabuf);
+                   modebuf[1] = '\0';
+                   parabuf[0] = '\0';
+                   modebuf[0] = '+';
+                   send = 0;
+               }
+           }
+       }
+
+       strcat (modebuf, "\0");
+       strcat (parabuf, "\0");
+       if (send) {
+           sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s", me.name,
+                                   chptr->chname, modebuf, parabuf);
+       }
+       if (SJSynchList == NULL)
+           goto EndMode;
+
+       modebuf[1] = '\0';
+       parabuf[0] = '\0';
+       modebuf[0] = '+';
+
+       send = 0;
+
+       for (synchptr2 = SJSynchList; synchptr2; synchptr2 = synchptr3) {
+
+           synchptr3 = synchptr2->next;
+
+           if ((synchptr2->voice)) {
+               strcat (modebuf, "v");
+               strcat (parabuf, synchptr2->nick);
+               strcat (parabuf, " ");
+               send++;
+
+               if (send == 10) {
+                   strcat (modebuf, "\0");
+                   strcat (parabuf, "\0");
+
+                   sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s",
+                                           me.name, chptr->chname, modebuf,
+                                           parabuf);
+                   modebuf[1] = '\0';
+                   parabuf[0] = '\0';
+                   modebuf[0] = '+';
+
+                   send = 0;
+               }
+           }
+       }
+
+       strcat (modebuf, "\0");
+       strcat (parabuf, "\0");
+       if (send) {
+           sendto_channel_butserv (chptr, cptr, ":%s MODE %s %s %s", me.name,
+                                   chptr->chname, modebuf, parabuf);
+       }
+       if (SJSynchList == NULL)
+           goto EndMode;
+
+    }
+    /* Ok! all done, time to clean up the mess */
+
+    for (synchptr2 = SJSynchList; synchptr2; synchptr2 = synchptr3) {
+
+       synchptr3 = synchptr2->next;
+
+       if (synchptr2->prev)
+           synchptr2->prev->next = synchptr2->next;
+       else
+           SJSynchList = synchptr2->next;
+       if (synchptr2->next)
+           synchptr2->next->prev = synchptr2->prev;
+       free_synchlist (synchptr2);
+
+    }
+
+  EndMode:
+
+    /* After all is done, synch timestamps as channels should now be identical */
+
+    if (!(ts > chptr->creationtime) && !susp_ts)
+       chptr->creationtime = ts;
+
+    else if (chptr->creationtime == 0 && ts > 0)
+       chptr->creationtime = ts;
+
+    /* And we are desynched! - kidding! - *whew!* */
+
+    /* Propagate SJOIN to other servers after it has been fully processed */
+
+    sendto_serv_butone (cptr, ":%s SJOIN %s %s %s %s :%s", parv[0], parv[1],
+                       parv[2], parv[3], parv[4], parv[5]);
+
+    return 0;
+}
+
+/*
+   ** m_svsjoin
+   **
+   ** This function allows an U lined server to force a client to
+   ** join a channel.  -- Remmy
+   **
+   **   parv[0] = sender prefix
+   **   parv[1] = nick
+   **   parv[2] = channel to join
+ */
+int m_svsjoin (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+
+    if (!IsULine (cptr, sptr))
+       return -1;
+
+    /* If this person is online and using this server, 
+       push them into the channel. */
+    if (!hunt_server (cptr, sptr, ":%s SVSJOIN %s %s", 1, parc, parv) !=
+       HUNTED_ISME) {
+        if ((acptr = find_person (parv[1], NULL)))
+            if (MyConnect(acptr))
+                m_join (&me, acptr, parc, &parv[1]);
+    }
+    
+    return 0;
+}
+
+/*
+   ** m_join
+   **   parv[0] = sender prefix
+   **   parv[1] = channel
+   **   parv[2] = channel password (key)
+ */
+int m_join (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    static char jbuf[BUFSIZE];
+    Link *lp;
+    aChannel *chptr;
+    char *name, *key = NULL;
+    int i, operbypass = 0, flags = 0, zombie = 0;
+    char *p = NULL, *p2 = NULL;
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "JOIN");
+       return 0;
+    }
+    /* OPERBYPASS 
+       ** Allows SO's to JOIN any channel
+       ** regardless of being banned etc. I so hate this -Defiant
+     */
+    if ((parv[2]) && strncmp (parv[2], "operbypass", 10) == 0) {
+       if (IsSAdmin (sptr)) {
+           sendto_serv_butone (&me,
+                               ":%s GLOBOPS :OPERBYPASS requested by %s (%s@%s) [ %s ]",
+                               me.name, sptr->name, sptr->username,
+                               sptr->sockhost, parv[1]);
+           sendto_umode (UMODE_OPER,
+                         "*** Notice -- OPERBYPASS requested by %s (%s@%s) [ %s ]",
+                         sptr->name, sptr->username, sptr->sockhost,
+                         parv[1]);
+           operbypass = 1;
+       }
+       else {
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+           return 0;
+       }
+    }
+    *jbuf = '\0';
+    /*
+       ** Rebuild list of channels joined to be the actual result of the
+       ** JOIN.  Note that "JOIN 0" is the destructive problem.
+     */
+    for (i = 0, name = strtoken (&p, parv[1], ","); name;
+        name = strtoken (&p, NULL, ",")) {
+       /* pathological case only on longest channel name.
+          ** If not dealt with here, causes desynced channel ops
+          ** since ChannelExists() doesn't see the same channel
+          ** as one being joined. cute bug. Oct 11 1997, Dianora/comstud
+          ** Copied from Dianora's "hybrid 5" ircd.
+        */
+
+       if (strlen (name) > CHANNELLEN) /* same thing is done in get_channel() */
+           name[CHANNELLEN] = '\0';
+
+       if (MyConnect (sptr))
+           clean_channelname (name);
+       if (check_channelmask (sptr, cptr, name) == -1)
+           continue;
+       if (*name == '&' && !MyConnect (sptr))
+           continue;
+       if (*name == '0' && !atoi (name)) {
+           (void) strcpy (jbuf, "0");
+           i = 1;
+           continue;
+       }
+       else if (!IsChannelName (name)) {
+           if (MyClient (sptr))
+               sendto_one (sptr, err_str (ERR_NOSUCHCHANNEL),
+                           me.name, parv[0], name);
+           continue;
+       }
+       if (*jbuf)
+           (void) strcat (jbuf, ",");
+       (void) strncat (jbuf, name, sizeof (jbuf) - i - 1);
+       i += strlen (name) + 1;
+    }
+    (void) strcpy (parv[1], jbuf);
+
+    p = NULL;
+    if (parv[2])
+       key = strtoken (&p2, parv[2], ",");
+    parv[2] = NULL;              /* for m_names call later, parv[parc] must == NULL */
+    for (name = strtoken (&p, jbuf, ","); name;
+        key = (key) ? strtoken (&p2, NULL, ",") : NULL,
+        name = strtoken (&p, NULL, ",")) {
+       /*
+          ** JOIN 0 sends out a part for all channels a user
+          ** has joined.
+        */
+       if (*name == '0' && !atoi (name)) {
+           while ((lp = sptr->user->channel)) {
+               chptr = lp->value.chptr;
+               sendto_channel_butserv (chptr, sptr, PartFmt, parv[0],
+                                       chptr->chname);
+               remove_user_from_channel (sptr, chptr);
+           }
+           sendto_serv_butone (cptr, ":%s JOIN 0", parv[0]);
+           continue;
+       }
+       if (MyConnect (sptr)) {
+           /*
+              ** local client is first to enter previously nonexistant
+              ** channel so make them (rightfully) the Channel
+              ** Operator.
+            */
+           if (!IsModelessChannel (name))
+               flags = (ChannelExists (name)) ? CHFL_DEOPPED : CHFL_CHANOP;
+           else
+               flags = CHFL_DEOPPED;
+
+           if (sptr->user->joined >= MAXCHANNELSPERUSER) {
+               sendto_one (sptr, err_str (ERR_TOOMANYCHANNELS),
+                           me.name, parv[0], name);
+               return 0;
+           }
+       }
+       chptr = get_channel (sptr, name, CREATE);
+       if (chptr && (lp = find_user_link (chptr->members, sptr))) {
+           if (lp->flags & CHFL_ZOMBIE) {
+               zombie = 1;
+               flags = lp->flags & (CHFL_DEOPPED | CHFL_SERVOPOK);
+               remove_user_from_channel (sptr, chptr);
+               chptr = get_channel (sptr, name, CREATE);
+           }
+           else
+               continue;
+       }
+       if (!zombie) {
+           if (!MyConnect (sptr))
+               flags = CHFL_DEOPPED;
+           if (sptr->flags & FLAGS_TS8)
+               flags |= CHFL_SERVOPOK;
+       }
+       if (!chptr ||
+           (MyConnect (sptr) && (i = can_join (sptr, chptr, key)) &&
+            (operbypass == 0))) {
+           sendto_one (sptr, err_str (i), me.name, parv[0], name);
+           continue;
+       }
+       /*
+          **  Complete user entry to the new channel (if any)
+        */
+       add_user_to_channel (chptr, sptr, flags);
+       /*
+          ** notify all other users on the new channel
+        */
+       sendto_channel_butserv (chptr, sptr, ":%s JOIN :%s", parv[0], name);
+       sendto_match_servs (chptr, cptr, ":%s JOIN :%s", parv[0], name);
+
+       if (MyClient (sptr)) {
+           /*
+              ** Make a (temporal) creationtime, if someone joins
+              ** during a net.reconnect : between remote join and
+              ** the mode with TS. --Run
+            */
+           if (chptr->creationtime == 0) {
+               chptr->creationtime = time (NULL);
+               sendto_match_servs (chptr, cptr, ":%s MODE %s + %lu",
+                                   me.name, name, chptr->creationtime);
+           }
+           del_invite (sptr, chptr);
+           if (flags & CHFL_CHANOP)
+               sendto_match_servs (chptr, cptr,
+                                   ":%s MODE %s +o %s %lu",
+                                   me.name, name, parv[0],
+                                   chptr->creationtime);
+           if (chptr->topic[0] != '\0') {
+               sendto_one (sptr, rpl_str (RPL_TOPIC), me.name,
+                           parv[0], name, chptr->topic);
+               sendto_one (sptr, rpl_str (RPL_TOPICWHOTIME),
+                           me.name, parv[0], name,
+                           chptr->topic_nick, chptr->topic_time);
+           }
+           parv[1] = name;
+           cptr->flags |= FLAGS_JOINING;
+           (void) m_names (cptr, sptr, 2, parv);
+           cptr->flags &= ~FLAGS_JOINING;
+       }
+    }
+    return 0;
+}
+
+/*
+   ** m_svspart
+   **   parv[0] = sender prefix
+   **   parv[1] = channel
+ */
+int m_svspart (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+
+    if (!IsULine (cptr, sptr))
+       return -1;
+
+    /* If this person is online and using this server, 
+       push them out of the channel. */
+    if (!hunt_server (cptr, sptr, ":%s SVSPART %s %s", 1, parc, parv) !=
+       HUNTED_ISME) {
+        if ((acptr = find_person (parv[1], NULL)))
+            if (MyConnect(acptr))
+                m_part (&me, acptr, parc - 1, &parv[1]);
+    }
+
+    return 0;
+}
+
+/*
+   ** m_part
+   **   parv[0] = sender prefix
+   **   parv[1] = channel
+   **   parv[2] = comment (reason, ignored for jinxed users)
+ */
+int m_part (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aChannel *chptr;
+    Link *lp;
+    int ColorStrip = 0;
+    char *p = NULL, *name;
+    char *comment = (parc > 2 && parv[2]) ? parv[2] : NULL;
+
+    sptr->flags &= ~FLAGS_TS8;
+
+    if (parc < 2 || parv[1][0] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "PART");
+       return 0;
+    }
+
+    for (; (name = strtoken (&p, parv[1], ",")); parv[1] = NULL) {
+       chptr = get_channel (sptr, name, 0);
+       if (!chptr) {
+           sendto_one (sptr, err_str (ERR_NOSUCHCHANNEL), me.name, parv[0],
+                       name);
+           continue;
+       }
+       if (check_channelmask (sptr, cptr, name))
+           continue;
+
+       /* If the channel is mode +c, we'll need to block colored part
+          reasons */
+       if ((chptr->mode.mode & MODE_NOCOLORS) && comment)
+           if (strchr (comment, 3) != (char) NULL ||
+               strchr (comment, 27) != (char) NULL)
+                ColorStrip = 1;
+
+       /* Do not use IsMember here: zombies must be able to part too */
+       if (!(lp = find_user_link (chptr->members, sptr))) {
+           /* Normal to get get when our client did a kick
+              ** for a remote client (who sends back a PART),
+              ** so check for remote client or not --Run
+            */
+           if (MyClient (sptr))
+               sendto_one (sptr, err_str (ERR_NOTONCHANNEL), me.name,
+                           parv[0], name);
+           continue;
+       }
+       /*
+          **  Remove user from the old channel (if any)
+        */
+       if ((parc < 3) || (IsJinxed (sptr)) || (ColorStrip))
+            sendto_match_servs (chptr, cptr, PartFmt, parv[0], name);
+       else
+           sendto_match_servs (chptr, cptr, PartFmt2, parv[0], name, comment);
+
+       if ((parc < 3) || (IsJinxed (sptr)) || (ColorStrip))
+           sendto_channel_butserv (chptr, sptr, PartFmt, parv[0], name);
+       else
+           sendto_channel_butserv (chptr, sptr, PartFmt2, parv[0], name, comment); 
+
+       if (MyClient (sptr)) {
+           if ((parc < 3) || (IsJinxed (sptr)) || (ColorStrip))
+               sendto_one (sptr, PartFmt, parv[0], name);
+            else
+               sendto_one (sptr, PartFmt2, parv[0], name, comment);
+       }
+       remove_user_from_channel (sptr, chptr);
+    }
+    return 0;
+}
+
+/*
+   ** m_kick
+   **   parv[0] = sender prefix
+   **   parv[1] = channel
+   **   parv[2] = client to kick
+   **   parv[3] = kick comment
+ */
+int m_kick (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *who;
+    aChannel *chptr;
+    int chasing = 0;
+    char *comment, *name, *p = NULL, *user, *p2 = NULL;
+    Link *lp, *lp2;
+
+    if (IsJinxed (sptr))
+       return 0;                 /* Jinxed Can't kick  */
+
+    sptr->flags &= ~FLAGS_TS8;
+
+    if (parc < 3 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "KICK");
+       return 0;
+    }
+    if (IsServer (sptr) && !IsULine (cptr, sptr))
+       sendto_ops ("HACK: KICK from %s for %s %s", parv[0], parv[1],
+                   parv[2]);
+
+    comment = (BadPtr (parv[3])) ? parv[0] : parv[3];
+    if (strlen (comment) > (size_t) TOPICLEN)
+       comment[TOPICLEN] = '\0';
+
+    *nickbuf = *buf = '\0';
+
+    for (; (name = strtoken (&p, parv[1], ",")); parv[1] = NULL) {
+       chptr = get_channel (sptr, name, !CREATE);
+       if (!chptr) {
+           sendto_one (sptr, err_str (ERR_NOSUCHCHANNEL), me.name, parv[0],
+                       name);
+           continue;
+       }
+       if (check_channelmask (sptr, cptr, name))
+           continue;
+       if (!IsServer (cptr) && !IsULine (cptr, sptr)
+           && !is_chan_op (sptr, chptr)) {
+           sendto_one (sptr, err_str (ERR_CHANOPRIVSNEEDED),
+                       me.name, parv[0], chptr->chname);
+           continue;
+       }
+       lp2 = find_user_link (chptr->members, sptr);
+
+       for (; (user = strtoken (&p2, parv[2], ",")); parv[2] = NULL) {
+           if (!(who = find_chasing (sptr, user, &chasing)))
+               continue;         /* No such user left! */
+           /* if the user is root, prevent a kick from local user */
+           if (IsSRoot (who) && MyClient (sptr)) {
+               sendto_one (sptr, err_str (ERR_ISROOT), me.name,
+                           parv[0], parv[2], chptr->chname);
+               return 0;
+           }
+           if ((lp = find_user_link (chptr->members, who)) &&
+               (!(lp->flags & CHFL_ZOMBIE) || IsServer (sptr))) {
+               /* Bounce all KICKs from a non-chanop unless it
+                  would cause a fake direction -- Barubary */
+               if ((who->from != cptr) && !IsServer (sptr) &&
+                   !IsULine (cptr, sptr) && !is_chan_op (sptr, chptr))
+/*            if (who->from!=cptr &&
+   ((lp2 && (lp2->flags & CHFL_DEOPPED)) ||
+   (!lp2 && IsPerson(sptr))) && !IsULine(cptr,sptr)) */
+               {
+                   /* Bounce here:
+                    * cptr must be a server (or cptr==sptr and
+                    * sptr->flags can't have DEOPPED set
+                    * when CHANOP is set).
+                    */
+                   sendto_one (cptr, ":%s JOIN :%s", who->name, name);
+                   /* Slight change here - the MODE is sent only
+                      once even if overlapping -- Barubary */
+                   if (lp->flags & CHFL_OVERLAP) {
+                       char *temp = buf;
+                       *temp++ = '+';
+                       if (lp->flags & CHFL_CHANOP)
+                           *temp++ = 'o';
+                       if (lp->flags & CHFL_VOICE)
+                           *temp++ = 'v';
+                       *temp = 0;
+                       sendto_one (cptr, ":%s MODE %s %s %s %s %lu",
+                                   me.name, chptr->chname, buf, who->name,
+                                   ((lp->flags & CHFL_OVERLAP) ==
+                                    CHFL_OVERLAP)
+                                   ? who->name : "", chptr->creationtime);
+                   }
+                   /*  if (lp->flags & CHFL_CHANOP)
+                      sendmodeto_one(cptr, me.name, name, "+o",
+                      who->name, chptr->creationtime);
+                      if (lp->flags & CHFL_VOICE)
+                      sendmodeto_one(cptr, me.name, name, "+v",
+                      who->name, chptr->creationtime); */
+               }
+               else {
+                   if (lp)
+                       sendto_channel_butserv (chptr, sptr,
+                                               ":%s KICK %s %s :%s", parv[0],
+                                               name, who->name, comment);
+                   sendto_match_servs (chptr, cptr,
+                                       ":%s KICK %s %s :%s",
+                                       parv[0], name, who->name, comment);
+                   /* Finally, zombies totally removed -- Barubary */
+                   if (lp) {
+                       if (MyClient (who)) {
+                           sendto_match_servs (chptr, NULL, PartFmt,
+                                               who->name, name);
+                           remove_user_from_channel (who, chptr);
+                       }
+                       else {
+                           remove_user_from_channel (who, chptr);
+                       }
+                   }
+               }
+           }
+           else if (MyClient (sptr))
+               sendto_one (sptr, err_str (ERR_USERNOTINCHANNEL),
+                           me.name, parv[0], user, name);
+           if (!IsServer (cptr) || !IsULine (cptr, sptr))
+               break;
+       }                         /* loop on parv[2] */
+       if (!IsServer (cptr) || !IsULine (cptr, sptr))
+           break;
+    }                            /* loop on parv[1] */
+
+    return (0);
+}
+
+int count_channels (sptr)
+     aClient *sptr;
+{
+    aChannel *chptr;
+    int count = 0;
+
+    for (chptr = channel; chptr; chptr = chptr->nextch)
+       count++;
+    return (count);
+}
+
+/*
+   ** m_topic
+   **   parv[0] = sender prefix
+   **   parv[1] = topic text
+   **
+   **   For servers using TS: (Lefler)
+   **   parv[0] = sender prefix
+   **   parv[1] = channel list
+   **   parv[2] = topic nickname
+   **   parv[3] = topic time
+   **   parv[4] = topic text
+ */
+int m_topic (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aChannel *chptr = NullChn;
+    char *topic = NULL, *name, *p = NULL, *tnick = NULL;
+    time_t ttime = 0;
+
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "TOPIC");
+       return 0;
+    }
+    for (; (name = strtoken (&p, parv[1], ",")); parv[1] = NULL) {
+       if (parc > 1 && IsChannelName (name)) {
+           chptr = find_channel (name, NullChn);
+           if (!chptr) {
+               sendto_one (sptr, err_str (ERR_NOSUCHCHANNEL),
+                           me.name, parv[0], name);
+               continue;
+           }
+           if (parc > 2)
+               topic = parv[2];
+           if (parc > 4) {
+               tnick = parv[2];
+               ttime = atol (parv[3]);
+               topic = parv[4];
+           }
+       }
+       if (!chptr) {
+           sendto_one (sptr, rpl_str (RPL_NOTOPIC), me.name, parv[0], name);
+           return 0;
+       }
+       if (check_channelmask (sptr, cptr, name))
+           continue;
+
+       if (!topic) {             /* only asking  for topic  */
+           if (IsMember (sptr, chptr)) {       /* on channel */
+               if (chptr->topic[0] == '\0')
+                   sendto_one (sptr, rpl_str (RPL_NOTOPIC),
+                               me.name, parv[0], chptr->chname);
+               else {
+                   sendto_one (sptr, rpl_str (RPL_TOPIC),
+                               me.name, parv[0], chptr->chname,
+                               chptr->topic);
+                   sendto_one (sptr, rpl_str (RPL_TOPICWHOTIME), me.name,
+                               parv[0], chptr->chname, chptr->topic_nick,
+                               chptr->topic_time);
+               }
+           }
+           else {                /* not on channel */
+               if (!SecretChannel (chptr) || IsSAdmin (sptr))
+                   sendto_one (sptr, rpl_str (RPL_TOPIC),
+                               me.name, parv[0], chptr->chname,
+                               chptr->topic);
+               else
+                   /* Channel is secret, send back "not on
+                    * channel" to non-members.
+                    * - Wizzu
+                    */
+                   sendto_one (sptr, err_str (ERR_NOTONCHANNEL),
+                               me.name, parv[0], name);
+           }
+       }
+       /* else trying to set a topic */
+       else if (ttime && topic && (IsServer (sptr) || IsULine (cptr, sptr))) {
+           if (!chptr->topic_time || ttime < chptr->topic_time) {
+               /* setting a topic */
+               strncpyzt (chptr->topic, topic, sizeof (chptr->topic));
+               strcpy (chptr->topic_nick, tnick);
+               chptr->topic_time = ttime;
+               sendto_match_servs (chptr, cptr, ":%s TOPIC %s %s %lu :%s",
+                                   parv[0], chptr->chname,
+                                   chptr->topic_nick, chptr->topic_time,
+                                   chptr->topic);
+               sendto_channel_butserv (chptr, sptr, ":%s TOPIC %s :%s (%s)",
+                                       parv[0], chptr->chname,
+                                       chptr->topic, chptr->topic_nick);
+           }
+       }
+       else if ((is_chan_op (sptr, chptr) ||
+                 (IsULine (cptr, sptr) && topic)) ||
+                ((chptr->mode.mode & MODE_TOPICLIMIT) == 0 &&
+                 IsMember (sptr, chptr))) {
+           if (MyConnect (sptr) && IsJinxed (sptr))
+               return 0;         /* Jinxed Users Get Nothing */
+           /* setting a topic */
+           strncpyzt (chptr->topic, topic, sizeof (chptr->topic));
+           strcpy (chptr->topic_nick, sptr->name);
+           if (ttime && IsServer (cptr))
+               chptr->topic_time = ttime;
+           else
+               chptr->topic_time = time (NULL);
+           sendto_match_servs (chptr, cptr, ":%s TOPIC %s %s %lu :%s",
+                               parv[0], chptr->chname, parv[0],
+                               chptr->topic_time, chptr->topic);
+           sendto_channel_butserv (chptr, sptr, ":%s TOPIC %s :%s",
+                                   parv[0], chptr->chname, chptr->topic);
+       }
+       else if (!IsMember (sptr, chptr))
+           sendto_one (sptr, err_str (ERR_NOTONCHANNEL), me.name, parv[0],
+                       name);
+       else
+           sendto_one (sptr, err_str (ERR_CHANOPRIVSNEEDED),
+                       me.name, parv[0], name);
+    }
+    return 0;
+}
+
+/*
+   ** m_invite
+   **   parv[0] - sender prefix
+   **   parv[1] - user to invite
+   **   parv[2] - channel number
+ */
+int m_invite (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    aChannel *chptr;
+
+    if (parc < 3 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "INVITE");
+       return -1;
+    }
+    if (!(acptr = find_person (parv[1], (aClient *) NULL))) {
+       sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name, parv[0],
+                   parv[1]);
+       return -1;
+    }
+/* Prevent users from inviting to a channel they are not on -Studded */
+    if (!(ChannelExists (parv[2]))) {
+       sendto_one (sptr, err_str (ERR_NOTONCHANNEL), me.name, parv[0],
+                   parv[2]);
+       return -1;
+    }
+    if (MyConnect (sptr))
+       clean_channelname (parv[2]);
+    if (check_channelmask (sptr, cptr, parv[2]))
+       return 0;
+
+    if (!(chptr = find_channel (parv[2], NullChn))) {
+
+       sendto_prefix_one (acptr, sptr, ":%s INVITE %s :%s",
+                          parv[0], parv[1], parv[2]);
+       return 0;
+    }
+    if (chptr && !IsMember (sptr, chptr) && !IsULine (cptr, sptr)) {
+       sendto_one (sptr, err_str (ERR_NOTONCHANNEL), me.name, parv[0],
+                   parv[2]);
+       return -1;
+    }
+    if (IsMember (acptr, chptr)) {
+       sendto_one (sptr, err_str (ERR_USERONCHANNEL),
+                   me.name, parv[0], parv[1], parv[2]);
+       return 0;
+    }
+    if (chptr && (chptr->mode.mode & MODE_INVITEONLY)) {
+       if (!is_chan_op (sptr, chptr) && !IsULine (cptr, sptr)) {
+           sendto_one (sptr, err_str (ERR_CHANOPRIVSNEEDED),
+                       me.name, parv[0], chptr->chname);
+           return -1;
+       }
+       else if (!IsMember (sptr, chptr) && !IsULine (cptr, sptr)) {
+           sendto_one (sptr, err_str (ERR_CHANOPRIVSNEEDED),
+                       me.name, parv[0],
+                       ((chptr) ? (chptr->chname) : parv[2]));
+           return -1;
+       }
+    }
+    if (MyConnect (sptr)) {
+       if (check_for_target_limit (sptr, acptr, acptr->name))
+           return 0;
+
+       sendto_one (sptr, rpl_str (RPL_INVITING), me.name, parv[0],
+                   acptr->name, ((chptr) ? (chptr->chname) : parv[2]));
+       if (acptr->user->away)
+           sendto_one (sptr, rpl_str (RPL_AWAY), me.name, parv[0],
+                       acptr->name, acptr->user->away);
+    }
+
+    /* Why should non chanops be able to invite? */
+    if (!is_chan_op (sptr, chptr) && !IsULine (cptr, sptr) && !IsOper (cptr)) {
+       sendto_one (sptr, err_str (ERR_CHANOPRIVSNEEDED),
+                   me.name, parv[0], chptr->chname);
+       return -1;
+    }
+    /* Note: is_banned() here will cause some extra CPU load, 
+     *       and we're really only relying on the existence
+     *       of the limit because we could momentarily have
+     *       less people on channel.
+     */
+    if (MyConnect (acptr))
+       if (chptr && sptr->user && (is_banned (acptr, chptr) ||
+                                   (chptr->mode.mode & MODE_INVITEONLY) ||
+                                   chptr->mode.limit
+                                   || IsULine (cptr, sptr))) {
+           add_invite (acptr, chptr);
+           sendto_channelops_butone (NULL, &me, chptr,
+                                     ":%s NOTICE @%s :%s invited %s into the channel.",
+                                     me.name, chptr->chname, sptr->name,
+                                     acptr->name);
+       }
+    sendto_prefix_one (acptr, sptr, ":%s INVITE %s :%s", parv[0],
+                      acptr->name, ((chptr) ? (chptr->chname) : parv[2]));
+    return 0;
+}
+
+/*
+ * send_list
+ *
+ * The function which sends the actual /list output back to the user.
+ * Operates by stepping through the hashtable, sending the entries back if
+ * they match the criteria.
+ * cptr = Local client to send the output back to.
+ * numsend = Number (roughly) of lines to send back. Once this number has
+ * been exceeded, send_list will finish with the current hash bucket,
+ * and record that number as the number to start next time send_list
+ * is called for this user. So, this function will almost always send
+ * back more lines than specified by numsend (though not by much,
+ * assuming CHANNELHASHSIZE is was well picked). So be conservative
+ * if altering numsend };> -Rak
+ */
+void send_list (cptr, numsend)
+     aClient *cptr;
+     int numsend;
+{
+    int hashptr, done = 0;
+    aChannel *chptr;
+/*    Link  *tmpl; Compiler says this is unused */
+    char tempbuff[KEYLEN + 8 + 3 + 1 + 3],
+       modestuff[TOPICLEN + 3 + KEYLEN + 8 + 3 + 1];
+
+#define l cptr->lopt             /* lazy shortcut */
+
+    for (hashptr = l->starthash; hashptr < CHANNELHASHSIZE; hashptr++) {
+       for (chptr = hash_get_chan_bucket (hashptr);
+            chptr; chptr = chptr->hnextch) {
+           if (SecretChannel (chptr) && !IsMember (cptr, chptr)
+               && !IsSAdmin (cptr))
+               continue;
+           if (!l->showall && ((chptr->users <= l->usermin) ||
+                               ((l->usermax == -1) ? 0 : (chptr->users >=
+                                                          l->usermax))
+                               || ((chptr->creationtime || 1) <=
+                                   l->chantimemin)
+                               || (chptr->topic_time < l->topictimemin)
+                               || (chptr->creationtime >= l->chantimemax)
+                               || (chptr->topic_time > l->topictimemax)))
+               continue;
+           /* For now, just extend to topics as well. Use patterns starting
+            * with # to stick to searching channel names only. -Donwulff
+            */
+           if (l->nolist &&
+               (find_str_match_link (&(l->nolist), chptr->chname) ||
+                find_str_match_link (&(l->nolist), chptr->topic)))
+               continue;
+           if (l->yeslist &&
+               (!find_str_match_link (&(l->yeslist), chptr->chname) &&
+                !find_str_match_link (&(l->yeslist), chptr->topic)))
+               continue;
+
+           if (IsSAdmin (cptr)) {
+               *modebuf = *parabuf = '\0';
+               modebuf[1] = '\0';
+               channel_modes (cptr, modebuf, parabuf, chptr);
+               if (*parabuf)
+                   sprintf (tempbuff, " [%s %s]", modebuf, parabuf);
+               else
+                   sprintf (tempbuff, " [%s]", modebuf);
+               sprintf (modestuff, "%-20s %s", tempbuff, chptr->topic);
+
+               sendto_one (cptr, rpl_str (RPL_LIST), me.name, cptr->name,
+                           chptr->chname,
+                           chptr->users,
+                           modestuff);
+           }
+           else {
+               sendto_one (cptr, rpl_str (RPL_LIST), me.name, cptr->name,
+                           ShowChannel (cptr, chptr) ? chptr->chname : "*",
+                           chptr->users,
+                           ShowChannel (cptr, chptr) ? chptr->topic : "");
+           }
+
+
+           if (--numsend == 0)   /* Send to the end of the list and return */
+               done = 1;
+       }
+
+       if (done && (++hashptr < CHANNELHASHSIZE)) {
+           l->starthash = hashptr;
+           return;
+       }
+    }
+
+    sendto_one (cptr, rpl_str (RPL_LISTEND), me.name, cptr->name);
+    cptr->flags2 &= ~FLAGS2_HTC;
+
+    free_str_list (l->yeslist);
+    free_str_list (l->nolist);
+    MyFree (l);
+    l = NULL;
+
+    /* List finished, penalize by 10 seconds -Donwulff */
+    if (!IsPrivileged (cptr))
+       cptr->since += 10;
+
+    return;
+}
+
+
+
+/*
+ * m_list
+ *  parv[0] = sender prefix
+ *  parv[1,2,3...] = Channels or list options.
+ */
+int m_list (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aChannel *chptr;
+    char *name, *p = NULL;
+    LOpts *lopt;
+    short int showall = 0;
+    Link *yeslist = NULL, *nolist = NULL, *listptr;
+    short usermin = 0, usermax = -1;
+    time_t currenttime = time (NULL);
+    time_t chantimemin = 0, topictimemin = 0;
+    time_t chantimemax, topictimemax;
+
+    char tempbuff[KEYLEN + 8 + 3 + 1 + 3],
+       modestuff[TOPICLEN + 3 + KEYLEN + 8 + 3 + 1];
+
+    static char *usage[] = {
+       "   Usage: /raw LIST options (on mirc) or /quote LIST options (ircII)",
+       "",
+       "If you don't include any options, the default is to send you the",
+       "entire unfiltered list of channels. Below are the options you can",
+       "use, and what channels LIST will return when you use them.",
+       ">number  List channels with more than <number> people.",
+       "<number  List channels with less than <number> people.",
+       "C>number List channels created between now and <number> minutes ago.",
+       "C<number List channels created earlier than <number> minutes ago.",
+       "T>number List channels whose topics are older than <number> minutes",
+       "         (Ie, they have not changed in the last <number> minutes.",
+       "T<number List channels whose topics are not older than <number> minutes.",
+       "*mask*   List channels that match *mask*",
+       "!*mask*  List channels that do not match *mask*",
+       NULL
+    };
+
+    cptr->flags2 |= FLAGS2_HTC;          /* To prevent a dead socket */
+
+/* Not needed re change to parse.c
+   None of that unregistered LIST stuff.  -- Barubary
+   if (check_registered(sptr)) return 0;
+ */
+    /*
+     * I'm making the assumption it won't take over a day to transmit
+     * the list... -Rak
+     */
+    chantimemax = topictimemax = currenttime + 86400;
+
+
+    if ((parc == 2) && (!strcasecmp (parv[1], "?"))) {
+       char **ptr = usage;
+
+       for (; *ptr; ptr++)
+           sendto_one (sptr, rpl_str (RPL_LISTSYNTAX), me.name, cptr->name,
+                       *ptr);
+
+       return 0;
+    }
+    /*
+     * A list is already in process, for now we just interrupt the
+     * current listing, perhaps later we can allow stacked ones...
+     *  -Donwulff (Not that it's hard or anything, but I don't see
+     *             much use for it, beyond flooding)
+     */
+
+    if (cptr->lopt) {
+       free_str_list (cptr->lopt->yeslist);
+       free_str_list (cptr->lopt->nolist);
+       MyFree (cptr->lopt);
+       cptr->lopt = NULL;
+       sendto_one (sptr, rpl_str (RPL_LISTEND), me.name, cptr->name);
+       cptr->flags2 &= ~FLAGS2_HTC;
+
+       /* Interrupted list, penalize 10 seconds */
+       if (!IsPrivileged (sptr))
+           sptr->since += 10;
+       return 0;
+    }
+    sendto_one (sptr, rpl_str (RPL_LISTSTART), me.name, cptr->name);
+
+    /* LIST with no arguements */
+    if (parc < 2 || BadPtr (parv[1])) {
+       lopt = (LOpts *) MyMalloc (sizeof (LOpts));
+       if (!lopt)
+           return 0;
+
+       /*
+        * Changed to default to ignoring channels with only
+        * 1 person on, to decrease floods... -Donwulff
+        */
+       bzero (lopt, sizeof (LOpts));   /* To be sure! */
+       lopt->next = (LOpts *) lopt->yeslist = lopt->nolist = (Link *) NULL;
+       lopt->usermin = 0;        /* Default */
+       lopt->usermax = -1;
+       lopt->chantimemax = lopt->topictimemax = currenttime + 86400;
+       cptr->lopt = lopt;
+       if (IsSendable (cptr))
+           send_list (cptr, 64);
+       return 0;
+    }
+    /*
+     * General idea: We don't need parv[0], since we can get that
+     * information from cptr.name. So, let's parse each element of
+     * parv[], setting pointer parv to the element being parsed.
+     */
+    while (--parc) {
+       parv += 1;
+       if (BadPtr (parv))        /* Sanity check! */
+           continue;
+
+       name = strtoken (&p, *parv, ",");
+
+       while (name) {
+           switch (*name) {
+           case '>':
+               showall = 1;
+               usermin = strtol (++name, (char **) 0, 10);
+               break;
+
+           case '<':
+               showall = 1;
+               usermax = strtol (++name, (char **) 0, 10);
+               break;
+
+           case 't':
+           case 'T':
+               showall = 1;
+               switch (*++name) {
+               case '>':
+                   topictimemax =
+                       currenttime - 60 * strtol (++name, (char **) 0, 10);
+                   break;
+
+               case '<':
+                   topictimemin =
+                       currenttime - 60 * strtol (++name, (char **) 0, 10);
+                   break;
+
+               case '\0':
+                   topictimemin = 1;
+                   break;
+
+               default:
+                   sendto_one (sptr, err_str (ERR_LISTSYNTAX), me.name,
+                               cptr->name);
+                   free_str_list (yeslist);
+                   free_str_list (nolist);
+                   sendto_one (sptr, rpl_str (RPL_LISTEND), me.name,
+                               cptr->name);
+                   cptr->flags2 &= ~FLAGS2_HTC;
+                   return 0;
+               }
+               break;
+
+           case 'c':
+           case 'C':
+               showall = 1;
+               switch (*++name) {
+               case '>':
+                   chantimemin =
+                       currenttime - 60 * strtol (++name, (char **) 0, 10);
+                   break;
+
+               case '<':
+                   chantimemax =
+                       currenttime - 60 * strtol (++name, (char **) 0, 10);
+                   break;
+
+               default:
+                   sendto_one (sptr, err_str (ERR_LISTSYNTAX), me.name,
+                               cptr->name);
+                   free_str_list (yeslist);
+                   free_str_list (nolist);
+                   sendto_one (sptr, rpl_str (RPL_LISTEND), me.name,
+                               cptr->name);
+                   cptr->flags2 &= ~FLAGS2_HTC;
+
+                   return 0;
+               }
+               break;
+
+           default:              /* A channel or channel mask */
+
+               /*
+                * new syntax: !channelmask will tell ircd to ignore
+                * any channels matching that mask, and then
+                * channelmask will tell ircd to send us a list of
+                * channels only masking channelmask. Note: Specifying
+                * a channel without wildcards will return that
+                * channel even if any of the !channelmask masks
+                * matches it.
+                */
+
+               if (*name == '!') {
+                   showall = 1;
+                   listptr = make_link ();
+                   listptr->next = nolist;
+                   DupString (listptr->value.cp, name + 1);
+                   nolist = listptr;
+               }
+               else if (strchr (name, '*') || strchr (name, '?')) {
+                   showall = 1;
+                   listptr = make_link ();
+                   listptr->next = yeslist;
+                   DupString (listptr->value.cp, name);
+                   yeslist = listptr;
+               }
+               else {
+
+                   chptr = find_channel (name, NullChn);
+
+                   if (IsSAdmin (sptr) && chptr) {
+                       *modebuf = *parabuf = '\0';
+                       modebuf[1] = '\0';
+                       channel_modes (sptr, modebuf, parabuf, chptr);
+                       if (*parabuf)
+                           sprintf (tempbuff, " [%s %s]", modebuf, parabuf);
+                       else
+                           sprintf (tempbuff, " [%s]", modebuf);
+                       sprintf (modestuff, "%-20s %s", tempbuff,
+                                chptr->topic);
+
+                       sendto_one (sptr, rpl_str (RPL_LIST),
+                                   me.name, cptr->name,
+                                   ShowChannel (sptr, chptr) ? name : "*",
+                                   chptr->users,
+                                   modestuff);
+                   }
+                   else {
+                       if (chptr && ShowChannel (sptr, chptr))
+                           sendto_one (sptr, rpl_str (RPL_LIST),
+                                       me.name, cptr->name,
+                                       ShowChannel (sptr,
+                                                    chptr) ? name : "*",
+                                       chptr->users,
+                                       chptr->topic);
+                   }
+               }
+
+           }                     /* switch (*name) */
+           name = strtoken (&p, NULL, ",");
+       }                         /* while(name) */
+    }                            /* while(--parc) */
+
+    if (!showall || (chantimemin > currenttime)
+       || (topictimemin > currenttime)) {
+       free_str_list (yeslist);
+       free_str_list (nolist);
+       sendto_one (sptr, rpl_str (RPL_LISTEND), me.name, cptr->name);
+       cptr->flags2 &= ~FLAGS2_HTC;
+
+       return 0;
+    }
+    lopt = (LOpts *) MyMalloc (sizeof (LOpts));
+
+    lopt->showall = 0;
+    lopt->next = NULL;
+    lopt->yeslist = yeslist;
+    lopt->nolist = nolist;
+    lopt->starthash = 0;
+    lopt->usermin = usermin;
+    lopt->usermax = usermax;
+    lopt->currenttime = currenttime;
+    lopt->chantimemin = chantimemin;
+    lopt->chantimemax = chantimemax;
+    lopt->topictimemin = topictimemin;
+    lopt->topictimemax = topictimemax;
+
+    cptr->lopt = lopt;
+    send_list (cptr, 64);
+
+    return 0;
+}
+
+
+/************************************************************************
+ * m_names() - Added by Jto 27 Apr 1989
+ ************************************************************************/
+
+/*
+   ** m_names
+   **   parv[0] = sender prefix
+   **   parv[1] = channel
+ */
+int m_names (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aChannel *chptr;
+    aClient *c2ptr;
+    Link *lp = NULL;
+    aChannel *ch2ptr = NULL;
+    int idx, flag, len, mlen, x;
+    char *s, *para = parc > 1 ? parv[1] : NULL;
+
+    /* This command is pretty useless without a paramater. -GZ */
+    if (parc < 2)
+       return 0;
+
+    if (parc > 1
+       && hunt_server (cptr, sptr, ":%s NAMES %s %s", 2, parc, parv))
+       return 0;
+
+    mlen = strlen (me.name) + 10;
+
+    /* Only 10 requests per NAMES allowed remotely -- Barubary */
+    if (!BadPtr (para) && (cptr != sptr))
+       for (x = 0, s = para; *s; s++)
+           if (*s == ',')
+               if (++x == 10) {
+                   *s = 0;
+                   break;
+               }
+    if (BadPtr (para) && IsServer (cptr)) {
+       sendto_one (cptr, rpl_str (RPL_ENDOFNAMES), me.name, parv[0], "*");
+       return 0;
+    }
+
+    /* New fx4 HTC code */
+
+    if (MyClient (sptr) && !IsServer (sptr) && !(cptr->flags & FLAGS_JOINING)) {
+
+       if (cptr->htcignore) {
+           sendto_ops ("HTC ignore");
+           if ((time (NULL) - cptr->lasthtc) > (HTCTIME * 3)) {
+               cptr->htcignore = 0;
+               cptr->htccount = 0;
+               cptr->lasthtc = time (NULL);
+           }
+           else {
+               cptr->lasthtc = time (NULL);
+               sendto_one (sptr, rpl_str (ERR_HTCTOOFAST), me.name,
+                           sptr->name, (HTCTIME * 3));
+               return 0;
+           }
+       }
+
+       if (((time (NULL) - cptr->lasthtc) < HTCTIME) && !IsAnOper (cptr))
+           cptr->htccount++;
+       else
+           cptr->htccount = 0;
+
+       if (cptr->htccount > HTCTRIGGER) {
+           cptr->htcignore = 1;
+           cptr->lasthtc = time (NULL);
+           sendto_locfailops
+               ("Warning! %s!%s@%s is exceeding HTC trigger value.",
+                cptr->name, cptr->user->username, cptr->user->host);
+           sendto_one (sptr, rpl_str (ERR_HTCTOOFAST), me.name, sptr->name,
+                       (HTCTIME * 3));
+           return 0;
+       }
+
+       cptr->lasthtc = time (NULL);
+    }
+
+    cptr->flags2 |= FLAGS2_HTC;          /* To prevent a dead socket */
+
+    if (!BadPtr (para)) {
+       s = index (para, ',');
+       if (s) {
+           parv[1] = ++s;
+           (void) m_names (cptr, sptr, parc, parv);
+       }
+       if (MyConnect (sptr))
+           clean_channelname (para);
+       ch2ptr = find_channel (para, (aChannel *) NULL);
+    }
+    *buf = '\0';
+
+    /*
+     * First, do all visible channels (public and the one user self is)
+     */
+
+    for (chptr = channel; chptr; chptr = chptr->nextch) {
+       if ((chptr != ch2ptr) && !BadPtr (para))
+           continue;             /* -- wanted a specific channel */
+       if (!MyConnect (sptr) && BadPtr (para))
+           continue;
+       if (!ShowChannel (sptr, chptr))
+           continue;             /* -- users on this are not listed */
+
+       /* Find users on same channel (defined by chptr) */
+
+       (void) strcpy (buf, "* ");
+       len = strlen (chptr->chname);
+       (void) strcpy (buf + 2, chptr->chname);
+       (void) strcpy (buf + 2 + len, " :");
+
+       if (PubChannel (chptr))
+           *buf = '=';
+       else if (SecretChannel (chptr))
+           *buf = '@';
+       idx = len + 4;
+       flag = 1;
+       for (lp = chptr->members; lp; lp = lp->next) {
+           c2ptr = lp->value.cptr;
+           if (sptr != c2ptr && IsInvisible (c2ptr) &&
+               !IsMember (sptr, chptr) && !IsAnOper (sptr))
+               continue;
+           if (lp->flags & CHFL_ZOMBIE) {
+               if (lp->value.cptr != sptr)
+                   continue;
+               else
+                   (void) strcat (buf, "!");
+           }
+           else if (lp->flags & CHFL_CHANOP)
+               (void) strcat (buf, "@");
+           else if (lp->flags & CHFL_VOICE)
+               (void) strcat (buf, "+");
+           (void) strncat (buf, c2ptr->name, NICKLEN);
+           idx += strlen (c2ptr->name) + 1;
+           flag = 1;
+           (void) strcat (buf, " ");
+           if (mlen + idx + NICKLEN > BUFSIZE - 2) {
+               sendto_one (sptr, rpl_str (RPL_NAMREPLY), me.name, parv[0],
+                           buf);
+               (void) strncpy (buf, "* ", 3);
+               (void) strncpy (buf + 2, chptr->chname, len + 1);
+               (void) strcat (buf, " :");
+               if (PubChannel (chptr))
+                   *buf = '=';
+               else if (SecretChannel (chptr))
+                   *buf = '@';
+               idx = len + 4;
+               flag = 0;
+           }
+       }
+       if (flag)
+           sendto_one (sptr, rpl_str (RPL_NAMREPLY), me.name, parv[0], buf);
+    }
+    if (!BadPtr (para)) {
+       sendto_one (sptr, rpl_str (RPL_ENDOFNAMES), me.name, parv[0], para);
+
+       /* End of HTC protection code */
+
+       cptr->flags2 |= FLAGS2_HTC;
+
+       return (1);
+    }
+    /* Second, do all non-public, non-secret channels in one big sweep */
+
+    (void) strncpy (buf, "* * :", 6);
+    idx = 5;
+    flag = 0;
+    for (c2ptr = client; c2ptr; c2ptr = c2ptr->next) {
+       aChannel *ch3ptr;
+       int showflag = 0, secret = 0;
+
+       if ((!IsPerson (c2ptr) || (sptr != c2ptr && IsInvisible (c2ptr)))
+           && !IsAnOper (sptr))
+           continue;
+       if (!lp)
+           continue;
+       lp = c2ptr->user->channel;
+       /*
+        * dont show a client if they are on a secret channel or
+        * they are on a channel sptr is on since they have already
+        * been show earlier. -avalon
+        */
+       while (lp) {
+           ch3ptr = lp->value.chptr;
+           if (PubChannel (ch3ptr) || IsMember (sptr, ch3ptr))
+               showflag = 1;
+           if (SecretChannel (ch3ptr) && !IsSAdmin (sptr))
+               secret = 1;
+           lp = lp->next;
+       }
+       if (showflag)             /* have we already shown them ? */
+           continue;
+       if (secret)               /* on any secret channels ? */
+           continue;
+       (void) strncat (buf, c2ptr->name, NICKLEN);
+       idx += strlen (c2ptr->name) + 1;
+       (void) strcat (buf, " ");
+       flag = 1;
+       if (mlen + idx + NICKLEN > BUFSIZE - 2) {
+           sendto_one (sptr, rpl_str (RPL_NAMREPLY), me.name, parv[0], buf);
+           (void) strncpy (buf, "* * :", 6);
+           idx = 5;
+           flag = 0;
+       }
+    }
+    if (flag)
+       sendto_one (sptr, rpl_str (RPL_NAMREPLY), me.name, parv[0], buf);
+    sendto_one (sptr, rpl_str (RPL_ENDOFNAMES), me.name, parv[0], "*");
+
+    /* Everything should be sent by now, turn the protection off */
+
+    cptr->flags2 &= ~FLAGS2_HTC;
+
+    return (1);
+}
+
+void send_user_joins (cptr, user)
+     aClient *cptr, *user;
+{
+    Link *lp;
+    aChannel *chptr;
+    int cnt = 0, len = 0, clen;
+    char *mask;
+
+    sprintf (buf, ":%s %s ", user->name,
+            (IsToken (cptr) ? TOK_JOIN : MSG_JOIN));
+    len = strlen (buf);
+
+    for (lp = user->user->channel; lp; lp = lp->next) {
+       chptr = lp->value.chptr;
+       if ((mask = index (chptr->chname, ':')))
+           if (match (++mask, cptr->name))
+               continue;
+       if (*chptr->chname == '&')
+           continue;
+       clen = strlen (chptr->chname);
+       if (clen + 1 + len > BUFSIZE - 3) {
+           if (cnt) {
+               buf[len - 1] = '\0';
+               sendto_one (cptr, "%s", buf);
+           }
+           sprintf (buf, ":%s %s ", user->name,
+                    (IsToken (cptr) ? TOK_JOIN : MSG_JOIN));
+           len = strlen (buf);
+           cnt = 0;
+       }
+       (void) strcpy (buf + len, chptr->chname);
+       cnt++;
+       len += clen;
+       if (lp->next) {
+           len++;
+           (void) strcat (buf, ",");
+       }
+    }
+    if (*buf && cnt)
+       sendto_one (cptr, "%s", buf);
+
+    return;
+}
diff --git a/src/chkconf.c b/src/chkconf.c
new file mode 100644 (file)
index 0000000..52af651
--- /dev/null
@@ -0,0 +1,629 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/chkconf.c
+ *   Copyright (C) 1993 Darren Reed
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifdef __hpux
+#include "inet.h"
+#endif
+#ifdef PCS
+#include <time.h>
+#endif
+
+#ifdef DYNIXPTX
+#include <sys/types.h>
+#include <time.h>
+#endif
+
+#undef free
+#define        MyMalloc(x)     malloc(x)
+#define Reg    register
+
+static void new_class ();
+static char *getfield (), confchar ();
+static int openconf (), validate ();
+static aClass *get_class ();
+static aConfItem *initconf ();
+
+static int numclasses = 0, *classarr = (int *) NULL, debugflag = 0;
+static char *configfile = CONFIGFILE;
+static char nullfield[] = "";
+static char maxsendq[12];
+
+int main (argc, argv)
+     int argc;
+     char *argv[];
+{
+    new_class (0);
+
+    if (chdir (DPATH)) {
+       perror ("chdir");
+       exit (-1);
+    }
+    if (argc > 1 && !strncmp (argv[1], "-d", 2)) {
+       debugflag = 1;
+       if (argv[1][2])
+           debugflag = atoi (argv[1] + 2);
+       argc--, argv++;
+    }
+    else if (argc > 1 && !strncmp (argv[1], "-h", 2)) {
+       printf ("chkconf [-d] [conf file]\n");
+       printf ("-d = debug mode\n");
+       printf ("conf file = path to ircd.conf\n\n");
+       exit (0);
+    }
+    if (argc > 1)
+       configfile = argv[1];
+    return validate (initconf ());
+}
+
+/*
+ * openconf
+ *
+ * returns -1 on any error or else the fd opened from which to read the
+ * configuration file from.  This may either be th4 file direct or one end
+ * of a pipe from m4.
+ */
+static int openconf ()
+{
+#ifdef M4_PREPROC
+    int pi[2];
+
+    if (pipe (pi) == -1)
+       return -1;
+    switch (fork ()) {
+    case -1:
+       return -1;
+    case 0:
+       (void) close (pi[0]);
+       if (pi[1] != 1) {
+           (void) dup2 (pi[1], 1);
+           (void) close (pi[1]);
+       }
+       (void) dup2 (1, 2);
+       /*
+        * m4 maybe anywhere, use execvp to find it.  Any error
+        * goes out with report_error.  Could be dangerous,
+        * two servers running with the same fd's >:-) -avalon
+        */
+       (void) execlp ("m4", "m4", "ircd.m4", configfile, 0);
+       perror ("m4");
+       exit (-1);
+    default:
+       (void) close (pi[1]);
+       return pi[0];
+    }
+#else
+    return open (configfile, O_RDONLY);
+#endif
+}
+
+static int oper_access[] = {
+    ~(OFLAG_ADMIN | OFLAG_SADMIN | OFLAG_ZLINE), '*',
+    OFLAG_LOCAL, 'o',
+    OFLAG_GLOBAL, 'O',
+    OFLAG_REHASH, 'r',
+    OFLAG_DIE, 'D',
+    OFLAG_RESTART, 'R',
+    OFLAG_GLOBOP, 'g',
+    OFLAG_LOCOP, 'l',
+    OFLAG_LROUTE, 'c',
+    OFLAG_GROUTE, 'C',
+    OFLAG_LKILL, 'k',
+    OFLAG_GKILL, 'K',
+    OFLAG_KLINE, 'b',
+    OFLAG_UNKLINE, 'B',
+    OFLAG_LNOTICE, 'n',
+    OFLAG_GNOTICE, 'N',
+    OFLAG_ADMIN, 'A',
+    OFLAG_SADMIN, 'a',
+    OFLAG_UMODEC, 'u',
+    OFLAG_UMODEF, 'f',
+    OFLAG_ZLINE, 'z',
+    0, 0
+};
+/*
+   ** initconf() 
+   **    Read configuration file.
+   **
+   **    returns -1, if file cannot be opened
+   **             0, if file opened
+ */
+
+static aConfItem *initconf (opt)
+     int opt;
+{
+    int fd;
+    char line[512], *tmp, c[80], *s;
+    int ccount = 0, ncount = 0, dh, flags = 0;
+    int lineno;
+    aConfItem *aconf = NULL, *ctop = NULL;
+
+    (void) fprintf (stderr, "\nOpening %s as ircd configuration file\n\n",
+                   configfile);
+    if ((fd = openconf ()) == -1) {
+#ifdef M4_PREPROC
+       (void) wait (0);
+#endif
+       return NULL;
+    }
+    lineno = 0;
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    while ((dh = dgets (fd, line, sizeof (line) - 1)) > 0) {
+       /* The +2 here is a silly hack, but it does cause the correct line number
+        * to be reported when it actually reaches the end of file case. This is
+        * the only place lineno should be incremented, otherwise we're causing
+        * an off by one error in the line number for each error reported. -Studded 
+        */
+       printf ("%u:\tEnd of file\r", (lineno++ + 2));
+       if (aconf) {
+           if (aconf->host)
+               (void) free (aconf->host);
+           if (aconf->passwd)
+               (void) free (aconf->passwd);
+           if (aconf->name)
+               (void) free (aconf->name);
+       }
+       else
+           aconf = (aConfItem *) malloc (sizeof (*aconf));
+       aconf->host = (char *) NULL;
+       aconf->passwd = (char *) NULL;
+       aconf->name = (char *) NULL;
+       aconf->class = (aClass *) NULL;
+       if ((tmp = (char *) index (line, '\n')))
+           *tmp = 0;
+       else
+           while (dgets (fd, c, sizeof (c) - 1))
+               if ((tmp = (char *) index (c, '\n'))) {
+                   *tmp = 0;
+                   break;
+               }
+       /*
+        * Do quoting of characters and # detection.
+        */
+       for (tmp = line; *tmp; tmp++) {
+           if (*tmp == '\\') {
+               switch (*(tmp + 1)) {
+               case 'n':
+                   *tmp = '\n';
+                   break;
+               case 'r':
+                   *tmp = '\r';
+                   break;
+               case 't':
+                   *tmp = '\t';
+                   break;
+               case '0':
+                   *tmp = '\0';
+                   break;
+               default:
+                   *tmp = *(tmp + 1);
+                   break;
+               }
+               if (!*(tmp + 1))
+                   break;
+               else
+                   for (s = tmp; (*s = *++s););
+               tmp++;
+           }
+           else if (*tmp == '#')
+               *tmp = '\0';
+       }
+       if (!*line || *line == '#' || *line == '\n' ||
+           *line == ' ' || *line == '\t')
+           continue;
+
+       if (line[1] != ':') {
+           (void) fprintf (stderr, "%u:\tERROR: Bad config line (%s)\n",
+                           lineno, line);
+           continue;
+       }
+       if (debugflag)
+           (void) printf ("\n%s\n", line);
+       (void) fflush (stdout);
+
+       tmp = getfield (line);
+       if (!tmp) {
+           (void) fprintf (stderr, "\tERROR: no fields found\n");
+           continue;
+       }
+       aconf->status = CONF_ILLEGAL;
+
+       switch (*tmp) {
+       case 'A':                 /* Name, e-mail address of administrator */
+           aconf->status = CONF_ADMIN;
+           break;
+       case 'a':                 /* of this server. */
+           aconf->status = CONF_SADMIN;
+           break;
+       case 'C':                 /* Server where I should try to connect */
+       case 'c':                 /* in case of lp failures             */
+           ccount++;
+           aconf->status = CONF_CONNECT_SERVER;
+           break;
+       case 'f':                 /* Temp Z-line time */
+       case 'F':
+           aconf->status = CONF_ZTIME;
+           break;
+       case 'H':                 /* Hub server line */
+       case 'h':
+           aconf->status = CONF_HUB;
+           break;
+       case 'I':                 /* Just plain normal irc client trying  */
+       case 'i':                 /* to connect me */
+           aconf->status = CONF_CLIENT;
+           break;
+       case 'K':                 /* Kill user line on irc.conf           */
+       case 'k':
+           aconf->status = CONF_KILL;
+           break;
+           /* Me. Host field is name used for this host */
+           /* and port number is the number of the port */
+       case 'M':
+       case 'm':
+           aconf->status = CONF_ME;
+           break;
+       case 'N':                 /* Server where I should NOT try to     */
+       case 'n':                 /* connect in case of lp failures     */
+           /* but which tries to connect ME        */
+           ++ncount;
+           aconf->status = CONF_NOCONNECT_SERVER;
+           break;
+       case 'O':
+           aconf->status = CONF_OPERATOR;
+           break;
+           /* Local Operator, (limited privs --SRB)
+            * Not anymore, OperFlag access levels. -Cabal95 */
+       case 'o':
+           aconf->status = CONF_OPERATOR;
+           break;
+       case 'P':                 /* listen port line */
+       case 'p':
+           aconf->status = CONF_LISTEN_PORT;
+           break;
+       case 'Q':                 /* a server that you don't want in your */
+       case 'q':                 /* network. USE WITH CAUTION! */
+           aconf->status = CONF_QUARANTINED_SERVER;
+           break;
+       case 'S':                 /* Service. Same semantics as   */
+       case 's':                 /* CONF_OPERATOR                */
+           aconf->status = CONF_SERVICE;
+           break;
+       case 'U':
+       case 'u':
+           aconf->status = CONF_UWORLD;
+           break;
+       case 'X':
+       case 'x':
+           aconf->status = CONF_DRPASS;
+           break;
+       case 'Y':
+       case 'y':
+           aconf->status = CONF_CLASS;
+           break;
+       case 'Z':
+       case 'z':
+           aconf->status = CONF_ZAP;
+           break;
+       default:
+           (void) fprintf (stderr,
+                           "%u:\tERROR: unknown conf line letter (%c)\n",
+                           lineno, *tmp);
+           break;
+       }
+
+       if (IsIllegal (aconf))
+           continue;
+
+       for (;;) {                /* Fake loop, that I can use break here --msa */
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           DupString (aconf->host, tmp);
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           DupString (aconf->passwd, tmp);
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           DupString (aconf->name, tmp);
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           if (aconf->status & CONF_OPERATOR) {
+               int *i, flag;
+               char *m = "*";
+               /*
+                * Now we use access flags to define  
+                * what an operator can do with their O.   
+                */
+               for (m = (*tmp) ? tmp : m; *m; m++) {
+                   for (i = oper_access; (flag = *i); i += 2)
+                       if (*m == (char) (*(i + 1))) {
+                           aconf->port |= flag;
+                           break;
+                       }
+                   if (flag == 0)
+                       fprintf (stderr,
+                                "%u:\tWARNING: Unknown oper access level '%c'\n",
+                                lineno, *m);
+               }
+               if (!(aconf->port & OFLAG_ISGLOBAL))
+                   aconf->status = CONF_LOCOP;
+           }
+           else
+               aconf->port = atoi (tmp);
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           if (!(aconf->status & CONF_CLASS))
+               aconf->class = get_class (atoi (tmp));
+           break;
+       }
+       if (!aconf->class && (aconf->status & (CONF_CONNECT_SERVER |
+                                              CONF_NOCONNECT_SERVER |
+                                              CONF_OPS | CONF_CLIENT))) {
+           (void) fprintf (stderr, "%u:\tWARNING: No class.  Default 0\n",
+                           lineno);
+           aconf->class = get_class (0);
+       }
+       /* Check for bad Z-lines */
+       if (aconf->status == CONF_ZAP) {
+           char *tempc = aconf->host;
+           if (!tempc) {
+               fprintf (stderr, "%u:\tERROR: Bad Z-line\n", lineno);
+           }
+           for (; *tempc; tempc++)
+               if ((*tempc >= '0') && (*tempc <= '9'))
+                   goto zap_safe;
+           fprintf (stderr, "%u:\tERROR: Z-line mask too broad\n", lineno);
+         zap_safe:;
+       }
+       /* Check for bad F lines */
+       if (aconf->status == CONF_ZTIME) {
+           if (!((aconf->host) && isdigit (*aconf->host)))
+               (void) fprintf (stderr,
+                               "%u:\tERROR: F-lines must contain a digit in the first slot\n",
+                               lineno);
+       }
+       /*
+          ** If conf line is a class definition, create a class entry
+          ** for it and make the conf_line illegal and delete it.
+        */
+       if (aconf->status & CONF_CLASS) {
+           if (!aconf->host) {
+               (void) fprintf (stderr, "\tERROR: no class #\n");
+               continue;
+           }
+           if (!tmp) {
+               (void) fprintf (stderr,
+                               "%u:\tWARNING: missing sendq field\n",
+                               lineno);
+               (void) fprintf (stderr, "\t\t default: %d\n", MAXSENDQLENGTH);
+               (void) sprintf (maxsendq, "%d", MAXSENDQLENGTH);
+           }
+           else
+               (void) sprintf (maxsendq, "%d", atoi (tmp));
+           new_class (atoi (aconf->host));
+           aconf->class = get_class (atoi (aconf->host));
+           goto print_confline;
+       }
+       if (aconf->status & CONF_LISTEN_PORT) {
+           if (!aconf->host)
+               (void) fprintf (stderr, "\tERROR: %s\n",
+                               "null host field in P-line");
+           else if (index (aconf->host, '/'))
+               (void) fprintf (stderr,
+                               "%u:\tWARNING: / present in P-line "
+                               "UNIXPORT configuration no longer supported\n",
+                               lineno);
+           aconf->class = get_class (0);
+           goto print_confline;
+       }
+       if (aconf->status & CONF_SERVER_MASK &&
+           (!aconf->host || index (aconf->host, '*') ||
+            index (aconf->host, '?'))) {
+           (void) fprintf (stderr, "\tERROR: bad host field\n");
+           continue;
+       }
+       if (aconf->status & CONF_SERVER_MASK && BadPtr (aconf->passwd)) {
+           (void) fprintf (stderr, "\tERROR: empty/no password field\n");
+           continue;
+       }
+       if (aconf->status & CONF_SERVER_MASK && !aconf->name) {
+           (void) fprintf (stderr, "\tERROR: bad name field\n");
+           continue;
+       }
+       if (aconf->status & (CONF_SERVER_MASK | CONF_OPS))
+           if (!index (aconf->host, '@')) {
+               char *newhost;
+               int len = 3;    /* *@\0 = 3 */
+
+               len += strlen (aconf->host);
+               newhost = (char *) MyMalloc (len);
+               (void) sprintf (newhost, "*@%s", aconf->host);
+               (void) free (aconf->host);
+               aconf->host = newhost;
+           }
+
+       if (!aconf->class)
+           aconf->class = get_class (0);
+       (void) sprintf (maxsendq, "%d", aconf->class->class);
+
+       if (!aconf->name)
+           aconf->name = nullfield;
+       if (!aconf->passwd)
+           aconf->passwd = nullfield;
+       if (!aconf->host)
+           aconf->host = nullfield;
+       if (aconf->status & (CONF_ME | CONF_ADMIN)) {
+           if (flags & aconf->status)
+               (void) fprintf (stderr,
+                               "ERROR: multiple %c-lines\n",
+                               toupper (confchar (aconf->status)));
+           else
+               flags |= aconf->status;
+       }
+      print_confline:
+       if (debugflag > 8)
+           (void) printf ("(%d) (%s) (%s) (%s) (%d) (%s)\n",
+                          aconf->status, aconf->host, aconf->passwd,
+                          aconf->name, aconf->port, maxsendq);
+       (void) fflush (stdout);
+       if (aconf->status & (CONF_SERVER_MASK | CONF_HUB)) {
+           aconf->next = ctop;
+           ctop = aconf;
+           aconf = NULL;
+       }
+    }
+    printf ("\n");
+    (void) close (fd);
+#ifdef M4_PREPROC
+    (void) wait (0);
+#endif
+    return ctop;
+}
+
+static aClass *get_class (cn)
+     int cn;
+{
+    static aClass cls;
+    int i = numclasses - 1;
+
+    cls.class = -1;
+    for (; i >= 0; i--)
+       if (classarr[i] == cn) {
+           cls.class = cn;
+           break;
+       }
+    if (i == -1)
+       (void) fprintf (stderr, "\tWARNING: class %d not found\n", cn);
+    return &cls;
+}
+
+static void new_class (cn)
+     int cn;
+{
+    numclasses++;
+    if (classarr)
+       classarr = (int *) realloc (classarr, sizeof (int) * numclasses);
+    else
+       classarr = (int *) malloc (sizeof (int));
+    classarr[numclasses - 1] = cn;
+}
+
+/*
+ * field breakup for ircd.conf file.
+ */
+static char *getfield (newline)
+     char *newline;
+{
+    static char *line = NULL;
+    char *end, *field;
+
+    if (newline)
+       line = newline;
+    if (line == NULL)
+       return (NULL);
+
+    field = line;
+    if ((end = (char *) index (line, ':')) == NULL) {
+       line = NULL;
+       if ((end = (char *) index (field, '\n')) == NULL)
+           end = field + strlen (field);
+    }
+    else
+       line = end + 1;
+    *end = '\0';
+    return (field);
+}
+
+static int validate (top)
+     aConfItem *top;
+{
+    Reg aConfItem *aconf, *bconf;
+    u_int otype = 0, valid = 0;
+
+    if (!top)
+       return 0;
+
+    for (aconf = top; aconf; aconf = aconf->next) {
+       if (aconf->status & CONF_MATCH)
+           continue;
+
+       if (aconf->status & CONF_SERVER_MASK) {
+           if (aconf->status & CONF_CONNECT_SERVER)
+               otype = CONF_NOCONNECT_SERVER;
+           else if (aconf->status & CONF_NOCONNECT_SERVER)
+               otype = CONF_CONNECT_SERVER;
+
+           for (bconf = top; bconf; bconf = bconf->next) {
+               if (bconf == aconf || !(bconf->status & otype))
+                   continue;
+               if (bconf->class == aconf->class &&
+                   !mycmp (bconf->name, aconf->name) &&
+                   !mycmp (bconf->host, aconf->host)) {
+                   aconf->status |= CONF_MATCH;
+                   bconf->status |= CONF_MATCH;
+                   break;
+               }
+           }
+       }
+       else
+           for (bconf = top; bconf; bconf = bconf->next) {
+               if ((bconf == aconf) || !(bconf->status & CONF_SERVER_MASK))
+                   continue;
+               if (!mycmp (bconf->name, aconf->name)) {
+                   aconf->status |= CONF_MATCH;
+                   break;
+               }
+           }
+    }
+
+    (void) fprintf (stderr, "\n");
+    for (aconf = top; aconf; aconf = aconf->next)
+       if (aconf->status & CONF_MATCH)
+           valid++;
+       else
+           (void) fprintf (stderr, "Unmatched %c:%s:%s:%s\n",
+                           confchar (aconf->status), aconf->host,
+                           aconf->passwd, aconf->name);
+    return valid ? 0 : -1;
+}
+
+static char confchar (status)
+     u_int status;
+{
+    static char letrs[] = "QICNoOMKARYSLPH";
+    char *s = letrs;
+
+    status &= ~(CONF_MATCH | CONF_ILLEGAL);
+
+    for (; *s; s++, status >>= 1)
+       if (status & 1)
+           return *s;
+    return '-';
+}
+
+int outofmemory ()
+{
+    (void) write (2, "Out of memory\n", 14);
+    exit (-1);
+}
diff --git a/src/class.c b/src/class.c
new file mode 100644 (file)
index 0000000..ad6c564
--- /dev/null
@@ -0,0 +1,223 @@
+
+
+
+/*
+ *   IRC - Internet Relay Chat, ircd/class.c
+ *   Copyright (C) 1990 Darren Reed
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "numeric.h"
+#include "h.h"
+
+#define BAD_CONF_CLASS         -1
+#define BAD_PING               -2
+#define BAD_CLIENT_CLASS       -3
+
+aClass *classes;
+
+int get_conf_class (aconf)
+     aConfItem *aconf;
+{
+    if ((aconf) && Class (aconf))
+       return (ConfClass (aconf));
+
+    Debug ((DEBUG_DEBUG, "No Class For %s",
+           (aconf) ? aconf->name : "*No Conf*"));
+
+    return (BAD_CONF_CLASS);
+
+}
+
+static int get_conf_ping (aconf)
+     aConfItem *aconf;
+{
+    if ((aconf) && Class (aconf))
+       return (ConfPingFreq (aconf));
+
+    Debug ((DEBUG_DEBUG, "No Ping For %s",
+           (aconf) ? aconf->name : "*No Conf*"));
+
+    return (BAD_PING);
+}
+
+
+
+int get_client_class (acptr)
+     aClient *acptr;
+{
+    Link *tmp;
+    aClass *cl;
+    int retc = BAD_CLIENT_CLASS;
+
+    if (acptr && !IsMe (acptr) && (acptr->confs))
+       for (tmp = acptr->confs; tmp; tmp = tmp->next) {
+           if (!tmp->value.aconf || !(cl = tmp->value.aconf->class))
+               continue;
+           if (Class (cl) > retc)
+               retc = Class (cl);
+       }
+    Debug ((DEBUG_DEBUG, "Returning Class %d For %s", retc, acptr->name));
+
+    return (retc);
+}
+
+int get_client_ping (acptr)
+     aClient *acptr;
+{
+    int ping = 0, ping2;
+    aConfItem *aconf;
+    Link *link;
+
+    link = acptr->confs;
+
+    if (link)
+       while (link) {
+           aconf = link->value.aconf;
+           if (aconf->status & (CONF_CLIENT | CONF_CONNECT_SERVER |
+                                CONF_NOCONNECT_SERVER)) {
+               ping2 = get_conf_ping (aconf);
+               if ((ping2 != BAD_PING) && ((ping > ping2) || !ping))
+                   ping = ping2;
+           }
+           link = link->next;
+       }
+    else {
+       ping = PINGFREQUENCY;
+       Debug ((DEBUG_DEBUG, "No Attached Confs"));
+    }
+    if (ping <= 0)
+       ping = PINGFREQUENCY;
+    Debug ((DEBUG_DEBUG, "Client %s Ping %d", acptr->name, ping));
+    return (ping);
+}
+
+int get_con_freq (clptr)
+     aClass *clptr;
+{
+    if (clptr)
+       return (ConFreq (clptr));
+    else
+       return (CONNECTFREQUENCY);
+}
+
+/*
+ * When adding a class, check to see if it is already present first.
+ * if so, then update the information for that class, rather than create
+ * a new entry for it and later delete the old entry.
+ * if no present entry is found, then create a new one and add it in
+ * immeadiately after the first one (class 0).
+ */
+void add_class (class, ping, confreq, maxli, sendq)
+     int class, ping, confreq, maxli;
+     long sendq;
+{
+    aClass *t, *p;
+
+    t = find_class (class);
+    if ((t == classes) && (class != 0)) {
+       p = (aClass *) make_class ();
+       NextClass (p) = NextClass (t);
+       NextClass (t) = p;
+    }
+    else
+       p = t;
+    Debug ((DEBUG_DEBUG,
+           "Add Class %d: p %x t %x - cf: %d pf: %d ml: %d sq: %l",
+           class, p, t, confreq, ping, maxli, sendq));
+    Class (p) = class;
+    ConFreq (p) = confreq;
+    PingFreq (p) = ping;
+    MaxLinks (p) = maxli;
+    MaxSendq (p) = (sendq > 0) ? sendq : MAXSENDQLENGTH;
+    if (p != t)
+       Links (p) = 0;
+}
+
+aClass *find_class (cclass)
+     int cclass;
+{
+    aClass *cltmp;
+
+    for (cltmp = FirstClass (); cltmp; cltmp = NextClass (cltmp))
+       if (Class (cltmp) == cclass)
+           return cltmp;
+    return classes;
+}
+
+void check_class ()
+{
+    aClass *cltmp, *cltmp2;
+
+    Debug ((DEBUG_DEBUG, "Class check:"));
+
+    for (cltmp2 = cltmp = FirstClass (); cltmp; cltmp = NextClass (cltmp2)) {
+       Debug ((DEBUG_DEBUG,
+               "Class %d : CF: %d PF: %d ML: %d LI: %d SQ: %ld",
+               Class (cltmp), ConFreq (cltmp), PingFreq (cltmp),
+               MaxLinks (cltmp), Links (cltmp), MaxSendq (cltmp)));
+       if (MaxLinks (cltmp) < 0) {
+           NextClass (cltmp2) = NextClass (cltmp);
+           if (Links (cltmp) <= 0)
+               free_class (cltmp);
+       }
+       else
+           cltmp2 = cltmp;
+    }
+}
+
+void initclass ()
+{
+    classes = (aClass *) make_class ();
+
+    Class (FirstClass ()) = 0;
+    ConFreq (FirstClass ()) = CONNECTFREQUENCY;
+    PingFreq (FirstClass ()) = PINGFREQUENCY;
+    MaxLinks (FirstClass ()) = MAXIMUM_LINKS;
+    MaxSendq (FirstClass ()) = MAXSENDQLENGTH;
+    Links (FirstClass ()) = 0;
+    NextClass (FirstClass ()) = NULL;
+}
+
+void report_classes (sptr)
+     aClient *sptr;
+{
+    aClass *cltmp;
+
+    for (cltmp = FirstClass (); cltmp; cltmp = NextClass (cltmp))
+       sendto_one (sptr, rpl_str (RPL_STATSYLINE), me.name, sptr->name,
+                   'Y', Class (cltmp), PingFreq (cltmp), ConFreq (cltmp),
+                   MaxLinks (cltmp), MaxSendq (cltmp));
+}
+
+long get_sendq (cptr)
+     aClient *cptr;
+{
+    int sendq = MAXSENDQLENGTH, retc = BAD_CLIENT_CLASS;
+    Link *tmp;
+    aClass *cl;
+
+    if (cptr && !IsMe (cptr) && (cptr->confs))
+       for (tmp = cptr->confs; tmp; tmp = tmp->next) {
+           if (!tmp->value.aconf || !(cl = tmp->value.aconf->class))
+               continue;
+           if (Class (cl) > retc)
+               sendq = MaxSendq (cl);
+       }
+    return sendq;
+}
diff --git a/src/crypt/Makefile b/src/crypt/Makefile
new file mode 100755 (executable)
index 0000000..3569c37
--- /dev/null
@@ -0,0 +1,36 @@
+#************************************************************************
+#*   IRC - Internet Relay Chat, ircd/crypt/Makefile
+#*   Copyright (C) 1991 Darren Reed
+#*
+#*   This program is free software; you can redistribute it and/or modify
+#*   it under the terms of the GNU General Public License as published by
+#*   the Free Software Foundation; either version 1, or (at your option)
+#*   any later version.
+#*
+#*   This program is distributed in the hope that it will be useful,
+#*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#*   GNU General Public License for more details.
+#*
+#*   You should have received a copy of the GNU General Public License
+#*   along with this program; if not, write to the Free Software
+#*   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#*/
+#
+# Change this to the path of your local ircd.conf file
+#
+IRCDCONF = /home/407/avalon/ircd.conf
+
+all: mkpasswd
+crypt: install
+
+mkpasswd: mkpasswd.c
+       gcc -O mkpasswd.c -o mkpasswd -lcrypt
+
+install:
+       crypter ${IRCDCONF}
+       @echo 'done.'
+
+clean:
+       /bin/rm -f mkpasswd
+
diff --git a/src/crypt/README b/src/crypt/README
new file mode 100644 (file)
index 0000000..4866afc
--- /dev/null
@@ -0,0 +1,62 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/crypt/README
+ *   Copyright (C) 1991 Nelson Minar
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+The change implemented here is that the operator password in irc.conf
+is no longer stored in plaintext form, but is encrypted the same way
+that user passwords are encrypted on normal UNIX systems. Ie, instead
+of having
+
+       O:*:goodboy:Nelson
+
+in your ircd.conf file, you have
+
+       O:*:sCnvYRmbFJ7oI:Nelson
+
+You still type "/oper Nelson goodboy" to become operator. However, if
+someone gets ahold of your irc.conf file, they can no longer figure
+out what the password is from reading it.  There are still other
+security holes, namely server-server passwords, but this closes one
+obvious problem.
+
+So how do you generate these icky looking strings for passwords?
+There's a simple program called mkpasswd to do that for you.  Just run
+mkpasswd, and at the prompt type in your plaintext password.  It will
+spit out the encrypted password, which you should then just copy into
+the irc.conf file. This should be done only when adding new passwords
+to your irc.conf file. To change over your irc.conf file to use
+encrypted passwords, define CRYPT_OPER_PASSWORD in config.h. You will
+need to recompile your server if you already compiled it with this
+feature disabled. Once compiled, edit the Makefile in this directory
+and chang "IRCDCONF" to your irc.conf file. Then "make install" in this
+directory to replace all the operator passwords in your irc.conf file
+with the encrypted format.
+
+Choose your passwords carefully. Do not choose something in a
+dictionary, make sure its at least 5 characters. Anything past 8
+characters is ignored.
+
+One thing to note about crypt() passwords - for every plaintext, there
+are 4096 different passwords. Some valid encryptions of "goodboy"
+include t1Ub2RhRQHd4g sCnvYRmbFJ7oI and Xr4Z.Kg5tcdy6. The first
+two characters (the "salt") determine which of the 4096 passwords
+you will get. mkpasswd chooses the salt randomly, or alternately
+will let you specify one on the command line.
+
+see also - crypt(3)
+
diff --git a/src/crypt/crypter b/src/crypt/crypter
new file mode 100755 (executable)
index 0000000..e3cc725
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/local/bin/perl
+#************************************************************************
+#*   IRC - Internet Relay Chat, ircd/crypt/crypter
+#*   Copyright (C) 1991 Sean Batt
+#*
+#*   This program is free software; you can redistribute it and/or modify
+#*   it under the terms of the GNU General Public License as published by
+#*   the Free Software Foundation; either version 1, or (at your option)
+#*   any later version.
+#*
+#*   This program is distributed in the hope that it will be useful,
+#*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#*   GNU General Public License for more details.
+#*
+#*   You should have received a copy of the GNU General Public License
+#*   along with this program; if not, write to the Free Software
+#*   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#*/
+
+#From Sean Batt sean@coombs.anu.edu.au
+#
+#Temporary output file
+#
+$tmpfile = "/tmp/ircd.conf.tmp";
+
+#
+#Original ircd.conf file
+#
+$ircdconf = @ARGV[0];
+
+print "crypting ",$ircdconf,"\n";
+@saltset = ('a' .. 'z', 'A' .. 'Z', '0' .. '9', '.', '/');
+
+umask(0077);
+open ($ircdout, ">/tmp/ircd.conf.tmp") || die "open $!";
+
+while ($text = <>) {
+#if its not an "O" line we can ignore it
+    $text =~ /^o/i || print ($ircdout $text) && next;
+    chop($text);
+    @oline = split(':', $text);
+    $salt = $saltset[rand(time)%64].$saltset[(rand(time)>>6)%64];
+    $oline[2] = crypt(@oline[2], $salt);
+    print ($ircdout join(':',@oline)."\n");
+}
+close ($ircdout);
+close ($ircdin);
+print "/bin/cp ",$tmpfile," ",$ircdconf,"\n";
+(fork()==0) ? exec("/bin/cp", $tmpfile, $ircdconf) : wait;
+
+#unlink($tmpfile);
+
diff --git a/src/crypt/mkpasswd.c b/src/crypt/mkpasswd.c
new file mode 100755 (executable)
index 0000000..eb6a855
--- /dev/null
@@ -0,0 +1,40 @@
+/* simple password generator by Nelson Minar (minar@reed.edu)
+ * copyright 1991, all rights reserved.
+ * You can use this code as long as my name stays with it.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern char *getpass();
+
+int main(argc, argv)
+int argc;
+char *argv[];
+{
+  static char saltChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
+  char salt[3];
+  char * plaintext;
+  int i;
+
+  if (argc < 2) {
+    srandom(time(0));          /* may not be the BEST salt, but its close */
+    salt[0] = saltChars[random() % 64];
+    salt[1] = saltChars[random() % 64];
+    salt[2] = 0;
+  }
+  else {
+    salt[0] = argv[1][0];
+    salt[1] = argv[1][1];
+    salt[2] = '\0';
+    if ((strchr(saltChars, salt[0]) == NULL) || (strchr(saltChars, salt[1]) == NULL))
+      fprintf(stderr, "illegal salt %s\n", salt), exit(1);
+  }
+
+  plaintext = getpass("plaintext: ");
+
+  printf("%s\n", crypt(plaintext, salt));
+  return 0;
+}
+
diff --git a/src/dbuf.c b/src/dbuf.c
new file mode 100644 (file)
index 0000000..bb3ffcf
--- /dev/null
@@ -0,0 +1,334 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, common/dbuf.c
+ *   Copyright (C) 1990 Markku Savela
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* -- Jto -- 20 Jun 1990
+ * extern void free() fixed as suggested by
+ * gruner@informatik.tu-muenchen.de
+ */
+
+/* -- Jto -- 10 May 1990
+ * Changed memcpy into bcopy and removed the declaration of memset
+ * because it was unnecessary.
+ * Added the #includes for "struct.h" and "sys.h" to get bcopy/memcpy
+ * work
+ */
+
+/*
+   ** For documentation of the *global* functions implemented here,
+   ** see the header file (dbuf.h).
+   **
+ */
+
+#include <stdio.h>
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+
+#if !defined(VALLOC) && !defined(valloc)
+#define        valloc malloc
+#endif
+
+int dbufalloc = 0, dbufblocks = 0;
+static dbufbuf *freelist = NULL;
+
+/* This is a dangerous define because a broken compiler will set DBUFSIZ
+   ** to 4, which will work but will be very inefficient. However, there
+   ** are other places where the code breaks badly if this is screwed
+   ** up, so... -- Wumpus
+ */
+
+#define DBUFSIZ sizeof(((dbufbuf *)0)->data)
+
+/*
+   ** dbuf_alloc - allocates a dbufbuf structure either from freelist or
+   ** creates a new one.
+ */
+static dbufbuf *dbuf_alloc ()
+{
+#if defined(VALLOC) && !defined(DEBUGMODE)
+    dbufbuf *dbptr, *db2ptr;
+    int num;
+#else
+    dbufbuf *dbptr;
+#endif
+
+    dbufalloc++;
+    if ((dbptr = freelist)) {
+       freelist = freelist->next;
+       return dbptr;
+    }
+    if (dbufalloc * DBUFSIZ > BUFFERPOOL) {
+       dbufalloc--;
+       return NULL;
+    }
+#if defined(VALLOC) && !defined(DEBUGMODE)
+#if defined(SOL20) || defined(_SC_PAGESIZE)
+    num = sysconf (_SC_PAGESIZE) / sizeof (dbufbuf);
+#else
+    num = getpagesize () / sizeof (dbufbuf);
+#endif
+    if (num < 0)
+       num = 1;
+
+    dbufblocks += num;
+
+    dbptr = (dbufbuf *) valloc (num * sizeof (dbufbuf));
+    if (!dbptr)
+       return (dbufbuf *) NULL;
+
+    num--;
+    for (db2ptr = dbptr; num; num--) {
+       db2ptr = (dbufbuf *) ((char *) db2ptr + sizeof (dbufbuf));
+       db2ptr->next = freelist;
+       freelist = db2ptr;
+    }
+    return dbptr;
+#else
+    dbufblocks++;
+    return (dbufbuf *) MyMalloc (sizeof (dbufbuf));
+#endif
+}
+
+/*
+   ** dbuf_free - return a dbufbuf structure to the freelist
+ */
+static void dbuf_free (ptr)
+     dbufbuf *ptr;
+{
+    dbufalloc--;
+    ptr->next = freelist;
+    freelist = ptr;
+}
+
+/*
+   ** This is called when malloc fails. Scrap the whole content
+   ** of dynamic buffer and return -1. (malloc errors are FATAL,
+   ** there is no reason to continue this buffer...). After this
+   ** the "dbuf" has consistent EMPTY status... ;)
+ */
+static int dbuf_malloc_error (dyn)
+     dbuf *dyn;
+{
+    dbufbuf *p;
+
+    dyn->length = 0;
+    dyn->offset = 0;
+    while ((p = dyn->head) != NULL) {
+       dyn->head = p->next;
+       dbuf_free (p);
+    }
+    dyn->tail = dyn->head;
+    return -1;
+}
+
+
+int dbuf_put (dyn, buf, length)
+     dbuf *dyn;
+     char *buf;
+     int length;
+{
+    dbufbuf **h, *d;
+    int off;
+    int chunk;
+
+    if (!length)
+       return 1;                 /* Nothing to do */
+
+    off = (dyn->offset + dyn->length) % DBUFSIZ;
+    /*
+       ** Locate the last non-empty buffer. If the last buffer is
+       ** full, the loop will terminate with 'd==NULL'. This loop
+       ** assumes that the 'dyn->length' field is correctly
+       ** maintained, as it should--no other check really needed.
+     */
+    if (!dyn->length)
+       h = &(dyn->head);
+    else {
+       if (off)
+           h = &(dyn->tail);
+       else
+           h = &(dyn->tail->next);
+    }
+    /*
+       ** Append users data to buffer, allocating buffers as needed
+     */
+    chunk = DBUFSIZ - off;
+    dyn->length += length;
+    for (; length > 0; h = &(d->next)) {
+       if ((d = *h) == NULL) {
+           if ((d = (dbufbuf *) dbuf_alloc ()) == NULL)
+               return dbuf_malloc_error (dyn);
+           dyn->tail = d;
+           *h = d;
+           d->next = NULL;
+       }
+       if (chunk > length)
+           chunk = length;
+       bcopy (buf, d->data + off, chunk);
+       length -= chunk;
+       buf += chunk;
+       off = 0;
+       chunk = DBUFSIZ;
+    }
+    return 1;
+}
+
+
+char *dbuf_map (dyn, length)
+     dbuf *dyn;
+     int *length;
+{
+    if (dyn->head == NULL) {
+       dyn->tail = NULL;
+       *length = 0;
+       return NULL;
+    }
+    *length = DBUFSIZ - dyn->offset;
+    if (*length > dyn->length)
+       *length = dyn->length;
+    return (dyn->head->data + dyn->offset);
+}
+
+int dbuf_delete (dyn, length)
+     dbuf *dyn;
+     int length;
+{
+    dbufbuf *d;
+    int chunk;
+
+    if (length > dyn->length)
+       length = dyn->length;
+    chunk = DBUFSIZ - dyn->offset;
+    while (length > 0) {
+       if (chunk > length)
+           chunk = length;
+       length -= chunk;
+       dyn->offset += chunk;
+       dyn->length -= chunk;
+       if (dyn->offset == DBUFSIZ || dyn->length == 0) {
+           d = dyn->head;
+           dyn->head = d->next;
+           dyn->offset = 0;
+           dbuf_free (d);
+       }
+       chunk = DBUFSIZ;
+    }
+    if (dyn->head == (dbufbuf *) NULL) {
+       dyn->length = 0;
+       dyn->tail = 0;
+    }
+    return 0;
+}
+
+int dbuf_get (dyn, buf, length)
+     dbuf *dyn;
+     char *buf;
+     int length;
+{
+    int moved = 0;
+    int chunk;
+    char *b;
+
+    while (length > 0 && (b = dbuf_map (dyn, &chunk)) != NULL) {
+       if (chunk > length)
+           chunk = length;
+       bcopy (b, buf, (int) chunk);
+       (void) dbuf_delete (dyn, chunk);
+       buf += chunk;
+       length -= chunk;
+       moved += chunk;
+    }
+    return moved;
+}
+
+/*
+   ** dbuf_getmsg
+   **
+   ** Check the buffers to see if there is a string which is terminted with
+   ** either a \r or \n prsent.  If so, copy as much as possible (determined by
+   ** length) into buf and return the amount copied - else return 0.
+ */
+int dbuf_getmsg (dyn, buf, length)
+     dbuf *dyn;
+     char *buf;
+     register int length;
+{
+    dbufbuf *d;
+    register char *s;
+    register int dlen;
+    register int i;
+    int copy;
+
+  getmsg_init:
+    d = dyn->head;
+    dlen = dyn->length;
+    i = DBUFSIZ - dyn->offset;
+    if (i <= 0)
+       return -1;
+    copy = 0;
+    if (d && dlen)
+       s = dyn->offset + d->data;
+    else
+       return 0;
+
+    if (i > dlen)
+       i = dlen;
+    while (length > 0 && dlen > 0) {
+       dlen--;
+       if (*s == '\n' || *s == '\r') {
+           copy = dyn->length - dlen;
+           /*
+              ** Shortcut this case here to save time elsewhere.
+              ** -avalon
+            */
+           if (copy == 1) {
+               (void) dbuf_delete (dyn, 1);
+               goto getmsg_init;
+           }
+           break;
+       }
+       length--;
+       if (!--i) {
+           if ((d = d->next)) {
+               s = d->data;
+               i = MIN (DBUFSIZ, dlen);
+           }
+       }
+       else
+           s++;
+    }
+
+    if (copy <= 0)
+       return 0;
+
+    /*
+       ** copy as much of the message as wanted into parse buffer
+     */
+    i = dbuf_get (dyn, buf, MIN (copy, length));
+    /*
+       ** and delete the rest of it!
+     */
+    if (copy - i > 0)
+       (void) dbuf_delete (dyn, copy - i);
+    if (i >= 0)
+       *(buf + i) = '\0';        /* mark end of messsage */
+
+    return i;
+}
diff --git a/src/hash.c b/src/hash.c
new file mode 100644 (file)
index 0000000..c816831
--- /dev/null
@@ -0,0 +1,1190 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/hash.c
+ *   Copyright (C) 1991 Darren Reed
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <limits.h>
+#include "numeric.h"
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "hash.h"
+#include "h.h"
+
+
+/* Quick & dirty inline version of mycmp for hash-tables -Donwulff */
+#define thecmp(str1, str2, where) { \
+                                    register char *st1=str1, *st2=str2; \
+                                    while (tolower(*st1)==tolower(*st2)) \
+                                    { \
+                                      if (!*st1) goto where; \
+                                      st1++; st2++; \
+                                    } \
+                                  }
+
+#ifdef DEBUGMODE
+static aHashEntry *clientTable = NULL;
+static aHashEntry *channelTable = NULL;
+static int clhits, clmiss;
+static int chhits, chmiss;
+int HASHSIZE = 32003;
+int CHANNELHASHSIZE = 10007;
+#else /* DEBUGMODE */
+static aHashEntry clientTable[HASHSIZE];
+static aHashEntry channelTable[CHANNELHASHSIZE];
+#endif /* DEBUGMODE */
+
+static aNotify *notifyTable[NOTIFYHASHSIZE];
+
+/*
+ * Hashing.
+ *
+ *   The server uses a chained hash table to provide quick and efficient
+ * hash table mantainence (providing the hash function works evenly over
+ * the input range).  The hash table is thus not susceptible to problems
+ * of filling all the buckets or the need to rehash.
+ *    It is expected that the hash table would look somehting like this
+ * during use:
+ *                   +-----+    +-----+    +-----+   +-----+
+ *                ---| 224 |----| 225 |----| 226 |---| 227 |---
+ *                   +-----+    +-----+    +-----+   +-----+
+ *                      |          |          |
+ *                   +-----+    +-----+    +-----+
+ *                   |  A  |    |  C  |    |  D  |
+ *                   +-----+    +-----+    +-----+
+ *                      |
+ *                   +-----+
+ *                   |  B  |
+ *                   +-----+
+ *
+ * A - GOPbot, B - chang, C - hanuaway, D - *.mu.OZ.AU
+ *
+ * The order shown above is just one instant of the server.  Each time a
+ * lookup is made on an entry in the hash table and it is found, the entry
+ * is moved to the top of the chain.
+ */
+
+#ifndef ELFHASH
+/*
+ * hash_nn_name
+ *
+ * Well, extensive profiling seems to indicate that the hash-function
+ * is a major provider to ircd's CPU-usage after all, so we're using
+ * _really_ minimalistic hash-function and make up for it with huge
+ * hash-tables. -Donwulff
+ */
+unsigned int hash_nn_name (hname)
+     const char *hname;
+{
+    unsigned int hash_value;
+
+    for (hash_value = 0; *hname; ++hname)
+       hash_value = (hash_value << 2) ^ tolower (*hname);
+
+    return (hash_value);
+}
+#else
+/* Take equal propotions from the size of int on all arcitechtures */
+#define BITS_IN_int             ( sizeof(int) * CHAR_BIT )
+#define THREE_QUARTERS         ((int) ((BITS_IN_int * 3) / 4))
+#define ONE_EIGHTH             ((int) (BITS_IN_int / 8))
+#define HIGH_BITS              ( ~((unsigned int)(~0) >> ONE_EIGHTH ))
+
+unsigned int hash_nn_name (hname)
+     const char *hname;
+{
+    unsigned int hash_value, i;
+
+    for (hash_value = 0; *hname; ++hname) {
+       /* Shift hash-value by one eights of int for adding every letter */
+       hash_value = (hash_value << ONE_EIGHTH) + tolower (*hname);
+       /* If the next shift would cause an overflow... */
+       if ((i = hash_value & HIGH_BITS) != 0)
+           /* Then wrap the upper quarter of bits back to the value */
+           hash_value = (hash_value ^ (i >> THREE_QUARTERS)) & ~HIGH_BITS;
+    }
+
+    return (hash_value);
+}
+#endif
+
+/*
+ * clear_*_hash_table
+ *
+ * Nullify the hashtable and its contents so it is completely empty.
+ */
+void clear_client_hash_table ()
+{
+#ifdef DEBUGMODE
+    clhits = 0;
+    clmiss = 0;
+    if (!clientTable)
+       clientTable =
+           (aHashEntry *) MyMalloc (HASHSIZE * sizeof (aHashEntry));
+#endif /* DEBUGMODE */
+
+    bzero ((char *) clientTable, sizeof (aHashEntry) * HASHSIZE);
+}
+
+void clear_channel_hash_table ()
+{
+#ifdef DEBUGMODE
+    chmiss = 0;
+    chhits = 0;
+    if (!channelTable)
+       channelTable = (aHashEntry *) MyMalloc (CHANNELHASHSIZE *
+                                               sizeof (aHashEntry));
+#endif /* DEBUGMODE */
+    bzero ((char *) channelTable, sizeof (aHashEntry) * CHANNELHASHSIZE);
+}
+
+void clear_notify_hash_table (void)
+{
+    bzero ((char *) notifyTable, sizeof (notifyTable));
+}
+
+/*
+ * add_to_client_hash_table
+ */
+int add_to_client_hash_table (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    int hashv;
+/* Also define this if you're using the insert test mode */
+#ifdef DEBUGMODE
+    aClient *acptr;
+#endif
+    hashv = hash_nn_name (name) % HASHSIZE;
+
+#ifdef DEBUGMODE
+    if (acptr = (aClient *) clientTable[hashv].list) {
+       while (acptr && mycmp (acptr->name, name))
+           acptr = acptr->hnext;
+       if (acptr)
+           dumpcore ("add_to_client_hash_table %s", name);
+    }
+    cptr->hnext = (aClient *) clientTable[hashv].list;
+    clientTable[hashv].list = (void *) cptr;
+    clientTable[hashv].links++;
+    clientTable[hashv].hits++;
+#else /* DEBUGMODE */
+#ifdef INSERTCHECK
+    if (acptr = (aClient *) clientTable[hashv]) {
+       while (acptr && mycmp (acptr->name, name))
+           acptr = acptr->hnext;
+       if (acptr)
+           dumpcore ("add_to_client_hash_table %s", name);
+    }
+#endif /* INSERTCHECK */
+    cptr->hnext = (aClient *) clientTable[hashv];
+    clientTable[hashv] = (void *) cptr;
+#endif /* DEBUGMODE */
+    return 0;
+}
+
+/*
+ * add_to_channel_hash_table
+ */
+int add_to_channel_hash_table (name, chptr)
+     char *name;
+     aChannel *chptr;
+{
+    int hashv;
+/* Also define this for the insertcheck mode */
+#ifdef DEBUGMODE
+    aChannel *chan;
+#endif
+    hashv = hash_nn_name (name) % CHANNELHASHSIZE;
+
+#ifdef DEBUGMODE
+    if (chan = (aChannel *) channelTable[hashv].list) {
+       while (chan && mycmp (chan->chname, name))
+           chan = chan->hnextch;
+       if (chan)
+           dumpcore ("add_to_channel_hash_table %s", name);
+    }
+    chptr->hnextch = (aChannel *) channelTable[hashv].list;
+    channelTable[hashv].list = (void *) chptr;
+    channelTable[hashv].links++;
+    channelTable[hashv].hits++;
+#else /* DEBUGMODE */
+#ifdef INSERTCHECK
+    if (chan = (aChannel *) channelTable[hashv]) {
+       while (chan && mycmp (chan->chname, name))
+           chan = chan->hnextch;
+       if (chan)
+           dumpcore ("add_to_channel_hash_table %s", name);
+    }
+#endif /* INSERTCHECK */
+    chptr->hnextch = (aChannel *) channelTable[hashv];
+    channelTable[hashv] = (void *) chptr;
+#endif /* DEBUGMODE */
+    return 0;
+}
+
+/*
+ * Rough figure of the datastructures for notify:
+ *
+ * NOTIFY HASH      cptr1
+ *   |                |- nick1
+ * nick1-|- cptr1     |- nick2
+ *   |   |- cptr2                cptr3
+ *   |   |- cptr3   cptr2          |- nick1
+ *   |                |- nick1
+ * nick2-|- cptr2     |- nick2
+ *       |- cptr1
+ *
+ * add-to-notify-hash-table:
+ * del-from-notify-hash-table:
+ * hash-del-notify-list:
+ * hash-check-notify:
+ * hash-get-notify:
+ */
+
+/*
+ * count_watch_memory
+ */
+void count_watch_memory (count, memory)
+     int *count;
+     u_long *memory;
+{
+    int i = NOTIFYHASHSIZE;
+    aNotify *anptr;
+
+
+    while (i--) {
+       anptr = notifyTable[i];
+       while (anptr) {
+           (*count)++;
+           (*memory) += sizeof (aNotify) + strlen (anptr->nick);
+           anptr = anptr->hnext;
+       }
+    }
+}
+
+/*
+ * add_to_notify_hash_table
+ */
+int add_to_notify_hash_table (nick, cptr)
+     char *nick;
+     aClient *cptr;
+{
+    int hashv;
+    aNotify *anptr;
+    Link *lp;
+
+
+    /* Get the right bucket... */
+    hashv = hash_nn_name (nick) % NOTIFYHASHSIZE;
+
+    /* Find the right nick (header) in the bucket, or NULL... */
+    if ((anptr = (aNotify *) notifyTable[hashv]))
+       while (anptr && mycmp (anptr->nick, nick))
+           anptr = anptr->hnext;
+
+    /* If found NULL (no header for this nick), make one... */
+    if (!anptr) {
+       anptr = (aNotify *) MyMalloc (sizeof (aNotify) + strlen (nick));
+       anptr->lasttime = 0;
+       strcpy (anptr->nick, nick);
+
+       anptr->notify = NULL;
+
+       anptr->hnext = notifyTable[hashv];
+       notifyTable[hashv] = anptr;
+    }
+    /* Is this client already on the notify-list? */
+    if ((lp = anptr->notify))
+       while (lp && (lp->value.cptr != cptr))
+           lp = lp->next;
+
+    /* No it isn't, so add it in the bucket and client addint it */
+    if (!lp) {
+       lp = anptr->notify;
+       anptr->notify = make_link ();
+       anptr->notify->value.cptr = cptr;
+       anptr->notify->next = lp;
+
+       lp = make_link ();
+       lp->next = cptr->notify;
+       lp->value.nptr = anptr;
+       cptr->notify = lp;
+       cptr->notifies++;
+    }
+    return 0;
+}
+
+/*
+ * hash_check_notify
+ */
+int hash_check_notify (cptr, reply)
+     aClient *cptr;
+     int reply;
+{
+    int hashv;
+    aNotify *anptr;
+    Link *lp;
+
+
+    /* Get us the right bucket */
+    hashv = hash_nn_name (cptr->name) % NOTIFYHASHSIZE;
+
+    /* Find the right header in this bucket */
+    if ((anptr = (aNotify *) notifyTable[hashv]))
+       while (anptr && mycmp (anptr->nick, cptr->name))
+           anptr = anptr->hnext;
+    if (!anptr)
+       return 0;                 /* This nick isn't on notify */
+
+    /* Update the time of last change to item */
+    anptr->lasttime = time (NULL);
+
+    /* Send notifies out to everybody on the list in header */
+    for (lp = anptr->notify; lp; lp = lp->next)
+       sendto_one (lp->value.cptr, rpl_str (reply), me.name,
+                   lp->value.cptr->name, cptr->name,
+                   (IsPerson (cptr) ? cptr->user->username : "<N/A>"),
+                   (IsPerson (cptr) ? MaskHost (cptr) : "<N/A>"),
+                   anptr->lasttime, cptr->info);
+
+    return 0;
+}
+
+/*
+ * hash_get_notify
+ */
+aNotify *hash_get_notify (name)
+     char *name;
+{
+    int hashv;
+    aNotify *anptr;
+
+
+    hashv = hash_nn_name (name) % NOTIFYHASHSIZE;
+
+    if ((anptr = (aNotify *) notifyTable[hashv]))
+       while (anptr && mycmp (anptr->nick, name))
+           anptr = anptr->hnext;
+
+    return anptr;
+}
+
+/*
+ * del_from_notify_hash_table
+ */
+int del_from_notify_hash_table (nick, cptr)
+     char *nick;
+     aClient *cptr;
+{
+    int hashv;
+    aNotify *anptr, *nlast = NULL;
+    Link *lp, *last = NULL;
+
+
+    /* Get the bucket for this nick... */
+    hashv = hash_nn_name (nick) % NOTIFYHASHSIZE;
+
+    /* Find the right header, maintaining last-link pointer... */
+    if ((anptr = (aNotify *) notifyTable[hashv]))
+       while (anptr && mycmp (anptr->nick, nick)) {
+           nlast = anptr;
+           anptr = anptr->hnext;
+       }
+    if (!anptr)
+       return 0;                 /* No such notify */
+
+    /* Find this client from the list of notifies... with last-ptr. */
+    if ((lp = anptr->notify))
+       while (lp && (lp->value.cptr != cptr)) {
+           last = lp;
+           lp = lp->next;
+       }
+    if (!lp)
+       return 0;                 /* No such client to notify */
+
+    /* Fix the linked list under header, then remove the notify entry */
+    if (!last)
+       anptr->notify = lp->next;
+    else
+       last->next = lp->next;
+    free_link (lp);
+
+    /* Do the same regarding the links in client-record... */
+    last = NULL;
+    if ((lp = cptr->notify))
+       while (lp && (lp->value.nptr != anptr)) {
+           last = lp;
+           lp = lp->next;
+       }
+    /*
+     * Give error on the odd case... probobly not even neccessary
+     *
+     * No error checking in ircd is unneccessary ;) -Cabal95
+     */
+    if (!lp)
+       sendto_ops ("WATCH debug error: del_from_notify_hash_table "
+                   "found a watch entry with no client "
+                   "counterpoint processing nick %s on client %s!",
+                   nick, cptr->user);
+    else {
+       if (!last)                /* First one matched */
+           cptr->notify = lp->next;
+       else
+           last->next = lp->next;
+       free_link (lp);
+    }
+
+    /* In case this header is now empty of notices, remove it */
+    if (!anptr->notify) {
+       if (!nlast)
+           notifyTable[hashv] = anptr->hnext;
+       else
+           nlast->hnext = anptr->hnext;
+       MyFree (anptr);
+    }
+    /* Update count of notifies on nick */
+    cptr->notifies--;
+
+    return 0;
+}
+
+/*
+ * hash_del_notify_list
+ */
+int hash_del_notify_list (cptr)
+     aClient *cptr;
+{
+    int hashv;
+    aNotify *anptr;
+    Link *np, *lp, *last;
+
+
+    if (!(np = cptr->notify))
+       return 0;                 /* Nothing to do */
+
+    cptr->notify = NULL;         /* Break the notify-list for client */
+    while (np) {
+       /* Find the notify-record from hash-table... */
+       anptr = np->value.nptr;
+       last = NULL;
+       for (lp = anptr->notify; lp && (lp->value.cptr != cptr);
+            lp = lp->next)
+           last = lp;
+
+       /* Not found, another "worst case" debug error */
+       if (!lp)
+           sendto_ops ("WATCH Debug error: hash_del_notify_list "
+                       "found a WATCH entry with no table "
+                       "counterpoint processing client %s!", cptr->name);
+       else {
+           /* Fix the notify-list and remove entry */
+           if (!last)
+               anptr->notify = lp->next;
+           else
+               last->next = lp->next;
+           free_link (lp);
+
+           /*
+            * If this leaves a header without notifies,
+            * remove it. Need to find the last-pointer!
+            */
+           if (!anptr->notify) {
+               aNotify *np2, *nl;
+
+               hashv = hash_nn_name (anptr->nick) % NOTIFYHASHSIZE;
+
+               nl = NULL;
+               np2 = notifyTable[hashv];
+               while (np2 != anptr) {
+                   nl = np2;
+                   np2 = np2->hnext;
+               }
+
+               if (nl)
+                   nl->hnext = anptr->hnext;
+               else
+                   notifyTable[hashv] = anptr->hnext;
+               MyFree (anptr);
+           }
+       }
+
+       lp = np;                  /* Save last pointer processed */
+       np = np->next;            /* Jump to the next pointer */
+       free_link (lp);           /* Free the previous */
+    }
+
+    cptr->notifies = 0;
+
+    return 0;
+}
+
+
+/*
+ * del_from_client_hash_table
+ */
+int del_from_client_hash_table (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    aClient *tmp, *prev = NULL;
+    int hashv;
+
+    hashv = hash_nn_name (name) % HASHSIZE;
+#ifdef DEBUGMODE
+    for (tmp = (aClient *) clientTable[hashv].list; tmp; tmp = tmp->hnext) {
+       if (tmp == cptr) {
+           if (prev)
+               prev->hnext = tmp->hnext;
+           else
+               clientTable[hashv].list = (void *) tmp->hnext;
+           tmp->hnext = NULL;
+           if (clientTable[hashv].links > 0) {
+               clientTable[hashv].links--;
+               return 1;
+           }
+           else
+               /*
+                * Should never actually return from here and
+                * if we do it is an error/inconsistency in the
+                * hash table.
+                */
+               return -1;
+           return 0;             /* Found, we can return -Donwulff */
+       }
+       prev = tmp;
+    }
+#else /* DEBUGMODE */
+    for (tmp = (aClient *) clientTable[hashv]; tmp; tmp = tmp->hnext) {
+       if (tmp == cptr) {
+           if (prev)
+               prev->hnext = tmp->hnext;
+           else
+               clientTable[hashv] = (void *) tmp->hnext;
+           tmp->hnext = NULL;
+           return 0;             /* Found, we can return -Donwulff */
+       }
+       prev = tmp;
+    }
+#endif /* DEBUGMODE */
+    return 0;
+}
+
+/*
+ * del_from_channel_hash_table
+ */
+int del_from_channel_hash_table (name, chptr)
+     char *name;
+     aChannel *chptr;
+{
+    aChannel *tmp, *prev = NULL;
+    int hashv;
+
+    hashv = hash_nn_name (name) % CHANNELHASHSIZE;
+#ifdef DEBUGMODE
+    for (tmp = (aChannel *) channelTable[hashv].list; tmp; tmp = tmp->hnextch) {
+       if (tmp == chptr) {
+           if (prev)
+               prev->hnextch = tmp->hnextch;
+           else
+               channelTable[hashv].list = (void *) tmp->hnextch;
+           tmp->hnextch = NULL;
+           if (channelTable[hashv].links > 0) {
+               channelTable[hashv].links--;
+               return 1;
+           }
+           else
+               return -1;
+           return 0;             /* Found, we can return -Donwulff */
+       }
+       prev = tmp;
+    }
+#else /* DEBUGMODE */
+    for (tmp = (aChannel *) channelTable[hashv]; tmp; tmp = tmp->hnextch) {
+       if (tmp == chptr) {
+           if (prev)
+               prev->hnextch = tmp->hnextch;
+           else
+               channelTable[hashv] = (void *) tmp->hnextch;
+           tmp->hnextch = NULL;
+           return 0;             /* Found, we can return -Donwulff */
+       }
+       prev = tmp;
+    }
+#endif /* DEBUGMODE */
+    return 0;
+}
+
+
+/*
+ * hash_get_chan_bucket
+ */
+aChannel *hash_get_chan_bucket (hashv)
+     int hashv;
+{
+    if (hashv > CHANNELHASHSIZE)
+       return NULL;
+#ifdef DEBUGMODE
+    return (aChannel *) channelTable[hashv].list;
+#else
+    return (aChannel *) channelTable[hashv];
+#endif
+}
+
+
+/*
+ * hash_find_client
+ */
+aClient *hash_find_client (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    aClient *tmp;
+    aClient *prv = NULL;
+#ifdef DEBUGMODE
+    aHashEntry *tmp3;
+#endif /* DEBUGMODE */
+    int hashv;
+
+    hashv = hash_nn_name (name) % HASHSIZE;
+
+    /*
+     * Got the bucket, now search the chain.
+     */
+#ifdef  DEBUGMODE
+    tmp3 = &clientTable[hashv];
+
+    for (tmp = (aClient *) tmp3->list; tmp; prv = tmp, tmp = tmp->hnext)
+       thecmp (name, tmp->name, c_move_to_top);
+    clmiss++;
+    return (cptr);
+  c_move_to_top:
+    clhits++;
+    /*
+     * If the member of the hashtable we found isnt at the top of its
+     * chain, put it there.  This builds a most-frequently used order into
+     * the chains of the hash table, giving speadier lookups on those nicks
+     * which are being used currently.  This same block of code is also
+     * used for channels and servers for the same performance reasons.
+     */
+    if (prv) {
+       aClient *tmp2;
+
+       tmp2 = (aClient *) tmp3->list;
+       tmp3->list = (void *) tmp;
+       prv->hnext = tmp->hnext;
+       tmp->hnext = tmp2;
+    }
+#else /* DEBUGMODE */
+    for (tmp = (aClient *) clientTable[hashv]; tmp;
+        prv = tmp, tmp = tmp->hnext)
+       thecmp (name, tmp->name, c_move_to_top);
+    return (cptr);
+  c_move_to_top:
+    if (prv) {
+       aClient *tmp2;
+
+       tmp2 = (aClient *) clientTable[hashv];
+       clientTable[hashv] = (void *) tmp;
+       prv->hnext = tmp->hnext;
+       tmp->hnext = tmp2;
+    }
+#endif /* DEBUGMODE */
+    return (tmp);
+}
+
+/*
+ * hash_find_nickserver
+ */
+aClient *hash_find_nickserver (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    aClient *tmp;
+    aClient *prv = NULL;
+#ifdef DEBUGMODE
+    aHashEntry *tmp3;
+#endif /* DEBUGMODE */
+    int hashv;
+    char *serv;
+
+    serv = index (name, '@');
+    *serv++ = '\0';
+    hashv = hash_nn_name (name) % HASHSIZE;
+
+    /*
+     * Got the bucket, now search the chain.
+     */
+#ifdef  DEBUGMODE
+    tmp3 = &clientTable[hashv];
+    for (tmp = (aClient *) tmp3->list; tmp; prv = tmp, tmp = tmp->hnext)
+       if (tmp->user &&
+           mycmp (name, tmp->name) == 0
+           && mycmp (serv, tmp->user->server) == 0)
+           goto c_move_to_top;
+
+    clmiss++;
+/*  *--serv = '\0'; *//* This code has no function, perhaps meant @? */
+    return (cptr);               /* For now, just remarking it out... -Donwulff */
+
+  c_move_to_top:
+    clhits++;
+    if (prv) {
+       aClient *tmp2;
+
+       tmp2 = (aClient *) tmp3->list;
+       tmp3->list = (void *) tmp;
+       prv->hnext = tmp->hnext;
+       tmp->hnext = tmp2;
+    }
+#else /* DEBUGMODE */
+    for (tmp = (aClient *) clientTable[hashv]; tmp;
+        prv = tmp, tmp = tmp->hnext)
+       if (tmp->user && mycmp (name, tmp->name) == 0
+           && mycmp (serv, tmp->user->server) == 0)
+           goto c_move_to_top;
+/*  *--serv = '\0'; *//* This code has no function, perhaps meant @? */
+    return (cptr);               /* For now, just remarking it out... -Donwulff */
+
+  c_move_to_top:
+    if (prv) {
+       aClient *tmp2;
+
+       tmp2 = (aClient *) clientTable[hashv];
+       clientTable[hashv] = (void *) tmp;
+       prv->hnext = tmp->hnext;
+       tmp->hnext = tmp2;
+    }
+#endif /* DEBUGMODE */
+/*  *--serv = '\0'; *//* This code has no function, perhaps meant @? */
+    return (tmp);                /* For now, just remarking it out... -Donwulff */
+}
+
+/* find_server_wildcard 
+
+ * Same thing as below, except you can use wildcards
+ * I supposed this is quite a CPU-consuming routine. -GZ 
+ *
+ */
+
+aClient *find_server_wildcard (char *server)
+{
+    aClient *acptr;
+
+    for (acptr = &me; acptr; acptr = acptr->prev) {
+       if (IsServer (acptr)) {
+           if (match (server, acptr->name) == 0)
+               return acptr;
+       }
+    }
+
+    return NULL;
+}
+
+/*
+ * hash_find_server
+ */
+aClient *hash_find_server (server, cptr)
+     char *server;
+     aClient *cptr;
+{
+    aClient *tmp, *prv = NULL;
+    char *t;
+    char ch;
+#ifdef DEBUGMODE
+    aHashEntry *tmp3;
+#endif /* DEBUGMODE */
+
+    int hashv;
+
+    hashv = hash_nn_name (server) % HASHSIZE;
+
+#ifdef  DEBUGMODE
+    tmp3 = &clientTable[hashv];
+
+    for (tmp = (aClient *) tmp3->list; tmp; prv = tmp, tmp = tmp->hnext) {
+       if (!IsServer (tmp) && !IsMe (tmp))
+           continue;
+       thecmp (server, tmp->name, s_move_to_top);
+    }
+#else /* DEBUGMODE */
+    for (tmp = (aClient *) clientTable[hashv]; tmp;
+        prv = tmp, tmp = tmp->hnext) {
+       if (!IsServer (tmp) && !IsMe (tmp))
+           continue;
+       thecmp (server, tmp->name, s_move_to_top);
+    }
+#endif /* DEBUGMODE */
+    t = ((char *) server + strlen (server));
+    /*
+     * Whats happening in this next loop ? Well, it takes a name like
+     * foo.bar.edu and proceeds to search for *.edu and then *.bar.edu.
+     * This is for checking full server names against masks although
+     * it isnt often done this way in lieu of using match().
+     */
+    for (;;) {
+       t--;
+       for (; t > server; t--)
+           if (*(t + 1) == '.')
+               break;
+       if (t <= server || *t == '*')
+           break;
+       ch = *t;
+       *t = '*';
+       /*
+        * Dont need to check IsServer() here since nicknames cant
+        *have *'s in them anyway.
+        */
+       if (((tmp = hash_find_client (t, cptr))) != cptr) {
+           *t = ch;
+           return (tmp);
+       }
+       *t = ch;
+    }
+#ifdef DEBUGMODE
+    clmiss++;
+    return (cptr);
+  s_move_to_top:
+    clhits++;
+
+    if (prv) {
+       aClient *tmp2;
+
+       tmp2 = (aClient *) tmp3->list;
+       tmp3->list = (void *) tmp;
+       prv->hnext = tmp->hnext;
+       tmp->hnext = tmp2;
+    }
+#else /* DEBUGMODE */
+    return (cptr);
+  s_move_to_top:
+    if (prv) {
+       aClient *tmp2;
+
+       tmp2 = (aClient *) clientTable[hashv];
+       clientTable[hashv] = (void *) tmp;
+       prv->hnext = tmp->hnext;
+       tmp->hnext = tmp2;
+    }
+#endif /* DEBUGMODE */
+    return (tmp);
+}
+
+/*
+ * hash_find_channel
+ */
+aChannel *hash_find_channel (name, chptr)
+     char *name;
+     aChannel *chptr;
+{
+    int hashv;
+    aChannel *tmp;
+    aChannel *prv = NULL;
+#ifdef DEBUGMODE
+    aHashEntry *tmp3;
+#endif /* DEBUGMODE */
+    hashv = hash_nn_name (name) % CHANNELHASHSIZE;
+
+#ifdef  DEBUGMODE
+    tmp3 = &channelTable[hashv];
+
+    for (tmp = (aChannel *) tmp3->list; tmp; prv = tmp, tmp = tmp->hnextch)
+       thecmp (name, tmp->chname, c_move_to_top);
+    chmiss++;
+    return chptr;
+  c_move_to_top:
+    chhits++;
+    if (prv) {
+       register aChannel *tmp2;
+
+       tmp2 = (aChannel *) tmp3->list;
+       tmp3->list = (void *) tmp;
+       prv->hnextch = tmp->hnextch;
+       tmp->hnextch = tmp2;
+    }
+#else /* DEBUGMODE */
+    for (tmp = (aChannel *) channelTable[hashv]; tmp;
+        prv = tmp, tmp = tmp->hnextch)
+       thecmp (name, tmp->chname, c_move_to_top);
+    return chptr;
+  c_move_to_top:
+    if (prv) {
+       register aChannel *tmp2;
+
+       tmp2 = (aChannel *) channelTable[hashv];
+       channelTable[hashv] = (void *) tmp;
+       prv->hnextch = tmp->hnextch;
+       tmp->hnextch = tmp2;
+    }
+#endif /* DEBUGMODE */
+    return (tmp);
+}
+
+/*
+ * NOTE: this command is not supposed to be an offical part of the ircd
+ *       protocol.  It is simply here to help debug and to monitor the
+ *       performance of the hash functions and table, enabling a better
+ *       algorithm to be sought if this one becomes troublesome.
+ *       -avalon
+ */
+
+int m_hash (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+#ifdef DEBUGMODE
+    register int l, i;
+    register aHashEntry *tab;
+    int deepest = 0, deeplink = 0, showlist = 0, tothits = 0;
+    int mosthit = 0, mosthits = 0, used = 0, used_now = 0, totlink = 0;
+    int link_pop[10], size = HASHSIZE;
+    char ch;
+    aHashEntry *table;
+#endif
+    sendto_one (sptr, "NOTICE %s :[1750 56 s_bsd.c] [5151 105 s_user.c]",
+               parv[0]);
+    sendto_one (sptr, "NOTICE %s :[50500 80 s_serv.c] [60357 30 ircd.c]",
+               parv[0]);
+    sendto_one (sptr, "NOTICE %s :[1771 101 channel.c] [39540 24 s_misc.c]",
+               parv[0]);
+    sendto_one (sptr, "NOTICE %s :[26625 30 hash.c.old] [359 4 version.c.SH]",
+               parv[0]);
+    sendto_one (sptr, "NOTICE %s :[] HOSTID", parv[0]);
+#ifndef DEBUGMODE
+    return 0;
+#else
+    if (!IsPrivileged (sptr))
+       return 0;
+
+    if (parc > 1) {
+       ch = *parv[1];
+       if (islower (ch))
+           table = clientTable;
+       else {
+           table = channelTable;
+           size = CHANNELHASHSIZE;
+       }
+       if (ch == 'L' || ch == 'l')
+           showlist = 1;
+    }
+    else {
+       ch = '\0';
+       table = clientTable;
+    }
+
+    for (i = 0; i < 10; i++)
+       link_pop[i] = 0;
+    for (i = 0; i < size; i++) {
+       tab = &table[i];
+       l = tab->links;
+       if (showlist)
+           sendto_one (sptr,
+                       "NOTICE %s :Hash Entry:%6d Hits:%7d Links:%6d",
+                       parv[0], i, tab->hits, l);
+       if (l > 0) {
+           if (l < 10)
+               link_pop[l]++;
+           else
+               link_pop[9]++;
+           used_now++;
+           totlink += l;
+           if (l > deepest) {
+               deepest = l;
+               deeplink = i;
+           }
+       }
+       else
+           link_pop[0]++;
+       l = tab->hits;
+       if (l) {
+           used++;
+           tothits += l;
+           if (l > mosthits) {
+               mosthits = l;
+               mosthit = i;
+           }
+       }
+    }
+    switch ((int) ch) {
+    case 'V':
+    case 'v':
+       {
+           register aClient *acptr;
+           int bad = 0, listlength = 0;
+
+           for (acptr = client; acptr; acptr = acptr->next) {
+               if (hash_find_client (acptr->name, acptr) != acptr) {
+                   if (ch == 'V')
+                       sendto_one (sptr, "NOTICE %s :Bad hash for %s",
+                                   parv[0], acptr->name);
+                   bad++;
+               }
+               listlength++;
+           }
+           sendto_one (sptr, "NOTICE %s :List Length: %d Bad Hashes: %d",
+                       parv[0], listlength, bad);
+       }
+    case 'P':
+    case 'p':
+       for (i = 0; i < 10; i++)
+           sendto_one (sptr, "NOTICE %s :Entires with %d links : %d",
+                       parv[0], i, link_pop[i]);
+       return (0);
+    case 'r':
+       {
+           aClient *acptr;
+
+           sendto_one (sptr, "NOTICE %s :Rehashing Client List.", parv[0]);
+           clear_client_hash_table ();
+           for (acptr = client; acptr; acptr = acptr->next)
+               (void) add_to_client_hash_table (acptr->name, acptr);
+           break;
+       }
+    case 'R':
+       {
+           aChannel *acptr;
+
+           sendto_one (sptr, "NOTICE %s :Rehashing Channel List.", parv[0]);
+           clear_channel_hash_table ();
+           for (acptr = channel; acptr; acptr = acptr->nextch)
+               (void) add_to_channel_hash_table (acptr->chname, acptr);
+           break;
+       }
+    case 'H':
+       if (parc > 2)
+           sendto_one (sptr, "NOTICE %s :%s hash to entry %d",
+                       parv[0], parv[2],
+                       hash_nn_name (parv[2]) % CHANNELHASHSIZE);
+       return (0);
+    case 'h':
+       if (parc > 2)
+           sendto_one (sptr, "NOTICE %s :%s hash to entry %d",
+                       parv[0], parv[2], hash_nn_name (parv[2]) % HASHSIZE);
+       return (0);
+/* Quick hack for getting memory statistics from list.c -Donwulff */
+    case 'm':
+       send_listinfo (sptr, parv[0]);
+       return (0);
+    case 'n':
+       {
+           aClient *tmp;
+           int max;
+
+           if (parc <= 2)
+               return (0);
+           l = atoi (parv[2]) % HASHSIZE;
+           if (parc > 3)
+               max = atoi (parv[3]) % HASHSIZE;
+           else
+               max = l;
+           for (; l <= max; l++)
+               for (i = 0, tmp = (aClient *) clientTable[l].list; tmp;
+                    i++, tmp = tmp->hnext) {
+                   if (parv[1][2] == '1' && tmp != tmp->from)
+                       continue;
+                   sendto_one (sptr, "NOTICE %s :Node: %d #%d %s",
+                               parv[0], l, i, tmp->name);
+               }
+           return (0);
+       }
+    case 'N':
+       {
+           aChannel *tmp;
+           int max;
+
+           if (parc <= 2)
+               return (0);
+           l = atoi (parv[2]) % CHANNELHASHSIZE;
+           if (parc > 3)
+               max = atoi (parv[3]) % CHANNELHASHSIZE;
+           else
+               max = l;
+           for (; l <= max; l++)
+               for (i = 0, tmp = (aChannel *) channelTable[l].list; tmp;
+                    i++, tmp = tmp->hnextch)
+                   sendto_one (sptr, "NOTICE %s :Node: %d #%d %s",
+                               parv[0], l, i, tmp->chname);
+           return (0);
+       }
+    case 'z':
+       {
+           aClient *acptr;
+
+           if (parc <= 2)
+               return 0;
+           l = atoi (parv[2]);
+           if (l < 256)
+               return 0;
+           (void) MyFree ((char *) clientTable);
+           clientTable = (aHashEntry *) MyMalloc (sizeof (aHashEntry) * l);
+           HASHSIZE = l;
+           clear_client_hash_table ();
+           for (acptr = client; acptr; acptr = acptr->next) {
+               acptr->hnext = NULL;
+               (void) add_to_client_hash_table (acptr->name, acptr);
+           }
+           sendto_one (sptr, "NOTICE %s :HASHSIZE now %d", parv[0], l);
+           break;
+       }
+    case 'Z':
+       {
+           aChannel *acptr;
+
+           if (parc <= 2)
+               return 0;
+           l = atoi (parv[2]);
+           if (l < 256)
+               return 0;
+           (void) MyFree ((char *) channelTable);
+           channelTable = (aHashEntry *) MyMalloc (sizeof (aHashEntry) * l);
+           CHANNELHASHSIZE = l;
+           clear_channel_hash_table ();
+           for (acptr = channel; acptr; acptr = acptr->nextch) {
+               acptr->hnextch = NULL;
+               (void) add_to_channel_hash_table (acptr->chname, acptr);
+           }
+           sendto_one (sptr, "NOTICE %s :CHANNELHASHSIZE now %d", parv[0],
+                       l);
+           break;
+       }
+    default:
+       break;
+    }
+    sendto_one (sptr, "NOTICE %s :Entries Hashed: %d NonEmpty: %d of %d",
+               parv[0], totlink, used_now, size);
+    if (!used_now)
+       used_now = 1;
+    sendto_one (sptr, "NOTICE %s :Hash Ratio (av. depth): %f %%Full: %f",
+               parv[0], (float) ((1.0 * totlink) / (1.0 * used_now)),
+               (float) ((1.0 * used_now) / (1.0 * size)));
+    sendto_one (sptr, "NOTICE %s :Deepest Link: %d Links: %d",
+               parv[0], deeplink, deepest);
+    if (!used)
+       used = 1;
+    sendto_one (sptr, "NOTICE %s :Total Hits: %d Unhit: %d Av Hits: %f",
+               parv[0], tothits, size - used,
+               (float) ((1.0 * tothits) / (1.0 * used)));
+    sendto_one (sptr, "NOTICE %s :Entry Most Hit: %d Hits: %d",
+               parv[0], mosthit, mosthits);
+    sendto_one (sptr, "NOTICE %s :Client hits %d miss %d",
+               parv[0], clhits, clmiss);
+    sendto_one (sptr, "NOTICE %s :Channel hits %d miss %d",
+               parv[0], chhits, chmiss);
+    return 0;
+#endif /* DEBUGMODE */
+}
diff --git a/src/ircd.c b/src/ircd.c
new file mode 100644 (file)
index 0000000..797cb33
--- /dev/null
@@ -0,0 +1,967 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/ircd.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "userload.h"
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <pwd.h>
+#include <sys/time.h>
+#ifdef HPUX
+#define _KERNEL                          /* HPUX has the world's worst headers... */
+#endif
+#include <sys/resource.h>
+#ifdef HPUX
+#undef _KERNEL
+#endif
+#include <errno.h>
+#include "h.h"
+
+#ifdef __FreeBSD__
+char *malloc_options = "h" MALLOC_FLAGS_EXTRA;
+#endif
+
+#ifdef NOSPOOF
+u_int32_t NOSPOOF_SEED01, NOSPOOF_SEED02;
+#endif /* NOSPOOF */
+
+int R_do_dns, R_fin_dns, R_fin_dnsc, R_fail_dns, R_do_id, R_fin_id, R_fail_id;
+
+char REPORT_DO_DNS[128], REPORT_FIN_DNS[128], REPORT_FIN_DNSC[128],
+    REPORT_FAIL_DNS[128], REPORT_DO_ID[128], REPORT_FIN_ID[128],
+    REPORT_FAIL_ID[128];
+
+aClient me;       /* That's me */
+aClient *client = &me; /* Pointer to beginning of Client list */
+
+void server_reboot (char *);
+void restart PROTO ((char *));
+static void open_debugfile (), setup_signals ();
+void check_lusers (void);
+
+char **myargv;
+int portnum = -1;  /* Server port number, listening this */
+char *configfile = CONFIGFILE; /* Server configuration file */
+int debuglevel = -1;   /* Server debug level */
+int bootopt = 0;   /* Server boot option flags */
+char *debugmode = "";  /*  -"-    -"-   -"-  */
+char *sbrk0;      /* initial sbrk(0) */
+static int dorehash = 0;
+static char *dpath = DPATH;
+
+time_t nextconnect = 1;        /* time for next try_connections call */
+time_t nextping = 1;   /* same as above for check_pings() */
+time_t nextdnscheck = 0;       /* next time to poll dns to force timeouts */
+time_t nextexpire = 1; /* next expire run on the dns cache */
+time_t lastlucheck = 0;
+time_t lastbwcheck = 0;        /* next time to do the bandwidtch check */
+
+int lu_noninv, lu_inv, lu_serv, lu_oper, lu_unknown, lu_channel, lu_lu,
+    lu_lulocal, lu_lserv, lu_clu, lu_mlu, lu_cglobalu, lu_mglobalu;
+
+#ifdef CLONE_CHECK
+aClone *Clones = NULL;
+char clonekillhost[100];
+#endif
+
+int client_count = 0;  /* I guess -SJW */
+
+time_t NOW;
+#if    defined(PROFIL)
+extern etext ();
+
+VOIDSIG s_monitor ()
+{
+    static int mon = 0;
+#ifdef POSIX_SIGNALS
+    struct sigaction act;
+#endif
+
+    (void) moncontrol (mon);
+    mon = 1 - mon;
+#ifdef POSIX_SIGNALS
+    act.sa_handler = s_rehash;
+    act.sa_flags = 0;
+    (void) sigemptyset (&act.sa_mask);
+    (void) sigaddset (&act.sa_mask, SIGUSR1);
+    (void) sigaction (SIGUSR1, &act, NULL);
+#else
+    (void) signal (SIGUSR1, s_monitor);
+#endif
+}
+
+#endif
+
+VOIDSIG s_die ()
+{
+
+    int fd;
+    char buff[20];
+    if ((fd = open (LUSERS, O_CREAT | O_WRONLY, 0600)) >= 0) {
+       bzero (buff, sizeof (buff));
+       (void) sprintf (buff, "%i %i\n", lu_mglobalu, lu_mlu);
+       if (write (fd, buff, strlen (buff)) == -1)
+           (void) close (fd);
+    }
+#ifdef USE_SYSLOG
+    (void) syslog (LOG_CRIT, "Server Killed By SIGTERM");
+#endif
+    flush_connections (me.fd);
+
+    exit (-1);
+}
+
+static VOIDSIG s_rehash ()
+{
+#ifdef POSIX_SIGNALS
+    struct sigaction act;
+#endif
+    dorehash = 1;
+#ifdef POSIX_SIGNALS
+    act.sa_handler = s_rehash;
+    act.sa_flags = 0;
+    (void) sigemptyset (&act.sa_mask);
+    (void) sigaddset (&act.sa_mask, SIGHUP);
+    (void) sigaction (SIGHUP, &act, NULL);
+#else
+    (void) signal (SIGHUP, s_rehash);  /* sysV -argv */
+#endif
+}
+
+void restart (mesg)
+     char *mesg;
+{
+#ifdef USE_SYSLOG
+    (void) syslog (LOG_WARNING, "Restarting Server because: %s", mesg);
+#endif
+    server_reboot (mesg);
+}
+
+VOIDSIG s_restart ()
+{
+    static int restarting = 0;
+
+#ifdef USE_SYSLOG
+    (void) syslog (LOG_WARNING, "Server Restarting on SIGINT");
+#endif
+    if (restarting == 0) {
+       /* Send (or attempt to) a dying scream to oper if present */
+
+       restarting = 1;
+       server_reboot ("SIGINT");
+    }
+}
+
+void server_reboot (mesg)
+     char *mesg;
+{
+    int i;
+
+    sendto_ops ("Restarting server... %s", mesg);
+    Debug ((DEBUG_NOTICE, "Restarting server... %s", mesg));
+    flush_connections (me.fd);
+    /*
+       ** fd 0 must be 'preserved' if either the -d or -i options have
+       ** been passed to us before restarting.
+     */
+#ifdef USE_SYSLOG
+    (void) closelog ();
+#endif
+    for (i = 3; i < MAXCONNECTIONS; i++)
+       (void) close (i);
+    if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
+       (void) close (2);
+    (void) close (1);
+    if ((bootopt & BOOT_CONSOLE) || isatty (0))
+       (void) close (0);
+    if (!(bootopt & (BOOT_INETD | BOOT_OPER)))
+       (void) execv (MYNAME, myargv);
+#ifdef USE_SYSLOG
+    /* Have to reopen since it has been closed above */
+
+    openlog (myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
+    syslog (LOG_CRIT, "execv(%s,%s) failed: %m\n", MYNAME, myargv[0]);
+    closelog ();
+#endif
+    Debug ((DEBUG_FATAL, "Couldn't restart server: %s", strerror (errno)));
+    exit (-1);
+}
+
+
+/*
+   ** try_connections
+   **
+   **   Scan through configuration and try new connections.
+   **   Returns the calendar time when the next call to this
+   **   function should be made latest. (No harm done if this
+   **   is called earlier or later...)
+ */
+static time_t try_connections (currenttime)
+     time_t currenttime;
+{
+    aConfItem *aconf;
+    aClient *cptr;
+    aConfItem **pconf;
+    int connecting, confrq, am_connected_leaf;
+    time_t next = 0;
+    aClass *cltmp;
+    aConfItem *cconf, *con_conf = NULL;
+    int con_class = 0;
+
+    connecting = FALSE;
+    am_connected_leaf = FALSE;
+    Debug ((DEBUG_NOTICE, "Connection check at   : %s",
+           myctime (currenttime)));
+    for (aconf = conf; aconf; aconf = aconf->next) {
+       /* Also when already connecting! (update holdtimes) --SRB */
+       if (!(aconf->status & (CONF_CONNECT_SERVER | CONF_NZCONNECT_SERVER))
+           || aconf->port <= 0)
+           continue;
+       cltmp = Class (aconf);
+       /*
+          ** Skip this entry if the use of it is still on hold until
+          ** future. Otherwise handle this entry (and set it on hold
+          ** until next time). Will reset only hold times, if already
+          ** made one successfull connection... [this algorithm is
+          ** a bit fuzzy... -- msa >;) ]
+        */
+
+       if ((aconf->hold > currenttime)) {
+           if ((next > aconf->hold) || (next == 0))
+               next = aconf->hold;
+           continue;
+       }
+       confrq = get_con_freq (cltmp);
+       aconf->hold = currenttime + confrq;
+       /*
+          ** Found a CONNECT config with port specified, scan clients
+          ** and see if this server is already connected?
+        */
+       cptr = find_name (aconf->name, (aClient *) NULL);
+#ifndef HUB
+       /* Leaf servers should not connect to other servers, no matter 
+          what connect rules might exist when they are already connected 
+          to another server.  -- Remmy */
+       if (cptr)
+           am_connected_leaf = TRUE;
+#endif /* HUB */
+       /* zero second connect frequincy should mean "DISABLED" not 
+          "connect every time we find the unconnected server in this 
+          loop" dammit
+          -InnerFIRE 
+        */
+       if (confrq > 0 && !cptr && (Links (cltmp) < MaxLinks (cltmp)) &&
+           (!connecting || (Class (cltmp) > con_class))) {
+           con_class = Class (cltmp);
+           con_conf = aconf;
+           /* We connect only one at time... */
+           connecting = TRUE;
+       }
+       if ((next > aconf->hold) || (next == 0))
+           next = aconf->hold;
+    }
+    if (connecting && !am_connected_leaf) {
+       if (con_conf->next) {     /* are we already last? */
+           for (pconf = &conf; (aconf = *pconf); pconf = &(aconf->next))
+               /* put the current one at the end and
+                * make sure we try all connections
+                */
+               if (aconf == con_conf)
+                   *pconf = aconf->next;
+           (*pconf = con_conf)->next = 0;
+       }
+       if (connect_server (con_conf, (aClient *) NULL,
+                           (struct hostent *) NULL) == 0)
+           sendto_ops ("Connection to %s[%s] activated.",
+                       con_conf->name, con_conf->host);
+    }
+    Debug ((DEBUG_NOTICE, "Next connection check : %s", myctime (next)));
+    return (next);
+}
+
+/* Now find_kill is only called when a kline-related command is used:
+   AKILL/RAKILL/KLINE/UNKLINE/REHASH.  Very significant CPU usage decrease.
+   I made changes to evm_lusers every check_pings call to add new parameter.
+   -- Barubary */
+
+extern time_t check_pings (time_t currenttime, int check_kills)
+{
+    aClient *cptr;
+    int killflag;
+    int ping = 0, i, rflag = 0;
+    time_t oldest = 0, timeout;
+
+    for (i = 0; i <= highest_fd; i++) {
+       if (!(cptr = local[i]) || IsMe (cptr) || IsLog (cptr))
+           continue;
+
+       /*
+          ** Note: No need to notify opers here. It's
+          ** already done when "FLAGS_DEADSOCKET" is set.
+        */
+       if (cptr->flags & FLAGS_DEADSOCKET) {
+           (void) exit_client (cptr, cptr, &me, "Dead socket");
+            i--; /* If we don't do this, we'd skip a client! */
+           continue;
+       }
+       if (check_kills)
+           killflag = IsPerson (cptr) ? find_kill (cptr) : 0;
+       else
+           killflag = 0;
+       if (check_kills && !killflag && IsPerson (cptr))
+           if (find_zap (cptr, 1))
+               killflag = 1;
+       ping = IsRegistered (cptr) ? get_client_ping (cptr) : CONNECTTIMEOUT;
+       Debug ((DEBUG_DEBUG, "c(%s)=%d p %d k %d r %d a %d",
+               cptr->name, cptr->status, ping, killflag, rflag,
+               currenttime - cptr->lasttime));
+       /*
+        * Ok, so goto's are ugly and can be avoided here but this code
+        * is already indented enough so I think its justified. -avalon
+        */
+       if (!killflag && !rflag && IsRegistered (cptr) &&
+           (ping >= currenttime - cptr->lasttime))
+           goto ping_timeout;
+       /*
+        * If the server hasnt talked to us in 2*ping seconds
+        * and it has a ping time, then close its connection.
+        * If the client is a user and a KILL line was found
+        * to be active, close this connection too.
+        */
+       if (killflag || rflag ||
+           ((currenttime - cptr->lasttime) >= (2 * ping) &&
+            (cptr->flags & FLAGS_PINGSENT)) || (!IsRegistered (cptr)
+                                                && (currenttime -
+                                                    cptr->firsttime) >=
+                                                ping)) {
+           if (!IsRegistered (cptr) && DoingDNS (cptr)) {
+               if (cptr->cc) {
+                   if (DoingDNS (cptr))
+                       write (cptr->fd, REPORT_FAIL_DNS, R_fail_dns);
+               }
+
+               Debug ((DEBUG_NOTICE,
+                       "DNS timeout %s", get_client_name (cptr, TRUE)));
+               del_queries ((char *) cptr);
+               ClearDNS (cptr);
+               SetAccess (cptr);
+               cptr->firsttime = currenttime;
+               cptr->lasttime = currenttime;
+               continue;
+           }
+           if (IsServer (cptr) || IsConnecting (cptr) || IsHandshake (cptr)) {
+               sendto_ops ("No response from %s, closing link",
+                           get_client_name (cptr, FALSE));
+               sendto_serv_butone (&me,
+                                   ":%s GNOTICE :No response from %s, closing link",
+                                   me.name, get_client_name (cptr, FALSE));
+           }
+           /*
+            * this is used for KILL lines with time restrictions
+            * on them - send a messgae to the user being killed
+            * first.
+            */
+           if (killflag && IsPerson (cptr))
+               sendto_ops ("Kill line active for %s",
+                           get_client_name (cptr, FALSE));
+
+           if (killflag) {
+               (void) exit_client (cptr, cptr, &me,
+                                   "User has been banned from this server");
+                i--; /* If we don't do this, we'd skip a client! */
+            } else {
+               (void) exit_client (cptr, cptr, &me, "Ping timeout");
+                i--; /* If we don't do this, we'd skip a client! */
+            }
+           continue;
+       }
+       else if (IsRegistered (cptr) && (cptr->flags & FLAGS_PINGSENT) == 0) {
+           /*
+            * if we havent PINGed the connection and we havent
+            * heard from it in a while, PING it to make sure
+            * it is still alive.
+            */
+           cptr->flags |= FLAGS_PINGSENT;
+           /* not nice but does the job */
+           cptr->lasttime = currenttime - ping;
+           sendto_one (cptr, "PING :%s", me.name);
+       }
+      ping_timeout:
+       timeout = cptr->lasttime + ping;
+       while (timeout <= currenttime)
+           timeout += ping;
+       if (timeout < oldest || !oldest)
+           oldest = timeout;
+    }
+    if (!oldest || oldest < currenttime)
+       oldest = currenttime + PINGFREQUENCY;
+    Debug ((DEBUG_NOTICE, "Next check_ping() call at: %s, %d %d %d",
+           myctime (oldest), ping, oldest, currenttime));
+
+    return (oldest);
+}
+
+/*
+   ** bad_command
+   **   This is called when the commandline is not acceptable.
+   **   Give error message and exit without starting anything.
+ */
+static int bad_command ()
+{
+    (void)
+       printf
+       ("Usage: ircd %s[-h servername] [-p portnumber] [-x loglevel] [-ts]\n",
+#ifdef CMDLINE_CONFIG
+        "[-f config] "
+#else
+        ""
+#endif /* CMDLINE_CONFIG */
+       );
+    (void) printf ("Server not started\n\n");
+    return (-1);
+}
+
+int main (argc, argv)
+     int argc;
+     char *argv[];
+
+{
+    uid_t uid, euid;
+    time_t delay = 0;
+    int portarg = 0;
+#ifdef  FORCE_CORE
+    struct rlimit corelim;
+#endif
+
+    sbrk0 = (char *) sbrk ((size_t) 0);
+    uid = getuid ();
+    euid = geteuid ();
+#ifdef PROFIL
+    (void) monstartup (0, etext);
+    (void) moncontrol (1);
+    (void) signal (SIGUSR1, s_monitor);
+#endif /* PROFIL */
+
+    client_count = 0;
+    global_count = 0;
+
+#ifdef CHROOTDIR
+    if (chdir (dpath)) {
+       perror ("chdir");
+       exit (-1);
+    }
+    res_init ();
+    if (chroot (DPATH)) {
+       (void) fprintf (stderr, "ERROR:  Cannot chdir/chroot\n");
+       exit (5);
+    }
+#endif /*CHROOTDIR */
+
+    myargv = argv;
+    (void) umask (077);                  /* better safe than sorry --SRB */
+    bzero ((char *) &me, sizeof (me));
+
+    setup_signals ();
+    initload ();
+
+#ifdef FORCE_CORE
+    corelim.rlim_cur = corelim.rlim_max = RLIM_INFINITY;
+    if (setrlimit (RLIMIT_CORE, &corelim))
+       printf ("unlimit core size failed; errno = %d\n", errno);
+#endif
+
+    /*
+       ** All command line parameters have the syntax "-fstring"
+       ** or "-f string" (e.g. the space is optional). String may
+       ** be empty. Flag characters cannot be concatenated (like
+       ** "-fxyz"), it would conflict with the form "-fstring".
+     */
+    while (--argc > 0 && (*++argv)[0] == '-') {
+       char *p = argv[0] + 1;
+       int flag = *p++;
+
+       if (flag == '\0' || *p == '\0') {
+           if (argc > 1 && argv[1][0] != '-') {
+               p = *++argv;
+               argc -= 1;
+           }
+           else
+               p = "";
+       }
+       switch (flag) {
+       case 'a':
+           bootopt |= BOOT_AUTODIE;
+           break;
+       case 'c':
+           bootopt |= BOOT_CONSOLE;
+           break;
+       case 'q':
+           bootopt |= BOOT_QUICK;
+           break;
+       case 'd':
+           (void) setuid ((uid_t) uid);
+           dpath = p;
+           break;
+       case 'o':                 /* Per user local daemon... */
+           (void) setuid ((uid_t) uid);
+           bootopt |= BOOT_OPER;
+           break;
+#ifdef CMDLINE_CONFIG
+       case 'f':
+           (void) setuid ((uid_t) uid);
+           configfile = p;
+           break;
+#endif /* CMDLINE_CONFIG */
+       case 'h':
+           strncpyzt (me.name, p, sizeof (me.name));
+           break;
+       case 'i':
+           bootopt |= BOOT_INETD | BOOT_AUTODIE;
+           break;
+       case 'p':
+           if ((portarg = atoi (p)) > 0)
+               portnum = portarg;
+           break;
+       case 't':
+           (void) setuid ((uid_t) uid);
+           bootopt |= BOOT_TTY;
+           break;
+       case 'v':
+           (void) printf ("ircd %s\n", version);
+           exit (0);
+       case 'x':
+#ifdef DEBUGMODE
+           (void) setuid ((uid_t) uid);
+           debuglevel = atoi (p);
+           debugmode = *p ? p : "0";
+           bootopt |= BOOT_DEBUG;
+           break;
+#else
+           (void) fprintf (stderr,
+                           "%s: DEBUGMODE must be defined for -x y\n",
+                           myargv[0]);
+           exit (0);
+#endif
+       default:
+           bad_command ();
+           break;
+       }
+    }
+
+#ifndef        CHROOT
+    if (chdir (dpath)) {
+       perror ("chdir");
+       exit (-1);
+    }
+#endif
+
+#if !defined(IRC_UID)
+    if ((uid != euid) && !euid) {
+       (void) fprintf (stderr,
+                       "ERROR: do not run ircd setuid root. Make it setuid a\
+ normal user.\n");
+       exit (-1);
+    }
+#endif
+
+#if (!defined(CHROOTDIR) || (defined(IRC_UID) && defined(IRC_GID)))
+#ifndef        AIX
+    (void) setuid ((uid_t) uid);
+    (void) setuid ((uid_t) euid);
+#endif
+
+    if ((int) getuid () == 0) {
+#if defined(IRC_UID) && defined(IRC_GID)
+
+       /* run as a specified user */
+       (void) fprintf (stderr, "WARNING: running ircd with uid = %d\n",
+                       IRC_UID);
+       (void) fprintf (stderr, "         changing to gid %d.\n", IRC_GID);
+       (void) setuid (IRC_UID);
+       (void) setgid (IRC_GID);
+#else
+       /* check for setuid root as usual */
+       (void) fprintf (stderr,
+                       "ERROR: do not run ircd setuid root. Make it setuid a\
+ normal user.\n");
+       exit (-1);
+#endif
+    }
+#endif /*CHROOTDIR/UID/GID */
+
+    /* didn't set debuglevel */
+    /* but asked for debugging output to tty */
+    /*
+       if ((debuglevel < 0) && (bootopt & BOOT_TTY)) {
+       (void) fprintf(stderr,
+       "you specified -t without -x. use -x <n>\n");
+       exit(-1);
+       }
+     */
+    if (argc > 0)
+       return bad_command ();    /* This should exit out */
+
+    clear_client_hash_table ();
+    clear_channel_hash_table ();
+    clear_notify_hash_table ();
+    inittoken ();
+    initlists ();
+    initclass ();
+    initwhowas ();
+    initstats ();
+    open_debugfile ();
+    if (portnum < 0)
+       portnum = PORTNUM;
+    me.port = portnum;
+    (void) init_sys ();
+    me.flags = FLAGS_LISTEN;
+    if (bootopt & BOOT_INETD) {
+       me.fd = 0;
+       local[0] = &me;
+       me.flags = FLAGS_LISTEN;
+    }
+    else
+       me.fd = -1;
+
+#ifdef USE_SYSLOG
+    openlog (myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY);
+#endif
+    if (initconf (bootopt) == -1) {
+       Debug ((DEBUG_FATAL, "Failed in reading configuration file %s",
+               configfile));
+       (void) printf ("Couldn't open configuration file %s\n", configfile);
+       exit (-1);
+    }
+    if (!(bootopt & BOOT_INETD)) {
+/*      static  char    star[] = "*"; Compiler says this is unused */
+       aConfItem *aconf;
+
+       if ((aconf = find_me ()) && portarg <= 0 && aconf->port > 0)
+           portnum = aconf->port;
+       Debug ((DEBUG_ERROR, "Port = %d", portnum));
+       if (inetport (&me, aconf->passwd, portnum))
+           exit (1);
+    }
+    else if (inetport (&me, "*", 0))
+       exit (1);
+
+    (void) setup_ping ();
+    if (me.name[0] == '\0')
+       strncpyzt (me.name, me.sockhost, sizeof (me.name));
+    me.hopcount = 0;
+    me.confs = NULL;
+    me.next = NULL;
+    me.user = NULL;
+    me.from = &me;
+    SetMe (&me);
+    make_server (&me);
+    (void) strcpy (me.serv->up, me.name);
+
+    me.lasttime = me.since = me.firsttime = time (NULL);
+    (void) add_to_client_hash_table (me.name, &me);
+
+    (void) sprintf (REPORT_DO_DNS, ":%s %s", me.name, BREPORT_DO_DNS);
+    (void) sprintf (REPORT_FIN_DNS, ":%s %s", me.name, BREPORT_FIN_DNS);
+    (void) sprintf (REPORT_FIN_DNSC, ":%s %s", me.name, BREPORT_FIN_DNSC);
+    (void) sprintf (REPORT_FAIL_DNS, ":%s %s", me.name, BREPORT_FAIL_DNS);
+    (void) sprintf (REPORT_DO_ID, ":%s %s", me.name, BREPORT_DO_ID);
+    (void) sprintf (REPORT_FIN_ID, ":%s %s", me.name, BREPORT_FIN_ID);
+    (void) sprintf (REPORT_FAIL_ID, ":%s %s", me.name, BREPORT_FAIL_ID);
+    R_do_dns = strlen (REPORT_DO_DNS);
+    R_fin_dns = strlen (REPORT_FIN_DNS);
+    R_fin_dnsc = strlen (REPORT_FIN_DNSC);
+    R_fail_dns = strlen (REPORT_FAIL_DNS);
+    R_do_id = strlen (REPORT_DO_ID);
+    R_fin_id = strlen (REPORT_FIN_ID);
+    R_fail_id = strlen (REPORT_FAIL_ID);
+
+#ifdef NOSPOOF
+    srand (time (NULL));
+
+    NOSPOOF_SEED01 = rand () % 2147483647;
+    NOSPOOF_SEED02 = rand () % 2147483647;
+#endif /* NOSPOOF */
+
+    check_class ();
+    if (bootopt & BOOT_OPER) {
+       aClient *tmp = add_connection (&me, 0);
+
+       if (!tmp)
+           exit (1);
+       SetMaster (tmp);
+    }
+    else
+       write_pidfile ();
+
+    /* Let's set a server not SVSNOOP'd here - GZ */
+    SVSNOOP = 0;
+
+    /* Get maximum global users from a file */
+
+    get_max_users ();
+    max_connection_count = lu_mlu;
+    max_client_count = max_connection_count - 1;
+
+    Debug ((DEBUG_NOTICE, "Server ready..."));
+#ifdef USE_SYSLOG
+    syslog (LOG_NOTICE, "Server Ready");
+#endif
+
+    for (;;) {
+       now = time (NULL);
+       /*
+          ** Run through the hashes and check lusers every
+          ** second
+          ** Check events every second also.       -taz
+          ** Check bandwidth too. -GZ
+        */
+       if (lastlucheck < now) {
+           check_lusers ();
+           CheckEvents ();
+           CheckBandwidth ();
+           lastlucheck = now;
+       }
+       /*
+          ** We only want to connect if a connection is due,
+          ** not every time through.  Note, if there are no
+          ** active C lines, this call to Tryconnections is
+          ** made once only; it will return 0. - avalon
+        */
+       if (nextconnect && now >= nextconnect)
+           nextconnect = try_connections (now);
+       /*
+          ** DNS checks. One to timeout queries, one for cache expiries.
+        */
+       if (now >= nextdnscheck)
+           nextdnscheck = timeout_query_list (now);
+       if (now >= nextexpire)
+           nextexpire = expire_cache (now);
+       /*
+          ** take the smaller of the two 'timed' event times as
+          ** the time of next event (stops us being late :) - avalon
+          ** WARNING - nextconnect can return 0!
+        */
+       if (nextconnect)
+           delay = MIN (nextping, nextconnect);
+       else
+           delay = nextping;
+       delay = MIN (nextdnscheck, delay);
+       delay = MIN (nextexpire, delay);
+       delay -= now;
+       /*
+          ** Adjust delay to something reasonable [ad hoc values]
+          ** (one might think something more clever here... --msa)
+          ** We don't really need to check that often and as long
+          ** as we don't delay too long, everything should be ok.
+          ** waiting too long can cause things to timeout...
+          ** i.e. PINGS -> a disconnection :(
+          ** - avalon
+        */
+       if (delay < 1)
+           delay = 1;
+       else
+           delay = MIN (delay, TIMESEC);
+       (void) read_message (delay);
+
+       Debug ((DEBUG_DEBUG, "Got message(s)"));
+
+       now = time (NULL);
+       /*
+          ** ...perhaps should not do these loops every time,
+          ** but only if there is some chance of something
+          ** happening (but, note that conf->hold times may
+          ** be changed elsewhere--so precomputed next event
+          ** time might be too far away... (similarly with
+          ** ping times) --msa
+        */
+       if (now >= nextping)
+           nextping = check_pings (now, 0);
+
+       if (dorehash) {
+           (void) rehash (&me, &me, 1);
+           dorehash = 0;
+       }
+       /*
+          ** Flush output buffers on all connections now if they
+          ** have data in them (or at least try to flush)
+          ** -avalon
+        */
+       flush_connections (me.fd);
+    }
+}
+
+/*
+ * open_debugfile
+ *
+ * If the -t option is not given on the command line when the server is
+ * started, all debugging output is sent to the file set by LPATH in config.h
+ * Here we just open that file and make sure it is opened to fd 2 so that
+ * any fprintf's to stderr also goto the logfile.  If the debuglevel is not
+ * set from the command line by -x, use /dev/null as the dummy logfile as long
+ * as DEBUGMODE has been defined, else dont waste the fd.
+ */
+static void open_debugfile ()
+{
+#ifdef DEBUGMODE
+    int fd;
+    aClient *cptr;
+
+    if (debuglevel >= 0) {
+       cptr = make_client (NULL, NULL);
+       cptr->fd = 2;
+       SetLog (cptr);
+       cptr->port = debuglevel;
+       cptr->flags = 0;
+       cptr->acpt = cptr;
+       local[2] = cptr;
+       (void) strcpy (cptr->sockhost, me.sockhost);
+       (void) printf ("isatty = %d ttyname = %#x\n",
+                      isatty (2), (u_int) ttyname (2));
+       if (!(bootopt & BOOT_TTY)) {    /* leave debugging output on fd 2 */
+           (void) truncate (LOGFILE, 0);
+           if ((fd = open (LOGFILE, O_WRONLY | O_CREAT, 0600)) < 0)
+               if ((fd = open ("/dev/null", O_WRONLY)) < 0)
+                   exit (-1);
+           if (fd != 2) {
+               (void) dup2 (fd, 2);
+               (void) close (fd);
+           }
+           strncpyzt (cptr->name, LOGFILE, sizeof (cptr->name));
+       }
+       else if (isatty (2) && ttyname (2))
+           strncpyzt (cptr->name, ttyname (2), sizeof (cptr->name));
+       else
+           (void) strcpy (cptr->name, "FD2-Pipe");
+       Debug ((DEBUG_FATAL, "Debug: File <%s> Level: %d at %s",
+               cptr->name, cptr->port, myctime (time (NULL))));
+    }
+    else
+       local[2] = NULL;
+#endif
+    return;
+}
+
+static void setup_signals ()
+{
+#ifdef POSIX_SIGNALS
+    struct sigaction act;
+
+    act.sa_handler = SIG_IGN;
+    act.sa_flags = 0;
+    (void) sigemptyset (&act.sa_mask);
+    (void) sigaddset (&act.sa_mask, SIGPIPE);
+    (void) sigaddset (&act.sa_mask, SIGALRM);
+#ifdef SIGWINCH
+    (void) sigaddset (&act.sa_mask, SIGWINCH);
+    (void) sigaction (SIGWINCH, &act, NULL);
+#endif /* SIGWINCH */
+    (void) sigaction (SIGPIPE, &act, NULL);
+    act.sa_handler = dummy;
+    (void) sigaction (SIGALRM, &act, NULL);
+    act.sa_handler = s_rehash;
+    (void) sigemptyset (&act.sa_mask);
+    (void) sigaddset (&act.sa_mask, SIGHUP);
+    (void) sigaction (SIGHUP, &act, NULL);
+    act.sa_handler = s_restart;
+    (void) sigaddset (&act.sa_mask, SIGINT);
+    (void) sigaction (SIGINT, &act, NULL);
+    act.sa_handler = s_die;
+    (void) sigaddset (&act.sa_mask, SIGTERM);
+    (void) sigaction (SIGTERM, &act, NULL);
+
+#else /* POSIX_SIGNALS */
+#ifndef        HAVE_RELIABLE_SIGNALS
+    (void) signal (SIGPIPE, dummy);
+#ifdef SIGWINCH
+    (void) signal (SIGWINCH, dummy);
+#endif
+#else
+#ifdef SIGWINCH
+    (void) signal (SIGWINCH, SIG_IGN);
+#endif
+    (void) signal (SIGPIPE, SIG_IGN);
+#endif
+    (void) signal (SIGALRM, dummy);
+    (void) signal (SIGHUP, s_rehash);
+    (void) signal (SIGTERM, s_die);
+    (void) signal (SIGINT, s_restart);
+#endif /* POSIX_SIGNALS */
+
+#ifdef RESTARTING_SYSTEMCALLS
+    /*
+       ** At least on Apollo sr10.1 it seems continuing system calls
+       ** after signal is the default. The following 'siginterrupt'
+       ** should change that default to interrupting calls.
+     */
+    (void) siginterrupt (SIGALRM, 1);
+#endif
+}
+
+
+#define DOMAINNAMEMASK "*" DOMAINNAME
+
+void check_lusers (void)
+{
+    aClient *acptr;
+    lu_noninv = lu_inv = lu_serv = lu_oper = lu_unknown = lu_channel = lu_lu =
+       lu_lserv = lu_clu = lu_cglobalu = 0;
+    for (acptr = client; acptr; acptr = acptr->next) {
+       switch (acptr->status) {
+       case STAT_SERVER:
+           if (MyConnect (acptr))
+               lu_lserv++;
+       case STAT_ME:
+           lu_serv++;
+           break;
+       case STAT_CLIENT:
+           if (IsOper (acptr))
+               lu_oper++;
+           if (MyConnect (acptr)) {
+               lu_lu++;
+               if (match (DOMAINNAMEMASK, acptr->sockhost) == 0)
+                   lu_lulocal++;
+           }
+           if (!IsInvisible (acptr))
+               lu_noninv++;
+           else
+               lu_inv++;
+           break;
+       default:
+           lu_unknown++;
+           break;
+       }
+    }
+    lu_clu = lu_lu;
+    lu_cglobalu = lu_noninv + lu_inv;
+    if (lu_clu > lu_mlu)
+       lu_mlu = lu_lu;
+    lu_cglobalu = lu_noninv + lu_inv;
+    if (lu_cglobalu > lu_mglobalu)
+       lu_mglobalu = lu_cglobalu;
+
+    lu_channel = count_channels (&me);
+}
+
diff --git a/src/list.c b/src/list.c
new file mode 100644 (file)
index 0000000..3dde68d
--- /dev/null
@@ -0,0 +1,590 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/list.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Finland
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include "numeric.h"
+#ifdef DBMALLOC
+#include "malloc.h"
+#endif
+void free_link PROTO ((Link *));
+Link *make_link PROTO (());
+
+#ifdef DEBUGMODE
+static struct liststats
+{
+    int inuse;
+}
+cloc, crem, users, servs, links, classs, aconfs;
+
+#endif
+
+void outofmemory ();
+
+int flinks = 0;
+Link *freelink = NULL;
+
+int numclients = 0;
+
+void initlists ()
+{
+#ifdef DEBUGMODE
+    bzero ((char *) &cloc, sizeof (cloc));
+    bzero ((char *) &crem, sizeof (crem));
+    bzero ((char *) &users, sizeof (users));
+    bzero ((char *) &servs, sizeof (servs));
+    bzero ((char *) &links, sizeof (links));
+    bzero ((char *) &classs, sizeof (classs));
+    bzero ((char *) &aconfs, sizeof (aconfs));
+#endif
+}
+
+void outofmemory ()
+{
+    Debug ((DEBUG_FATAL, "Out of memory: restarting server..."));
+    restart ("Out of Memory");
+}
+
+
+/*
+   ** Create a new aClient structure and set it to initial state.
+   **
+   **   from == NULL,   create local client (a client connected
+   **           to a socket).
+   **
+   **   from,   create remote client (behind a socket
+   **           associated with the client defined by
+   **           'from'). ('from' is a local client!!).
+ */
+aClient *make_client (from, servr)
+     aClient *from, *servr;
+{
+    aClient *cptr = NULL;
+    unsigned size = CLIENT_REMOTE_SIZE;
+
+    /*
+     * Check freelists first to see if we can grab a client without
+     * having to call malloc.
+     */
+    if (!from)
+       size = CLIENT_LOCAL_SIZE;
+
+    if (!(cptr = (aClient *) MyMalloc (size)))
+       outofmemory ();
+    bzero ((char *) cptr, (int) size);
+
+#ifdef DEBUGMODE
+    if (size == CLIENT_LOCAL_SIZE)
+       cloc.inuse++;
+    else
+       crem.inuse++;
+#endif
+
+    /* Note:  structure is zero (calloc) */
+    cptr->from = from ? from : cptr;   /* 'from' of local client is self! */
+    cptr->next = NULL;           /* For machines with NON-ZERO NULL pointers >;) */
+    cptr->prev = NULL;
+    cptr->hnext = NULL;
+    cptr->user = NULL;
+    cptr->serv = NULL;
+    cptr->srvptr = servr;
+    cptr->status = STAT_UNKNOWN;
+    cptr->fd = -1;
+    (void) strcpy (cptr->username, "unknown");
+    if (size == CLIENT_LOCAL_SIZE) {
+       cptr->since = cptr->lasttime =
+           cptr->lastnick = cptr->firsttime = time (NULL);
+       cptr->confs = NULL;
+       cptr->sockhost[0] = '\0';
+       cptr->buffer[0] = '\0';
+       cptr->lasthtc = 0;
+    }
+    return (cptr);
+}
+
+void free_client (cptr)
+     aClient *cptr;
+{
+    MyFree ((char *) cptr);
+}
+
+/*
+   ** 'make_user' add's an User information block to a client
+   ** if it was not previously allocated.
+ */
+anUser *make_user (cptr)
+     aClient *cptr;
+{
+    anUser *user;
+
+    user = cptr->user;
+    if (!user) {
+       user = (anUser *) MyMalloc (sizeof (anUser));
+#ifdef DEBUGMODE
+       users.inuse++;
+#endif
+       user->away = NULL;
+       user->refcnt = 1;
+       user->joined = 0;
+       user->channel = NULL;
+       user->invited = NULL;
+       user->silence = NULL;
+       cptr->user = user;
+    }
+    return user;
+}
+
+/* Related to timer -taz */
+aEvent *make_event ()
+{
+    aEvent *eptr;
+    eptr = (aEvent *) MyMalloc (sizeof (aEvent));
+    eptr->arg[0] = 0;
+    eptr->func = NULL;
+    eptr->exectime = 0;
+    eptr->prev = eptr->next = NULL;
+    return eptr;
+}
+
+void free_event (eptr)
+     aEvent *eptr;
+{
+    fprintf (stderr, "%s\n", eptr->arg);
+    MyFree ((char *) eptr);
+}
+
+void free_synchlist (synchptr)
+     aSynchList *synchptr;
+{
+    MyFree ((char *) synchptr);
+}
+
+aServer *make_server (cptr)
+     aClient *cptr;
+{
+    aServer *serv = cptr->serv;
+
+    if (!serv) {
+       serv = (aServer *) MyMalloc (sizeof (aServer));
+#ifdef DEBUGMODE
+       servs.inuse++;
+#endif
+       serv->user = NULL;
+       serv->nexts = NULL;
+       *serv->by = '\0';
+       *serv->up = '\0';
+       cptr->serv = serv;
+    }
+    return cptr->serv;
+}
+
+/* Create a SynchList */
+aSynchList *make_synchlist ()
+{
+    aSynchList *synchptr;
+
+    synchptr = (aSynchList *) MyMalloc (sizeof (aSynchList));
+
+    synchptr->nick[0] = 0;
+    synchptr->deop = 0;
+    synchptr->devoice = 0;
+    synchptr->op = 0;
+    synchptr->voice = 0;
+    synchptr->prev = synchptr->next = NULL;
+
+    return synchptr;
+}
+
+/*
+   ** free_user
+   **   Decrease user reference count by one and realease block,
+   **   if count reaches 0
+ */
+void free_user (user, cptr)
+     anUser *user;
+     aClient *cptr;
+{
+    if (--user->refcnt <= 0) {
+       if (user->away)
+           MyFree ((char *) user->away);
+       /*
+        * sanity check
+        */
+       if (user->joined || user->refcnt < 0 || user->invited
+           || user->channel)
+#ifdef DEBUGMODE
+           dumpcore ("%#x user (%s!%s@%s) %#x %#x %#x %d %d",
+                     cptr, cptr ? cptr->name : "<noname>",
+                     user->username, user->host, user,
+                     user->invited, user->channel, user->joined,
+                     user->refcnt);
+#else
+           sendto_ops ("* %#x user (%s!%s@%s) %#x %#x %#x %d %d *",
+                       cptr, cptr ? cptr->name : "<noname>",
+                       user->username, user->host, user,
+                       user->invited, user->channel, user->joined,
+                       user->refcnt);
+#endif
+       MyFree ((char *) user);
+#ifdef DEBUGMODE
+       users.inuse--;
+#endif
+    }
+}
+
+/*
+ * taken the code from ExitOneClient() for this and placed it here.
+ * - avalon
+ */
+void remove_client_from_list (cptr)
+     aClient *cptr;
+{
+    checklist ();
+    if (cptr->prev)
+       cptr->prev->next = cptr->next;
+    else {
+       client = cptr->next;
+       client->prev = NULL;
+    }
+    if (cptr->next)
+       cptr->next->prev = cptr->prev;
+    if (IsPerson (cptr)) {       /* Only persons can have been added before */
+       add_history (cptr);
+       off_history (cptr);       /* Remove all pointers to cptr */
+    }
+    if (cptr->user)
+       (void) free_user (cptr->user, cptr);
+    if (cptr->serv) {
+       if (cptr->serv->user)
+           free_user (cptr->serv->user, cptr);
+       MyFree ((char *) cptr->serv);
+#ifdef DEBUGMODE
+       servs.inuse--;
+#endif
+    }
+#ifdef DEBUGMODE
+    if (cptr->fd == -2)
+       cloc.inuse--;
+    else
+       crem.inuse--;
+#endif
+    (void) free_client (cptr);
+    numclients--;
+    return;
+}
+
+/*
+ * although only a small routine, it appears in a number of places
+ * as a collection of a few lines...functions like this *should* be
+ * in this file, shouldnt they ?  after all, this is list.c, isnt it ?
+ * -avalon
+ */
+void add_client_to_list (cptr)
+     aClient *cptr;
+{
+    /*
+     * since we always insert new clients to the top of the list,
+     * this should mean the "me" is the bottom most item in the list.
+     */
+    cptr->next = client;
+    client = cptr;
+    if (cptr->next)
+       cptr->next->prev = cptr;
+    return;
+}
+
+/*
+ * Look for ptr in the linked listed pointed to by link.
+ */
+Link *find_user_link (lp, ptr)
+     Link *lp;
+     aClient *ptr;
+{
+    if (ptr)
+       while (lp) {
+           if (lp->value.cptr == ptr)
+               return (lp);
+           lp = lp->next;
+       }
+    return NULL;
+}
+
+/*
+ * Look for a match in a list of strings. Go through the list, and run
+ * match() on it. Side effect: if found, this link is moved to the top of
+ * the list.
+ */
+int find_str_match_link (lp, str)
+     Link **lp;        /* Two **'s, since we might modify the original *lp */
+     char *str;
+{
+/*  Link    *ptr; Compiler says this is unused */
+    Link **head = lp;
+
+    if (lp && *lp) {
+       if (!match ((*lp)->value.cp, str))
+           return 1;
+       for (; (*lp)->next; *lp = (*lp)->next)
+           if (!match ((*lp)->next->value.cp, str)) {
+               Link *temp = (*lp)->next;
+               *lp = (*lp)->next->next;
+               temp->next = *head;
+               *head = temp;
+               return 1;
+           }
+       return 0;
+    }
+    return 0;
+}
+
+void free_str_list (lp)
+     Link *lp;
+{
+    Link *next;
+
+
+    while (lp) {
+       next = lp->next;
+       MyFree ((char *) lp->value.cp);
+       free_link (lp);
+       lp = next;
+    }
+
+    return;
+}
+
+
+#define        LINKSIZE        (4072/sizeof(Link))
+
+Link *make_link ()
+{
+    Link *lp;
+    int i;
+
+    /* "caching" slab-allocator... ie. we're allocating one pages
+       (hopefully - upped to the Linux default, not dbuf.c) worth of 
+       link-structures at time to avoid all the malloc overhead.
+       All links left free from this process or separately freed 
+       by a call to free_link() are moved over to freelink-list.
+       Impact? Let's see... -Donwulff */
+    if (freelink == NULL) {
+       lp = (Link *) MyMalloc (LINKSIZE * sizeof (Link));
+       freelink = lp + 1;
+       flinks += LINKSIZE;
+       for (i = 1; i < (LINKSIZE - 1); i++)
+           (lp + i)->next = lp + i + 1;
+       (lp + i)->next = NULL;
+    }
+    else {
+       lp = freelink;
+       freelink = freelink->next;
+    }
+#ifdef DEBUGMODE
+    links.inuse++;
+#endif
+    return lp;
+}
+
+void free_link (lp)
+     Link *lp;
+{
+    lp->next = freelink;
+    freelink = lp;
+#ifdef DEBUGMODE
+    links.inuse--;
+#endif
+}
+
+Ban *make_ban ()
+{
+    Ban *lp;
+
+    lp = (Ban *) MyMalloc (sizeof (Ban));
+#ifdef DEBUGMODE
+    links.inuse++;
+#endif
+    return lp;
+}
+
+void free_ban (lp)
+     Ban *lp;
+{
+    MyFree ((char *) lp);
+#ifdef DEBUGMODE
+    links.inuse--;
+#endif
+}
+
+aClass *make_class ()
+{
+    aClass *tmp;
+
+    tmp = (aClass *) MyMalloc (sizeof (aClass));
+#ifdef DEBUGMODE
+    classs.inuse++;
+#endif
+    return tmp;
+}
+
+void free_class (tmp)
+     aClass *tmp;
+{
+    MyFree ((char *) tmp);
+#ifdef DEBUGMODE
+    classs.inuse--;
+#endif
+}
+
+aSqlineItem *make_sqline ()
+{
+    aSqlineItem *asqline;
+
+    asqline = (struct SqlineItem *) MyMalloc (sizeof (aSqlineItem));
+    asqline->next = NULL;
+    asqline->sqline = asqline->reason = NULL;
+
+    return (asqline);
+}
+
+aJinxItem *make_jinx ()
+{
+    aJinxItem *ajinx;
+
+    ajinx = (struct JinxItem *) MyMalloc (sizeof (aJinxItem));
+    memset (ajinx, 0, sizeof (aJinxItem));
+
+    return (ajinx);
+}
+
+aConfItem *make_conf ()
+{
+    aConfItem *aconf;
+
+    aconf = (struct ConfItem *) MyMalloc (sizeof (aConfItem));
+#ifdef DEBUGMODE
+    aconfs.inuse++;
+#endif
+    bzero ((char *) &aconf->ipnum, sizeof (struct in_addr));
+    aconf->next = NULL;
+    aconf->host = aconf->passwd = aconf->name = NULL;
+    aconf->status = CONF_ILLEGAL;
+    aconf->clients = 0;
+    aconf->port = 0;
+    aconf->hold = 0;
+    Class (aconf) = 0;
+    return (aconf);
+}
+
+void delist_conf (aconf)
+     aConfItem *aconf;
+{
+    if (aconf == conf)
+       conf = conf->next;
+    else {
+       aConfItem *bconf;
+
+       for (bconf = conf; aconf != bconf->next; bconf = bconf->next);
+       bconf->next = aconf->next;
+    }
+    aconf->next = NULL;
+}
+
+void free_sqline (asqline)
+     aSqlineItem *asqline;
+{
+    del_queries ((char *) asqline);
+    MyFree (asqline->sqline);
+    MyFree (asqline->reason);
+    MyFree ((char *) asqline);
+    return;
+}
+
+void free_jinx (aJinxItem * ajinx)
+{
+    del_queries ((char *) ajinx);
+    MyFree (ajinx->reason);
+    MyFree (ajinx->userhost);
+    MyFree ((char *) ajinx);
+    return;
+}
+
+void free_conf (aconf)
+     aConfItem *aconf;
+{
+    del_queries ((char *) aconf);
+    MyFree (aconf->host);
+    if (aconf->passwd)
+       bzero (aconf->passwd, strlen (aconf->passwd));
+    MyFree (aconf->passwd);
+    MyFree (aconf->name);
+    MyFree ((char *) aconf);
+#ifdef DEBUGMODE
+    aconfs.inuse--;
+#endif
+    return;
+}
+
+#ifdef DEBUGMODE
+void send_listinfo (cptr, name)
+     aClient *cptr;
+     char *name;
+{
+    int inuse = 0, mem = 0, tmp = 0;
+
+    sendto_one (cptr, ":%s %d %s :Local: inuse: %d(%d)",
+               me.name, RPL_STATSDEBUG, name, inuse += cloc.inuse,
+               tmp = cloc.inuse * CLIENT_LOCAL_SIZE);
+    mem += tmp;
+    sendto_one (cptr, ":%s %d %s :Remote: inuse: %d(%d)",
+               me.name, RPL_STATSDEBUG, name,
+               crem.inuse, tmp = crem.inuse * CLIENT_REMOTE_SIZE);
+    mem += tmp;
+    inuse += crem.inuse;
+    sendto_one (cptr, ":%s %d %s :Users: inuse: %d(%d)",
+               me.name, RPL_STATSDEBUG, name, users.inuse,
+               tmp = users.inuse * sizeof (anUser));
+    mem += tmp;
+    inuse += users.inuse,
+       sendto_one (cptr, ":%s %d %s :Servs: inuse: %d(%d)",
+                   me.name, RPL_STATSDEBUG, name, servs.inuse,
+                   tmp = servs.inuse * sizeof (aServer));
+    mem += tmp;
+    inuse += servs.inuse,
+       sendto_one (cptr, ":%s %d %s :Links: inuse: %d(%d)",
+                   me.name, RPL_STATSDEBUG, name, links.inuse,
+                   tmp = links.inuse * sizeof (Link));
+    mem += tmp;
+    inuse += links.inuse,
+       sendto_one (cptr, ":%s %d %s :Classes: inuse: %d(%d)",
+                   me.name, RPL_STATSDEBUG, name, classs.inuse,
+                   tmp = classs.inuse * sizeof (aClass));
+    mem += tmp;
+    inuse += classs.inuse,
+       sendto_one (cptr, ":%s %d %s :Confs: inuse: %d(%d)",
+                   me.name, RPL_STATSDEBUG, name, aconfs.inuse,
+                   tmp = aconfs.inuse * sizeof (aConfItem));
+    mem += tmp;
+    inuse += aconfs.inuse,
+       sendto_one (cptr, ":%s %d %s :Totals: inuse %d %d",
+                   me.name, RPL_STATSDEBUG, name, inuse, mem);
+}
+#endif
diff --git a/src/list2.c b/src/list2.c
new file mode 100644 (file)
index 0000000..2664e0b
--- /dev/null
@@ -0,0 +1,464 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/list.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Finland
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#ifdef DBMALLOC
+#include "malloc.h"
+#endif
+void free_link PROTO ((Link *));
+Link *make_link PROTO (());
+
+static struct liststats
+{
+    int inuse;
+    int free;
+}
+listc[8];
+
+#define        LC_CLOC 0
+#define        LC_CREM 1
+#define        LC_SERV 2
+#define        LC_LINK 3
+#define        LC_USER 4
+#define        LC_CONF 5
+#define        LC_CLAS 6
+#define        LC_DBUF 7
+
+void outofmemory ();
+
+static aClient *clofree = NULL;
+static aClient *crefree = NULL;
+static aClass *clfree = NULL;
+static aConfItem *cofree = NULL;
+static anUser *ufree = NULL;
+static Link *lfree = NULL;
+static aServer *sfree = NULL;
+
+int numclients = 0;
+
+void initlists ()
+{
+    bzero (listc, sizeof (struct liststats) * 7);
+}
+
+void outofmemory ()
+{
+    Debug ((DEBUG_FATAL, "Out of memory: restarting server..."));
+    restart ("Out of Memory");
+}
+
+
+/*
+   ** Create a new aClient structure and set it to initial state.
+   **
+   **   from == NULL,   create local client (a client connected
+   **           to a socket).
+   **
+   **   from,   create remote client (behind a socket
+   **           associated with the client defined by
+   **           'from'). ('from' is a local client!!).
+ */
+aClient *make_client (from)
+     aClient *from;
+{
+    aClient *cptr = NULL;
+    unsigned size = CLIENT_REMOTE_SIZE;
+
+    /*
+     * Check freelists first to see if we can grab a client without
+     * having to call malloc.
+     */
+    if (!from) {
+       size = CLIENT_LOCAL_SIZE;
+       if ((cptr = clofree)) {
+           clofree = cptr->next;
+           listc[LC_CLOC].free--;
+           Debug ((DEBUG_LIST, "make_client(%#x) = %#x", from, cptr));
+       }
+    }
+    else if ((cptr = crefree)) {
+       crefree = cptr->next;
+       listc[LC_CREM].free--;
+       Debug ((DEBUG_LIST, "make_client(%#x) = %#x", from, cptr));
+    }
+    if (!cptr) {
+       if (!(cptr = (aClient *) MyMalloc (size)))
+           outofmemory ();
+       else {
+           if (size == CLIENT_LOCAL_SIZE)
+               listc[LC_CLOC].inuse++;
+           else
+               listc[LC_CREM].inuse++;
+       }
+    }
+    bzero ((char *) cptr, (int) size);
+
+    /* Note:  structure is zero (calloc) */
+    cptr->from = from ? from : cptr;   /* 'from' of local client is self! */
+    cptr->next = NULL;           /* For machines with NON-ZERO NULL pointers >;) */
+    cptr->prev = NULL;
+    cptr->hnext = NULL;
+    cptr->user = NULL;
+    cptr->serv = NULL;
+    cptr->status = STAT_UNKNOWN;
+    cptr->fd = -1;
+    (void) strcpy (cptr->username, "unknown");
+    if (size == CLIENT_LOCAL_SIZE) {
+       cptr->since = cptr->lasttime =
+           cptr->lastnick = cptr->firsttime = time (NULL);
+       cptr->confs = NULL;
+       cptr->sockhost[0] = '\0';
+       cptr->buffer[0] = '\0';
+    }
+    return (cptr);
+}
+
+
+checksanity ()
+{
+    register aClient *c;
+    register anUser *u;
+    register aServer *s;
+
+    for (c = client; c; c = c->next)
+#ifdef LIST_DEBUG
+       if ((u = c->user) && (u->bcptr != c))
+           dumpcore ("c %#x u %#x b %#x", c, u, u->bcptr);
+       else if ((s = c->serv) && s->bcptr != c)
+           dumpcore ("c %#x s %#x b %#x", c, s, s->bcptr);
+       else
+#endif
+       if (u && u->refcnt <= 0)
+           dumpcore ("c %#x u %#x r %d", c, u, u->refcnt);
+}
+
+
+void free_client (cptr)
+     aClient *cptr;
+{
+    Debug ((DEBUG_LIST, "free_client(%#x) %d", cptr, cptr->fd));
+    if (cptr->fd != -1) {
+       bzero ((char *) cptr, CLIENT_LOCAL_SIZE);
+       listc[LC_CLOC].free++;
+       cptr->next = clofree;
+       clofree = cptr;
+    }
+    else {
+       bzero ((char *) cptr, CLIENT_REMOTE_SIZE);
+       listc[LC_CREM].free++;
+       cptr->next = crefree;
+       crefree = cptr;
+    }
+}
+
+/*
+   ** 'make_user' add's an User information block to a client
+   ** if it was not previously allocated.
+ */
+anUser *make_user (cptr)
+     aClient *cptr;
+{
+    anUser *user;
+    char c;
+
+    user = cptr->user;
+    if (!user)
+       if ((user = ufree)) {
+           ufree = user->nextu;
+           listc[LC_USER].free--;
+           c = '-';
+       }
+    if (!user) {
+       user = (anUser *) MyMalloc (sizeof (anUser));
+       listc[LC_USER].inuse++;
+       c = '=';
+    }
+    cptr->user = user;
+    user->nextu = NULL;
+    user->away = NULL;
+    user->refcnt = 1;
+    user->joined = 0;
+    user->channel = NULL;
+    user->invited = NULL;
+    user->silence = NULL;
+    Debug ((DEBUG_LIST, "make_user(%#x) %c %#x %d",
+           cptr, c, user, user->refcnt));
+    user->bcptr = cptr;
+    return user;
+}
+
+aServer *make_server (cptr)
+     aClient *cptr;
+{
+    aServer *serv = cptr->serv;
+    char c;
+
+    if (!serv)
+       if ((serv = sfree)) {
+           sfree = serv->nexts;
+           listc[LC_SERV].free--;
+           c = '-';
+       }
+    if (!serv) {
+       serv = (aServer *) MyMalloc (sizeof (aServer));
+       listc[LC_SERV].inuse++;
+       c = '=';
+    }
+    serv->user = NULL;
+    serv->nexts = NULL;
+    *serv->by = '\0';
+    *serv->up = '\0';
+    cptr->serv = serv;
+#ifdef LIST_DEBUG
+    serv->bcptr = cptr;
+#endif
+    Debug ((DEBUG_LIST, "make_server(%#x) %c %#x", cptr, c, serv));
+    return cptr->serv;
+}
+
+/*
+   ** free_user
+   **   Decrease user reference count by one and realease block,
+   **   if count reaches 0
+ */
+void free_user (user, cptr)
+     anUser *user;
+     aClient *cptr;
+{
+    if (cptr && user->bcptr && (user->bcptr != cptr)) {
+       dumpcore ("user %#x bcptr %#x cptr %#x", user, user->bcptr, cptr);
+       exit (0);
+    }
+    user->bcptr = cptr;
+    user->refcnt--;
+    Debug ((DEBUG_LIST, "free_user(%#x,%#x) %d", user, cptr, user->refcnt));
+    if (user->refcnt <= 0) {
+       if (user->away)
+           (void) MyFree ((char *) user->away);
+       bzero ((char *) user, sizeof (*user));
+       user->nextu = ufree;
+       ufree = user;
+       listc[LC_USER].free++;
+    }
+}
+
+/*
+ * taken the code from ExitOneClient() for this and placed it here.
+ * - avalon
+ */
+void remove_client_from_list (cptr)
+     aClient *cptr;
+{
+    checklist ();
+    if (cptr->prev)
+       cptr->prev->next = cptr->next;
+    else {
+       client = cptr->next;
+       client->prev = NULL;
+    }
+    if (cptr->next)
+       cptr->next->prev = cptr->prev;
+    if (cptr->user) {
+       add_history (cptr);
+       off_history (cptr);
+       (void) free_user (cptr->user, cptr);
+    }
+    if (cptr->serv) {
+       if (cptr->serv->user)
+           free_user (cptr->serv->user, cptr);
+       listc[LC_SERV].free++;
+       cptr->serv->nexts = sfree;
+       cptr->serv->bcptr = NULL;
+       sfree = cptr->serv;
+    }
+    free_client (cptr);
+    return;
+}
+
+/*
+ * although only a small routine, it appears in a number of places
+ * as a collection of a few lines...functions like this *should* be
+ * in this file, shouldnt they ?  after all, this is list.c, isnt it ?
+ * -avalon
+ */
+void add_client_to_list (cptr)
+     aClient *cptr;
+{
+    /*
+     * since we always insert new clients to the top of the list,
+     * this should mean the "me" is the bottom most item in the list.
+     */
+    cptr->next = client;
+    client = cptr;
+    if (cptr->next)
+       cptr->next->prev = cptr;
+    return;
+}
+
+/*
+ * Look for ptr in the linked listed pointed to by link.
+ */
+Link *find_user_link (lp, ptr)
+     Link *lp;
+     aClient *ptr;
+{
+    while (lp && ptr) {
+       if (lp->value.cptr == ptr)
+           return (lp);
+       lp = lp->next;
+    }
+    return NULL;
+}
+
+Link *make_link ()
+{
+    Link *lp;
+    char c;
+
+    if ((lp = lfree)) {
+       lfree = lp->next;
+       listc[LC_LINK].free--;
+       c = '-';
+    }
+    else {
+       lp = (Link *) MyMalloc (sizeof (Link) * 3);
+       bzero ((char *) lp + 1, sizeof (Link) * 2);
+       lp->next = lp + 1;
+       lp->next->next = lp + 2;
+       lp->next->next->next = lfree;
+       lfree = lp->next;
+       listc[LC_LINK].inuse += 3;
+       listc[LC_LINK].free += 2;
+       c = '=';
+    }
+    Debug ((DEBUG_LIST, "make_link() %c %#x", c, lp));
+    return lp;
+}
+
+void free_link (lp)
+     Link *lp;
+{
+    bzero ((char *) lp, sizeof (*lp));
+    lp->next = lfree;
+    lfree = lp;
+    listc[LC_LINK].free++;
+    Debug ((DEBUG_LIST, "free_link(%#x)", lp));
+}
+
+
+aClass *make_class ()
+{
+    aClass *tmp;
+
+    if ((tmp = clfree)) {
+       listc[LC_CLAS].free--;
+       clfree = tmp->next;
+       Debug ((DEBUG_LIST, "make_class() - %#x", tmp));
+    }
+    else {
+       tmp = (aClass *) MyMalloc (sizeof (aClass));
+       listc[LC_CLAS].inuse++;
+       Debug ((DEBUG_LIST, "make_class() = %#x", tmp));
+    }
+    return tmp;
+}
+
+void free_class (tmp)
+     aClass *tmp;
+{
+    bzero ((char *) tmp, sizeof (*tmp));
+    tmp->next = clfree;
+    clfree = tmp;
+    listc[LC_CLAS].free++;
+    Debug ((DEBUG_LIST, "free_class(%#x)", tmp));
+}
+
+aConfItem *make_conf ()
+{
+    aConfItem *aconf;
+    char c;
+
+    if ((aconf = cofree)) {
+       cofree = aconf->next;
+       listc[LC_CONF].free--;
+       c = '-';
+    }
+    else {
+       aconf = (struct ConfItem *) MyMalloc (sizeof (aConfItem));
+       listc[LC_CONF].inuse++;
+       c = '=';
+       bzero ((char *) aconf, sizeof (*aconf));
+    }
+    aconf->next = NULL;
+    aconf->host = aconf->passwd = aconf->name = NULL;
+    aconf->status = CONF_ILLEGAL;
+    Class (aconf) = 0;
+    Debug ((DEBUG_LIST, "make_conf() %c %#x", c, aconf));
+    return (aconf);
+}
+
+void free_conf (aconf)
+     aConfItem *aconf;
+{
+    MyFree (aconf->host);
+    if (aconf->passwd)
+       bzero (aconf->passwd, strlen (aconf->passwd));
+    MyFree (aconf->passwd);
+    MyFree (aconf->name);
+    bzero ((char *) aconf, sizeof (*aconf));
+    aconf->next = cofree;
+    cofree = aconf;
+    Debug ((DEBUG_LIST, "free_conf(%#x)", aconf));
+    listc[LC_CONF].free++;
+    return;
+}
+
+void send_listinfo (cptr, name)
+     aClient *cptr;
+     char *name;
+{
+    static char *labels[] = { "Local", "Remote", "Servs", "Links",
+       "Users", "Confs", "Classes", "dbufs"
+    };
+    static int sizes[] = { CLIENT_LOCAL_SIZE, CLIENT_REMOTE_SIZE,
+       sizeof (aServer), sizeof (Link),
+       sizeof (anUser), sizeof (aConfItem),
+       sizeof (aClass), sizeof (dbufbuf)
+    };
+
+    struct liststats *ls = listc;
+    int inuse = 0, mem = 0, tmp = 0, i;
+
+    listc[LC_DBUF].inuse = dbufblocks;
+    listc[LC_DBUF].free = dbufblocks - dbufalloc;
+    for (i = 0; i < 8; i++, ls++) {
+       tmp = sizes[i] * ls->inuse;
+       sendto_one (cptr, ":%s NOTICE %s :%s: inuse: %d(%d) free: %d",
+                   me.name, cptr->name, labels[i], ls->inuse, tmp, ls->free);
+       inuse += ls->inuse;
+       mem += tmp;
+    }
+
+    sendto_one (cptr, ":%s NOTICE %s :Totals: inuse %d %d",
+               me.name, name, inuse, mem);
+}
diff --git a/src/masking.c b/src/masking.c
new file mode 100644 (file)
index 0000000..9028cc3
--- /dev/null
@@ -0,0 +1,179 @@
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "userload.h"
+#include <unistd.h>
+
+extern char *return_user_mask(char[]) ;
+extern char *return_oper_mask(struct Client *) ;
+extern int str2array(char **pparv, char *string, char *delim) ;
+extern char *crypt ();
+
+#define MAXVIRTSIZE     (3 + 5 + 1)
+
+void calc_mask(aClient *acptr) 
+{
+#ifdef CLIENT_MASKING
+
+#ifdef OPER_MASKS
+  if (IsNetAdmin(acptr) || 
+      IsSRoot(acptr) ||
+      IsSAdmin(acptr) ||
+      IsAdmin(acptr) ||
+      IsOper(acptr) ||
+      IsLocOp(acptr)) {
+    strncpy(acptr->user->mask, return_oper_mask(acptr), HOSTLEN + 1) ;
+    return ;
+  }
+#endif
+
+  strncpy(acptr->user->mask, return_user_mask(acptr->user->host), HOSTLEN +1) ;
+  printf("check: %s \n",return_user_mask(acptr->user->host) );  
+#endif /* CLIENT_MASKING */   
+}
+
+char *return_host(struct Client *caller, struct Client *user) 
+{
+#ifdef CLIENT_MASKING
+  /* hiding a user's host from it's self is stupid and leads to bugs */
+  if (!IsHidden(user) || IsOper(caller) || caller==user)  
+    return user->user->host ;
+  else 
+    return user->user->mask ;
+#else
+  return user->user->host ;
+#endif        
+}
+
+char *return_oper_host(struct Client *caller, struct Client *user)
+{
+#ifdef CLIENT_MASKING
+# ifdef OPER_MASKS
+  if (!IsNetAdmin(caller) &&
+      !IsSRoot(caller) &&
+      !IsSAdmin(caller) &&
+      !IsAdmin(caller) &&
+      !IsOper(caller) &&
+      !IsLocOp(caller) && 
+      MaskHost(user) && 
+      caller!=user)
+    return user->user->mask  ;
+  else
+    return user->user->host ;    
+# else
+  if (!IsHidden(user) || IsOper(caller) || caller==user)
+    return user->user->host ;
+  else
+    return user->user->mask ;
+# endif
+#else
+  return user->user->host ;
+#endif
+}
+
+#ifdef OPER_MASKS
+
+char *return_oper_mask(struct Client *acptr)
+{
+    if (IsHidden(acptr)) {
+       if (IsNetAdmin(acptr))
+           return netadmin_host;
+       else if (IsSRoot(acptr))
+           return sroot_host;
+       else if (IsSAdmin(acptr))
+           return sadmin_host;
+       else if (IsAdmin(acptr))
+           return admin_host;
+       else if (IsOper(acptr))
+           return ircop_host;
+       else if (IsLocOp(acptr))
+           return locop_host;
+    } else {
+       return acptr->user->host;
+    }
+};
+#endif
+
+#ifdef CLIENT_MASKING
+
+char *Maskchecksum(char *data, char *salt)
+{
+  char static tmp[HOSTLEN + 1] ;
+  char result[HOSTLEN +1] ;  
+  
+  strncpy(tmp,crypt(data, salt),HOSTLEN) ;
+  printf("%s/n",tmp) ;           
+  return (tmp) ;
+}
+
+
+char *return_user_mask(char *s)
+{
+    static char mask[HOSTLEN + 1];
+    char *csum;
+    char *dot, *ptr ;
+    static char destroy[HOSTLEN + 1], *parv[HOSTLEN + 1];
+    char salt[12] = "$1$\0\0\0\0\0\0\0\0\0";
+    int len = 0, overflow = 0, parc = 0;
+    
+    strncpy(destroy, s, HOSTLEN);
+    len = strlen(destroy);
+
+    if ((len + MAXVIRTSIZE) > HOSTLEN) {
+        overflow = (len + MAXVIRTSIZE) - HOSTLEN;
+        ptr = &destroy[overflow];
+    } else {
+        ptr = &destroy[0];
+    }
+    memset(mask, 0, HOSTLEN);
+    
+    parc = str2array(parv, ptr, ".");
+
+    if (strlen(s) > HOSTLEN) {
+        s[HOSTLEN] = 0;
+    }
+    
+    if (parc == 4) {
+        len = strlen(parv[3]);
+        if (isdigit(parv[3][len - 1])) {
+            /* Numeric IP, lets use the last octets of the IP address
+             * as salt */
+            strcat(salt, parv[3]);
+            strcat(salt, parv[2]);
+            csum = Maskchecksum(s, salt);
+            sprintf(mask, "%s.%s.%s.%i", parv[0], parv[1],parv[2],csum[15]+256+csum[19]);
+            return mask;
+        }
+    }
+
+    /* Hostname.... lets use it as our salt... */
+    strncat(salt, s, 8);
+    csum = Maskchecksum(s, salt);
+
+    dot = (char *) strchr(s, '.');
+    
+    if (dot == NULL) {
+        sprintf(mask, userspace_mask_prefix "%i%i%i%i%i%i.%s",csum[14]%10,csum[15]%10,csum[16]%10,csum[17]%10,csum[18]%10,csum[19]%10, s);
+        return mask;
+    } else {
+        sprintf(mask, userspace_mask_prefix "%i%i%i%i%i%i.%s",csum[14]%10,csum[15]%10,csum[16]%10,csum[17]%10,csum[18]%10,csum[19]%10, dot + 1);
+        return mask;
+    }
+}
+
+#endif /* CLIENT_MASKING */
+
+int str2array(char **pparv, char *string, char *delim)
+{
+    char *tok;
+    int pparc = 0;
+
+    tok = (char *)strtok(string, delim);
+    while (tok != NULL) {
+        pparv[pparc++] = tok;
+        tok = (char *)strtok(NULL, delim);
+    }
+
+    return pparc;
+}
diff --git a/src/match.c b/src/match.c
new file mode 100644 (file)
index 0000000..ee5475a
--- /dev/null
@@ -0,0 +1,349 @@
+
+
+/*
+ *   IRC - Internet Relay Chat, common/match.c
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+
+/* Add a prototype to quiet compiler warnings -Studded */
+int r_match (char *, char *);
+
+/*
+ *  Compare if a given string (name) matches the given
+ *  mask (which can contain wild cards: '*' - match any
+ *  number of chars, '?' - match any single character.
+ *
+ *  return  0, if match
+ *      1, if no match
+ */
+
+/*
+ * match()
+ Concept by JoelKatz (David Schwartz) <djls@gate.net>
+ Thanks to Barubary
+ */
+
+u_char touppertab[], tolowertab[];
+#define tolowertab2 tolowertab
+
+int match (mask, string)
+     char *mask, *string;
+{
+    char *rmask = mask;
+    char *rstring = string;
+    int l, p;
+    while ((l = *(rmask++)) != 0) {
+       if ((l == '*') || (l == '?'))
+           continue;
+#ifdef ESCAPED_MATCHING
+       if (l == '\\') {
+           l = *(rmask++);
+           if (!l)
+               return r_match (mask, string);
+       }
+#endif
+       p = *(rstring++);
+       while ((p != 0) && (tolowertab2[p] != tolowertab2[l]))
+           p = *(rstring++);
+       /* If we're out of string, no match */
+       if (p == 0)
+           return 1;
+    }
+    return r_match (mask, string);
+}
+
+/*
+ * r_match()
+ * Iterative matching function, rather than recursive.
+ * Written by Douglas A Lewis (dalewis@acsu.buffalo.edu)
+ */
+
+int r_match (mask, name)
+     char *mask, *name;
+{
+    u_char *m;
+    u_char *n;
+    char *ma;
+    char *na;
+    int wild;
+    int q;
+
+    m = (u_char *) mask;
+    n = (u_char *) name;
+    ma = mask;
+    na = name;
+    wild = 0;
+    q = 0;
+
+    while (1) {
+       if (*m == '*') {
+           while (*m == '*')
+               m++;
+           wild = 1;
+           ma = (char *) m;
+           na = (char *) n;
+       }
+       if (!*m) {
+           if (!*n)
+               return 0;
+           for (m--; (m > (u_char *) mask) && (*m == '?'); m--);
+#ifdef ESCAPED_MASKING
+           if ((*m == '*') && (m > (u_char *) mask) && (m[-1] != '\\'))
+               return 0;
+#endif
+           if (!wild)
+               return 1;
+           m = (u_char *) ma;
+           n = (u_char *)++ na;
+       }
+       else if (!*n) {
+           while (*m == '*')
+               m++;
+           return (*m != 0);
+       }
+#ifdef ESCAPED_MASKING
+       if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?'))) {
+           m++;
+           q = 1;
+       }
+       else
+#endif
+           q = 0;
+
+       if ((tolower (*m) != tolower (*n)) && ((*m != '?') || q)) {
+           if (!wild)
+               return 1;
+           m = (u_char *) ma;
+           n = (u_char *)++ na;
+       }
+       else {
+           if (*m)
+               m++;
+           if (*n)
+               n++;
+       }
+    }
+}
+
+/*
+ * collapse a pattern string into minimal components.
+ * This particular version is "in place", so that it changes the pattern
+ * which is to be reduced to a "minimal" size.
+ */
+char *collapse (pattern)
+     char *pattern;
+{
+    char *s;
+    char *s1;
+    char *t;
+
+    s = pattern;
+
+    if (BadPtr (pattern))
+       return pattern;
+    /*
+     * Collapse all \** into \*, \*[?]+\** into \*[?]+
+     */
+    for (; *s; s++)
+       if (*s == '\\') {
+           if (!*(s + 1))
+               break;
+           else
+               s++;
+       }
+       else if (*s == '*') {
+           if (*(t = s1 = s + 1) == '*')
+               while (*t == '*')
+                   t++;
+           else if (*t == '?')
+               for (t++, s1++; *t == '*' || *t == '?'; t++)
+                   if (*t == '?')
+                       *s1++ = *t;
+           while ((*s1++ = *t++));
+       }
+    return pattern;
+}
+
+
+/*
+ *  Case insensitive comparison of two NULL terminated strings.
+ *
+ *  returns  0, if s1 equal to s2
+ *      <0, if s1 lexicographically less than s2
+ *      >0, if s1 lexicographically greater than s2
+ */
+int mycmp (s1, s2)
+     char *s1;
+     char *s2;
+{
+    u_char *str1;
+    u_char *str2;
+    int res;
+
+    str1 = (u_char *) s1;
+    str2 = (u_char *) s2;
+
+    while ((res = toupper (*str1) - toupper (*str2)) == 0) {
+       if (*str1 == '\0')
+           return 0;
+       str1++;
+       str2++;
+    }
+    return (res);
+}
+
+u_char tolowertab[] = {
+    0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
+    0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
+    0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+    0x1e, 0x1f,
+    ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
+    '*', '+', ',', '-', '.', '/',
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+    ':', ';', '<', '=', '>', '?',
+    '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+    'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+    't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '\\', ']', '^',
+    '_',
+    '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
+    'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+    't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
+    0x7f,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+    0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+    0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
+    0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+    0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+    0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+    0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+    0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+    0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+u_char touppertab[] = {
+    0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa,
+    0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14,
+    0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+    0x1e, 0x1f,
+    ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')',
+    '*', '+', ',', '-', '.', '/',
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+    ':', ';', '<', '=', '>', '?',
+    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
+    'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^',
+    0x5f,
+    '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
+    'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~',
+    0x7f,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+    0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+    0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
+    0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+    0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+    0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+    0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+    0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
+    0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+u_char char_atribs[] = {
+/* 0-7 */ CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL,
+/* 8-12 */ CNTRL, CNTRL | SPACE, CNTRL | SPACE, CNTRL | SPACE,
+    CNTRL | SPACE,
+/* 13-15 */ CNTRL | SPACE, CNTRL, CNTRL,
+/* 16-23 */ CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL,
+/* 24-31 */ CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL, CNTRL,
+/* space */ PRINT | SPACE,
+/* !"#$%&'( */ PRINT, PRINT, PRINT, PRINT, PRINT, PRINT, PRINT, PRINT,
+/* )*+,-./ */ PRINT, PRINT, PRINT, PRINT, PRINT | ALLOW, PRINT | ALLOW,
+    PRINT,
+/* 012 */ PRINT | DIGIT | ALLOW, PRINT | DIGIT | ALLOW,
+    PRINT | DIGIT | ALLOW,
+/* 345 */ PRINT | DIGIT | ALLOW, PRINT | DIGIT | ALLOW,
+    PRINT | DIGIT | ALLOW,
+/* 678 */ PRINT | DIGIT | ALLOW, PRINT | DIGIT | ALLOW,
+    PRINT | DIGIT | ALLOW,
+/* 9:; */ PRINT | DIGIT | ALLOW, PRINT, PRINT,
+/* <=>? */ PRINT, PRINT, PRINT, PRINT,
+/* @ */ PRINT,
+/* ABC */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* DEF */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* GHI */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* JKL */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* MNO */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* PQR */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* STU */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* VWX */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* YZ[ */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW, PRINT | ALPHA,
+/* \]^ */ PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+/* _`  */ PRINT | ALLOW, PRINT,
+/* abc */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* def */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* ghi */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* jkl */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* mno */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* pqr */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* stu */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* vwx */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW,
+    PRINT | ALPHA | ALLOW,
+/* yz{ */ PRINT | ALPHA | ALLOW, PRINT | ALPHA | ALLOW, PRINT | ALPHA,
+/* |}~ */ PRINT | ALPHA, PRINT | ALPHA, PRINT | ALPHA,
+/* del */ 0,
+/* 80-8f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* 90-9f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* a0-af */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* b0-bf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* c0-cf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* d0-df */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* e0-ef */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* f0-ff */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
diff --git a/src/md5.c b/src/md5.c
new file mode 100644 (file)
index 0000000..ffcf87c
--- /dev/null
+++ b/src/md5.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1996 Michael Shalayeff.
+ *
+ * This software derived from one contributed by Colin Plumb.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *  This product includes software developed by Colin Plumb.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ *    to endorse or promote products derived from this software without
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * The code for MD5 transform was taken from Colin Plumb's
+ * implementation, which has been placed in the public domain.  The
+ * MD5 cryptographic checksum was devised by Ronald Rivest, and is
+ * documented in RFC 1321, "The MD5 Message Digest Algorithm".
+ * 
+ */
+
+#include <sys/types.h>
+
+#include "struct.h"
+
+/*
+ * MD5 transform algorithm, taken from code written by Colin Plumb,
+ * and put into the public domain
+ *
+ * QUESTION: Replace this with SHA, which as generally received better
+ * reviews from the cryptographic community?
+ */
+void MD5Init (buf)
+     u_int32_t buf[4];
+{
+    buf[0] = 0x67452301;
+    buf[1] = 0xefcdab89;
+    buf[2] = 0x98badcfe;
+    buf[3] = 0x10325476;
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.
+ */
+void MD5Transform (buf, in)
+     u_int32_t buf[4];
+     u_int32_t in[16];
+{
+    u_int32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
diff --git a/src/packet.c b/src/packet.c
new file mode 100644 (file)
index 0000000..35abaf6
--- /dev/null
@@ -0,0 +1,205 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, common/packet.c
+ *   Copyright (C) 1990  Jarkko Oikarinen and
+ *                       University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "msg.h"
+#include "h.h"
+
+/*
+ * inittoken
+ * Cheat here, blah. Build the lookup tables from msgtab's,
+ * call them msgmap's. Called in main() with other inits.
+ * Yes, I know this is not the right module, but I said I cheat ;)
+ */
+void inittoken (void)
+{
+    int loopy;
+    int final;
+
+    /* Find the zero-entry */
+    for (final = 0; msgtab[final].cmd; final++);
+    /* Point all entries to it */
+    for (loopy = 0; loopy < 256; loopy++)
+       msgmap[loopy] = &msgtab[final];
+    /* Build references to existing commands */
+    for (loopy = 0; msgtab[loopy].cmd; loopy++)
+       msgmap[msgtab[loopy].token[0]] = &msgtab[loopy];
+}
+
+/*
+   ** dopacket
+   **   cptr - pointer to client structure for which the buffer data
+   **          applies.
+   **   buffer - pointr to the buffer containing the newly read data
+   **   length - number of valid bytes of data in the buffer
+   **
+   ** Note:
+   **   It is implicitly assumed that dopacket is called only
+   **   with cptr of "local" variation, which contains all the
+   **   necessary fields (buffer etc..)
+ */
+int dopacket (cptr, buffer, length)
+     aClient *cptr;
+     char *buffer;
+     int length;
+{
+    register char *ch1;
+    register char *ch2;
+    int zipped = 0;
+    int unzipped_length = 0;
+
+    aClient *acpt = cptr->acpt;
+
+    unzipped_length = length;
+    me.receiveB += length;       /* Update bytes received */
+    cptr->receiveB += length;
+    if (cptr->receiveB > 1023) {
+       cptr->receiveK += (cptr->receiveB >> 10);
+       cptr->receiveB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
+    }
+    if (acpt != &me) {
+       acpt->receiveB += length;
+       if (acpt->receiveB > 1023) {
+           acpt->receiveK += (acpt->receiveB >> 10);
+           acpt->receiveB &= 0x03ff;
+       }
+    }
+    else if (me.receiveB > 1023) {
+       me.receiveK += (me.receiveB >> 10);
+       me.receiveB &= 0x03ff;
+    }
+    ch1 = cptr->buffer + cptr->count;
+    ch2 = buffer;
+
+#ifdef ZIP_LINKS
+    if (cptr->flags2 & FLAGS2_ZIPFIRST) {
+       if (*ch2 == '\n' || *ch2 == '\r') {
+           ch2++;
+           length--;
+       }
+
+       cptr->flags2 &= ~FLAGS2_ZIPFIRST;
+    }
+
+    if (length && (cptr->flags2 & FLAGS2_ZIP)) {
+       zipped = length;
+
+       ch2 = unzip_packet (cptr, ch2, &zipped);
+       length = zipped;
+       zipped = 1;
+
+       if (length == -1)
+           return 0;
+
+       me.u_receiveB += length;
+
+       if (me.u_receiveB > 1023) {
+           me.u_receiveK += (me.u_receiveB >> 10);
+           me.u_receiveB &= 0x03ff;
+       }
+
+    }
+#endif
+
+    while (--length >= 0 && ch2) {
+       register char g = (*ch1 = *ch2++);
+       /*
+        * Yuck.  Stuck.  To make sure we stay backward compatible,
+        * we must assume that either CR or LF terminates the message
+        * and not CR-LF.  By allowing CR or LF (alone) into the body
+        * of messages, backward compatibility is lost and major
+        * problems will arise. - Avalon
+        */
+       if (g < '\16' && (g == '\n' || g == '\r')) {
+           if (ch1 == cptr->buffer)
+               continue;         /* Skip extra LF/CR's */
+           *ch1 = '\0';
+           me.receiveM += 1;     /* Update messages received */
+           cptr->receiveM += 1;
+           if (cptr->acpt != &me)
+               cptr->acpt->receiveM += 1;
+           cptr->count = 0;      /* ...just in case parse returns with
+                                    ** FLUSH_BUFFER without removing the
+                                    ** structure pointed by cptr... --msa
+                                  */
+           if (parse (cptr, cptr->buffer, ch1, msgtab) == FLUSH_BUFFER)
+               /*
+                  ** FLUSH_BUFFER means actually that cptr
+                  ** structure *does* not exist anymore!!! --msa
+                */
+               return FLUSH_BUFFER;
+           /*
+              ** Socket is dead so exit (which always returns with
+              ** FLUSH_BUFFER here).  - avalon
+            */
+           if (cptr->flags & FLAGS_DEADSOCKET)
+
+               return exit_client (cptr, cptr, &me, "Dead Socket");
+#ifdef ZIP_LINKS
+           if ((cptr->flags2 & FLAGS2_ZIP) && zipped == 0 && length > 0) {
+               zipped = length;
+
+               if (zipped > 0 && (*ch2 == '\n' || *ch2 == '\r')) {
+                   ch2++;
+                   zipped--;
+               }
+
+               cptr->flags2 &= ~FLAGS2_ZIPFIRST;
+
+               ch2 = unzip_packet (cptr, ch2, &zipped);
+
+               length = zipped;
+               zipped = 1;
+
+               if (length == -1) {
+                   sendto_ops ("Fatal error in unzip_packet().");
+                   return 0;
+               }
+
+               me.u_receiveB += length;
+
+               if (me.u_receiveB > 1023) {
+                   me.u_receiveK += (me.u_receiveB >> 10);
+                   me.u_receiveB &= 0x03ff;
+               }
+
+           }
+#endif
+
+           ch1 = cptr->buffer;
+       }
+       else if (ch1 < cptr->buffer + (sizeof (cptr->buffer) - 1))
+           ch1++;                /* There is always room for the null */
+    }
+    cptr->count = ch1 - cptr->buffer;
+
+    if (zipped != 1) {
+       me.u_receiveB += unzipped_length;
+
+       if (me.u_receiveB > 1023) {
+           me.u_receiveK += (me.u_receiveB >> 10);
+           me.u_receiveB &= 0x03ff;
+       }
+    }
+
+    return 0;
+}
diff --git a/src/parse.c b/src/parse.c
new file mode 100644 (file)
index 0000000..4180c4f
--- /dev/null
@@ -0,0 +1,556 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, common/parse.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* -- Jto -- 03 Jun 1990
+ * Changed the order of defines...
+ */
+
+#include "struct.h"
+#include "common.h"
+#define MSGTAB
+#include "msg.h"
+#undef MSGTAB
+#include "sys.h"
+#include "numeric.h"
+#include "h.h"
+
+/*
+ * NOTE: parse() should not be called recursively by other functions!
+ */
+static char *para[MAXPARA + 1];
+
+static char sender[HOSTLEN + 1];
+static int cancel_clients PROTO ((aClient *, aClient *, char *));
+static void remove_unknown PROTO ((aClient *, char *));
+
+/*
+   **  Find a client (server or user) by name.
+   **
+   **  *Note*
+   **   Semantics of this function has been changed from
+   **   the old. 'name' is now assumed to be a null terminated
+   **   string and the search is the for server and user.
+ */
+aClient *find_client (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    if (name)
+       cptr = hash_find_client (name, cptr);
+
+    return cptr;
+}
+
+aClient *find_nickserv (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    if (name)
+       cptr = hash_find_nickserver (name, cptr);
+
+    return cptr;
+}
+
+
+/*
+   **  Find server by name.
+   **
+   **   This implementation assumes that server and user names
+   **   are unique, no user can have a server name and vice versa.
+   **   One should maintain separate lists for users and servers,
+   **   if this restriction is removed.
+   **
+   **  *Note*
+   **   Semantics of this function has been changed from
+   **   the old. 'name' is now assumed to be a null terminated
+   **   string.
+ */
+aClient *find_server (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    if (name)
+       cptr = hash_find_server (name, cptr);
+    return cptr;
+}
+
+aClient *find_name (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    aClient *c2ptr = cptr;
+
+    if (!collapse (name))
+       return c2ptr;
+
+    if ((c2ptr = hash_find_server (name, cptr)))
+       return (c2ptr);
+    if (!index (name, '*'))
+       return c2ptr;
+    for (c2ptr = client; c2ptr; c2ptr = c2ptr->next) {
+       if (!IsServer (c2ptr) && !IsMe (c2ptr))
+           continue;
+       if (match (name, c2ptr->name) == 0)
+           break;
+       if (index (c2ptr->name, '*'))
+           if (match (c2ptr->name, name) == 0)
+               break;
+    }
+    return (c2ptr ? c2ptr : cptr);
+}
+
+/*
+   **  Find person by (nick)name.
+ */
+aClient *find_person (name, cptr)
+     char *name;
+     aClient *cptr;
+{
+    aClient *c2ptr = cptr;
+
+    c2ptr = find_client (name, c2ptr);
+
+    if (c2ptr && IsClient (c2ptr) && c2ptr->user)
+       return c2ptr;
+    else
+       return cptr;
+}
+
+/*
+ * parse a buffer.
+ *
+ * NOTE: parse() should not be called recusively by any other fucntions!
+ */
+int parse (cptr, buffer, bufend, mptr)
+     aClient *cptr;
+     char *buffer, *bufend;
+     struct Message *mptr;
+{
+    aClient *from = cptr;
+    char *ch, *s;
+    int len, i, numeric = 0, paramcount, noprefix = 0;
+#ifdef DEBUGMODE
+    time_t then, ticks;
+    int retval;
+#endif
+
+    Debug ((DEBUG_DEBUG, "Parsing: %s", buffer));
+
+    /* This will stop lamers from sending crap when not registered 
+     * bubbye port-bombers. -GZ
+     */
+
+    if (!IsRegistered (cptr)) {
+       cptr->connectQ += strlen (buffer);
+       if (cptr->connectQ > 2000) {
+           sendto_ops
+               ("Connect-flooding from %s. Connection has been closed and IP banned.",
+                cptr->sockhost);
+           add_temp_conf (CONF_ZAP, cptr->sockhost, "Flooding", NULL, 0, 0,
+                          KLINE_TEMP);
+           return exit_client (cptr, cptr, &me,
+                               "Your host/ip has been banned for flooding");
+       }
+    }
+
+    if (IsDead (cptr))
+       return 0;
+
+    s = sender;
+    *s = '\0';
+    for (ch = buffer; *ch == ' '; ch++);
+    para[0] = from->name;
+    if (*ch == ':') {
+       /*
+          ** Copy the prefix to 'sender' assuming it terminates
+          ** with SPACE (or NULL, which is an error, though).
+        */
+       for (++ch, i = 0; *ch && *ch != ' '; ++ch)
+           if (s < (sender + sizeof (sender) - 1))
+               *s++ = *ch;       /* leave room for NULL */
+       *s = '\0';
+       /*
+          ** Actually, only messages coming from servers can have
+          ** the prefix--prefix silently ignored, if coming from
+          ** a user client...
+          **
+          ** ...sigh, the current release "v2.2PL1" generates also
+          ** null prefixes, at least to NOTIFY messages (e.g. it
+          ** puts "sptr->nickname" as prefix from server structures
+          ** where it's null--the following will handle this case
+          ** as "no prefix" at all --msa  (": NOTICE nick ...")
+        */
+       if (*sender && IsServer (cptr)) {
+           from = find_client (sender, (aClient *) NULL);
+           if (!from || match (from->name, sender))
+               from = find_server (sender, (aClient *) NULL);
+           else if (!from && index (sender, '@'))
+               from = find_nickserv (sender, (aClient *) NULL);
+
+           para[0] = sender;
+
+           /* Hmm! If the client corresponding to the
+            * prefix is not found--what is the correct
+            * action??? Now, I will ignore the message
+            * (old IRC just let it through as if the
+            * prefix just wasn't there...) --msa
+            */
+           if (!from) {
+               Debug ((DEBUG_ERROR,
+                       "Unknown prefix (%s)(%s) from (%s)",
+                       sender, buffer, cptr->name));
+               ircstp->is_unpf++;
+               remove_unknown (cptr, sender);
+               return -1;
+           }
+           if (from->from != cptr) {
+               ircstp->is_wrdi++;
+               Debug ((DEBUG_ERROR,
+                       "Message (%s) coming from (%s)", buffer, cptr->name));
+               return cancel_clients (cptr, from, ch);
+           }
+       }
+       while (*ch == ' ')
+           ch++;
+    }
+    else
+       noprefix = 1;
+    if (*ch == '\0') {
+       ircstp->is_empt++;
+       Debug ((DEBUG_NOTICE, "Empty message from host %s:%s",
+               cptr->name, from->name));
+       return (-1);
+    }
+    /*
+       ** Extract the command code from the packet.  Point s to the end
+       ** of the command code and calculate the length using pointer
+       ** arithmetic.  Note: only need length for numerics and *all*
+       ** numerics must have paramters and thus a space after the command
+       ** code. -avalon
+     */
+    s = (char *) index (ch, ' '); /* s -> End of the command code */
+    len = (s) ? (s - ch) : 0;
+    if (len == 3 && isdigit (*ch) && isdigit (*(ch + 1))
+       && isdigit (*(ch + 2))) {
+       mptr = NULL;
+       numeric =
+           (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0');
+       paramcount = MAXPARA;
+       ircstp->is_num++;
+    }
+    else {
+       if (s)
+           *s++ = '\0';
+       if (ch[1] == '\0' && IsToken (cptr))
+           mptr = msgmap[(u_char) * ch];
+       else
+           for (; mptr->cmd; mptr++)
+               if (mycmp (mptr->cmd, ch) == 0)
+                   break;
+
+       if (!mptr->cmd) {
+           /*
+              ** Note: Give error message *only* to recognized
+              ** persons. It's a nightmare situation to have
+              ** two programs sending "Unknown command"'s or
+              ** equivalent to each other at full blast....
+              ** If it has got to person state, it at least
+              ** seems to be well behaving. Perhaps this message
+              ** should never be generated, though...  --msa
+              ** Hm, when is the buffer empty -- if a command
+              ** code has been found ?? -Armin
+            */
+           if (buffer[0] != '\0') {
+               if (IsPerson (from))
+                   sendto_one (from,
+                               ":%s %d %s %s :Unknown command",
+                               me.name, ERR_UNKNOWNCOMMAND, from->name, ch);
+               Debug ((DEBUG_ERROR, "Unknown (%s) from %s",
+                       ch, get_client_name (cptr, TRUE)));
+           }
+           ircstp->is_unco++;
+           return (-1);
+       }
+       paramcount = mptr->parameters;
+       i = bufend - ch;          /* Is this right? -Donwulff */
+       mptr->bytes += i;
+       if ((mptr->flags & 1) && !(IsServer (cptr) || IsService (cptr)))
+           cptr->since += (2 + i / 120);
+       /* Allow only 1 msg per 2 seconds
+        * (on average) to prevent dumping.
+        * to keep the response rate up,
+        * bursts of up to 5 msgs are allowed
+        * -SRB
+        */
+    }
+    /*
+       ** Must the following loop really be so devious? On
+       ** surface it splits the message to parameters from
+       ** blank spaces. But, if paramcount has been reached,
+       ** the rest of the message goes into this last parameter
+       ** (about same effect as ":" has...) --msa
+     */
+
+    /* Note initially true: s==NULL || *(s-1) == '\0' !! */
+
+    i = 0;
+    if (s) {
+       if (paramcount > MAXPARA)
+           paramcount = MAXPARA;
+       for (;;) {
+           /*
+              ** Never "FRANCE " again!! ;-) Clean
+              ** out *all* blanks.. --msa
+            */
+           while (*s == ' ')
+               *s++ = '\0';
+
+           if (*s == '\0')
+               break;
+           if (*s == ':') {
+               /*
+                  ** The rest is single parameter--can
+                  ** include blanks also.
+                */
+               para[++i] = s + 1;
+               break;
+           }
+           para[++i] = s;
+           if (i >= paramcount)
+               break;
+           for (; *s != ' ' && *s; s++);
+       }
+    }
+    para[++i] = NULL;
+    if (mptr == NULL)
+       return (do_numeric (numeric, cptr, from, i, para));
+
+    /* now, lets make sure they use a legit commnd... -nikb */
+    /* There is code in s_serv.c for ADMIN and VERSION and
+     * in s_user.c for NOTICE to limit commands by 
+     * unregistered users. -Studded */
+    if ((!IsRegistered (cptr)) &&
+       (((mptr->func != m_user) && (mptr->func != m_nick) &&
+         (mptr->func != m_server) && (mptr->func != m_pong) &&
+         (mptr->func != m_pass) && (mptr->func != m_quit) &&
+         (mptr->func != m_protoctl) && (mptr->func != m_error) &&
+         (mptr->func != m_admin) && (mptr->func != m_version)
+#ifdef NOSPOOF
+         && (mptr->func != m_notice)
+#endif
+        ))) {
+       sendto_one (from, ":%s %d %s :You have not registered",
+                   me.name, ERR_NOTREGISTERED, ch);
+       return -1;
+    }
+    mptr->count++;
+
+    if (IsRegisteredUser (cptr) &&
+#ifdef IDLE_FROM_MSG
+       mptr->func == m_private)
+#else
+       mptr->func != m_ping && mptr->func != m_pong)
+#endif
+       from->user->last = time (NULL);
+
+    /* Lame protocol 4 stuff... this if can be removed when all are 2.9 */
+    if (noprefix && IsServer (cptr) && i >= 2 && mptr->func == m_squit &&
+       (!(from = find_server (para[1], (aClient *) NULL)) ||
+        from->from != cptr)) {
+       Debug ((DEBUG_DEBUG, "Ignoring protocol 4 \"%s %s %s ...\"",
+               para[0], para[1], para[2]));
+       return 0;
+    }
+#ifndef DEBUGMODE
+    return (*mptr->func) (cptr, from, i, para);
+#else
+    then = clock ();
+    retval = (*mptr->func) (cptr, from, i, para);
+    if (retval != FLUSH_BUFFER) {
+       ticks = (clock () - then);
+       if (IsServer (cptr))
+           mptr->rticks += ticks;
+       else
+           mptr->lticks += ticks;
+       cptr->cputime += ticks;
+    }
+    return retval;
+#endif
+}
+
+/*
+ * field breakup for ircd.conf file.
+ */
+char *getfield (newline)
+     char *newline;
+{
+    static char *line = NULL;
+    char *end, *field;
+
+    if (newline)
+       line = newline;
+    if (line == NULL)
+       return (NULL);
+
+    field = line;
+    if ((end = (char *) index (line, ':')) == NULL) {
+       line = NULL;
+       if ((end = (char *) index (field, '\n')) == NULL)
+           end = field + strlen (field);
+    }
+    else
+       line = end + 1;
+    *end = '\0';
+    return (field);
+}
+
+static int cancel_clients (cptr, sptr, cmd)
+     aClient *cptr, *sptr;
+     char *cmd;
+{
+/*  char *cmdpriv; Compiler says this is unused */
+    /*
+     * kill all possible points that are causing confusion here,
+     * I'm not sure I've got this all right...
+     * - avalon
+     * No you didn't...
+     * - Run
+     */
+    /* This little bit of code allowed paswords to nickserv to be 
+     * seen.  A definite no-no.  --Russell
+     sendto_ops("Message (%s) for %s[%s!%s@%s] from %s", cmd,
+     sptr->name, sptr->from->name, sptr->from->username,
+     sptr->from->sockhost, get_client_name(cptr, TRUE));*/
+    /*
+     * Incorrect prefix for a server from some connection.  If it is a
+     * client trying to be annoying, just QUIT them, if it is a server
+     * then the same deal.
+     */
+    if (IsServer (sptr) || IsMe (sptr)) {
+       /*
+        * First go at tracking down what really causes the
+        * dreaded Fake Direction error.  It should not be possible
+        * ever to happen.  Assume nothing here since this is an
+        * impossibility.
+        *
+        * Check for valid fields, then send out globops with
+        * the msg command recieved, who apperently sent it,
+        * where it came from, and where it was suppose to come
+        * from.  We send the msg command to find out if its some
+        * bug somebody found with an old command, maybe some
+        * weird thing like, /ping serverto.* serverfrom.* and on
+        * the way back, fake direction?  Don't know, maybe this
+        * will tell us.  -Cabal95
+        *
+        * Take #2 on Fake Direction.  Most of them seem to be
+        * numerics.  But sometimes its getting fake direction on
+        * SERVER msgs.. HOW??  Display the full message now to
+        * figure it out... -Cabal95
+        *
+        * Okay I give up.  Can't find it.  Seems like it will
+        * exist untill ircd is completely rewritten. :/ For now
+        * just completely ignore them.  Needs to be modified to
+        * send these messages to a special oper channel. -Cabal95
+        *
+        aClient *from;
+        char   *fromname=NULL, *sptrname=NULL, *cptrname=NULL, *s;
+
+        while (*cmd == ' ')
+        cmd++;
+        if (s = index(cmd, ' '))
+        *s++ = '\0';
+        if (!strcasecmp(cmd, "PRIVMSG") ||
+        !strcasecmp(cmd, "NOTICE") ||
+        !strcasecmp(cmd, "PASS"))
+        s = NULL;
+        if (sptr && sptr->name)
+        sptrname = sptr->name;
+        if (cptr && cptr->name)
+        cptrname = cptr->name;
+        if (sptr && sptr->from && sptr->from->name)
+        fromname = sptr->from->name;
+
+        sendto_serv_butone(NULL, ":%s GLOBOPS :"
+        "Fake Direction: Message[%s %s] from %s via %s "
+        "instead of %s (Tell Cabal95)", me.name, cmd,
+        (s ? s : ""),
+        (sptr->name!=NULL)?sptr->name:"<unknown>",
+        (cptr->name!=NULL)?cptr->name:"<unknown>",
+        (fromname!=NULL)?fromname:"<unknown>");
+        sendto_ops(
+        "Fake Direction: Message[%s %s] from %s via %s "
+        "instead of %s (Tell Cabal95)", cmd,
+        (s ? s : ""),
+        (sptr->name!=NULL)?sptr->name:"<unknown>",
+        (cptr->name!=NULL)?cptr->name:"<unknown>",
+        (fromname!=NULL)?fromname:"<unknown>");
+
+        * We don't drop the server anymore.  Just ignore
+        * the message and go about your business.  And hope
+        * we don't get flooded. :-)  -Cabal95
+        sendto_ops("Dropping server %s", cptr->name);
+        return exit_client(cptr, cptr, &me, "Fake Direction");
+        */
+       return 0;
+    }
+    /*
+     * Ok, someone is trying to impose as a client and things are
+     * confused.  If we got the wrong prefix from a server, send out a
+     * kill, else just exit the lame client.
+     */
+    if (IsServer (cptr)) {
+       /*
+          ** It is NOT necessary to send a KILL here...
+          ** We come here when a previous 'NICK new'
+          ** nick collided with an older nick, and was
+          ** ignored, and other messages still were on
+          ** the way (like the following USER).
+          ** We simply ignore it all, a purge will be done
+          ** automatically by the server 'cptr' as a reaction
+          ** on our 'NICK older'. --Run
+        */
+       return 0;                 /* On our side, nothing changed */
+    }
+    return exit_client (cptr, cptr, &me, "Fake prefix");
+}
+
+static void remove_unknown (cptr, sender)
+     aClient *cptr;
+     char *sender;
+{
+    if (!IsRegistered (cptr) || IsClient (cptr))
+       return;
+    /*
+     * Not from a server so don't need to worry about it.
+     */
+    if (!IsServer (cptr))
+       return;
+    /*
+     * Do kill if it came from a server because it means there is a ghost
+     * user on the other server which needs to be removed. -avalon
+     */
+    if (!index (sender, '.'))
+       sendto_one (cptr, ":%s KILL %s :%s (%s(?) <- %s)",
+                   me.name, sender, me.name, sender,
+                   get_client_name (cptr, FALSE));
+    else
+       sendto_one (cptr, ":%s SQUIT %s :(Unknown from %s)",
+                   me.name, sender, get_client_name (cptr, FALSE));
+}
diff --git a/src/res.c b/src/res.c
new file mode 100644 (file)
index 0000000..dbfd0eb
--- /dev/null
+++ b/src/res.c
@@ -0,0 +1,1334 @@
+/*
+ * ircd/res.c (C)opyright 1992, 1993, 1994 Darren Reed. All rights reserved.
+ * This file may not be distributed without the author's prior permission in
+ * any shape or form. The author takes no responsibility for any damage or
+ * loss of property which results from the use of this software.  Distribution
+ * of this file must include this notice.
+ */
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "res.h"
+#include "numeric.h"
+#include "h.h"
+
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include "nameser.h"
+#include "resolv.h"
+#include "inet.h"                /* inet_addr() */
+
+#undef DEBUG                     /* because there is a lot of debug code in here :-) */
+
+extern int dn_expand PROTO ((char *, char *, char *, char *, int));
+extern int dn_skipname PROTO ((char *, char *));
+extern int res_mkquery PROTO ((int, char *, int, int, char *, int,
+                              struct rrec *, char *, int));
+
+extern int errno, h_errno;
+extern int highest_fd;
+extern aClient *local[];
+
+static char hostbuf[HOSTLEN + 1];
+static char dot[] = ".";
+static int incache = 0;
+static CacheTable hashtable[ARES_CACSIZE];
+static aCache *cachetop = NULL;
+static ResRQ *last, *first;
+
+static void rem_cache PROTO ((aCache *));
+static void rem_request PROTO ((ResRQ *));
+static int do_query_name PROTO ((Link *, char *, ResRQ *));
+static int do_query_number PROTO ((Link *, struct in_addr *, ResRQ *));
+static void resend_query PROTO ((ResRQ *));
+static int proc_answer PROTO ((ResRQ *, HEADER *, char *, char *));
+static int query_name PROTO ((char *, int, int, ResRQ *));
+static aCache *make_cache PROTO ((ResRQ *));
+static aCache *find_cache_name PROTO ((char *));
+static aCache *find_cache_number PROTO ((ResRQ *, char *));
+static int add_request PROTO ((ResRQ *));
+static ResRQ *make_request PROTO ((Link *));
+static int send_res_msg PROTO ((char *, int, int));
+static ResRQ *find_id PROTO ((int));
+static int hash_number PROTO ((unsigned char *));
+static void update_list PROTO ((ResRQ *, aCache *));
+static int hash_name PROTO ((char *));
+
+static struct cacheinfo
+{
+    int ca_adds;
+    int ca_dels;
+    int ca_expires;
+    int ca_lookups;
+    int ca_na_hits;
+    int ca_nu_hits;
+    int ca_updates;
+}
+cainfo;
+
+static struct resinfo
+{
+    int re_errors;
+    int re_nu_look;
+    int re_na_look;
+    int re_replies;
+    int re_requests;
+    int re_resends;
+    int re_sent;
+    int re_timeouts;
+    int re_shortttl;
+    int re_unkrep;
+}
+reinfo;
+
+int init_resolver (op)
+     int op;
+{
+    int ret = 0;
+
+#ifdef HAVE_LRAND48
+    srand48 (time (NULL));
+#endif
+    if (op & RES_INITLIST) {
+       bzero ((char *) &reinfo, sizeof (reinfo));
+       first = last = NULL;
+    }
+    if (op & RES_CALLINIT) {
+       ret = res_init ();
+       if (!_res.nscount) {
+           _res.nscount = 1;
+           _res.nsaddr_list[0].sin_addr.s_addr = inet_addr ("127.0.0.1");
+       }
+    }
+    if (op & RES_INITSOCK) {
+       int on = 0;
+       ret = resfd = socket (AF_INET, SOCK_DGRAM, 0);
+       set_non_blocking (resfd, &me);
+       (void) setsockopt (ret, SOL_SOCKET, SO_BROADCAST,
+                          (char *) &on, sizeof (on));
+    }
+#ifdef DEBUG
+    if (op & RES_INITDEBG);
+    _res.options |= RES_DEBUG;
+#endif
+    if (op & RES_INITCACH) {
+       bzero ((char *) &cainfo, sizeof (cainfo));
+       bzero ((char *) hashtable, sizeof (hashtable));
+    }
+    if (op == 0)
+       ret = resfd;
+    return ret;
+}
+
+static int add_request (new)
+     ResRQ *new;
+{
+    if (!new)
+       return -1;
+    if (!first)
+       first = last = new;
+    else {
+       last->next = new;
+       last = new;
+    }
+    new->next = NULL;
+    reinfo.re_requests++;
+    return 0;
+}
+
+/*
+ * remove a request from the list. This must also free any memory that has
+ * been allocated for temporary storage of DNS results.
+ */
+static void rem_request (old)
+     ResRQ *old;
+{
+    ResRQ **rptr, *r2ptr = NULL;
+    int i;
+    char *s;
+
+    if (!old)
+       return;
+    for (rptr = &first; *rptr; r2ptr = *rptr, rptr = &(*rptr)->next)
+       if (*rptr == old) {
+           *rptr = old->next;
+           if (last == old)
+               last = r2ptr;
+           break;
+       }
+#ifdef DEBUG
+    Debug ((DEBUG_INFO, "rem_request:Remove %#x at %#x %#x",
+           old, *rptr, r2ptr));
+#endif
+    r2ptr = old;
+    if (r2ptr->he.h_name)
+       MyFree ((char *) r2ptr->he.h_name);
+    for (i = 0; i < MAXALIASES; i++)
+       if ((s = r2ptr->he.h_aliases[i]))
+           MyFree (s);
+    if (r2ptr->name)
+       MyFree (r2ptr->name);
+    MyFree (r2ptr);
+
+    return;
+}
+
+/*
+ * Create a DNS request record for the server.
+ */
+static ResRQ *make_request (lp)
+     Link *lp;
+{
+    ResRQ *nreq;
+
+    nreq = (ResRQ *) MyMalloc (sizeof (ResRQ));
+    bzero ((char *) nreq, sizeof (ResRQ));
+    nreq->next = NULL;           /* where NULL is non-zero ;) */
+    nreq->sentat = time (NULL);
+    nreq->retries = 3;
+    nreq->resend = 1;
+    nreq->srch = -1;
+    if (lp)
+       bcopy ((char *) lp, (char *) &nreq->cinfo, sizeof (Link));
+    else
+       bzero ((char *) &nreq->cinfo, sizeof (Link));
+    nreq->timeout = 4;           /* start at 4 and exponential inc. */
+    nreq->he.h_addrtype = AF_INET;
+    nreq->he.h_name = NULL;
+    nreq->he.h_aliases[0] = NULL;
+    (void) add_request (nreq);
+    return nreq;
+}
+
+/*
+ * Remove queries from the list which have been there too long without
+ * being resolved.
+ */
+time_t timeout_query_list (now)
+     time_t now;
+{
+    ResRQ *rptr, *r2ptr;
+    time_t next = 0, tout;
+    aClient *cptr;
+
+    Debug ((DEBUG_DNS, "timeout_query_list at %s", myctime (now)));
+    for (rptr = first; rptr; rptr = r2ptr) {
+       r2ptr = rptr->next;
+       tout = rptr->sentat + rptr->timeout;
+       if (now >= tout) {
+           if (--rptr->retries <= 0) {
+#ifdef DEBUG
+               Debug ((DEBUG_ERROR, "timeout %x now %d cptr %x",
+                       rptr, now, rptr->cinfo.value.cptr));
+#endif
+               reinfo.re_timeouts++;
+               cptr = rptr->cinfo.value.cptr;
+               switch (rptr->cinfo.flags) {
+               case ASYNC_CLIENT:
+                   if (cptr->cc)
+                       write (cptr->fd, REPORT_FAIL_DNS, R_fail_dns);
+                   ClearDNS (cptr);
+                   SetAccess (cptr);
+                   break;
+               case ASYNC_SERVER:
+                   sendto_ops ("Host %s unknown", rptr->name);
+                   ClearDNS (cptr);
+                   if (check_server (cptr, NULL, NULL, NULL, 1))
+                       (void) exit_client (cptr, cptr, &me, "No Permission");
+                   break;
+               case ASYNC_CONNECT:
+                   sendto_ops ("Host %s unknown", rptr->name);
+                   break;
+               }
+               rem_request (rptr);
+               continue;
+           }
+           else {
+               rptr->sentat = now;
+               rptr->timeout += rptr->timeout;
+               resend_query (rptr);
+               tout = now + rptr->timeout;
+#ifdef DEBUG
+               Debug ((DEBUG_INFO, "r %x now %d retry %d c %x",
+                       rptr, now, rptr->retries, rptr->cinfo.value.cptr));
+#endif
+           }
+       }
+       if (!next || tout < next)
+           next = tout;
+    }
+    Debug ((DEBUG_DNS, "Next timeout_query_list() at %s, %d",
+           myctime ((next > now) ? next : (now + AR_TTL)),
+           (next > now) ? (next - now) : AR_TTL));
+    return (next > now) ? next : (now + AR_TTL);
+}
+
+/*
+ * del_queries - called by the server to cleanup outstanding queries for
+ * which there no longer exist clients or conf lines.
+ */
+void del_queries (cp)
+     char *cp;
+{
+    ResRQ *rptr, *r2ptr;
+
+    for (rptr = first; rptr; rptr = r2ptr) {
+       r2ptr = rptr->next;
+       if (cp == rptr->cinfo.value.cp)
+           rem_request (rptr);
+    }
+}
+
+/*
+ * sends msg to all nameservers found in the "_res" structure.
+ * This should reflect /etc/resolv.conf. We will get responses
+ * which arent needed but is easier than checking to see if nameserver
+ * isnt present. Returns number of messages successfully sent to 
+ * nameservers or -1 if no successful sends.
+ */
+static int send_res_msg (msg, len, rcount)
+     char *msg;
+     int len, rcount;
+{
+    int i;
+    int sent = 0, max;
+
+    if (!msg)
+       return -1;
+
+    max = MIN (_res.nscount, rcount);
+    if (_res.options & RES_PRIMARY)
+       max = 1;
+    if (!max)
+       max = 1;
+
+    for (i = 0; i < max; i++) {
+       _res.nsaddr_list[i].sin_family = AF_INET;
+       if (sendto (resfd, msg, len, 0, (struct sockaddr *)
+                   &(_res.nsaddr_list[i]), sizeof (struct sockaddr)) == len) {
+           reinfo.re_sent++;
+           sent++;
+       }
+       else
+           Debug ((DEBUG_ERROR, "s_r_m:sendto: %d on %d", errno, resfd));
+    }
+
+    return (sent) ? sent : -1;
+}
+
+/*
+ * find a dns request id (id is determined by dn_mkquery)
+ */
+static ResRQ *find_id (id)
+     int id;
+{
+    ResRQ *rptr;
+
+    for (rptr = first; rptr; rptr = rptr->next)
+       if (rptr->id == id)
+           return rptr;
+    return NULL;
+}
+
+struct hostent *gethost_byname (name, lp)
+     char *name;
+     Link *lp;
+{
+    aCache *cp;
+
+    reinfo.re_na_look++;
+    if ((cp = find_cache_name (name)))
+       return (struct hostent *) &(cp->he);
+    if (!lp)
+       return NULL;
+    (void) do_query_name (lp, name, NULL);
+    return NULL;
+}
+
+struct hostent *gethost_byaddr (addr, lp)
+     char *addr;
+     Link *lp;
+{
+    aCache *cp;
+
+    reinfo.re_nu_look++;
+    if ((cp = find_cache_number (NULL, addr)))
+       return (struct hostent *) &(cp->he);
+    if (!lp)
+       return NULL;
+    (void) do_query_number (lp, (struct in_addr *) addr, NULL);
+    return NULL;
+}
+
+static int do_query_name (lp, name, rptr)
+     Link *lp;
+     char *name;
+     ResRQ *rptr;
+{
+    char hname[HOSTLEN + 1];
+    int len;
+
+    (void) strncpy (hname, name, sizeof (hname) - 1);
+    len = strlen (hname);
+
+    if (rptr && !index (hname, '.') && _res.options & RES_DEFNAMES) {
+       (void) strncat (hname, dot, sizeof (hname) - len - 1);
+       len++;
+       (void) strncat (hname, _res.defdname, sizeof (hname) - len - 1);
+    }
+    /*
+     * Store the name passed as the one to lookup and generate other host
+     * names to pass onto the nameserver(s) for lookups.
+     */
+    if (!rptr) {
+       rptr = make_request (lp);
+       rptr->type = T_A;
+       rptr->name = (char *) MyMalloc (strlen (name) + 1);
+       (void) strcpy (rptr->name, name);
+    }
+    return (query_name (hname, C_IN, T_A, rptr));
+}
+
+/*
+ * Use this to do reverse IP# lookups.
+ */
+static int do_query_number (lp, numb, rptr)
+     Link *lp;
+     struct in_addr *numb;
+     ResRQ *rptr;
+{
+    char ipbuf[32];
+    u_char *cp;
+
+    cp = (u_char *) & numb->s_addr;
+    (void) sprintf (ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
+                   (u_int) (cp[3]), (u_int) (cp[2]),
+                   (u_int) (cp[1]), (u_int) (cp[0]));
+    if (!rptr) {
+       rptr = make_request (lp);
+       rptr->type = T_PTR;
+       rptr->addr.s_addr = numb->s_addr;
+       bcopy ((char *) &numb->s_addr,
+              (char *) &rptr->he.h_addr, sizeof (struct in_addr));
+       rptr->he.h_length = sizeof (struct in_addr);
+    }
+    return (query_name (ipbuf, C_IN, T_PTR, rptr));
+}
+
+/*
+ * generate a query based on class, type and name.
+ */
+static int query_name (name, class, type, rptr)
+     char *name;
+     int class, type;
+     ResRQ *rptr;
+{
+    struct timeval tv;
+    char buf[MAXPACKET];
+    int r, s, k = 0;
+    HEADER *hptr;
+
+    Debug ((DEBUG_DNS, "query_name: na %s cl %d ty %d", name, class, type));
+    bzero (buf, sizeof (buf));
+    r = res_mkquery (QUERY, name, class, type, NULL, 0, NULL,
+                    buf, sizeof (buf));
+    if (r <= 0) {
+       h_errno = NO_RECOVERY;
+       return r;
+    }
+    hptr = (HEADER *) buf;
+#ifdef HAVE_LRAND48
+    do {
+       hptr->id = htons (ntohs (hptr->id) + k + lrand48 () & 0xffff);
+#else
+    (void) gettimeofday (&tv, NULL);
+    do {
+       /* htons/ntohs can be assembler macros, which cannot
+          be nested. Thus two lines.   -Vesa           */
+       u_short nstmp =
+           ntohs (hptr->id) + k + (u_short) (tv.tv_usec & 0xffff);
+       hptr->id = htons (nstmp);
+#endif /* HAVE_LRAND48 */
+       k++;
+    }
+    while (find_id (ntohs (hptr->id)));
+    rptr->id = ntohs (hptr->id);
+    rptr->sends++;
+    s = send_res_msg (buf, r, rptr->sends);
+    if (s == -1) {
+       h_errno = TRY_AGAIN;
+       return -1;
+    }
+    else
+       rptr->sent += s;
+    return 0;
+}
+
+static void resend_query (rptr)
+     ResRQ *rptr;
+{
+    if (rptr->resend == 0)
+       return;
+    reinfo.re_resends++;
+    switch (rptr->type) {
+    case T_PTR:
+       (void) do_query_number (NULL, &rptr->addr, rptr);
+       break;
+    case T_A:
+       (void) do_query_name (NULL, rptr->name, rptr);
+       break;
+    default:
+       break;
+    }
+    return;
+}
+
+/*
+ * process name server reply.
+ */
+static int proc_answer (rptr, hptr, buf, eob)
+     ResRQ *rptr;
+     char *buf, *eob;
+     HEADER *hptr;
+{
+    char *cp, **alias;
+    struct hent *hp;
+    int class, type, dlen, len, ans = 0, n;
+    struct in_addr dr, *adr;
+
+    cp = buf + sizeof (HEADER);
+    hp = (struct hent *) &(rptr->he);
+    adr = &hp->h_addr;
+    while (adr->s_addr)
+       adr++;
+    alias = hp->h_aliases;
+    while (*alias)
+       alias++;
+#ifdef SOL20                     /* brain damaged compiler (Solaris2) it seems */
+    for (; hptr->qdcount > 0; hptr->qdcount--)
+#else
+    while (hptr->qdcount-- > 0)
+#endif /* SOL20 */
+       if ((n = dn_skipname (cp, eob)) == -1)
+           break;
+       else
+           cp += (n + QFIXEDSZ);
+    /*
+     * proccess each answer sent to us blech.
+     */
+    while (hptr->ancount-- > 0 && cp && cp < eob) {
+       n = dn_expand (buf, eob, cp, hostbuf, sizeof (hostbuf));
+       if (n <= 0)
+           break;
+
+       cp += n;
+       type = (int) _getshort (cp);
+       cp += sizeof (short);
+       class = (int) _getshort (cp);
+       cp += sizeof (short);
+       rptr->ttl = _getlong (cp);
+       cp += sizeof (rptr->ttl);
+       dlen = (int) _getshort (cp);
+       cp += sizeof (short);
+       rptr->type = type;
+
+       len = strlen (hostbuf);
+       /* name server never returns with trailing '.' */
+       if (!index (hostbuf, '.') && (_res.options & RES_DEFNAMES)) {
+           (void) strcat (hostbuf, dot);
+           len++;
+           (void) strncat (hostbuf, _res.defdname,
+                           sizeof (hostbuf) - 1 - len);
+           len = MIN (len + strlen (_res.defdname), sizeof (hostbuf) - 1);
+       }
+       switch (type) {
+       case T_A:
+           hp->h_length = dlen;
+           if (ans == 1)
+               hp->h_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC;
+           bcopy (cp, (char *) &dr, dlen);
+           adr->s_addr = dr.s_addr;
+           Debug ((DEBUG_INFO, "got ip # %s for %s",
+                   inetntoa ((char *) adr), hostbuf));
+           if (!hp->h_name) {
+               hp->h_name = (char *) MyMalloc (len + 1);
+               (void) strcpy (hp->h_name, hostbuf);
+           }
+           ans++;
+           adr++;
+           cp += dlen;
+           break;
+       case T_PTR:
+           if ((n = dn_expand (buf, eob, cp, hostbuf, sizeof (hostbuf))) < 0) {
+               cp = NULL;
+               break;
+           }
+           cp += n;
+           len = strlen (hostbuf);
+           Debug ((DEBUG_INFO, "got host %s", hostbuf));
+           /*
+            * copy the returned hostname into the host name
+            * or alias field if there is a known hostname
+            * already.
+            */
+           if (hp->h_name) {
+               if (alias >= &(hp->h_aliases[MAXALIASES - 1]))
+                   break;
+               *alias = (char *) MyMalloc (len + 1);
+               (void) strcpy (*alias++, hostbuf);
+               *alias = NULL;
+           }
+           else {
+               hp->h_name = (char *) MyMalloc (len + 1);
+               (void) strcpy (hp->h_name, hostbuf);
+           }
+           ans++;
+           break;
+       case T_CNAME:
+           cp += dlen;
+           Debug ((DEBUG_INFO, "got cname %s", hostbuf));
+           if (alias >= &(hp->h_aliases[MAXALIASES - 1]))
+               break;
+           *alias = (char *) MyMalloc (len + 1);
+           (void) strcpy (*alias++, hostbuf);
+           *alias = NULL;
+           ans++;
+           break;
+       default:
+#ifdef DEBUG
+           Debug ((DEBUG_INFO, "proc_answer: type:%d for:%s", type,
+                   hostbuf));
+#endif
+           break;
+       }
+    }
+    return ans;
+}
+
+
+/*
+ * read a dns reply from the nameserver and process it.
+ */
+struct hostent *get_res (lp)
+     char *lp;
+{
+    static char buf[sizeof (HEADER) + MAXPACKET];
+    HEADER *hptr;
+    struct sockaddr_in sin;
+    int rc, a, len = sizeof (sin), max;
+    ResRQ *rptr = NULL;
+    aCache *cp = NULL;
+
+    rc = recvfrom (resfd, buf, sizeof (buf), 0, (struct sockaddr *) &sin,
+                  &len);
+    if (rc == -1 || rc <= sizeof (HEADER))
+       goto getres_err;
+    /*
+     * convert DNS reply reader from Network byte order to CPU byte order.
+     */
+    hptr = (HEADER *) buf;
+    hptr->id = ntohs (hptr->id);
+    hptr->ancount = ntohs (hptr->ancount);
+    hptr->qdcount = ntohs (hptr->qdcount);
+    hptr->nscount = ntohs (hptr->nscount);
+    hptr->arcount = ntohs (hptr->arcount);
+#ifdef DEBUG
+    Debug ((DEBUG_NOTICE, "get_res:id = %d rcode = %d ancount = %d",
+           hptr->id, hptr->rcode, hptr->ancount));
+#endif
+    reinfo.re_replies++;
+    /*
+     * response for an id which we have already received an answer for
+     * just ignore this response.
+     */
+    rptr = find_id (hptr->id);
+    if (!rptr)
+       goto getres_err;
+    /*
+     * check against possibly fake replies
+     */
+    max = MIN (_res.nscount, rptr->sends);
+    if (!max)
+       max = 1;
+
+    for (a = 0; a < max; a++)
+       if (!_res.nsaddr_list[a].sin_addr.s_addr ||
+           !bcmp ((char *) &sin.sin_addr,
+                  (char *) &_res.nsaddr_list[a].sin_addr,
+                  sizeof (struct in_addr)))
+           break;
+    if (a == max) {
+       reinfo.re_unkrep++;
+       goto getres_err;
+    }
+    if ((hptr->rcode != NOERROR) || (hptr->ancount == 0)) {
+       switch (hptr->rcode) {
+       case NXDOMAIN:
+           h_errno = TRY_AGAIN;
+           break;
+       case SERVFAIL:
+           h_errno = TRY_AGAIN;
+           break;
+       case NOERROR:
+           h_errno = NO_DATA;
+           break;
+       case FORMERR:
+       case NOTIMP:
+       case REFUSED:
+       default:
+           h_errno = NO_RECOVERY;
+           break;
+       }
+       reinfo.re_errors++;
+       /*
+          ** If a bad error was returned, we stop here and dont send
+          ** send any more (no retries granted).
+        */
+       if (h_errno != TRY_AGAIN) {
+           Debug ((DEBUG_DNS, "Fatal DNS error %d for %d", h_errno,
+                   hptr->rcode));
+           rptr->resend = 0;
+           rptr->retries = 0;
+       }
+       goto getres_err;
+    }
+    a = proc_answer (rptr, hptr, buf, buf + rc);
+#ifdef DEBUG
+    Debug ((DEBUG_INFO, "get_res:Proc answer = %d", a));
+#endif
+    if (a && rptr->type == T_PTR) {
+       struct hostent *hp2 = NULL;
+
+       Debug ((DEBUG_DNS, "relookup %s <-> %s",
+               rptr->he.h_name, inetntoa ((char *) &rptr->he.h_addr)));
+
+       if (!rptr->he.h_name) {
+           char badip[128];
+           sprintf (badip, "%s (a = %d)",
+                    inetntoa ((char *) &rptr->he.h_addr), a);
+
+           sendto_realops ("WARNING: Error in lookup of %s.", badip);
+           sendto_serv_butone (&me, ":%s GLOBOPS :Error in lookup of %s.",
+                               me.name, badip);
+
+           return (struct hostent *) NULL;
+       }
+       /*
+        * Lookup the 'authoritive' name that we were given for the
+        * ip#.  By using this call rather than regenerating the
+        * type we automatically gain the use of the cache with no
+        * extra kludges.
+        */
+       if ((hp2 = gethost_byname (rptr->he.h_name, &rptr->cinfo)))
+           if (lp)
+               bcopy ((char *) &rptr->cinfo, lp, sizeof (Link));
+       /*
+        * If name wasn't found, a request has been queued and it will
+        * be the last one queued.  This is rather nasty way to keep
+        * a host alias with the query. -avalon
+        */
+       if (!hp2 && rptr->he.h_aliases[0])
+           for (a = 0; rptr->he.h_aliases[a]; a++) {
+               Debug ((DEBUG_DNS, "Copied CNAME %s for %s",
+                       rptr->he.h_aliases[a], rptr->he.h_name));
+               last->he.h_aliases[a] = rptr->he.h_aliases[a];
+               rptr->he.h_aliases[a] = NULL;
+           }
+       rem_request (rptr);
+       return hp2;
+    }
+    if (a > 0) {
+       if (lp)
+           bcopy ((char *) &rptr->cinfo, lp, sizeof (Link));
+       cp = make_cache (rptr);
+#ifdef DEBUG
+       Debug ((DEBUG_INFO, "get_res:cp=%#x rptr=%#x (made)", cp, rptr));
+#endif
+
+       rem_request (rptr);
+    }
+    else if (!rptr->sent)
+       rem_request (rptr);
+    return cp ? (struct hostent *) &cp->he : NULL;
+
+  getres_err:
+    /*
+     * Reprocess an error if the nameserver didnt tell us to "TRY_AGAIN".
+     */
+    if (rptr) {
+       if (h_errno != TRY_AGAIN) {
+           /*
+            * If we havent tried with the default domain and its
+            * set, then give it a try next.
+            */
+           if (_res.options & RES_DEFNAMES && ++rptr->srch == 0) {
+               rptr->retries = _res.retry;
+               rptr->sends = 0;
+               rptr->resend = 1;
+               resend_query (rptr);
+           }
+           else
+               resend_query (rptr);
+       }
+       else if (lp)
+           bcopy ((char *) &rptr->cinfo, lp, sizeof (Link));
+    }
+    return (struct hostent *) NULL;
+}
+
+static int hash_number (ip)
+     unsigned char *ip;
+{
+    u_int hashv = 0;
+
+    /* could use loop but slower */
+    hashv += (int) *ip++;
+    hashv += hashv + (int) *ip++;
+    hashv += hashv + (int) *ip++;
+    hashv += hashv + (int) *ip++;
+    hashv %= ARES_CACSIZE;
+    return (hashv);
+}
+
+static int hash_name (name)
+     register char *name;
+{
+    u_int hashv = 0;
+
+    if (name == NULL) {
+       sendto_realops
+           ("Caught NULL pointer in hash_name().  (Bad thing -- tell rwg.)");
+       return (0);
+    }
+    for (; *name && *name != '.'; name++)
+       hashv += *name;
+    hashv %= ARES_CACSIZE;
+    return (hashv);
+}
+
+/*
+   ** Add a new cache item to the queue and hash table.
+ */
+static aCache *add_to_cache (ocp)
+     aCache *ocp;
+{
+    aCache *cp = NULL;
+    int hashv;
+
+#ifdef DEBUG
+    Debug ((DEBUG_INFO,
+           "add_to_cache:ocp %#x he %#x name %#x addrl %#x 0 %#x",
+           ocp, &ocp->he, ocp->he.h_name, ocp->he.h_addr_list,
+           ocp->he.h_addr_list[0]));
+#endif /* DEBUG */
+    ocp->list_next = cachetop;
+    cachetop = ocp;
+
+    hashv = hash_name (ocp->he.h_name);
+    ocp->hname_next = hashtable[hashv].name_list;
+    hashtable[hashv].name_list = ocp;
+
+    hashv = hash_number ((u_char *) ocp->he.h_addr);
+    ocp->hnum_next = hashtable[hashv].num_list;
+    hashtable[hashv].num_list = ocp;
+
+#ifdef DEBUG
+    Debug ((DEBUG_INFO, "add_to_cache:added %s[%08x] cache %#x.",
+           ocp->he.h_name, ocp->he.h_addr_list[0], ocp));
+    Debug ((DEBUG_INFO,
+           "add_to_cache:h1 %d h2 %x lnext %#x namnext %#x numnext %#x",
+           hash_name (ocp->he.h_name), hashv, ocp->list_next,
+           ocp->hname_next, ocp->hnum_next));
+#endif /* DEBUG */
+
+    /*
+     * LRU deletion of excessive cache entries.
+     */
+    if (++incache > MAXCACHED) {
+       for (cp = cachetop; cp->list_next; cp = cp->list_next);
+       rem_cache (cp);
+    }
+    cainfo.ca_adds++;
+
+    return ocp;
+}
+
+/*
+   ** update_list does not alter the cache structure passed. It is assumed that
+   ** it already contains the correct expire time, if it is a new entry. Old
+   ** entries have the expirey time updated.
+ */
+static void update_list (rptr, cachep)
+     ResRQ *rptr;
+     aCache *cachep;
+{
+    aCache **cpp, *cp = cachep;
+    char *s, *t, **base;
+    int i, j;
+    int addrcount;
+
+    /*
+       ** search for the new cache item in the cache list by hostname.
+       ** If found, move the entry to the top of the list and return.
+     */
+    cainfo.ca_updates++;
+
+    for (cpp = &cachetop; *cpp; cpp = &((*cpp)->list_next))
+       if (cp == *cpp)
+           break;
+    if (!*cpp)
+       return;
+    *cpp = cp->list_next;
+    cp->list_next = cachetop;
+    cachetop = cp;
+    if (!rptr)
+       return;
+
+#ifdef DEBUG
+    Debug ((DEBUG_DEBUG, "u_l:cp %#x na %#x al %#x ad %#x",
+           cp, cp->he.h_name, cp->he.h_aliases, cp->he.h_addr));
+    Debug ((DEBUG_DEBUG, "u_l:rptr %#x h_n %#x", rptr, rptr->he.h_name));
+#endif
+    /*
+     * Compare the cache entry against the new record.  Add any
+     * previously missing names for this entry.
+     */
+    for (i = 0; cp->he.h_aliases[i]; i++);
+    addrcount = i;
+    for (i = 0, s = rptr->he.h_name; s && i < MAXALIASES;
+        s = rptr->he.h_aliases[i++]) {
+       for (j = 0, t = cp->he.h_name; t && j < MAXALIASES;
+            t = cp->he.h_aliases[j++])
+           if (!mycmp (t, s))
+               break;
+       if (!t && j < MAXALIASES - 1) {
+           base = cp->he.h_aliases;
+
+           addrcount++;
+           base = (char **) MyRealloc ((char *) base,
+                                       sizeof (char *) * (addrcount + 1));
+           cp->he.h_aliases = base;
+#ifdef DEBUG
+           Debug ((DEBUG_DNS, "u_l:add name %s hal %x ac %d",
+                   s, cp->he.h_aliases, addrcount));
+#endif
+           base[addrcount - 1] = s;
+           base[addrcount] = NULL;
+           if (i)
+               rptr->he.h_aliases[i - 1] = NULL;
+           else
+               rptr->he.h_name = NULL;
+       }
+    }
+    for (i = 0; cp->he.h_addr_list[i]; i++);
+    addrcount = i;
+
+    /*
+     * Do the same again for IP#'s.
+     */
+    for (s = (char *) &rptr->he.h_addr.s_addr;
+        ((struct in_addr *) s)->s_addr; s += sizeof (struct in_addr)) {
+       for (i = 0; (t = cp->he.h_addr_list[i]); i++)
+           if (!bcmp (s, t, sizeof (struct in_addr)))
+               break;
+       if (i >= MAXADDRS || addrcount >= MAXADDRS)
+           break;
+       /*
+        * Oh man this is bad...I *HATE* it. -avalon
+        *
+        * Whats it do ?  Reallocate two arrays, one of pointers
+        * to "char *" and the other of IP addresses.  Contents of
+        * the IP array *MUST* be preserved and the pointers into
+        * it recalculated.
+        */
+       if (!t) {
+           base = cp->he.h_addr_list;
+           addrcount++;
+           t = (char *) MyRealloc (*base,
+                                   addrcount * sizeof (struct in_addr));
+           base =
+               (char **) MyRealloc ((char *) base,
+                                    (addrcount + 1) * sizeof (char *));
+           cp->he.h_addr_list = base;
+#ifdef DEBUG
+           Debug ((DEBUG_DNS, "u_l:add IP %x hal %x ac %d",
+                   ntohl (((struct in_addr *) s)->s_addr),
+                   cp->he.h_addr_list, addrcount));
+#endif
+           for (; addrcount; addrcount--) {
+               *base++ = t;
+               t += sizeof (struct in_addr);
+           }
+           *base = NULL;
+           bcopy (s, *--base, sizeof (struct in_addr));
+       }
+    }
+    return;
+}
+
+static aCache *find_cache_name (name)
+     char *name;
+{
+    aCache *cp;
+    char *s;
+    int hashv, i;
+
+    hashv = hash_name (name);
+
+    cp = hashtable[hashv].name_list;
+#ifdef DEBUG
+    Debug ((DEBUG_DNS, "find_cache_name:find %s : hashv = %d", name, hashv));
+#endif
+
+    for (; cp; cp = cp->hname_next)
+       for (i = 0, s = cp->he.h_name; s; s = cp->he.h_aliases[i++])
+           if (mycmp (s, name) == 0) {
+               cainfo.ca_na_hits++;
+               update_list (NULL, cp);
+               return cp;
+           }
+    for (cp = cachetop; cp; cp = cp->list_next) {
+       /*
+        * if no aliases or the hash value matches, we've already
+        * done this entry and all possiblilities concerning it.
+        */
+       if (!*cp->he.h_aliases)
+           continue;
+       if (hashv == hash_name (cp->he.h_name))
+           continue;
+       for (i = 0, s = cp->he.h_aliases[i]; s && i < MAXALIASES; i++)
+           if (!mycmp (name, s)) {
+               cainfo.ca_na_hits++;
+               update_list (NULL, cp);
+               return cp;
+           }
+    }
+    return NULL;
+}
+
+/*
+ * find a cache entry by ip# and update its expire time
+ */
+static aCache *find_cache_number (rptr, numb)
+     ResRQ *rptr;
+     char *numb;
+{
+    aCache *cp;
+    int hashv, i;
+#ifdef DEBUG
+    struct in_addr *ip = (struct in_addr *) numb;
+#endif
+
+    hashv = hash_number ((u_char *) numb);
+
+    cp = hashtable[hashv].num_list;
+#ifdef DEBUG
+    Debug ((DEBUG_DNS, "find_cache_number:find %s[%08x]: hashv = %d",
+           inetntoa (numb), ntohl (ip->s_addr), hashv));
+#endif
+
+    for (; cp; cp = cp->hnum_next)
+       for (i = 0; cp->he.h_addr_list[i]; i++)
+           if (!bcmp (cp->he.h_addr_list[i], numb, sizeof (struct in_addr))) {
+               cainfo.ca_nu_hits++;
+               update_list (rptr, cp);
+               return cp;
+           }
+    for (cp = cachetop; cp; cp = cp->list_next) {
+       /*
+        * single address entry...would have been done by hashed
+        * search above...
+        */
+       if (!cp->he.h_addr_list[1])
+           continue;
+       /*
+        * if the first IP# has the same hashnumber as the IP# we
+        * are looking for, its been done already.
+        */
+       if (hashv == hash_number ((u_char *) cp->he.h_addr_list[0]))
+           continue;
+       for (i = 1; cp->he.h_addr_list[i]; i++)
+           if (!bcmp (cp->he.h_addr_list[i], numb, sizeof (struct in_addr))) {
+               cainfo.ca_nu_hits++;
+               update_list (rptr, cp);
+               return cp;
+           }
+    }
+    return NULL;
+}
+
+static aCache *make_cache (rptr)
+     ResRQ *rptr;
+{
+    aCache *cp;
+    int i, n;
+    struct hostent *hp;
+    char *s, **t;
+
+    /*
+       ** shouldn't happen but it just might...
+     */
+    if (!rptr->he.h_name || !rptr->he.h_addr.s_addr)
+       return NULL;
+    /*
+       ** Make cache entry.  First check to see if the cache already exists
+       ** and if so, return a pointer to it.
+     */
+    if ((cp = find_cache_number (rptr, (char *) &rptr->he.h_addr.s_addr)))
+       return cp;
+    for (i = 1; rptr->he.h_addr_list[i].s_addr; i++)
+       if ((cp = find_cache_number (rptr,
+                                    (char *) &(rptr->he.h_addr_list[i].
+                                               s_addr))))
+           return cp;
+
+    /*
+       ** a matching entry wasnt found in the cache so go and make one up.
+     */
+    cp = (aCache *) MyMalloc (sizeof (aCache));
+    bzero ((char *) cp, sizeof (aCache));
+
+    hp = &cp->he;
+    for (i = 0; i < MAXADDRS; i++)
+       if (!rptr->he.h_addr_list[i].s_addr)
+           break;
+
+    /*
+       ** build two arrays, one for IP#'s, another of pointers to them.
+     */
+    t = hp->h_addr_list = (char **) MyMalloc (sizeof (char *) * (i + 1));
+    bzero ((char *) t, sizeof (char *) * (i + 1));
+
+    s = (char *) MyMalloc (sizeof (struct in_addr) * i);
+    bzero (s, sizeof (struct in_addr) * i);
+
+    for (n = 0; n < i; n++, s += sizeof (struct in_addr)) {
+       *t++ = s;
+       bcopy ((char *) &(rptr->he.h_addr_list[n].s_addr), s,
+              sizeof (struct in_addr));
+    }
+    *t = (char *) NULL;
+
+    /*
+       ** an array of pointers to CNAMEs.
+     */
+    for (i = 0; i < MAXALIASES; i++)
+       if (!rptr->he.h_aliases[i])
+           break;
+    i++;
+    t = hp->h_aliases = (char **) MyMalloc (sizeof (char *) * i);
+    for (n = 0; n < i; n++, t++) {
+       *t = rptr->he.h_aliases[n];
+       rptr->he.h_aliases[n] = NULL;
+    }
+
+    hp->h_addrtype = rptr->he.h_addrtype;
+    hp->h_length = rptr->he.h_length;
+    hp->h_name = rptr->he.h_name;
+
+    if (rptr->ttl < 600) {
+       reinfo.re_shortttl++;
+       cp->ttl = 600;
+    }
+    else
+       cp->ttl = rptr->ttl;
+    cp->expireat = time (NULL) + cp->ttl;
+    rptr->he.h_name = NULL;
+
+#ifdef DEBUG
+    Debug ((DEBUG_INFO, "make_cache:made cache %#x", cp));
+#endif
+    return add_to_cache (cp);
+}
+
+/*
+ * rem_cache
+ *     delete a cache entry from the cache structures and lists and return
+ *     all memory used for the cache back to the memory pool.
+ */
+static void rem_cache (ocp)
+     aCache *ocp;
+{
+    aCache **cp;
+    struct hostent *hp = &ocp->he;
+    int hashv;
+    aClient *cptr;
+
+#ifdef DEBUG
+    Debug ((DEBUG_DNS, "rem_cache: ocp %#x hp %#x l_n %#x aliases %#x",
+           ocp, hp, ocp->list_next, hp->h_aliases));
+#endif
+    /*
+       ** Cleanup any references to this structure by destroying the
+       ** pointer.
+     */
+    for (hashv = highest_fd; hashv >= 0; hashv--)
+       if ((cptr = local[hashv]) && (cptr->hostp == hp))
+           cptr->hostp = NULL;
+    /*
+     * remove cache entry from linked list
+     */
+    for (cp = &cachetop; *cp; cp = &((*cp)->list_next))
+       if (*cp == ocp) {
+           *cp = ocp->list_next;
+           break;
+       }
+    /*
+     * remove cache entry from hashed name lists
+     */
+    hashv = hash_name (hp->h_name);
+#ifdef DEBUG
+    Debug ((DEBUG_DEBUG, "rem_cache: h_name %s hashv %d next %#x first %#x",
+           hp->h_name, hashv, ocp->hname_next, hashtable[hashv].name_list));
+#endif
+    for (cp = &hashtable[hashv].name_list; *cp; cp = &((*cp)->hname_next))
+       if (*cp == ocp) {
+           *cp = ocp->hname_next;
+           break;
+       }
+    /*
+     * remove cache entry from hashed number list
+     */
+    hashv = hash_number ((u_char *) hp->h_addr);
+#ifdef DEBUG
+    Debug ((DEBUG_DEBUG, "rem_cache: h_addr %s hashv %d next %#x first %#x",
+           inetntoa (hp->h_addr), hashv, ocp->hnum_next,
+           hashtable[hashv].num_list));
+#endif
+    for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next))
+       if (*cp == ocp) {
+           *cp = ocp->hnum_next;
+           break;
+       }
+    /*
+     * free memory used to hold the various host names and the array
+     * of alias pointers.
+     */
+    if (hp->h_name)
+       MyFree (hp->h_name);
+    if (hp->h_aliases) {
+       for (hashv = 0; hp->h_aliases[hashv]; hashv++)
+           MyFree (hp->h_aliases[hashv]);
+       MyFree ((char *) hp->h_aliases);
+    }
+    /*
+     * free memory used to hold ip numbers and the array of them.
+     */
+    if (hp->h_addr_list) {
+       if (*hp->h_addr_list)
+           MyFree ((char *) *hp->h_addr_list);
+       MyFree ((char *) hp->h_addr_list);
+    }
+    MyFree ((char *) ocp);
+
+    incache--;
+    cainfo.ca_dels++;
+
+    return;
+}
+
+/*
+ * removes entries from the cache which are older than their expirey times.
+ * returns the time at which the server should next poll the cache.
+ */
+time_t expire_cache (now)
+     time_t now;
+{
+    aCache *cp, *cp2;
+    time_t next = 0;
+
+    for (cp = cachetop; cp; cp = cp2) {
+       cp2 = cp->list_next;
+
+       if (now >= cp->expireat) {
+           cainfo.ca_expires++;
+           rem_cache (cp);
+       }
+       else if (!next || next > cp->expireat)
+           next = cp->expireat;
+    }
+    return (next > now) ? next : (now + AR_TTL);
+}
+
+/*
+ * remove all dns cache entries.
+ */
+void flush_cache ()
+{
+    aCache *cp;
+
+    while ((cp = cachetop))
+       rem_cache (cp);
+}
+
+int m_dns (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aCache *cp;
+    int i;
+
+    if (MyOper (sptr) && (parv[1] && *parv[1] == 'l')) {
+       for (cp = cachetop; cp; cp = cp->list_next) {
+           sendto_one (sptr, "NOTICE %s :Ex %d ttl %d host %s(%s)",
+                       parv[0], cp->expireat - time (NULL), cp->ttl,
+                       cp->he.h_name, inetntoa (cp->he.h_addr));
+           for (i = 0; cp->he.h_aliases[i]; i++)
+               sendto_one (sptr, "NOTICE %s : %s = %s (CN)",
+                           parv[0], cp->he.h_name, cp->he.h_aliases[i]);
+           for (i = 1; cp->he.h_addr_list[i]; i++)
+               sendto_one (sptr, "NOTICE %s : %s = %s (IP)",
+                           parv[0], cp->he.h_name,
+                           inetntoa (cp->he.h_addr_list[i]));
+       }
+       return 0;
+    }
+    sendto_one (sptr, "NOTICE %s :Ca %d Cd %d Ce %d Cl %d Ch %d:%d Cu %d",
+               sptr->name,
+               cainfo.ca_adds, cainfo.ca_dels, cainfo.ca_expires,
+               cainfo.ca_lookups,
+               cainfo.ca_na_hits, cainfo.ca_nu_hits, cainfo.ca_updates);
+
+    sendto_one (sptr, "NOTICE %s :Re %d Rl %d/%d Rp %d Rq %d",
+               sptr->name, reinfo.re_errors, reinfo.re_nu_look,
+               reinfo.re_na_look, reinfo.re_replies, reinfo.re_requests);
+    sendto_one (sptr, "NOTICE %s :Ru %d Rsh %d Rs %d(%d) Rt %d", sptr->name,
+               reinfo.re_unkrep, reinfo.re_shortttl, reinfo.re_sent,
+               reinfo.re_resends, reinfo.re_timeouts);
+    return 0;
+}
+
+u_long cres_mem (sptr)
+     aClient *sptr;
+{
+    register aCache *c = cachetop;
+    register struct hostent *h;
+    register int i;
+    u_long nm = 0, im = 0, sm = 0, ts = 0;
+
+    for (; c; c = c->list_next) {
+       sm += sizeof (*c);
+       h = &c->he;
+       for (i = 0; h->h_addr_list[i]; i++) {
+           im += sizeof (char *);
+           im += sizeof (struct in_addr);
+       }
+       im += sizeof (char *);
+       for (i = 0; h->h_aliases[i]; i++) {
+           nm += sizeof (char *);
+           nm += strlen (h->h_aliases[i]);
+       }
+       nm += i - 1;
+       nm += sizeof (char *);
+       if (h->h_name)
+           nm += strlen (h->h_name);
+    }
+    ts = ARES_CACSIZE * sizeof (CacheTable);
+    sendto_one (sptr, ":%s %d %s :RES table %d",
+               me.name, RPL_STATSDEBUG, sptr->name, ts);
+    sendto_one (sptr, ":%s %d %s :Structs %d IP storage %d Name storage %d",
+               me.name, RPL_STATSDEBUG, sptr->name, sm, im, nm);
+    return ts + sm + im + nm;
+}
diff --git a/src/res_comp.c b/src/res_comp.c
new file mode 100644 (file)
index 0000000..a6266ec
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement:  ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "common.h"
+#include "sys.h"
+#include "nameser.h"
+
+static dn_find ();
+
+/*
+ * Expand compressed domain name 'comp_dn' to full domain name.
+ * 'msg' is a pointer to the begining of the message,
+ * 'eomorig' points to the first location after the message,
+ * 'exp_dn' is a pointer to a buffer of size 'length' for the result.
+ * Return size of compressed name or -1 if there was an error.
+ */
+dn_expand (msg, eomorig, comp_dn, exp_dn, length)
+     u_char *msg, *eomorig, *comp_dn, *exp_dn;
+     int length;
+{
+    register u_char *cp, *dn;
+    register int n, c;
+    u_char *eom;
+    int len = -1, checked = 0;
+
+    dn = exp_dn;
+    cp = comp_dn;
+    eom = exp_dn + length;
+    /*
+     * fetch next label in domain name
+     */
+    while (n = *cp++) {
+       /*
+        * Check for indirection
+        */
+       switch (n & INDIR_MASK) {
+       case 0:
+           if (dn != exp_dn) {
+               if (dn >= eom)
+                   return (-1);
+               *dn++ = '.';
+           }
+           if (dn + n >= eom)
+               return (-1);
+           checked += n + 1;
+           while (--n >= 0) {
+               if ((c = *cp++) == '.') {
+                   if (dn + n + 2 >= eom)
+                       return (-1);
+                   *dn++ = '\\';
+               }
+               *dn++ = c;
+               if (cp >= eomorig)      /* out of range */
+                   return (-1);
+           }
+           break;
+
+       case INDIR_MASK:
+           if (len < 0)
+               len = cp - comp_dn + 1;
+           cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff));
+           if (cp < msg || cp >= eomorig)      /* out of range */
+               return (-1);
+           checked += 2;
+           /*
+            * Check for loops in the compressed name;
+            * if we've looked at the whole message,
+            * there must be a loop.
+            */
+           if (checked >= eomorig - msg)
+               return (-1);
+           break;
+
+       default:
+           return (-1);          /* flag error */
+       }
+    }
+    *dn = '\0';
+    if (len < 0)
+       len = cp - comp_dn;
+    return (len);
+}
+
+/*
+ * Compress domain name 'exp_dn' into 'comp_dn'.
+ * Return the size of the compressed name or -1.
+ * 'length' is the size of the array pointed to by 'comp_dn'.
+ * 'dnptrs' is a list of pointers to previous compressed names. dnptrs[0]
+ * is a pointer to the beginning of the message. The list ends with NULL.
+ * 'lastdnptr' is a pointer to the end of the arrary pointed to
+ * by 'dnptrs'. Side effect is to update the list of pointers for
+ * labels inserted into the message as we compress the name.
+ * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
+ * is NULL, we don't update the list.
+ */
+dn_comp (exp_dn, comp_dn, length, dnptrs, lastdnptr)
+     u_char *exp_dn, *comp_dn;
+     int length;
+     u_char **dnptrs, **lastdnptr;
+{
+    register u_char *cp, *dn;
+    register int c, l;
+    u_char **cpp, **lpp, *sp, *eob;
+    u_char *msg;
+
+    dn = exp_dn;
+    cp = comp_dn;
+    eob = cp + length;
+    if (dnptrs != NULL) {
+       if ((msg = *dnptrs++) != NULL) {
+           for (cpp = dnptrs; *cpp != NULL; cpp++);
+           lpp = cpp;            /* end of list to search */
+       }
+    }
+    else
+       msg = NULL;
+    for (c = *dn++; c != '\0';) {
+       /* look to see if we can use pointers */
+       if (msg != NULL) {
+           if ((l = dn_find (dn - 1, msg, dnptrs, lpp)) >= 0) {
+               if (cp + 1 >= eob)
+                   return (-1);
+               *cp++ = (l >> 8) | INDIR_MASK;
+               *cp++ = l % 256;
+               return (cp - comp_dn);
+           }
+           /* not found, save it */
+           if (lastdnptr != NULL && cpp < lastdnptr - 1) {
+               *cpp++ = cp;
+               *cpp = NULL;
+           }
+       }
+       sp = cp++;                /* save ptr to length byte */
+       do {
+           if (c == '.') {
+               c = *dn++;
+               break;
+           }
+           if (c == '\\') {
+               if ((c = *dn++) == '\0')
+                   break;
+           }
+           if (cp >= eob) {
+               if (msg != NULL)
+                   *lpp = NULL;
+               return (-1);
+           }
+           *cp++ = c;
+       }
+       while ((c = *dn++) != '\0');
+       /* catch trailing '.'s but not '..' */
+       if ((l = cp - sp - 1) == 0 && c == '\0') {
+           cp--;
+           break;
+       }
+       if (l <= 0 || l > MAXLABEL) {
+           if (msg != NULL)
+               *lpp = NULL;
+           return (-1);
+       }
+       *sp = l;
+    }
+    if (cp >= eob) {
+       if (msg != NULL)
+           *lpp = NULL;
+       return (-1);
+    }
+    *cp++ = '\0';
+    return (cp - comp_dn);
+}
+
+/*
+ * Skip over a compressed domain name. Return the size or -1.
+ */
+dn_skipname (comp_dn, eom)
+     u_char *comp_dn, *eom;
+{
+    register u_char *cp;
+    register int n;
+
+    cp = comp_dn;
+    while (cp < eom && (n = *cp++)) {
+       /*
+        * check for indirection
+        */
+       switch (n & INDIR_MASK) {
+       case 0:           /* normal case, n == len */
+           cp += n;
+           continue;
+       default:                  /* illegal type */
+           return (-1);
+       case INDIR_MASK:          /* indirection */
+           cp++;
+       }
+       break;
+    }
+    return (cp - comp_dn);
+}
+
+/*
+ * Search for expanded name from a list of previously compressed names.
+ * Return the offset from msg if found or -1.
+ * dnptrs is the pointer to the first name on the list,
+ * not the pointer to the start of the message.
+ */
+static dn_find (exp_dn, msg, dnptrs, lastdnptr)
+     u_char *exp_dn, *msg;
+     u_char **dnptrs, **lastdnptr;
+{
+    register u_char *dn, *cp, **cpp;
+    register int n;
+    u_char *sp;
+
+    for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
+       dn = exp_dn;
+       sp = cp = *cpp;
+       while (n = *cp++) {
+           /*
+            * check for indirection
+            */
+           switch (n & INDIR_MASK) {
+           case 0:               /* normal case, n == len */
+               while (--n >= 0) {
+                   if (*dn == '.')
+                       goto next;
+                   if (*dn == '\\')
+                       dn++;
+                   if (*dn++ != *cp++)
+                       goto next;
+               }
+               if ((n = *dn++) == '\0' && *cp == '\0')
+                   return (sp - msg);
+               if (n == '.')
+                   continue;
+               goto next;
+
+           default:              /* illegal type */
+               return (-1);
+
+           case INDIR_MASK:      /* indirection */
+               cp = msg + (((n & 0x3f) << 8) | *cp);
+           }
+       }
+       if (*dn == '\0')
+           return (sp - msg);
+      next:;
+    }
+    return (-1);
+}
+
+/*
+ * Routines to insert/extract short/long's. Must account for byte
+ * order and non-alignment problems. This code at least has the
+ * advantage of being portable.
+ *
+ * used by sendmail.
+ */
+
+u_short _getshort (msgp)
+     u_char *msgp;
+{
+    register u_char *p = (u_char *) msgp;
+#ifdef vax
+    /*
+     * vax compiler doesn't put shorts in registers
+     */
+    register u_long u;
+#else
+    register u_short u;
+#endif
+
+    u = *p++ << 8;
+    return ((u_short) (u | *p));
+}
+
+u_long _getlong (msgp)
+     u_char *msgp;
+{
+    register u_char *p = (u_char *) msgp;
+    register u_long u;
+
+    u = *p++;
+    u <<= 8;
+    u |= *p++;
+    u <<= 8;
+    u |= *p++;
+    u <<= 8;
+    return (u | *p);
+}
+
+
+putshort (s, msgp)
+     register u_short s;
+     register u_char *msgp;
+{
+
+    msgp[1] = s;
+    msgp[0] = s >> 8;
+}
+
+putlong (l, msgp)
+     register u_long l;
+     register u_char *msgp;
+{
+
+    msgp[3] = l;
+    msgp[2] = (l >>= 8);
+    msgp[1] = (l >>= 8);
+    msgp[0] = l >> 8;
+}
diff --git a/src/res_init.c b/src/res_init.c
new file mode 100644 (file)
index 0000000..eb31335
--- /dev/null
@@ -0,0 +1,204 @@
+
+/*-
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement:  ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include "config.h"              /* To get #define SOL20       Vesa */
+#include "sys.h"
+#include "common.h"
+#include "nameser.h"
+#include "resolv.h"
+
+/*
+ * Resolver state default settings
+ */
+
+struct state _res = {
+    RES_TIMEOUT,   /* retransmition time interval */
+    4,            /* number of times to retransmit */
+    RES_DEFAULT,   /* options flags */
+    1,            /* number of name servers */
+};
+
+/*
+ * Set up default settings.  If the configuration file exist, the values
+ * there will have precedence.  Otherwise, the server address is set to
+ * INADDR_ANY and the default domain name comes from the gethostname().
+ *
+ * The configuration file should only be used if you want to redefine your
+ * domain or run without a server on your machine.
+ *
+ * Return 0 if completes successfully, -1 on error
+ */
+int res_init ()
+{
+    register FILE *fp;
+    register char *cp, *dp, **pp;
+    extern u_long inet_addr ();
+    register int n;
+    char buf[BUFSIZ];
+    extern char *getenv ();
+    int nserv = 0; /* number of nameserver records read from file */
+    int norder = 0;
+    int haveenv = 0;
+    int havesearch = 0;
+
+    _res.nsaddr.sin_addr.s_addr = INADDR_ANY;
+    _res.nsaddr.sin_family = AF_INET;
+    _res.nsaddr.sin_port = htons (NAMESERVER_PORT);
+    _res.nscount = 1;
+
+    /* Allow user to override the local domain definition */
+    if ((cp = getenv ("LOCALDOMAIN")) != NULL) {
+       (void) strncpy (_res.defdname, cp, sizeof (_res.defdname));
+       haveenv++;
+    }
+    if ((fp = fopen (_PATH_RESCONF, "r")) != NULL) {
+       /* read the config file */
+       while (fgets (buf, sizeof (buf), fp) != NULL) {
+           /* read default domain name */
+           if (!strncmp (buf, "domain", sizeof ("domain") - 1)) {
+               if (haveenv)      /* skip if have from environ */
+                   continue;
+               cp = buf + sizeof ("domain") - 1;
+               while (*cp == ' ' || *cp == '\t')
+                   cp++;
+               if ((*cp == '\0') || (*cp == '\n'))
+                   continue;
+               (void) strncpy (_res.defdname, cp,
+                               sizeof (_res.defdname) - 1);
+               if ((cp = index (_res.defdname, '\n')) != NULL)
+                   *cp = '\0';
+               havesearch = 0;
+               continue;
+           }
+           /* set search list */
+           if (!strncmp (buf, "search", sizeof ("search") - 1)) {
+               if (haveenv)      /* skip if have from environ */
+                   continue;
+               cp = buf + sizeof ("search") - 1;
+               while (*cp == ' ' || *cp == '\t')
+                   cp++;
+               if ((*cp == '\0') || (*cp == '\n'))
+                   continue;
+               (void) strncpy (_res.defdname, cp,
+                               sizeof (_res.defdname) - 1);
+               if ((cp = index (_res.defdname, '\n')) != NULL)
+                   *cp = '\0';
+               /*
+                * Set search list to be blank-separated strings
+                * on rest of line.
+                */
+               cp = _res.defdname;
+               pp = _res.dnsrch;
+               *pp++ = cp;
+               for (n = 0; *cp && pp < _res.dnsrch + MAXDNSRCH; cp++) {
+                   if (*cp == ' ' || *cp == '\t') {
+                       *cp = 0;
+                       n = 1;
+                   }
+                   else if (n) {
+                       *pp++ = cp;
+                       n = 0;
+                   }
+               }
+               /* null terminate last domain if there are excess */
+               while (*cp != '\0' && *cp != ' ' && *cp != '\t')
+                   cp++;
+               *cp = '\0';
+               *pp++ = 0;
+               havesearch = 1;
+               continue;
+           }
+           /* read nameservers to query */
+           if (!strncmp (buf, "nameserver", sizeof ("nameserver") - 1) &&
+               nserv < MAXNS) {
+               cp = buf + sizeof ("nameserver") - 1;
+               while (*cp == ' ' || *cp == '\t')
+                   cp++;
+               if ((*cp == '\0') || (*cp == '\n'))
+                   continue;
+               if ((_res.nsaddr_list[nserv].sin_addr.s_addr =
+                    inet_addr (cp)) == (unsigned) -1) {
+                   _res.nsaddr_list[nserv].sin_addr.s_addr = INADDR_ANY;
+                   continue;
+               }
+               _res.nsaddr_list[nserv].sin_family = AF_INET;
+               _res.nsaddr_list[nserv].sin_port = htons (NAMESERVER_PORT);
+               nserv++;
+               continue;
+           }
+           /* read service order */
+           if (!strncmp (buf, "order", sizeof ("order") - 1)) {
+               cp = buf + sizeof ("order") - 1;
+               while (*cp == ' ' || *cp == '\t')
+                   cp++;
+               if ((*cp == '\0') || (*cp == '\n'))
+                   continue;
+               norder = 0;
+               do {
+                   if ((dp = index (cp, ',')) != NULL)
+                       *dp = '\0';
+                   if (norder >= MAXSERVICES)
+                       continue;
+                   if (!strncmp (cp, "bind", sizeof ("bind") - 1))
+                       _res.order[norder++] = RES_SERVICE_BIND;
+                   else if (!strncmp (cp, "local", sizeof ("local") - 1))
+                       _res.order[norder++] = RES_SERVICE_LOCAL;
+                   cp = dp + 1;
+               }
+               while (dp != NULL);
+               _res.order[norder] = RES_SERVICE_NONE;
+               continue;
+           }
+       }
+       if (nserv > 1)
+           _res.nscount = nserv;
+       (void) fclose (fp);
+    }
+    if (_res.defdname[0] == 0) {
+       if (gethostname (buf, sizeof (_res.defdname)) == 0 &&
+           (cp = index (buf, '.')))
+           (void) strcpy (_res.defdname, cp + 1);
+    }
+    /* find components of local domain that might be searched */
+    if (havesearch == 0) {
+       pp = _res.dnsrch;
+       *pp++ = _res.defdname;
+       for (cp = _res.defdname, n = 0; *cp; cp++)
+           if (*cp == '.')
+               n++;
+       cp = _res.defdname;
+       for (; n >= LOCALDOMAINPARTS && pp < _res.dnsrch + MAXDFLSRCH; n--) {
+           cp = index (cp, '.');
+           *pp++ = ++cp;
+       }
+       *pp++ = 0;
+    }
+    /* default search order to bind only */
+    if (norder == 0) {
+       _res.order[0] = RES_SERVICE_BIND;
+       _res.order[1] = RES_SERVICE_NONE;
+    }
+    _res.options |= RES_INIT;
+    return (0);
+}
diff --git a/src/res_mkquery.c b/src/res_mkquery.c
new file mode 100644 (file)
index 0000000..c342fdc
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include "config.h"
+#include "sys.h"
+#include "nameser.h"
+#include "resolv.h"
+
+/*
+ * Form all types of queries.
+ * Returns the size of the result or -1.
+ */
+res_mkquery (op, dname, class, type, data, datalen, newrr, buf, buflen)
+     int op;      /* opcode of query */
+     char *dname;  /* domain name */
+     int class, type;  /* class and type of query */
+     char *data;   /* resource record data */
+     int datalen;  /* length of data */
+     struct rrec *newrr;       /* new rr for modify or append */
+     char *buf;           /* buffer to put query */
+     int buflen;   /* size of buffer */
+{
+    register HEADER *hp;
+    register char *cp;
+    register int n;
+    char *dnptrs[10], **dpp, **lastdnptr;
+
+#ifdef DEBUG
+    if (_res.options & RES_DEBUG)
+       printf ("res_mkquery(%d, %s, %d, %d)\n", op, dname, class, type);
+#endif /*DEBUG */
+    /*
+     * Initialize header fields.
+     */
+    if ((buf == NULL) || (buflen < sizeof (HEADER)))
+       return (-1);
+    bzero (buf, sizeof (HEADER));
+    hp = (HEADER *) buf;
+    hp->id = htons (++_res.id);
+    hp->opcode = op;
+    hp->pr = (_res.options & RES_PRIMARY) != 0;
+    hp->rd = (_res.options & RES_RECURSE) != 0;
+    hp->rcode = NOERROR;
+    cp = buf + sizeof (HEADER);
+    buflen -= sizeof (HEADER);
+    dpp = dnptrs;
+    *dpp++ = buf;
+    *dpp++ = NULL;
+    lastdnptr = dnptrs + sizeof (dnptrs) / sizeof (dnptrs[0]);
+    /*
+     * perform opcode specific processing
+     */
+    switch (op) {
+    case QUERY:
+       if ((buflen -= QFIXEDSZ) < 0)
+           return (-1);
+       if ((n = dn_comp (dname, cp, buflen, dnptrs, lastdnptr)) < 0)
+           return (-1);
+       cp += n;
+       buflen -= n;
+       putshort (type, cp);
+       cp += sizeof (u_short);
+       putshort (class, cp);
+       cp += sizeof (u_short);
+       hp->qdcount = htons (1);
+       if (op == QUERY || data == NULL)
+           break;
+       /*
+        * Make an additional record for completion domain.
+        */
+       buflen -= RRFIXEDSZ;
+       if ((n = dn_comp (data, cp, buflen, dnptrs, lastdnptr)) < 0)
+           return (-1);
+       cp += n;
+       buflen -= n;
+       putshort (T_NULL, cp);
+       cp += sizeof (u_short);
+       putshort (class, cp);
+       cp += sizeof (u_short);
+       putlong (0, cp);
+       cp += sizeof (u_long);
+       putshort (0, cp);
+       cp += sizeof (u_short);
+       hp->arcount = htons (1);
+       break;
+
+    case IQUERY:
+       /*
+        * Initialize answer section
+        */
+       if (buflen < 1 + RRFIXEDSZ + datalen)
+           return (-1);
+       *cp++ = '\0';             /* no domain name */
+       putshort (type, cp);
+       cp += sizeof (u_short);
+       putshort (class, cp);
+       cp += sizeof (u_short);
+       putlong (0, cp);
+       cp += sizeof (u_long);
+       putshort (datalen, cp);
+       cp += sizeof (u_short);
+       if (datalen) {
+           bcopy (data, cp, datalen);
+           cp += datalen;
+       }
+       hp->ancount = htons (1);
+       break;
+
+#ifdef ALLOW_UPDATES
+       /*
+        * For UPDATEM/UPDATEMA, do UPDATED/UPDATEDA followed by UPDATEA
+        * (Record to be modified is followed by its replacement in msg.)
+        */
+    case UPDATEM:
+    case UPDATEMA:
+
+    case UPDATED:
+       /*
+        * The res code for UPDATED and UPDATEDA is the same; user
+        * calls them differently: specifies data for UPDATED; server
+        * ignores data if specified for UPDATEDA.
+        */
+    case UPDATEDA:
+       buflen -= RRFIXEDSZ + datalen;
+       if ((n = dn_comp (dname, cp, buflen, dnptrs, lastdnptr)) < 0)
+           return (-1);
+       cp += n;
+       putshort (type, cp);
+       cp += sizeof (u_short);
+       putshort (class, cp);
+       cp += sizeof (u_short);
+       putlong (0, cp);
+       cp += sizeof (u_long);
+       putshort (datalen, cp);
+       cp += sizeof (u_short);
+       if (datalen) {
+           bcopy (data, cp, datalen);
+           cp += datalen;
+       }
+       if ((op == UPDATED) || (op == UPDATEDA)) {
+           hp->ancount = htons (0);
+           break;
+       }
+       /* Else UPDATEM/UPDATEMA, so drop into code for UPDATEA */
+
+    case UPDATEA:                /* Add new resource record */
+       buflen -= RRFIXEDSZ + datalen;
+       if ((n = dn_comp (dname, cp, buflen, dnptrs, lastdnptr)) < 0)
+           return (-1);
+       cp += n;
+       putshort (newrr->r_type, cp);
+       cp += sizeof (u_short);
+       putshort (newrr->r_class, cp);
+       cp += sizeof (u_short);
+       putlong (0, cp);
+       cp += sizeof (u_long);
+       putshort (newrr->r_size, cp);
+       cp += sizeof (u_short);
+       if (newrr->r_size) {
+           bcopy (newrr->r_data, cp, newrr->r_size);
+           cp += newrr->r_size;
+       }
+       hp->ancount = htons (0);
+       break;
+
+#endif /* ALLOW_UPDATES */
+    }
+    return (cp - buf);
+}
diff --git a/src/res_skipname.c b/src/res_skipname.c
new file mode 100644 (file)
index 0000000..dc63c3a
--- /dev/null
@@ -0,0 +1,32 @@
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "nameser.h"
+
+/*
+ * Skip over a compressed domain name. Return the size or -1.
+ */
+dn_skipname (comp_dn, eom)
+     u_char *comp_dn, *eom;
+{
+    register u_char *cp;
+    register int n;
+
+    cp = comp_dn;
+    while (cp < eom && (n = *cp++)) {
+       /*
+        * check for indirection
+        */
+       switch (n & INDIR_MASK) {
+       case 0:           /* normal case, n == len */
+           cp += n;
+           continue;
+       default:                  /* illegal type */
+           return (-1);
+       case INDIR_MASK:          /* indirection */
+           cp++;
+       }
+       break;
+    }
+    return (cp - comp_dn);
+}
diff --git a/src/s_bsd.c b/src/s_bsd.c
new file mode 100644 (file)
index 0000000..7079253
--- /dev/null
@@ -0,0 +1,1950 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_bsd.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "res.h"
+#include "numeric.h"
+#include "patchlevel.h"
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <utmp.h>
+#include <sys/resource.h>
+#if defined(SOL20)
+#include <sys/filio.h>
+#endif
+#if (!defined(SVR3) || defined(sgi) || defined(_SEQUENT_))
+#include <sys/un.h>
+#endif
+#include "inet.h"
+#include <stdio.h>
+#include <signal.h>
+#include <fcntl.h>
+#ifdef AIX
+#include <time.h>
+#include <arpa/nameser.h>
+#else
+#include "nameser.h"
+#endif
+#include "resolv.h"
+#include "sock.h"                /* If FD_ZERO isn't define up to this point,  */
+                       /* define it (BSD4.2 needs this) */
+#include "h.h"
+
+#ifndef IN_LOOPBACKNET
+#define IN_LOOPBACKNET 0x7f
+#endif
+
+aClient *local[MAXCONNECTIONS];
+int highest_fd = 0, readcalls = 0, udpfd = -1, resfd = -1;
+static struct sockaddr_in mysk;
+static void polludp ();
+
+static struct sockaddr *connect_inet PROTO ((aConfItem *, aClient *, int *));
+static int completed_connection PROTO ((aClient *));
+static int check_init PROTO ((aClient *, char *));
+static void do_dns_async PROTO (()), set_sock_opts PROTO ((int, aClient *));
+static char readbuf[READBUF_SIZE];
+char zlinebuf[BUFSIZE];
+extern char *version;
+
+/*
+ * Try and find the correct name to use with getrlimit() for setting the max.
+ * number of files allowed to be open by this process.
+ */
+#ifdef RLIMIT_FDMAX
+#define RLIMIT_FD_MAX   RLIMIT_FDMAX
+#else
+#ifdef RLIMIT_NOFILE
+#define RLIMIT_FD_MAX RLIMIT_NOFILE
+#else
+#ifdef RLIMIT_OPEN_MAX
+#define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
+#else
+#undef RLIMIT_FD_MAX
+#endif
+#endif
+#endif
+
+/*
+   ** add_local_domain()
+   ** Add the domain to hostname, if it is missing
+   ** (as suggested by eps@TOASTER.SFSU.EDU)
+ */
+
+void add_local_domain (hname, size)
+     char *hname;
+     int size;
+{
+#ifdef RES_INIT
+    /* try to fix up unqualified names */
+    if (!index (hname, '.')) {
+       if (!(_res.options & RES_INIT)) {
+           Debug ((DEBUG_DNS, "res_init()"));
+           res_init ();
+       }
+       if (_res.defdname[0]) {
+           (void) strncat (hname, ".", size - 1);
+           (void) strncat (hname, _res.defdname, size - 2);
+       }
+    }
+#endif
+    return;
+}
+
+/*
+   ** Cannot use perror() within daemon. stderr is closed in
+   ** ircd and cannot be used. And, worse yet, it might have
+   ** been reassigned to a normal connection...
+ */
+
+/*
+   ** report_error
+   **   This a replacement for perror(). Record error to log and
+   **   also send a copy to all *LOCAL* opers online.
+   **
+   **   text    is a *format* string for outputting error. It must
+   **       contain only two '%s', the first will be replaced
+   **       by the sockhost from the cptr, and the latter will
+   **       be taken from sys_errlist[errno].
+   **
+   **   cptr    if not NULL, is the *LOCAL* client associated with
+   **       the error.
+ */
+void report_error (text, cptr)
+     char *text;
+     aClient *cptr;
+{
+    int errtmp = errno;        /* debug may change 'errno' */
+    char *host;
+    int err, len = sizeof (err);
+
+    host = (cptr) ? get_client_name (cptr, FALSE) : "";
+
+    Debug ((DEBUG_ERROR, text, host, strerror (errtmp)));
+
+    /*
+     * Get the *real* error from the socket (well try to anyway..).
+     * This may only work when SO_DEBUG is enabled but its worth the
+     * gamble anyway.
+     */
+#ifdef SO_ERROR
+    if (cptr && !IsMe (cptr) && cptr->fd >= 0)
+       if (!getsockopt
+           (cptr->fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *) & err, &len))
+           if (err)
+               errtmp = err;
+#endif
+    sendto_ops (text, host, strerror (errtmp));
+#ifdef USE_SYSLOG
+    syslog (LOG_WARNING, text, host, strerror (errtmp));
+#endif
+    return;
+}
+
+/*
+ * inetport
+ *
+ * Create a socket in the AF_INET domain, bind it to the port given in
+ * 'port' and listen to it.  Connections are accepted to this socket
+ * depending on the IP# mask given by 'name'.  Returns the fd of the
+ * socket created or -1 on error.
+ */
+int inetport (cptr, name, port)
+     aClient *cptr;
+     char *name;
+     int port;
+{
+    static struct sockaddr_in server;
+    int ad[4], len = sizeof (server);
+    char ipname[20];
+
+    if (BadPtr (name))
+       name = "*";
+    ad[0] = ad[1] = ad[2] = ad[3] = 0;
+
+    /*
+     * do it this way because building ip# from separate values for each
+     * byte requires endian knowledge or some nasty messing. Also means
+     * easy conversion of "*" 0.0.0.0 or 134.* to 134.0.0.0 :-)
+     */
+    (void) sscanf (name, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
+    (void) sprintf (ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
+
+    if (cptr != &me) {
+       (void) sprintf (cptr->sockhost, "%-.42s.%u", name,
+                       (unsigned int) port);
+       (void) strcpy (cptr->name, me.name);
+    }
+    /*
+     * At first, open a new socket
+     */
+    if (cptr->fd == -1)
+       cptr->fd = socket (AF_INET, SOCK_STREAM, 0);
+
+    if (cptr->fd < 0) {
+       report_error ("opening stream socket %s:%s", cptr);
+       return -1;
+    }
+    else if (cptr->fd >= MAXCLIENTS) {
+       sendto_ops ("No more connections allowed (%s)", cptr->name);
+       (void) close (cptr->fd);
+       return -1;
+    }
+    set_sock_opts (cptr->fd, cptr);
+    /*
+     * Bind a port to listen for new connections if port is non-null,
+     * else assume it is already open and try get something from it.
+     */
+    if (port) {
+       server.sin_family = AF_INET;
+       /* per-port bindings, fixes /stats l */
+       server.sin_addr.s_addr = inet_addr (ipname);
+       server.sin_port = htons (port);
+       /*
+        * Try 10 times to bind the socket with an interval of 20
+        * seconds. Do this so we dont have to keepp trying manually
+        * to bind. Why ? Because a port that has closed often lingers
+        * around for a short time.
+        * This used to be the case.  Now it no longer is.
+        * Could cause the server to hang for too long - avalon
+        */
+       if (bind (cptr->fd, (struct sockaddr *) &server, sizeof (server)) ==
+           -1) {
+           report_error ("binding stream socket %s:%s", cptr);
+           (void) close (cptr->fd);
+           return -1;
+       }
+    }
+    if (getsockname (cptr->fd, (struct sockaddr *) &server, &len)) {
+       report_error ("getsockname failed for %s:%s", cptr);
+       (void) close (cptr->fd);
+       return -1;
+    }
+    if (cptr == &me) {           /* KLUDGE to get it work... */
+       char buf[1024];
+
+       (void) sprintf (buf, rpl_str (RPL_MYPORTIS), me.name, "*",
+                       ntohs (server.sin_port));
+       (void) write (0, buf, strlen (buf));
+    }
+
+    if (cptr->fd > highest_fd)
+       highest_fd = cptr->fd;
+    cptr->ip.s_addr = name ? inet_addr (ipname) : me.ip.s_addr;
+    cptr->port = (int) ntohs (server.sin_port);
+    (void) listen (cptr->fd, LISTEN_SIZE);
+    local[cptr->fd] = cptr;
+
+    return 0;
+}
+
+/*
+ * add_listener
+ *
+ * Create a new client which is essentially the stub like 'me' to be used
+ * for a socket that is passive (listen'ing for connections to be accepted).
+ */
+int add_listener (aconf)
+     aConfItem *aconf;
+{
+    aClient *cptr;
+
+    cptr = make_client (NULL, NULL);
+    cptr->flags = FLAGS_LISTEN;
+    cptr->acpt = cptr;
+    cptr->from = cptr;
+    SetMe (cptr);
+    strncpyzt (cptr->name, aconf->host, sizeof (cptr->name));
+    if (inetport (cptr, aconf->host, aconf->port))
+       cptr->fd = -2;
+
+    if (cptr->fd >= 0) {
+       cptr->confs = make_link ();
+       cptr->confs->next = NULL;
+       cptr->confs->value.aconf = aconf;
+       set_non_blocking (cptr->fd, cptr);
+    }
+    else
+       free_client (cptr);
+    return 0;
+}
+
+
+/*
+ * close_listeners
+ *
+ * Close and free all clients which are marked as having their socket open
+ * and in a state where they can accept connections.  Unix sockets have
+ * the path to the socket unlinked for cleanliness.
+ */
+void close_listeners ()
+{
+    aClient *cptr;
+    int i;
+    aConfItem *aconf;
+
+    /*
+     * close all 'extra' listening ports we have and unlink the file
+     * name if it was a unix socket.
+     */
+    for (i = highest_fd; i >= 0; i--) {
+       if (!(cptr = local[i]))
+           continue;
+       if (!IsMe (cptr) || cptr == &me || !IsListening (cptr))
+           continue;
+       aconf = cptr->confs->value.aconf;
+
+       if (IsIllegal (aconf) && aconf->clients == 0) {
+           close_connection (cptr);
+       }
+    }
+}
+
+/*
+ * init_sys
+ */
+void init_sys ()
+{
+    int fd;
+#ifdef RLIMIT_FD_MAX
+    struct rlimit limit;
+
+    if (!getrlimit (RLIMIT_FD_MAX, &limit)) {
+#ifdef pyr
+       if (limit.rlim_cur < MAXCONNECTIONS)
+#else
+       if (limit.rlim_max < MAXCONNECTIONS)
+#endif
+       {
+           (void) fprintf (stderr, "ircd fd table too big\n");
+           (void) fprintf (stderr, "Hard Limit: %d IRC max: %d\n",
+                           (int) limit.rlim_max, MAXCONNECTIONS);
+           (void) fprintf (stderr, "Fix MAXCONNECTIONS\n");
+           exit (-1);
+       }
+#ifndef        pyr
+       limit.rlim_cur = limit.rlim_max;        /* make soft limit the max */
+       if (setrlimit (RLIMIT_FD_MAX, &limit) == -1) {
+           (void) fprintf (stderr, "error setting max fd's to %d\n",
+                           (int) limit.rlim_cur);
+           exit (-1);
+       }
+#endif
+    }
+#endif
+#ifdef sequent
+#ifndef        DYNIXPTX
+    int fd_limit;
+
+    fd_limit = setdtablesize (MAXCONNECTIONS + 1);
+    if (fd_limit < MAXCONNECTIONS) {
+       (void) fprintf (stderr, "ircd fd table too big\n");
+       (void) fprintf (stderr, "Hard Limit: %d IRC max: %d\n",
+                       fd_limit, MAXCONNECTIONS);
+       (void) fprintf (stderr, "Fix MAXCONNECTIONS\n");
+       exit (-1);
+    }
+#endif
+#endif
+#if defined(PCS) || defined(DYNIXPTX) || defined(SVR3)
+    char logbuf[BUFSIZ];
+
+    (void) setvbuf (stderr, logbuf, _IOLBF, sizeof (logbuf));
+#else
+#if defined(HPUX)
+    (void) setvbuf (stderr, NULL, _IOLBF, 0);
+#else
+#if !defined(SOL20) && !defined(SCOUNIX)
+    (void) setlinebuf (stderr);
+#endif
+#endif
+#endif
+    for (fd = 3; fd < MAXCONNECTIONS; fd++) {
+       (void) close (fd);
+       local[fd] = NULL;
+    }
+    local[1] = NULL;
+    (void) close (1);
+
+    if (bootopt & BOOT_TTY)      /* debugging is going to a tty */
+       goto init_dgram;
+    if (!(bootopt & BOOT_DEBUG))
+       (void) close (2);
+
+    if (((bootopt & BOOT_CONSOLE) || isatty (0)) &&
+       !(bootopt & (BOOT_INETD | BOOT_OPER))) {
+       if (fork ())
+           exit (0);
+#ifdef TIOCNOTTY
+       if ((fd = open ("/dev/tty", O_RDWR)) >= 0) {
+           (void) ioctl (fd, TIOCNOTTY, (char *) NULL);
+           (void) close (fd);
+       }
+#endif
+#if defined(HPUX) || defined(SOL20) || defined(DYNIXPTX) || \
+    defined(_POSIX_SOURCE) || defined(SVR4) || defined(SGI) \
+       || defined(SCOUNIX)
+       (void) setsid ();
+#else
+       (void) setpgrp (0, (int) getpid ());
+#endif
+       (void) close (0);         /* fd 0 opened by inetd */
+       local[0] = NULL;
+    }
+  init_dgram:
+    resfd = init_resolver (0x1f);
+
+    return;
+}
+
+void write_pidfile ()
+{
+#ifdef IRCD_PIDFILE
+    int fd;
+    char buff[20];
+    if ((fd = open (IRCD_PIDFILE, O_CREAT | O_WRONLY, 0600)) >= 0) {
+       bzero (buff, sizeof (buff));
+       (void) sprintf (buff, "%5d\n", (int) getpid ());
+       if (write (fd, buff, strlen (buff)) == -1)
+           Debug ((DEBUG_NOTICE, "Error writing to pid file %s",
+                   IRCD_PIDFILE));
+       (void) close (fd);
+       return;
+    }
+#ifdef DEBUGMODE
+    else
+       Debug ((DEBUG_NOTICE, "Error opening pid file %s", IRCD_PIDFILE));
+#endif
+#endif
+}
+
+/*
+ * Initialize the various name strings used to store hostnames. This is set
+ * from either the server's sockhost (if client fd is a tty or localhost)
+ * or from the ip# converted into a string. 0 = success, -1 = fail.
+ */
+static int check_init (cptr, sockn)
+     aClient *cptr;
+     char *sockn;
+{
+    struct sockaddr_in sk;
+    int len = sizeof (struct sockaddr_in);
+
+
+    /* If descriptor is a tty, special checking... */
+    if (isatty (cptr->fd)) {
+       strncpyzt (sockn, me.sockhost, HOSTLEN);
+       bzero ((char *) &sk, sizeof (struct sockaddr_in));
+    }
+    else if (getpeername (cptr->fd, (struct sockaddr *) &sk, &len) == -1) {
+       report_error ("connect failure: %s %s", cptr);
+       return -1;
+    }
+    (void) strcpy (sockn, (char *) inetntoa ((char *) &sk.sin_addr));
+    if (inet_netof (sk.sin_addr) == IN_LOOPBACKNET) {
+       cptr->hostp = NULL;
+       strncpyzt (sockn, me.sockhost, HOSTLEN);
+    }
+    bcopy ((char *) &sk.sin_addr, (char *) &cptr->ip,
+          sizeof (struct in_addr));
+    cptr->port = (int) ntohs (sk.sin_port);
+
+    return 0;
+}
+
+/*
+ * Ordinary client access check. Look for conf lines which have the same
+ * status as the flags passed.
+ *  0 = Success
+ * -1 = Access denied
+ * -2 = Bad socket.
+ */
+int check_client (cptr)
+     aClient *cptr;
+{
+    static char sockname[HOSTLEN + 1];
+    struct hostent *hp = NULL;
+    int i;
+
+    ClearAccess (cptr);
+    Debug ((DEBUG_DNS, "ch_cl: check access for %s[%s]",
+           cptr->name, inetntoa ((char *) &cptr->ip)));
+
+    if (check_init (cptr, sockname))
+       return -2;
+
+    if (!IsUnixSocket (cptr))
+       hp = cptr->hostp;
+    /*
+     * Verify that the host to ip mapping is correct both ways and that
+     * the ip#(s) for the socket is listed for the host.
+     */
+    if (hp) {
+       for (i = 0; hp->h_addr_list[i]; i++)
+           if (!bcmp (hp->h_addr_list[i], (char *) &cptr->ip,
+                      sizeof (struct in_addr)))
+               break;
+       if (!hp->h_addr_list[i]) {
+           sendto_ops ("IP# Mismatch: %s != %s[%08x]",
+                       inetntoa ((char *) &cptr->ip), hp->h_name,
+                       *((unsigned long *) hp->h_addr));
+           hp = NULL;
+       }
+    }
+    if ((i = attach_Iline (cptr, hp, sockname))) {
+       Debug ((DEBUG_DNS, "ch_cl: access denied: %s[%s]", cptr->name,
+               sockname));
+       return i;
+    }
+    Debug ((DEBUG_DNS, "ch_cl: access ok: %s[%s]", cptr->name, sockname));
+
+    if (inet_netof (cptr->ip) == IN_LOOPBACKNET || IsUnixSocket (cptr) ||
+       inet_netof (cptr->ip) == inet_netof (mysk.sin_addr)) {
+       ircstp->is_loc++;
+       cptr->flags |= FLAGS_LOCAL;
+    }
+    return 0;
+}
+
+#define        CFLAG   (CONF_CONNECT_SERVER | CONF_NZCONNECT_SERVER)
+#define        NFLAG   CONF_NOCONNECT_SERVER
+/*
+ * check_server_init(), check_server()
+ *  check access for a server given its name (passed in cptr struct).
+ *  Must check for all C/N lines which have a name which matches the
+ *  name given and a host which matches. A host alias which is the
+ *  same as the server name is also acceptable in the host field of a
+ *  C/N line.
+ *  0 = Success
+ * -1 = Access denied
+ * -2 = Bad socket.
+ */
+int check_server_init (cptr)
+     aClient *cptr;
+{
+    char *name;
+    aConfItem *c_conf = NULL, *n_conf = NULL;
+    struct hostent *hp = NULL;
+    Link *lp;
+
+    name = cptr->name;
+    Debug ((DEBUG_DNS, "sv_cl: check access for %s[%s]", name,
+           cptr->sockhost));
+
+    if (IsUnknown (cptr) && !attach_confs (cptr, name, CFLAG | NFLAG)) {
+       Debug ((DEBUG_DNS, "No C/N lines for %s", name));
+       return -1;
+    }
+    lp = cptr->confs;
+    /*
+     * We initiated this connection so the client should have a C and N
+     * line already attached after passing through the connec_server()
+     * function earlier.
+     */
+    if (IsConnecting (cptr) || IsHandshake (cptr)) {
+       c_conf = find_conf (lp, name, CFLAG);
+       n_conf = find_conf (lp, name, NFLAG);
+       if (!c_conf || !n_conf) {
+           sendto_ops ("Connecting Error: %s[%s]", name, cptr->sockhost);
+           det_confs_butmask (cptr, 0);
+           return -1;
+       }
+    }
+    /*
+       ** If the servername is a hostname, either an alias (CNAME) or
+       ** real name, then check with it as the host. Use gethostbyname()
+       ** to check for servername as hostname.
+     */
+    if (!IsUnixSocket (cptr) && !cptr->hostp) {
+       aConfItem *aconf;
+
+       aconf = count_cnlines (lp);
+       if (aconf) {
+           char *s;
+           Link lin;
+
+           /*
+              ** Do a lookup for the CONF line *only* and not
+              ** the server connection else we get stuck in a
+              ** nasty state since it takes a SERVER message to
+              ** get us here and we cant interrupt that very
+              ** well.
+            */
+           ClearAccess (cptr);
+           lin.value.aconf = aconf;
+           lin.flags = ASYNC_CONF;
+           nextdnscheck = 1;
+           if ((s = index (aconf->host, '@')))
+               s++;
+           else
+               s = aconf->host;
+           Debug ((DEBUG_DNS, "sv_ci:cache lookup (%s)", s));
+           hp = gethost_byname (s, &lin);
+       }
+    }
+    return check_server (cptr, hp, c_conf, n_conf, 0);
+}
+
+int check_server (cptr, hp, c_conf, n_conf, estab)
+     aClient *cptr;
+     aConfItem *n_conf, *c_conf;
+     struct hostent *hp;
+     int estab;
+{
+    char *name;
+    char abuff[HOSTLEN + USERLEN + 2];
+    char sockname[HOSTLEN + 1], fullname[HOSTLEN + 1];
+    Link *lp = cptr->confs;
+    int i;
+
+    ClearAccess (cptr);
+    if (check_init (cptr, sockname))
+       return -2;
+
+  check_serverback:
+    if (hp) {
+       for (i = 0; hp->h_addr_list[i]; i++)
+           if (!bcmp (hp->h_addr_list[i], (char *) &cptr->ip,
+                      sizeof (struct in_addr)))
+               break;
+       if (!hp->h_addr_list[i]) {
+           sendto_ops ("IP# Mismatch: %s != %s[%08x]",
+                       inetntoa ((char *) &cptr->ip), hp->h_name,
+                       *((unsigned long *) hp->h_addr));
+           hp = NULL;
+       }
+    }
+    else if (cptr->hostp) {
+       hp = cptr->hostp;
+       goto check_serverback;
+    }
+    if (hp)
+       /*
+        * if we are missing a C or N line from above, search for
+        * it under all known hostnames we have for this ip#.
+        */
+       for (i = 0, name = hp->h_name; name; name = hp->h_aliases[i++]) {
+           strncpyzt (fullname, name, sizeof (fullname));
+           add_local_domain (fullname, HOSTLEN - strlen (fullname));
+           Debug ((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s", sockname,
+                   fullname));
+           (void) sprintf (abuff, "%s@%s", cptr->username, fullname);
+           if (!c_conf)
+               c_conf = find_conf_host (lp, abuff, CFLAG);
+           if (!n_conf)
+               n_conf = find_conf_host (lp, abuff, NFLAG);
+           if (c_conf && n_conf) {
+               get_sockhost (cptr, fullname);
+               break;
+           }
+       }
+    name = cptr->name;
+
+    /*
+     * Check for C and N lines with the hostname portion the ip number
+     * of the host the server runs on. This also checks the case where
+     * there is a server connecting from 'localhost'.
+     */
+    if (IsUnknown (cptr) && (!c_conf || !n_conf)) {
+       (void) sprintf (abuff, "%s@%s", cptr->username, sockname);
+       if (!c_conf)
+           c_conf = find_conf_host (lp, abuff, CFLAG);
+       if (!n_conf)
+           n_conf = find_conf_host (lp, abuff, NFLAG);
+    }
+    /*
+     * Attach by IP# only if all other checks have failed.
+     * It is quite possible to get here with the strange things that can
+     * happen when using DNS in the way the irc server does. -avalon
+     */
+    if (!hp) {
+       if (!c_conf)
+           c_conf =
+               find_conf_ip (lp, (char *) &cptr->ip, cptr->username, CFLAG);
+       if (!n_conf)
+           n_conf =
+               find_conf_ip (lp, (char *) &cptr->ip, cptr->username, NFLAG);
+    }
+    else
+       for (i = 0; hp->h_addr_list[i]; i++) {
+           if (!c_conf)
+               c_conf =
+                   find_conf_ip (lp, hp->h_addr_list[i], cptr->username,
+                                 CFLAG);
+           if (!n_conf)
+               n_conf =
+                   find_conf_ip (lp, hp->h_addr_list[i], cptr->username,
+                                 NFLAG);
+       }
+    /*
+     * detach all conf lines that got attached by attach_confs()
+     */
+    det_confs_butmask (cptr, 0);
+    /*
+     * if no C or no N lines, then deny access
+     */
+    if (!c_conf || !n_conf) {
+       get_sockhost (cptr, sockname);
+       Debug ((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s] c %x n %x",
+               name, cptr->username, cptr->sockhost, c_conf, n_conf));
+       return -1;
+    }
+    /*
+     * attach the C and N lines to the client structure for later use.
+     */
+    (void) attach_conf (cptr, n_conf);
+    (void) attach_conf (cptr, c_conf);
+    (void) attach_confs (cptr, name, CONF_HUB | CONF_UWORLD);
+
+    if ((c_conf->ipnum.s_addr == -1) && !IsUnixSocket (cptr))
+       bcopy ((char *) &cptr->ip, (char *) &c_conf->ipnum,
+              sizeof (struct in_addr));
+    if (!IsUnixSocket (cptr))
+       get_sockhost (cptr, c_conf->host);
+
+    Debug ((DEBUG_DNS, "sv_cl: access ok: %s[%s]", name, cptr->sockhost));
+    if (estab)
+       return m_server_estab (cptr);
+    return 0;
+}
+
+#undef CFLAG
+#undef NFLAG
+
+/*
+   ** completed_connection
+   **   Complete non-blocking connect()-sequence. Check access and
+   **   terminate connection, if trouble detected.
+   **
+   **   Return  TRUE, if successfully completed
+   **       FALSE, if failed and ClientExit
+ */
+static int completed_connection (cptr)
+     aClient *cptr;
+{
+    aConfItem *aconf;
+
+    SetHandshake (cptr);
+
+    aconf = find_conf (cptr->confs, cptr->name, CONF_CONNECT_SERVER);
+    if (!aconf) {
+       sendto_ops ("Lost C-Line for %s", get_client_name (cptr, FALSE));
+       return -1;
+    }
+
+    sendto_one (cptr, "PROTOCTL %s", PROTOCTL_SUPPORTED);
+
+    if (!BadPtr (aconf->passwd))
+       sendto_one (cptr, "PASS :%s", aconf->passwd);
+
+    aconf = find_conf (cptr->confs, cptr->name, CONF_NOCONNECT_SERVER);
+    if (!aconf) {
+       sendto_ops ("Lost N-Line for %s", get_client_name (cptr, FALSE));
+       return -1;
+    }
+
+    sendto_one (cptr, "SERVER %s 1 %s_cap%i :%s",
+               my_name_for_link (me.name, aconf), version, VERSION_SEND,
+               me.info);
+
+    return (IsDead (cptr)) ? -1 : 0;
+}
+
+/*
+   ** close_connection
+   **   Close the physical connection. This function must make
+   **   MyConnect(cptr) == FALSE, and set cptr->from == NULL.
+ */
+void close_connection (cptr)
+     aClient *cptr;
+{
+    aConfItem *aconf;
+    int i, j;
+    int empty = cptr->fd;
+
+    if (IsServer (cptr)) {
+       ircstp->is_sv++;
+       ircstp->is_sbs += cptr->sendB;
+       ircstp->is_sbr += cptr->receiveB;
+       ircstp->is_sks += cptr->sendK;
+       ircstp->is_skr += cptr->receiveK;
+       ircstp->is_sti += time (NULL) - cptr->firsttime;
+       if (ircstp->is_sbs > 1023) {
+           ircstp->is_sks += (ircstp->is_sbs >> 10);
+           ircstp->is_sbs &= 0x3ff;
+       }
+       if (ircstp->is_sbr > 1023) {
+           ircstp->is_skr += (ircstp->is_sbr >> 10);
+           ircstp->is_sbr &= 0x3ff;
+       }
+    }
+    else if (IsClient (cptr)) {
+       ircstp->is_cl++;
+       ircstp->is_cbs += cptr->sendB;
+       ircstp->is_cbr += cptr->receiveB;
+       ircstp->is_cks += cptr->sendK;
+       ircstp->is_ckr += cptr->receiveK;
+       ircstp->is_cti += time (NULL) - cptr->firsttime;
+       if (ircstp->is_cbs > 1023) {
+           ircstp->is_cks += (ircstp->is_cbs >> 10);
+           ircstp->is_cbs &= 0x3ff;
+       }
+       if (ircstp->is_cbr > 1023) {
+           ircstp->is_ckr += (ircstp->is_cbr >> 10);
+           ircstp->is_cbr &= 0x3ff;
+       }
+    }
+    else
+       ircstp->is_ni++;
+
+    /*
+     * remove outstanding DNS queries.
+     */
+    del_queries ((char *) cptr);
+    /*
+     * If the connection has been up for a long amount of time, schedule
+     * a 'quick' reconnect, else reset the next-connect cycle.
+     *
+     * Now just hold on a minute.  We're currently doing this when a
+     * CLIENT exits too?  I don't think so!  If its not a server, or
+     * the SQUIT flag has been set, then we don't schedule a fast
+     * reconnect.  Pisses off too many opers. :-)  -Cabal95
+     */
+    if (IsServer (cptr) && !(cptr->flags & FLAGS_SQUIT) &&
+       (aconf = find_conf_exact (cptr->name, cptr->username,
+                                 cptr->sockhost, CONF_CONNECT_SERVER))) {
+       /*
+        * Reschedule a faster reconnect, if this was a automaticly
+        * connected configuration entry. (Note that if we have had
+        * a rehash in between, the status has been changed to
+        * CONF_ILLEGAL). But only do this if it was a "good" link.
+        */
+       aconf->hold = time (NULL);
+       aconf->hold += (aconf->hold - cptr->since > HANGONGOODLINK) ?
+           HANGONRETRYDELAY : ConfConFreq (aconf);
+       if (nextconnect > aconf->hold)
+           nextconnect = aconf->hold;
+    }
+
+    if (cptr->fd >= 0) {
+       flush_connections (cptr->fd);
+       local[cptr->fd] = NULL;
+#ifdef ZIP_LINKS
+       if (IsServer (cptr) || IsListening (cptr))
+           zip_free (cptr);
+#endif
+       (void) close (cptr->fd);
+       cptr->fd = -2;
+       DBufClear (&cptr->sendQ);
+       DBufClear (&cptr->recvQ);
+       bzero (cptr->passwd, sizeof (cptr->passwd));
+       /*
+        * clean up extra sockets from P-lines which have been
+        * discarded.
+        */
+       if (cptr->acpt != &me && cptr->acpt != cptr) {
+           aconf = cptr->acpt->confs->value.aconf;
+           if (aconf->clients > 0)
+               aconf->clients--;
+           if (!aconf->clients && IsIllegal (aconf))
+               close_connection (cptr->acpt);
+       }
+    }
+    for (; highest_fd > 0; highest_fd--)
+       if (local[highest_fd])
+           break;
+
+    det_confs_butmask (cptr, 0);
+    cptr->from = NULL;           /* ...this should catch them! >:) --msa */
+
+    /*
+     * fd remap to keep local[i] filled at the bottom.
+     */
+    if (empty > 0)
+       if ((j = highest_fd) > (i = empty) && (local[j]->status != STAT_LOG)) {
+           if (dup2 (j, i) == -1)
+               return;
+           local[i] = local[j];
+           local[i]->fd = i;
+           local[j] = NULL;
+           (void) close (j);
+           while (!local[highest_fd])
+               highest_fd--;
+       }
+    return;
+}
+
+/*
+   ** set_sock_opts
+ */
+static void set_sock_opts (fd, cptr)
+     int fd;
+     aClient *cptr;
+{
+    int opt;
+#ifdef SO_REUSEADDR
+    opt = 1;
+    if (setsockopt
+       (fd, SOL_SOCKET, SO_REUSEADDR, (OPT_TYPE *) & opt, sizeof (opt)) < 0)
+       report_error ("setsockopt(SO_REUSEADDR) %s:%s", cptr);
+#endif
+#if  defined(SO_DEBUG) && defined(DEBUGMODE) && 0
+/* Solaris with SO_DEBUG writes to syslog by default */
+#if !defined(SOL20) || defined(USE_SYSLOG)
+    opt = 1;
+    if (setsockopt
+       (fd, SOL_SOCKET, SO_DEBUG, (OPT_TYPE *) & opt, sizeof (opt))
+       < 0)
+       report_error ("setsockopt(SO_DEBUG) %s:%s", cptr);
+#endif /* SOL20 */
+#endif
+#if defined(SO_USELOOPBACK)
+    opt = 1;
+    if (setsockopt
+       (fd, SOL_SOCKET, SO_USELOOPBACK, (OPT_TYPE *) & opt,
+        sizeof (opt)) < 0)
+       report_error ("setsockopt(SO_USELOOPBACK) %s:%s", cptr);
+#endif
+#ifdef SO_RCVBUF
+    if (IsServer (cptr))
+#ifdef ZIP_LINKS
+       opt = READBUF_SIZE;
+#else
+       opt = 8192;
+#endif
+    else
+       opt = 4096;
+    if (setsockopt
+       (fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *) & opt, sizeof (opt))
+       < 0)
+       report_error ("setsockopt(SO_RCVBUF) %s:%s", cptr);
+#endif
+#ifdef SO_SNDBUF
+#ifdef _SEQUENT_
+/* seems that Sequent freezes up if the receving buffer is a different size
+ * to the sending buffer (maybe a tcp window problem too).
+ */
+    opt = 8192;
+#else
+    opt = 8192;
+#endif
+    if (setsockopt
+       (fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *) & opt, sizeof (opt))
+       < 0)
+       report_error ("setsockopt(SO_SNDBUF) %s:%s", cptr);
+#endif
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP)
+    {
+       char *s = readbuf, *t = readbuf + sizeof (readbuf) / 2;
+
+       opt = sizeof (readbuf) / 8;
+       if (getsockopt (fd, IPPROTO_IP, IP_OPTIONS, (OPT_TYPE *) t, &opt) < 0)
+           report_error ("getsockopt(IP_OPTIONS) %s:%s", cptr);
+       else if (opt > 0 && opt != sizeof (readbuf) / 8) {
+           for (*readbuf = '\0'; opt > 0; opt--, s += 3)
+               (void) sprintf (s, "%2.2x:", *t++);
+           *s = '\0';
+           sendto_ops ("Connection %s using IP opts: (%s)",
+                       get_client_name (cptr, TRUE), readbuf);
+       }
+       if (setsockopt (fd, IPPROTO_IP, IP_OPTIONS, (OPT_TYPE *) NULL, 0) < 0)
+           report_error ("setsockopt(IP_OPTIONS) %s:%s", cptr);
+    }
+#endif
+}
+
+
+int get_sockerr (cptr)
+     aClient *cptr;
+{
+    int errtmp = errno, err = 0, len = sizeof (err);
+#ifdef SO_ERROR
+    if (cptr->fd >= 0)
+       if (!getsockopt
+           (cptr->fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *) & err, &len))
+           if (err)
+               errtmp = err;
+#endif
+    return errtmp;
+}
+
+/*
+   ** set_non_blocking
+   **   Set the client connection into non-blocking mode. If your
+   **   system doesn't support this, you can make this a dummy
+   **   function (and get all the old problems that plagued the
+   **   blocking version of IRC--not a problem if you are a
+   **   lightly loaded node...)
+ */
+void set_non_blocking (fd, cptr)
+     int fd;
+     aClient *cptr;
+{
+    int res, nonb = 0;
+
+    /*
+       ** NOTE: consult ALL your relevant manual pages *BEFORE* changing
+       **    these ioctl's.  There are quite a few variations on them,
+       **    as can be seen by the PCS one.  They are *NOT* all the same.
+       **    Heed this well. - Avalon.
+     */
+#ifdef NBLOCK_POSIX
+    nonb |= O_NONBLOCK;
+#endif
+#ifdef NBLOCK_BSD
+    nonb |= O_NDELAY;
+#endif
+#ifdef NBLOCK_SYSV
+    /* This portion of code might also apply to NeXT.  -LynX */
+    res = 1;
+
+    if (ioctl (fd, FIONBIO, &res) < 0)
+       report_error ("ioctl(fd,FIONBIO) failed for %s:%s", cptr);
+#else
+    if ((res = fcntl (fd, F_GETFL, 0)) == -1)
+       report_error ("fcntl(fd, F_GETFL) failed for %s:%s", cptr);
+    else if (fcntl (fd, F_SETFL, res | nonb) == -1)
+       report_error ("fcntl(fd, F_SETL, nonb) failed for %s:%s", cptr);
+#endif
+    return;
+}
+
+/*
+ * Creates a client which has just connected to us on the given fd.
+ * The sockhost field is initialized with the ip# of the host.
+ * The client is added to the linked list of clients but isnt added to any
+ * hash tables yuet since it doesnt have a name.
+ */
+aClient *add_connection (cptr, fd)
+     aClient *cptr;
+     int fd;
+{
+    Link lin;
+    aClient *acptr;
+    aConfItem *aconf = NULL;
+    aConfItem *aconf2 = NULL;
+    acptr = make_client (NULL, &me);
+
+    if (cptr != &me)
+       aconf = cptr->confs->value.aconf;
+    /* Removed preliminary access check. Full check is performed in
+     * m_server and m_user instead. Also connection time out help to
+     * get rid of unwanted connections.
+     */
+    if (isatty (fd))             /* If descriptor is a tty, special checking... */
+       get_sockhost (acptr, cptr->sockhost);
+    else {
+       struct sockaddr_in addr;
+       int len = sizeof (struct sockaddr_in);
+
+       if (getpeername (fd, (struct sockaddr *) &addr, &len) == -1) {
+           report_error ("Failed in connecting to %s :%s", cptr);
+         add_con_refuse:
+           ircstp->is_ref++;
+           acptr->fd = -2;
+           free_client (acptr);
+           (void) close (fd);
+           return NULL;
+       }
+       /* don't want to add "Failed in connecting to" here.. */
+       if (aconf && IsIllegal (aconf))
+           goto add_con_refuse;
+       /* Copy ascii address to 'sockhost' just in case. Then we
+        * have something valid to put into error messages...
+        */
+       get_sockhost (acptr, (char *) inetntoa ((char *) &addr.sin_addr));
+       bcopy ((char *) &addr.sin_addr, (char *) &acptr->ip,
+              sizeof (struct in_addr));
+       /* Check for zaps -- Barubary */
+       if (find_zap (acptr, 0)) {
+           set_non_blocking (fd, acptr);
+           set_sock_opts (fd, acptr);
+           send (fd, zlinebuf, strlen (zlinebuf), 0);
+           goto add_con_refuse;
+       }
+       acptr->port = ntohs (addr.sin_port);
+
+       /* Let's do a check here to see if somebody matches a CN pair.
+        * if they don't, we can send them the connection noticed, because
+        * they have to be a client and we can't send them anything compressed. -GZ 
+        */
+
+       for (aconf2 = conf; aconf2; aconf2 = aconf2->next)
+           if (aconf2->status == CONF_CONNECT_SERVER &&
+               (match (inetntoa ((char *) &addr.sin_addr), aconf2->host) == 0
+                || match (inetntoa ((char *) &addr.sin_addr),
+                          index (aconf2->host, '@') + 1) == 0))
+               break;
+
+       if (!aconf2) {
+           for (aconf2 = conf; aconf2; aconf2 = aconf2->next)
+               if (aconf2->status == CONF_NOCONNECT_SERVER &&
+                   (match (inetntoa ((char *) &addr.sin_addr), aconf2->host)
+                    == 0
+                    || match (inetntoa ((char *) &addr.sin_addr),
+                              index (aconf2->host, '@') + 1) == 0))
+                   break;
+       }
+
+       if (!aconf2)
+           acptr->cc = 1;
+
+       if (acptr->cc)
+           write (fd, REPORT_DO_DNS, R_do_dns);
+
+       lin.flags = ASYNC_CLIENT;
+       lin.value.cptr = acptr;
+       Debug ((DEBUG_DNS, "lookup %s", inetntoa ((char *) &addr.sin_addr)));
+       acptr->hostp = gethost_byaddr ((char *) &acptr->ip, &lin);
+       if (!acptr->hostp)
+           SetDNS (acptr);
+       else if (acptr->cc)
+           write (fd, REPORT_FIN_DNSC, R_fin_dnsc);
+       nextdnscheck = 1;
+    }
+
+    if (aconf)
+       aconf->clients++;
+    acptr->fd = fd;
+    if (fd > highest_fd)
+       highest_fd = fd;
+    local[fd] = acptr;
+    acptr->acpt = cptr;
+    add_client_to_list (acptr);
+    set_non_blocking (acptr->fd, acptr);
+    set_sock_opts (acptr->fd, acptr);
+
+    return acptr;
+}
+
+
+/*
+   ** read_packet
+   **
+   ** Read a 'packet' of data from a connection and process it.  Read in 8k
+   ** chunks to give a better performance rating (for server connections).
+   ** Do some tricky stuff for client connections to make sure they don't do
+   ** any flooding >:-) -avalon
+ */
+static int read_packet (cptr, rfd)
+     aClient *cptr;
+     fd_set *rfd;
+{
+    int dolen = 0, length = 0, done;
+    time_t now = time (NULL);
+
+    if (FD_ISSET (cptr->fd, rfd) &&
+       !(IsPerson (cptr) && DBufLength (&cptr->recvQ) > 6090)) {
+       errno = 0;
+       length = recv (cptr->fd, readbuf, sizeof (readbuf), 0);
+
+       cptr->lasttime = now;
+       if (cptr->lasttime > cptr->since)
+           cptr->since = cptr->lasttime;
+       cptr->flags &= ~(FLAGS_PINGSENT | FLAGS_NONL);
+       /*
+        * If not ready, fake it so it isnt closed
+        */
+       if (length == -1 && ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
+           return 1;
+       if (length <= 0)
+           return length;
+    }
+    /*
+       ** For server connections, we process as many as we can without
+       ** worrying about the time of day or anything :)
+     */
+    if (IsServer (cptr) || IsConnecting (cptr) || IsHandshake (cptr) ||
+       IsService (cptr)) {
+       if (length > 0)
+           if ((done = dopacket (cptr, readbuf, length)))
+               return done;
+    }
+    else {
+       /*
+          ** Before we even think of parsing what we just read, stick
+          ** it on the end of the receive queue and do it when its
+          ** turn comes around.
+        */
+       if (dbuf_put (&cptr->recvQ, readbuf, length) < 0)
+           return exit_client (cptr, cptr, cptr, "dbuf_put fail");
+
+       if (IsPerson (cptr) && DBufLength (&cptr->recvQ) > CLIENT_FLOOD) {
+           sendto_umode (UMODE_FLOOD | UMODE_OPER,
+                         "*** Flood -- %s!%s@%s (%d) exceeds %d recvQ",
+                         cptr->name[0] ? cptr->name : "*",
+                         cptr->user ? cptr->user->username : "*",
+                         cptr->user ? cptr->user->host : "*",
+                         DBufLength (&cptr->recvQ), CLIENT_FLOOD);
+           return exit_client (cptr, cptr, cptr, "Excess Flood");
+       }
+       while (DBufLength (&cptr->recvQ) && !NoNewLine (cptr) &&
+              ((cptr->status < STAT_UNKNOWN) || (cptr->since - now < 10))) {
+           /*
+              ** If it has become registered as a Service or Server
+              ** then skip the per-message parsing below.
+            */
+           if (IsService (cptr) || IsServer (cptr)) {
+               dolen = dbuf_get (&cptr->recvQ, readbuf, sizeof (readbuf));
+               if (dolen <= 0)
+                   break;
+               if ((done = dopacket (cptr, readbuf, dolen)))
+                   return done;
+               break;
+           }
+           dolen = dbuf_getmsg (&cptr->recvQ, readbuf, sizeof (readbuf));
+           /*
+              ** Devious looking...whats it do ? well..if a client
+              ** sends a *long* message without any CR or LF, then
+              ** dbuf_getmsg fails and we pull it out using this
+              ** loop which just gets the next 512 bytes and then
+              ** deletes the rest of the buffer contents.
+              ** -avalon
+            */
+           while (dolen <= 0) {
+               if (dolen < 0)
+                   return exit_client (cptr, cptr, cptr, "dbuf_getmsg fail");
+               if (DBufLength (&cptr->recvQ) < 510) {
+                   cptr->flags |= FLAGS_NONL;
+                   break;
+               }
+               dolen = dbuf_get (&cptr->recvQ, readbuf, 511);
+               if (dolen > 0 && DBufLength (&cptr->recvQ))
+                   DBufClear (&cptr->recvQ);
+           }
+
+           if (dolen > 0
+               && (dopacket (cptr, readbuf, dolen) == FLUSH_BUFFER))
+               return FLUSH_BUFFER;
+       }
+    }
+    return 1;
+}
+
+
+/*
+ * Check all connections for new connections and input data that is to be
+ * processed. Also check for connections with data queued and whether we can
+ * write it out.
+ */
+int read_message (delay)
+     time_t delay; /* Don't ever use ZERO here, unless you mean to poll and then
+                     * you have to have sleep/wait somewhere else in the code.--msa
+                   */
+{
+    aClient *cptr;
+    int nfds;
+    struct timeval wait;
+#ifdef pyr
+    struct timeval nowt;
+    u_long us;
+#endif
+    fd_set read_set, write_set;
+    time_t delay2 = delay, now;
+    u_long usec = 0;
+    int res, length, fd, i;
+    int ping = 0;
+    int sockerr;
+
+#ifdef pyr
+    (void) gettimeofday (&nowt, NULL);
+    now = nowt.tv_sec;
+#else
+    now = time (NULL);
+#endif
+
+    for (res = 0;;) {
+
+       FD_ZERO (&read_set);
+       FD_ZERO (&write_set);
+
+       for (i = highest_fd; i >= 0; i--) {
+           if (!(cptr = local[i]))
+               continue;
+           if (IsLog (cptr))
+               continue;
+           if (IsPing (cptr)) {
+               ping++;
+               Debug ((DEBUG_NOTICE, "open ping on %x %d", cptr, i));
+               FD_SET (i, &read_set);
+               if (DoPing (cptr)) {
+                   delay2 = 1;
+                   if (now > cptr->lasttime)
+                       FD_SET (i, &write_set);
+               }
+               else if (cptr->firsttime && now > cptr->firsttime) {
+                   FD_CLR (i, &read_set);
+                   del_queries ((char *) cptr);
+                   end_ping (cptr);
+               }
+               continue;
+           }
+           if (DoingDNS (cptr))
+               continue;
+           if (IsMe (cptr) && IsListening (cptr)) {
+               FD_SET (i, &read_set);
+           }
+           else if (!IsMe (cptr)) {
+               if (DBufLength (&cptr->recvQ) && delay2 > 2)
+                   delay2 = 1;
+               if (DBufLength (&cptr->recvQ) < 4088)
+                   FD_SET (i, &read_set);
+           }
+           if (DBufLength (&cptr->sendQ) || IsConnecting (cptr) ||
+#ifdef ZIP_LINKS
+               ((cptr->flags2 & FLAGS2_ZIP) && (cptr->zip->outcount > 0)) ||
+#endif
+               (DoList (cptr) && IsSendable (cptr)))
+#ifndef        pyr
+               FD_SET (i, &write_set);
+#else
+           {
+               if (!IsBlocked (cptr))
+                   FD_SET (i, &write_set);
+               else
+                   delay2 = 0, usec = 500000;
+           }
+           if (now - cptr->lw.tv_sec && nowt.tv_usec - cptr->lw.tv_usec < 0)
+               us = 1000000;
+           else
+               us = 0;
+           us += nowt.tv_usec;
+           if (us - cptr->lw.tv_usec > 500000)
+               ClearBlocked (cptr);
+#endif
+       }
+       if (udpfd >= 0)
+           FD_SET (udpfd, &read_set);
+       if (resfd >= 0)
+           FD_SET (resfd, &read_set);
+
+       wait.tv_sec = MIN (delay2, delay);
+       wait.tv_usec = usec;
+#ifdef HPUX
+       nfds = select (FD_SETSIZE, (int *) &read_set, (int *) &write_set,
+                      0, &wait);
+#else
+       nfds = select (FD_SETSIZE, &read_set, &write_set, 0, &wait);
+#endif
+       if (nfds == -1 && errno == EINTR)
+           return -1;
+       else if (nfds >= 0)
+           break;
+       report_error ("select %s:%s", &me);
+       res++;
+       if (res > 5)
+           restart ("too many select errors");
+       sleep (10);
+    }
+
+    if (udpfd >= 0 && FD_ISSET (udpfd, &read_set)) {
+       polludp ();
+       nfds--;
+       FD_CLR (udpfd, &read_set);
+    }
+    if (resfd >= 0 && FD_ISSET (resfd, &read_set)) {
+       do_dns_async ();
+       nfds--;
+       FD_CLR (resfd, &read_set);
+    }
+    /*
+     * Check fd sets for the ping fd's (if set and valid!) first
+     * because these can not be processed using the normal loops below.
+     * And we want them to be as fast as possible.
+     * -Run
+     */
+    for (i = highest_fd; (ping > 0) && (i >= 0); i--) {
+       if (!(cptr = local[i]))
+           continue;
+       if (!IsPing (cptr))
+           continue;
+       ping--;
+       if ((nfds > 0) && FD_ISSET (cptr->fd, &read_set)) {
+           nfds--;
+           FD_CLR (cptr->fd, &read_set);
+           read_ping (cptr);     /* This can MyFree(cptr) ! */
+       }
+       else if ((nfds > 0) && FD_ISSET (cptr->fd, &write_set)) {
+           nfds--;
+           cptr->lasttime = now;
+           FD_CLR (cptr->fd, &write_set);
+           send_ping (cptr);     /* This can MyFree(cptr) ! */
+       }
+    }
+
+    if (resfd >= 0 && FD_ISSET (resfd, &read_set)) {
+       do_dns_async ();
+       nfds--;
+       FD_CLR (resfd, &read_set);
+    }
+    
+    for (i = highest_fd; i >= 0; i--)
+       if ((cptr = local[i]) && FD_ISSET (i, &read_set)
+           && IsListening (cptr)) {
+           FD_CLR (i, &read_set);
+           nfds--;
+           cptr->lasttime = time (NULL);
+           /*
+              ** There may be many reasons for error return, but
+              ** in otherwise correctly working environment the
+              ** probable cause is running out of file descriptors
+              ** (EMFILE, ENFILE or others?). The man pages for
+              ** accept don't seem to list these as possible,
+              ** although it's obvious that it may happen here.
+              ** Thus no specific errors are tested at this
+              ** point, just assume that connections cannot
+              ** be accepted until some old is closed first.
+            */
+           if ((fd = accept (i, NULL, NULL)) < 0) {
+               report_error ("Cannot accept connections %s:%s", cptr);
+               break;
+           }
+           ircstp->is_ac++;
+           if (fd >= MAXCLIENTS) {
+               ircstp->is_ref++;
+               sendto_ops ("All connections in use. (%s)",
+                           get_client_name (cptr, TRUE));
+               (void) send (fd, "ERROR :All connections in use\r\n", 32, 0);
+               (void) close (fd);
+               break;
+           }
+           /*
+            * Use of add_connection (which never fails :) meLazy
+            */
+           (void) add_connection (cptr, fd);
+           nextping = time (NULL);
+           if (!cptr->acpt)
+               cptr->acpt = &me;
+       }
+    for (i = highest_fd; i >= 0; i--) {
+       if (!(cptr = local[i]) || IsMe (cptr))
+           continue;
+       if (FD_ISSET (i, &write_set)) {
+           int write_err = 0;
+           nfds--;
+           /*
+              ** ...room for writing, empty some queue then...
+            */
+           ClearBlocked (cptr);
+           if (IsConnecting (cptr))
+               write_err = completed_connection (cptr);
+           if (!write_err) {
+               if (DoList (cptr) && IsSendable (cptr))
+                   send_list (cptr, 32);
+               (void) send_queued (cptr);
+           }
+           if (IsDead (cptr) || write_err) {
+             deadsocket:
+               if (FD_ISSET (i, &read_set)) {
+                   nfds--;
+                   FD_CLR (i, &read_set);
+               }
+               (void) exit_client (cptr, cptr, &me,
+                                   ((sockerr = get_sockerr (cptr))
+                                    ? strerror (sockerr) : "Client exited"));
+               continue;
+           }
+       }
+       length = 1;               /* for fall through case */
+       if (!NoNewLine (cptr) || FD_ISSET (i, &read_set))
+           length = read_packet (cptr, &read_set);
+       if (length > 0)
+           flush_connections (i);
+       if ((length != FLUSH_BUFFER) && IsDead (cptr))
+           goto deadsocket;
+       if (!FD_ISSET (i, &read_set) && length > 0)
+           continue;
+       nfds--;
+       readcalls++;
+       if (length > 0)
+           continue;
+
+       /*
+          ** ...hmm, with non-blocking sockets we might get
+          ** here from quite valid reasons, although.. why
+          ** would select report "data available" when there
+          ** wasn't... so, this must be an error anyway...  --msa
+          ** actually, EOF occurs when read() returns 0 and
+          ** in due course, select() returns that fd as ready
+          ** for reading even though it ends up being an EOF. -avalon
+        */
+       Debug ((DEBUG_ERROR, "READ ERROR: fd = %d %d %d", i, errno, length));
+
+       /*
+          ** NOTE: if length == -2 then cptr has already been freed!
+        */
+       if (length != -2 && (IsServer (cptr) || IsHandshake (cptr))) {
+           if (length == 0) {
+               sendto_locfailops ("Server %s closed the connection",
+                                  get_client_name (cptr, FALSE));
+               sendto_serv_butone (&me,
+                                   ":%s GLOBOPS :Server %s closed the connection",
+                                   me.name, get_client_name (cptr, FALSE));
+           }
+           else
+               report_error ("Lost connection to %s:%s", cptr);
+       }
+       if (length != FLUSH_BUFFER)
+           (void) exit_client (cptr, cptr, &me,
+                               ((sockerr = get_sockerr (cptr))
+                                ? strerror (sockerr) : "Client exited"));
+    }
+
+    Debug ((DEBUG_DEBUG, "Going out of read_message"));
+    return 0;
+}
+
+/*
+ * connect_server
+ */
+int connect_server (aconf, by, hp)
+     aConfItem *aconf;
+     aClient *by;
+     struct hostent *hp;
+{
+    struct sockaddr *svp;
+    aClient *cptr, *c2ptr;
+    char *s;
+    int errtmp, len;
+
+    Debug ((DEBUG_NOTICE, "Connect to %s[%s] @%s",
+           aconf->name, aconf->host, inetntoa ((char *) &aconf->ipnum)));
+
+    if ((c2ptr = find_server (aconf->name, NULL))) {
+       sendto_ops ("Server %s already present from %s",
+                   aconf->name, get_client_name (c2ptr, TRUE));
+       if (by && IsPerson (by) && !MyClient (by))
+           sendto_one (by,
+                       ":%s NOTICE %s :Server %s already present from %s",
+                       me.name, by->name, aconf->name,
+                       get_client_name (c2ptr, TRUE));
+       return -1;
+    }
+    /*
+     * If we dont know the IP# for this host and itis a hostname and
+     * not a ip# string, then try and find the appropriate host record.
+     */
+    if ((!aconf->ipnum.s_addr)) {
+       Link lin;
+
+       lin.flags = ASYNC_CONNECT;
+       lin.value.aconf = aconf;
+       nextdnscheck = 1;
+       s = (char *) index (aconf->host, '@');
+       s++;                      /* should NEVER be NULL */
+       if ((aconf->ipnum.s_addr = inet_addr (s)) == -1) {
+           aconf->ipnum.s_addr = 0;
+           hp = gethost_byname (s, &lin);
+           Debug ((DEBUG_NOTICE, "co_sv: hp %x ac %x na %s ho %s",
+                   hp, aconf, aconf->name, s));
+           if (!hp)
+               return 0;
+           bcopy (hp->h_addr, (char *) &aconf->ipnum,
+                  sizeof (struct in_addr));
+       }
+    }
+    cptr = make_client (NULL, NULL);
+    cptr->hostp = hp;
+    /*
+     * Copy these in so we have something for error detection.
+     */
+    strncpyzt (cptr->name, aconf->name, sizeof (cptr->name));
+    strncpyzt (cptr->sockhost, aconf->host, HOSTLEN + 1);
+
+    svp = connect_inet (aconf, cptr, &len);
+
+    if (!svp) {
+       if (cptr->fd != -1)
+           (void) close (cptr->fd);
+       cptr->fd = -2;
+       free_client (cptr);
+       return -1;
+    }
+    set_non_blocking (cptr->fd, cptr);
+    set_sock_opts (cptr->fd, cptr);
+    (void) signal (SIGALRM, dummy);
+    if (connect (cptr->fd, svp, len) < 0 && errno != EINPROGRESS) {
+       errtmp = errno;           /* other system calls may eat errno */
+       report_error ("Connect to host %s failed: %s", cptr);
+       if (by && IsPerson (by) && !MyClient (by))
+           sendto_one (by,
+                       ":%s NOTICE %s :Connect to host %s failed.",
+                       me.name, by->name, cptr);
+       (void) close (cptr->fd);
+       cptr->fd = -2;
+       free_client (cptr);
+       errno = errtmp;
+       if (errno == EINTR)
+           errno = ETIMEDOUT;
+       return -1;
+    }
+    /* Attach config entries to client here rather than in
+     * completed_connection. This to avoid null pointer references
+     * when name returned by gethostbyaddr matches no C lines
+     * (could happen in 2.6.1a when host and servername differ).
+     * No need to check access and do gethostbyaddr calls.
+     * There must at least be one as we got here C line...  meLazy
+     */
+    (void) attach_confs_host (cptr, aconf->host,
+                             CONF_NOCONNECT_SERVER | CONF_CONNECT_SERVER);
+
+    if (!find_conf_host (cptr->confs, aconf->host, CONF_NOCONNECT_SERVER) ||
+       !find_conf_host (cptr->confs, aconf->host, CONF_CONNECT_SERVER)) {
+       sendto_ops ("Host %s is not enabled for connecting:no C/N-line",
+                   aconf->host);
+       if (by && IsPerson (by) && !MyClient (by))
+           sendto_one (by,
+                       ":%s NOTICE %s :Connect to host %s failed.",
+                       me.name, by->name, cptr);
+       det_confs_butmask (cptr, 0);
+       (void) close (cptr->fd);
+       cptr->fd = -2;
+       free_client (cptr);
+       return (-1);
+    }
+    /*
+       ** The socket has been connected or connect is in progress.
+     */
+    (void) make_server (cptr);
+    if (by && IsPerson (by)) {
+       (void) strcpy (cptr->serv->by, by->name);
+       if (cptr->serv->user)
+           free_user (cptr->serv->user, NULL);
+       cptr->serv->user = by->user;
+       by->user->refcnt++;
+    }
+    else {
+       (void) strcpy (cptr->serv->by, "AutoConn.");
+       if (cptr->serv->user)
+           free_user (cptr->serv->user, NULL);
+       cptr->serv->user = NULL;
+    }
+    (void) strcpy (cptr->serv->up, me.name);
+    if (cptr->fd > highest_fd)
+       highest_fd = cptr->fd;
+    local[cptr->fd] = cptr;
+    cptr->acpt = &me;
+    SetConnecting (cptr);
+
+    get_sockhost (cptr, aconf->host);
+    add_client_to_list (cptr);
+    nextping = time (NULL);
+
+    return 0;
+}
+
+static struct sockaddr *connect_inet (aconf, cptr, lenp)
+     aConfItem *aconf;
+     aClient *cptr;
+     int *lenp;
+{
+    static struct sockaddr_in server;
+    struct hostent *hp;
+
+    /*
+     * Might as well get sockhost from here, the connection is attempted
+     * with it so if it fails its useless.
+     */
+    cptr->fd = socket (AF_INET, SOCK_STREAM, 0);
+    if (cptr->fd >= MAXCLIENTS) {
+       sendto_ops ("No more connections allowed (%s)", cptr->name);
+       return NULL;
+    }
+    mysk.sin_port = 0;
+    bzero ((char *) &server, sizeof (server));
+    server.sin_family = AF_INET;
+    get_sockhost (cptr, aconf->host);
+
+    if (cptr->fd == -1) {
+       report_error ("opening stream socket to server %s:%s", cptr);
+       return NULL;
+    }
+    get_sockhost (cptr, aconf->host);
+    server.sin_port = 0;
+    server.sin_addr = me.ip;
+    server.sin_family = AF_INET;
+    /*
+       ** Bind to a local IP# (with unknown port - let unix decide) so
+       ** we have some chance of knowing the IP# that gets used for a host
+       ** with more than one IP#.
+     */
+    /* No we don't bind it, not all OS's can handle connecting with
+       ** an already bound socket, different ip# might occur anyway
+       ** leading to a freezing select() on this side for some time.
+       ** I had this on my Linux 1.1.88 --Run
+     */
+    /* We do now.  Virtual interface stuff --ns */
+    if (me.ip.s_addr != INADDR_ANY)
+       if (bind (cptr->fd, (struct sockaddr *) &server, sizeof (server)) ==
+           -1) {
+           report_error ("error binding to local port for %s:%s", cptr);
+           return NULL;
+       }
+    bzero ((char *) &server, sizeof (server));
+    server.sin_family = AF_INET;
+    /*
+     * By this point we should know the IP# of the host listed in the
+     * conf line, whether as a result of the hostname lookup or the ip#
+     * being present instead. If we dont know it, then the connect fails.
+     */
+    if (isdigit (*aconf->host) && (aconf->ipnum.s_addr == -1))
+       aconf->ipnum.s_addr = inet_addr (aconf->host);
+    if (aconf->ipnum.s_addr == -1) {
+       hp = cptr->hostp;
+       if (!hp) {
+           Debug ((DEBUG_FATAL, "%s: unknown host", aconf->host));
+           return NULL;
+       }
+       bcopy (hp->h_addr, (char *) &aconf->ipnum, sizeof (struct in_addr));
+    }
+    bcopy ((char *) &aconf->ipnum, (char *) &server.sin_addr,
+          sizeof (struct in_addr));
+    bcopy ((char *) &aconf->ipnum, (char *) &cptr->ip,
+          sizeof (struct in_addr));
+    server.sin_port = htons (((aconf->port > 0) ? aconf->port : portnum));
+    *lenp = sizeof (server);
+    return (struct sockaddr *) &server;
+}
+
+
+int utmp_read (fd, name, line, host, hlen)
+     int fd, hlen;
+     char *name, *line, *host;
+{
+    struct utmp ut;
+    while (read (fd, (char *) &ut, sizeof (struct utmp))
+          == sizeof (struct utmp)) {
+       strncpyzt (name, ut.ut_name, 9);
+       strncpyzt (line, ut.ut_line, 10);
+#ifdef USER_PROCESS
+#if defined(HPUX) || defined(AIX)
+       strncpyzt (host, (ut.ut_host[0]) ? (ut.ut_host) : me.name, 16);
+#else
+       strncpyzt (host, me.name, 9);
+#endif
+       if (ut.ut_type == USER_PROCESS)
+           return 0;
+#else
+       strncpyzt (host, (ut.ut_host[0]) ? (ut.ut_host) : me.name, hlen);
+       if (ut.ut_name[0])
+           return 0;
+#endif
+    }
+    return -1;
+}
+
+int utmp_close (fd)
+     int fd;
+{
+    return (close (fd));
+}
+
+/*
+   ** setup a UDP socket and listen for incoming packets
+ */
+int setup_ping ()
+{
+    struct sockaddr_in from;
+    int on = 1;
+
+    bzero ((char *) &from, sizeof (from));
+    from.sin_addr = me.ip;
+    from.sin_port = htons (7007);
+    from.sin_family = AF_INET;
+
+    if ((udpfd = socket (AF_INET, SOCK_DGRAM, 0)) == -1) {
+       Debug ((DEBUG_ERROR, "socket udp : %s", strerror (errno)));
+       return -1;
+    }
+    if (setsockopt (udpfd, SOL_SOCKET, SO_REUSEADDR,
+                   (OPT_TYPE *) & on, sizeof (on)) == -1) {
+#ifdef USE_SYSLOG
+       syslog (LOG_ERR, "setsockopt udp fd %d : %m", udpfd);
+#endif
+       Debug ((DEBUG_ERROR, "setsockopt so_reuseaddr : %s",
+               strerror (errno)));
+       (void) close (udpfd);
+       udpfd = -1;
+       return -1;
+    }
+    on = 0;
+    (void) setsockopt (udpfd, SOL_SOCKET, SO_BROADCAST,
+                      (char *) &on, sizeof (on));
+    if (bind (udpfd, (struct sockaddr *) &from, sizeof (from)) == -1) {
+#ifdef USE_SYSLOG
+       syslog (LOG_ERR, "bind udp.%d fd %d : %m", from.sin_port, udpfd);
+#endif
+       Debug ((DEBUG_ERROR, "bind : %s", strerror (errno)));
+       (void) close (udpfd);
+       udpfd = -1;
+       return -1;
+    }
+    if (fcntl (udpfd, F_SETFL, FNDELAY) == -1) {
+       Debug ((DEBUG_ERROR, "fcntl fndelay : %s", strerror (errno)));
+       (void) close (udpfd);
+       udpfd = -1;
+       return -1;
+    }
+    return udpfd;
+}
+
+/*
+ * max # of pings set to 15/sec.
+ */
+static void polludp ()
+{
+    char *s;
+    struct sockaddr_in from;
+    int n, fromlen = sizeof (from);
+    static time_t last = 0, now;
+    static int cnt = 0, mlen = 0;
+
+    /*
+     * find max length of data area of packet.
+     */
+    if (!mlen) {
+       mlen = sizeof (readbuf) - strlen (me.name) - strlen (version);
+       mlen -= 6;
+       if (mlen < 0)
+           mlen = 0;
+    }
+    Debug ((DEBUG_DEBUG, "udp poll"));
+
+    n = recvfrom (udpfd, readbuf, mlen, 0, (struct sockaddr *) &from,
+                 &fromlen);
+    now = time (NULL);
+    if (now == last)
+       if (++cnt > 14)
+           return;
+    cnt = 0;
+    last = now;
+
+    if (n == -1) {
+       if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
+           return;
+       else {
+           report_error ("udp port recvfrom (%s): %s", &me);
+           return;
+       }
+    }
+    ircstp->is_udp++;
+    if (n < 19)
+       return;
+
+    s = readbuf + n;
+    /*
+     * attach my name and version for the reply
+     */
+    *readbuf |= 1;
+    (void) strcpy (s, me.name);
+    s += strlen (s) + 1;
+    (void) strcpy (s, version);
+    s += strlen (s);
+    (void) sendto (udpfd, readbuf, s - readbuf, 0,
+                  (struct sockaddr *) &from, sizeof (from));
+    return;
+}
+
+/*
+ * do_dns_async
+ *
+ * Called when the fd returned from init_resolver() has been selected for
+ * reading.
+ */
+static void do_dns_async ()
+{
+    static Link ln;
+    aClient *cptr;
+    aConfItem *aconf;
+    struct hostent *hp;
+
+    ln.flags = -1;
+    hp = get_res ((char *) &ln);
+    while (hp != NULL) {
+       Debug ((DEBUG_DNS, "%#x = get_res(%d,%#x)", hp, ln.flags,
+               ln.value.cptr));
+
+       switch (ln.flags) {
+       case ASYNC_NONE:
+           /*
+            * no reply was processed that was outstanding or had a client
+            * still waiting.
+            */
+           break;
+       case ASYNC_CLIENT:
+           if ((cptr = ln.value.cptr)) {
+               del_queries ((char *) cptr);
+               if (cptr->cc)
+                   write (cptr->fd, REPORT_FIN_DNS, R_fin_dns);
+               ClearDNS (cptr);
+               SetAccess (cptr);
+               cptr->hostp = hp;
+           }
+           break;
+       case ASYNC_CONNECT:
+           aconf = ln.value.aconf;
+           if (hp && aconf) {
+               bcopy (hp->h_addr, (char *) &aconf->ipnum,
+                      sizeof (struct in_addr));
+               (void) connect_server (aconf, NULL, hp);
+           }
+           else
+               sendto_ops ("Connect to %s failed: host lookup",
+                           (aconf) ? aconf->host : "unknown");
+           break;
+       case ASYNC_CONF:
+           aconf = ln.value.aconf;
+           if (hp && aconf)
+               bcopy (hp->h_addr, (char *) &aconf->ipnum,
+                      sizeof (struct in_addr));
+           break;
+       case ASYNC_SERVER:
+           cptr = ln.value.cptr;
+           del_queries ((char *) cptr);
+           ClearDNS (cptr);
+           if (check_server (cptr, hp, NULL, NULL, 1))
+               (void) exit_client (cptr, cptr, &me, "No Authorization");
+           break;
+       case ASYNC_PING:
+           cptr = ln.value.cptr;
+           del_queries ((char *) cptr);
+           if (hp) {
+               memcpy (&cptr->ip, hp->h_addr, sizeof (struct in_addr));
+               if (ping_server (cptr, hp) != -1)
+                   break;
+           }
+           else {
+               sendto_ops ("Udp ping to %s failed: host lookup",
+                           cptr->sockhost);
+           }
+
+           end_ping (cptr);
+           break;
+       default:
+           break;
+       }
+
+       ln.flags = -1;
+       hp = get_res ((char *) &ln);
+    }                            /* while (hp != NULL) */
+}
diff --git a/src/s_conf.c b/src/s_conf.c
new file mode 100644 (file)
index 0000000..deb33f2
--- /dev/null
@@ -0,0 +1,2409 @@
+
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_conf.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Changed all calls of check_pings so that only when a kline-related command
+   is used will a kline check occur -- Barubary */
+
+#define KLINE_RET_AKILL 3
+#define KLINE_RET_PERM 2
+#define KLINE_RET_DELOK 1
+#define KLINE_DEL_ERR 0
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "channel.h"
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+/* If you have problems with this on your system please mail star-coding@starchat.net
+   #if defined(__hpux) || defined(__FreeBSD__)
+ */
+#include "inet.h"
+/* #endif */
+#if defined(PCS) || defined(AIX) || defined(DYNIXPTX) || defined(SVR3)
+#include <time.h>
+#endif
+
+#include "h.h"
+#define debug 1
+static int lookup_confhost PROTO ((aConfItem *));
+static int advanced_check (char *, int);
+int ZLineExists (char *);
+
+
+aSqlineItem *sqline = NULL;
+aJinxItem *jinx = NULL;
+aConfItem *conf = NULL;
+
+extern char zlinebuf[];
+int socks_zline_time = ZLINE_TIME;
+
+static int del_temp_conf (unsigned int, char *, char *, char *, int, int,
+                         unsigned int);
+
+void free_sqline (aSqlineItem *);      /* From list.c */
+void free_jinx (aJinxItem *);
+void delist_conf (aConfItem *);        /* This one too */
+aJinxItem *make_jinx ();
+/*
+ * remove all conf entries from the client except those which match
+ * the status field mask.
+ */
+void det_confs_butmask (cptr, mask)
+     aClient *cptr;
+     int mask;
+{
+    Link *tmp, *tmp2;
+
+    for (tmp = cptr->confs; tmp; tmp = tmp2) {
+       tmp2 = tmp->next;
+       if ((tmp->value.aconf->status & mask) == 0)
+           (void) detach_conf (cptr, tmp->value.aconf);
+    }
+}
+
+/*
+ * Add a temporary line to the configuration
+ */
+void add_temp_conf (status, host, passwd, name, port, class, temp)
+     unsigned int status;
+     char *host;
+     char *passwd;
+     char *name;
+     int port, class, temp;    /* temp: 0 = perm 1 = temp 2 = akill */
+{
+    aConfItem *aconf;
+
+    aconf = make_conf ();
+
+    aconf->tmpconf = temp;
+    aconf->status = status;
+    if (host)
+       DupString (aconf->host, host);
+    if (passwd)
+       DupString (aconf->passwd, passwd);
+    if (name)
+       DupString (aconf->name, name);
+    aconf->port = port;
+    if (class)
+       Class (aconf) = find_class (class);
+    if (!find_temp_conf_entry (aconf, status)) {
+       aconf->next = conf;
+       conf = aconf;
+       aconf = NULL;
+    }
+    if (aconf)
+       free_conf (aconf);
+}
+
+void RemoveZLine (char *zline)
+{
+    sendto_realops ("Removing temporary z:line (%s)", zline);
+    del_temp_conf (CONF_ZAP, zline, NULL, NULL, 0, 0, 0);
+}
+
+/*
+ * delete a temporary conf line.  *only* temporary conf lines may be deleted.
+ */
+int del_temp_conf (status, host, passwd, name, port, class, akill)
+     unsigned int status, akill;
+     char *host;
+     char *passwd;
+     char *name;
+     int port, class;
+{
+    aConfItem *aconf;
+    aConfItem *bconf;
+    u_int mask;
+    u_int result = KLINE_DEL_ERR;
+
+    aconf = make_conf ();
+
+    aconf->status = status;
+    if (host)
+       DupString (aconf->host, host);
+    if (passwd)
+       DupString (aconf->passwd, passwd);
+    if (name)
+       DupString (aconf->name, name);
+    aconf->port = port;
+    if (class)
+       Class (aconf) = find_class (class);
+    mask = status;
+    if ((bconf = find_temp_conf_entry (aconf, mask))) {        /* only if non-null ptr */
+/* Completely skirt the akill error messages if akill is set to 1
+ * this allows RAKILL to do its thing without having to go through the
+ * error checkers.  If it had to it would go kaplooey. --Russell
+ */
+       if (bconf->tmpconf == KLINE_PERM && (akill != 3))
+           result = KLINE_RET_PERM;    /* Kline permanent */
+       else if (!akill && (bconf->tmpconf == KLINE_AKILL))
+           result = KLINE_RET_AKILL;   /* Akill */
+       else if (akill && (bconf->tmpconf != KLINE_AKILL))
+           result = KLINE_RET_PERM;
+       else {
+           bconf->status |= CONF_ILLEGAL;      /* just mark illegal */
+           result = KLINE_RET_DELOK;   /* same as deletion */
+       }
+
+    }
+    if (aconf)
+       free_conf (aconf);
+    return result;               /* if it gets to here, it doesn't exist */
+}
+
+/*
+ * find the first (best) I line to attach.
+ */
+int attach_Iline (cptr, hp, sockhost)
+     aClient *cptr;
+     struct hostent *hp;
+     char *sockhost;
+{
+    aConfItem *aconf;
+    char *hname;
+    int i;
+    static char uhost[HOSTLEN + USERLEN + 3];
+    static char fullname[HOSTLEN + 1];
+
+    for (aconf = conf; aconf; aconf = aconf->next) {
+       if (aconf->status != CONF_CLIENT)
+           continue;
+       if (aconf->port && aconf->port != cptr->acpt->port)
+           continue;
+       if (!aconf->host || !aconf->name)
+           goto attach_iline;
+       if (hp)
+           for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++]) {
+               (void) strncpy (fullname, hname, sizeof (fullname) - 1);
+               add_local_domain (fullname, HOSTLEN - strlen (fullname));
+               Debug ((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname));
+               if (index (aconf->name, '@')) {
+                   (void) strcpy (uhost, cptr->username);
+                   (void) strcat (uhost, "@");
+               }
+               else
+                   *uhost = '\0';
+               (void) strncat (uhost, fullname,
+                               sizeof (uhost) - strlen (uhost));
+               if (!match (aconf->name, uhost))
+                   goto attach_iline;
+           }
+       if (index (aconf->host, '@')) {
+           strncpyzt (uhost, cptr->username, sizeof (uhost));
+           (void) strcat (uhost, "@");
+       }
+       else
+           *uhost = '\0';
+       (void) strncat (uhost, sockhost, sizeof (uhost) - strlen (uhost));
+       if (!match (aconf->host, uhost))
+           goto attach_iline;
+       continue;
+      attach_iline:
+       get_sockhost (cptr, uhost);
+       return attach_conf (cptr, aconf);
+    }
+    return -1;
+}
+
+/*
+ * Find the single N line and return pointer to it (from list).
+ * If more than one then return NULL pointer.
+ */
+aConfItem *count_cnlines (lp)
+     Link *lp;
+{
+    aConfItem *aconf, *cline = NULL, *nline = NULL;
+
+    for (; lp; lp = lp->next) {
+       aconf = lp->value.aconf;
+       if (!(aconf->status & CONF_SERVER_MASK))
+           continue;
+       if (aconf->status == CONF_CONNECT_SERVER && !cline)
+           cline = aconf;
+       else if (aconf->status == CONF_NOCONNECT_SERVER && !nline)
+           nline = aconf;
+    }
+    return nline;
+}
+
+/*
+   ** detach_conf
+   **   Disassociate configuration from the client.
+   **      Also removes a class from the list if marked for deleting.
+ */
+int detach_conf (cptr, aconf)
+     aClient *cptr;
+     aConfItem *aconf;
+{
+    Link **lp, *tmp;
+
+    lp = &(cptr->confs);
+
+    while (*lp) {
+       if ((*lp)->value.aconf == aconf) {
+           if ((aconf) && (Class (aconf))) {
+               if (aconf->status & CONF_CLIENT_MASK)
+                   if (ConfLinks (aconf) > 0)
+                       --ConfLinks (aconf);
+               if (ConfMaxLinks (aconf) == -1 && ConfLinks (aconf) == 0) {
+                   free_class (Class (aconf));
+                   Class (aconf) = NULL;
+               }
+           }
+           if (aconf && !--aconf->clients && IsIllegal (aconf))
+               free_conf (aconf);
+           tmp = *lp;
+           *lp = tmp->next;
+           free_link (tmp);
+           return 0;
+       }
+       else
+           lp = &((*lp)->next);
+    }
+    return -1;
+}
+
+static int is_attached (aconf, cptr)
+     aConfItem *aconf;
+     aClient *cptr;
+{
+    Link *lp;
+
+    for (lp = cptr->confs; lp; lp = lp->next)
+       if (lp->value.aconf == aconf)
+           break;
+
+    return (lp) ? 1 : 0;
+}
+
+/*
+   ** attach_conf
+   **   Associate a specific configuration entry to a *local*
+   **   client (this is the one which used in accepting the
+   **   connection). Note, that this automaticly changes the
+   **   attachment if there was an old one...
+ */
+int attach_conf (cptr, aconf)
+     aConfItem *aconf;
+     aClient *cptr;
+{
+    Link *lp;
+
+    if (is_attached (aconf, cptr))
+       return 1;
+    if (IsIllegal (aconf))
+       return -1;
+    if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) &&
+       aconf->clients >= ConfMaxLinks (aconf) && ConfMaxLinks (aconf) > 0)
+       return -3;                /* Use this for printing error message */
+    lp = make_link ();
+    lp->next = cptr->confs;
+    lp->value.aconf = aconf;
+    cptr->confs = lp;
+    aconf->clients++;
+    if (aconf->status & CONF_CLIENT_MASK)
+       ConfLinks (aconf)++;
+    return 0;
+}
+
+
+aConfItem *find_admin ()
+{
+    aConfItem *aconf;
+
+    for (aconf = conf; aconf; aconf = aconf->next)
+       if (aconf->status & CONF_ADMIN)
+           break;
+
+    return (aconf);
+}
+
+/* Find a DR_PASS line for the /DIE or /RESTART command
+ * Instead of returning the whole structure we return a
+ * char* which is the pass. 
+ * Added December 28 1997 -- NikB 
+ */
+char *find_diepass ()
+{
+    aConfItem *aconf;
+
+    for (aconf = conf; aconf; aconf = aconf->next)
+       if (aconf->status & CONF_DRPASS)
+           return (aconf->host);
+
+    return NULL;                 /* Return NULL (We did not find any) */
+}
+
+char *find_restartpass ()
+{
+    aConfItem *aconf;
+
+    for (aconf = conf; aconf; aconf = aconf->next)
+       if (aconf->status & CONF_DRPASS)
+           return (aconf->passwd);
+
+    return NULL;                 /* Return NULL (We did not find any) */
+}
+
+aConfItem *find_me ()
+{
+    aConfItem *aconf;
+    for (aconf = conf; aconf; aconf = aconf->next)
+       if (aconf->status & CONF_ME)
+           break;
+
+    return (aconf);
+}
+
+/*
+ * attach_confs
+ *  Attach a CONF line to a client if the name passed matches that for
+ * the conf file (for non-C/N lines) or is an exact match (C/N lines
+ * only).  The difference in behaviour is to stop C:*::* and N:*::*.
+ */
+aConfItem *attach_confs (cptr, name, statmask)
+     aClient *cptr;
+     char *name;
+     int statmask;
+{
+    aConfItem *tmp;
+    aConfItem *first = NULL;
+    int len = strlen (name);
+
+    if (!name || len > HOSTLEN)
+       return NULL;
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       if ((tmp->status & statmask) && !IsIllegal (tmp) &&
+           ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0) &&
+           tmp->name && !match (tmp->name, name)) {
+           if (!attach_conf (cptr, tmp) && !first)
+               first = tmp;
+       }
+       else if ((tmp->status & statmask) && !IsIllegal (tmp) &&
+                (tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
+                tmp->name && !mycmp (tmp->name, name)) {
+           if (!attach_conf (cptr, tmp) && !first)
+               first = tmp;
+       }
+    }
+    return (first);
+}
+
+/*
+ * Added for new access check    meLazy
+ */
+aConfItem *attach_confs_host (cptr, host, statmask)
+     aClient *cptr;
+     char *host;
+     int statmask;
+{
+    aConfItem *tmp;
+    aConfItem *first = NULL;
+    int len = strlen (host);
+
+    if (!host || len > HOSTLEN)
+       return NULL;
+
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       if ((tmp->status & statmask) && !IsIllegal (tmp) &&
+           (tmp->status & CONF_SERVER_MASK) == 0 &&
+           (!tmp->host || match (tmp->host, host) == 0)) {
+           if (!attach_conf (cptr, tmp) && !first)
+               first = tmp;
+       }
+       else if ((tmp->status & statmask) && !IsIllegal (tmp) &&
+                (tmp->status & CONF_SERVER_MASK) &&
+                (tmp->host && mycmp (tmp->host, host) == 0)) {
+           if (!attach_conf (cptr, tmp) && !first)
+               first = tmp;
+       }
+    }
+    return (first);
+}
+
+/*
+ * find a conf entry which matches the hostname and has the same name.
+ */
+aConfItem *find_conf_exact (name, user, host, statmask)
+     char *name, *host, *user;
+     int statmask;
+{
+    aConfItem *tmp;
+    char userhost[USERLEN + HOSTLEN + 3];
+
+    (void) sprintf (userhost, "%s@%s", user, host);
+
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
+           mycmp (tmp->name, name))
+           continue;
+       /*
+          ** Accept if the *real* hostname (usually sockecthost)
+          ** socket host) matches *either* host or name field
+          ** of the configuration.
+        */
+       if (match (tmp->host, userhost))
+           continue;
+       if (tmp->status & (CONF_OPERATOR | CONF_LOCOP)) {
+           if (tmp->clients < MaxLinks (Class (tmp)))
+               return tmp;
+           else
+               continue;
+       }
+       else
+           return tmp;
+    }
+    return NULL;
+}
+
+aConfItem *find_conf_name (name, statmask)
+     char *name;
+     int statmask;
+{
+    aConfItem *tmp;
+
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       /*
+          ** Accept if the *real* hostname (usually sockecthost)
+          ** matches *either* host or name field of the configuration.
+        */
+       if ((tmp->status & statmask) &&
+           (!tmp->name || match (tmp->name, name) == 0))
+           return tmp;
+    }
+    return NULL;
+}
+
+aConfItem *find_conf_servern (name)
+     char *name;
+{
+    aConfItem *tmp;
+
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       /*
+          ** Accept if the *real* hostname (usually sockecthost)
+          ** matches *either* host or name field of the configuration.
+        */
+       if ((tmp->status & CONF_NOCONNECT_SERVER) &&
+           (!tmp->name || match (tmp->name, name) == 0))
+           return tmp;
+    }
+    return NULL;
+}
+
+aConfItem *find_conf (lp, name, statmask)
+     char *name;
+     Link *lp;
+     int statmask;
+{
+    aConfItem *tmp;
+    int namelen = name ? strlen (name) : 0;
+
+    if (namelen > HOSTLEN)
+       return (aConfItem *) 0;
+
+    for (; lp; lp = lp->next) {
+       tmp = lp->value.aconf;
+       if ((tmp->status & statmask) &&
+           (((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
+             tmp->name && !mycmp (tmp->name, name)) ||
+            ((tmp->status & (CONF_SERVER_MASK | CONF_HUB)) == 0 &&
+             tmp->name && !match (tmp->name, name))))
+           return tmp;
+    }
+    return NULL;
+}
+
+/*
+ * Added for new access check    meLazy
+ */
+aConfItem *find_conf_host (lp, host, statmask)
+     Link *lp;
+     char *host;
+     int statmask;
+{
+    aConfItem *tmp;
+    int hostlen = host ? strlen (host) : 0;
+
+    if (hostlen > HOSTLEN || BadPtr (host))
+       return (aConfItem *) NULL;
+    for (; lp; lp = lp->next) {
+       tmp = lp->value.aconf;
+       if (tmp->status & statmask &&
+           (!(tmp->status & CONF_SERVER_MASK || tmp->host) ||
+            (tmp->host && !match (tmp->host, host))))
+           return tmp;
+    }
+    return NULL;
+}
+
+/*
+ * find_conf_ip
+ *
+ * Find a conf line using the IP# stored in it to search upon.
+ * Added 1/8/92 by Avalon.
+ */
+aConfItem *find_conf_ip (lp, ip, user, statmask)
+     char *ip, *user;
+     Link *lp;
+     int statmask;
+{
+    aConfItem *tmp;
+    char *s;
+
+    for (; lp; lp = lp->next) {
+       tmp = lp->value.aconf;
+       if (!(tmp->status & statmask))
+           continue;
+       s = index (tmp->host, '@');
+       *s = '\0';
+       if (match (tmp->host, user)) {
+           *s = '@';
+           continue;
+       }
+       *s = '@';
+       if (!bcmp ((char *) &tmp->ipnum, ip, sizeof (struct in_addr)))
+           return tmp;
+    }
+    return NULL;
+}
+
+/*
+ * find_conf_entry
+ *
+ * - looks for a match on all given fields.
+ */
+aConfItem *find_conf_entry (aconf, mask)
+     aConfItem *aconf;
+     u_int mask;
+{
+    aConfItem *bconf;
+
+    for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next) {
+       if (!(bconf->status & mask) || (bconf->port != aconf->port))
+           continue;
+
+       if ((BadPtr (bconf->host) && !BadPtr (aconf->host)) ||
+           (BadPtr (aconf->host) && !BadPtr (bconf->host)))
+           continue;
+       if (!BadPtr (bconf->host) && mycmp (bconf->host, aconf->host))
+           continue;
+
+       if ((BadPtr (bconf->passwd) && !BadPtr (aconf->passwd)) ||
+           (BadPtr (aconf->passwd) && !BadPtr (bconf->passwd)))
+           continue;
+       if (!BadPtr (bconf->passwd) && mycmp (bconf->passwd, aconf->passwd))
+           continue;
+
+       if ((BadPtr (bconf->name) && !BadPtr (aconf->name)) ||
+           (BadPtr (aconf->name) && !BadPtr (bconf->name)))
+           continue;
+       if (!BadPtr (bconf->name) && mycmp (bconf->name, aconf->name))
+           continue;
+       break;
+    }
+    return bconf;
+}
+
+/*
+ * ZLineExists
+ * return 1 is zline exists, 0 if it doesn't
+ * -david kopstain
+ */
+
+int ZLineExists (char *name)
+{
+    aConfItem *aconf;
+    for (aconf = conf; aconf; aconf = aconf->next) {
+       if ((aconf->status & CONF_ZAP) && !(aconf->status & CONF_ILLEGAL) &&
+           !BadPtr (aconf->host) && !strcasecmp (aconf->host, name))
+           return 1;
+    }
+    return 0;
+}
+
+/*
+ * find_temp_conf_entry
+ *
+ * - looks for a match on all given fields for a TEMP conf line.
+ *  Right now the passwd,port, and class fields are ignored, because it's
+ *  only useful for k:lines anyway.  -Russell   11/22/95
+ *  1/21/95 Now looks for any conf line.  I'm leaving this routine and its
+ *  call in because this routine has potential in future upgrades. -Russell
+ */
+aConfItem *find_temp_conf_entry (aconf, mask)
+     aConfItem *aconf;
+     u_int mask;
+{
+    aConfItem *bconf;
+
+    for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next) {
+       /* kline/unkline/kline fix -- Barubary */
+       if (bconf->status & CONF_ILLEGAL)
+           continue;
+       if (!(bconf->status & mask) || (bconf->port != aconf->port))
+           continue;
+/*                if (!bconf->tempconf) continue; */
+       if ((BadPtr (bconf->host) && !BadPtr (aconf->host)) ||
+           (BadPtr (aconf->host) && !BadPtr (bconf->host)))
+           continue;
+       if (!BadPtr (bconf->host) && mycmp (bconf->host, aconf->host))
+           continue;
+
+/*                if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) ||
+   (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd)))
+   continue;
+   if (!BadPtr(bconf->passwd) &&
+   mycmp(bconf->passwd, aconf->passwd))
+   continue; */
+
+       if ((BadPtr (bconf->name) && !BadPtr (aconf->name)) ||
+           (BadPtr (aconf->name) && !BadPtr (bconf->name)))
+           continue;
+       if (!BadPtr (bconf->name) && mycmp (bconf->name, aconf->name))
+           continue;
+       break;
+    }
+    return bconf;
+}
+
+aSqlineItem *find_sqline_nick (nickmask)
+     char *nickmask;
+{
+    aSqlineItem *asqline;
+
+    for (asqline = sqline; asqline; asqline = asqline->next) {
+       if (!BadPtr (asqline->sqline) && (asqline->status != CONF_ILLEGAL)
+           && !mycmp (asqline->sqline, nickmask))
+           return asqline;
+    }
+    return NULL;
+}
+
+aSqlineItem *find_sqline_match (nickname)
+     char *nickname;
+{
+    aSqlineItem *asqline;
+
+    for (asqline = sqline; asqline; asqline = asqline->next) {
+       if (!BadPtr (asqline->sqline) && (asqline->status != CONF_ILLEGAL)
+           && !match (asqline->sqline, nickname))
+           return asqline;
+    }
+    return NULL;
+}
+
+/*
+   **      parv[0] = sender prefix
+   **      parv[1] = server
+   **      parv[2] = +/-
+   **
+   **      revamped this code because the original code was kinda wacky. -GZ
+ */
+int m_svsnoop (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+
+    if (!IsULine (cptr, sptr) || parc < 2)
+       return 0;
+
+    sendto_serv_butone (sptr, ":%s SVSNOOP %s %s", parv[0], parv[1], parv[2]);
+
+    if (match (me.name, parv[1])) {
+       return 0;
+    }
+    if (parv[2][0] == '+') {
+       sendto_ops ("This server is now placed in SVSNOOP mode.");
+
+       /* This stuff is broken anyway - let's just try a different and very simple approach.
+        * if SVSNOOP == 1, you cant use m_oper...should do the trick I think.
+        * see m_oper for the rest of the revamp. -GZ
+        * 
+        * 
+        *    for (aconf = conf; aconf; aconf = aconf->next) {
+        *        if (aconf->status & CONF_OPERATOR || aconf->status & CONF_LOCOP)
+        *            aconf->status = CONF_ILLEGAL;
+        *    }
+        */
+       SVSNOOP = 1;
+    }
+    else {
+       SVSNOOP = 0;
+       sendto_ops ("This server is no longer placed in SVSNOOP mode.");
+    }
+    return 0;
+}
+
+/*
+ * rehash
+ *
+ * Actual REHASH service routine. Called with sig == 0 if it has been called
+ * as a result of an operator issuing this command, else assume it has been
+ * called as a result of the server receiving a HUP signal.
+ */
+int rehash (cptr, sptr, sig)
+     aClient *cptr, *sptr;
+     int sig;
+{
+    aConfItem **tmp = &conf, *tmp2;
+    aClass *cltmp;
+    aClient *acptr;
+    int i;
+    int ret = 0;
+    aEvent *eptr, *eptr2;
+
+    if (sig == 1 && SVSNOOP == 0) {
+       sendto_ops ("Got signal SIGHUP, reloading ircd conf. file");
+#ifdef ULTRIX
+       if (fork () > 0)
+           exit (0);
+       write_pidfile ();
+#endif
+    }
+    for (i = 0; i <= highest_fd; i++)
+       if ((acptr = local[i]) && !IsMe (acptr))
+           /*
+            * Nullify any references from client structures to
+            * this host structure which is about to be freed.
+            * Could always keep reference counts instead of
+            * this....-avalon
+            */
+           acptr->hostp = NULL;
+    while ((tmp2 = *tmp))
+       if (tmp2->clients || tmp2->status & CONF_LISTEN_PORT) {
+           /*
+              ** Configuration entry is still in use by some
+              ** local clients, cannot delete it--mark it so
+              ** that it will be deleted when the last client
+              ** exits...
+            */
+           if (!(tmp2->status & (CONF_LISTEN_PORT | CONF_CLIENT))) {
+               *tmp = tmp2->next;
+               tmp2->next = NULL;
+           }
+           else
+               tmp = &tmp2->next;
+           tmp2->status |= CONF_ILLEGAL;
+       }
+       else {
+           *tmp = tmp2->next;
+       }
+
+    /*
+     * We don't delete the class table, rather mark all entries
+     * for deletion. The table is cleaned up by check_class. - avalon
+     */
+    for (cltmp = NextClass (FirstClass ()); cltmp; cltmp = NextClass (cltmp))
+       MaxLinks (cltmp) = -1;
+
+    if (sig != 2)
+       flush_cache ();
+    (void) initconf (0);
+    close_listeners ();
+
+    /*
+     * flush out deleted I and P lines although still in use.
+     */
+    for (tmp = &conf; (tmp2 = *tmp);)
+       if (!(tmp2->status & CONF_ILLEGAL))
+           tmp = &tmp2->next;
+       else {
+           *tmp = tmp2->next;
+           tmp2->next = NULL;
+           if (!tmp2->clients)
+               free_conf (tmp2);
+       }
+    /* Added to make sure K-lines are checked -- Barubary */
+    check_pings (time (NULL), 1);
+
+    /* Recheck all U-lines -- Barubary */
+    for (i = 0; i < highest_fd; i++)
+       if ((acptr = local[i]) && !IsMe (acptr)) {
+           if (find_conf_host (acptr->from->confs, acptr->name,
+                               CONF_UWORLD) || (acptr->user
+                                                && find_conf_host (acptr->
+                                                                   from->
+                                                                   confs,
+                                                                   acptr->
+                                                                   user->
+                                                                   server,
+                                                                   CONF_UWORLD)))
+               acptr->flags |= FLAGS_ULINE;
+           else
+               acptr->flags &= ~FLAGS_ULINE;
+       }
+    /* Remove temporary z-lines */
+    for (eptr = EventList; eptr; eptr = eptr2) {
+       eptr2 = eptr->next;
+       if (eptr->func && eptr->func == RemoveZLine) {
+           if (eptr->prev)
+               eptr->prev->next = eptr->next;
+           else
+               EventList = eptr->next;
+           if (eptr->next)
+               eptr->next->prev = eptr->prev;
+           free_event (eptr);
+       }
+    }
+    return ret;
+}
+
+/*
+ * openconf
+ *
+ * returns -1 on any error or else the fd opened from which to read the
+ * configuration file from.  This may either be th4 file direct or one end
+ * of a pipe from m4.
+ */
+int openconf ()
+{
+#ifdef M4_PREPROC
+    int pi[2], i;
+
+    if (pipe (pi) == -1)
+       return -1;
+    switch (fork ()) {
+    case -1:
+       return -1;
+    case 0:
+       (void) close (pi[0]);
+       if (pi[1] != 1) {
+           (void) dup2 (pi[1], 1);
+           (void) close (pi[1]);
+       }
+       (void) dup2 (1, 2);
+       for (i = 3; i < MAXCONNECTIONS; i++)
+           if (local[i])
+               (void) close (i);
+       /*
+        * m4 maybe anywhere, use execvp to find it.  Any error
+        * goes out with report_error.  Could be dangerous,
+        * two servers running with the same fd's >:-) -avalon
+        */
+       (void) execlp ("m4", "m4", "ircd.m4", configfile, 0);
+       report_error ("Error executing m4 %s:%s", &me);
+       exit (-1);
+    default:
+       (void) close (pi[1]);
+       return pi[0];
+    }
+#else
+    return open (configfile, O_RDONLY);
+#endif
+}
+extern char *getfield ();
+
+static int oper_access[] = {
+    ~(OFLAG_ADMIN | OFLAG_SADMIN | OFLAG_ZLINE), '*',
+    OFLAG_LOCAL, 'o',
+    OFLAG_GLOBAL, 'O',
+    OFLAG_REHASH, 'r',
+    OFLAG_DIE, 'D',
+    OFLAG_RESTART, 'R',
+    OFLAG_GLOBOP, 'g',
+    OFLAG_LOCOP, 'l',
+    OFLAG_LROUTE, 'c',
+    OFLAG_GROUTE, 'C',
+    OFLAG_LKILL, 'k',
+    OFLAG_GKILL, 'K',
+    OFLAG_KLINE, 'b',
+    OFLAG_UNKLINE, 'B',
+    OFLAG_LNOTICE, 'n',
+    OFLAG_GNOTICE, 'N',
+    OFLAG_ADMIN, 'A',
+    OFLAG_SADMIN, 'a',
+    OFLAG_UMODEC, 'u',
+    OFLAG_UMODEF, 'f',
+    OFLAG_ZLINE, 'z',
+    0, 0
+};
+
+/*
+   ** initconf() 
+   **    Read configuration file.
+   **
+   **    returns -1, if file cannot be opened
+   **             0, if file opened
+ */
+
+#define MAXCONFLINKS 150
+
+int initconf (opt)
+     int opt;
+{
+    static char quotes[9][2] = {
+       {'b', '\b'},
+       {'f', '\f'},
+       {'n', '\n'},
+       {'r', '\r'},
+       {'t', '\t'},
+       {'v', '\v'},
+       {'\\', '\\'},
+       {0, 0}
+    };
+    char *tmp, *s;
+    int fd, i;
+    char line[512], c[80];
+    int ccount = 0, ncount = 0;
+    aConfItem *aconf = NULL;
+
+    Debug ((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
+    if ((fd = openconf ()) == -1) {
+#ifdef M4_PREPROC
+       (void) wait (0);
+#endif
+       return -1;
+    }
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    while ((i = dgets (fd, line, sizeof (line) - 1)) > 0) {
+       line[i] = '\0';
+       if (line[i-2] == '\r') { /* Strip DOS type end of lines */
+            line[i-2] = '\n';
+            line[i-1] = '\0';
+       }
+       if ((tmp = (char *) index (line, '\n')))
+           *tmp = 0;
+       else
+           while (dgets (fd, c, sizeof (c) - 1) > 0)
+               if ((tmp = (char *) index (c, '\n'))) {
+                   *tmp = 0;
+                   break;
+               }
+       /*
+        * Do quoting of characters and # detection.
+        */
+       for (tmp = line; *tmp; tmp++) {
+           if (*tmp == '\\') {
+               for (i = 0; quotes[i][0]; i++)
+                   if (quotes[i][0] == *(tmp + 1)) {
+                       *tmp = quotes[i][1];
+                       break;
+                   }
+               if (!quotes[i][0])
+                   *tmp = *(tmp + 1);
+               if (!*(tmp + 1))
+                   break;
+               else
+                   for (s = tmp; (*s = *(s + 1)); s++);
+           }
+           else if (*tmp == '#')
+               *tmp = '\0';
+       }
+       if (!*line || line[0] == '#' || line[0] == '\n' ||
+           line[0] == ' ' || line[0] == '\t')
+           continue;
+       /* Could we test if it's conf line at all?  -Vesa */
+       if (line[1] != ':') {
+           Debug ((DEBUG_ERROR, "Bad config line: %s", line));
+           continue;
+       }
+       if (aconf)
+           free_conf (aconf);
+       aconf = make_conf ();
+
+       tmp = getfield (line);
+       if (!tmp)
+           continue;
+       switch (*tmp) {
+       case 'A':                 /* Name, e-mail address of administrator */
+           aconf->status = CONF_ADMIN;
+           break;
+       case 'a':                 /* of this server. */
+           aconf->status = CONF_SADMIN;
+           break;
+       case 'C':                 /* Server where I should try to connect */
+       case 'c':                 /* in case of lp failures             */
+           ccount++;
+           aconf->status = CONF_CONNECT_SERVER;
+           break;
+        case 'e':
+       case 'E':                 /* Blocking of DCC transfers -GZ */
+           aconf->status = CONF_DCCBLOCK;
+           break;
+       case 'f':
+       case 'F':
+           aconf->status = CONF_ZTIME;
+           break;
+       case 'G':
+       case 'g':
+           /* General config options */
+           aconf->status = CONF_CONFIG;
+           break;
+       case 'H':                 /* Hub server line */
+       case 'h':
+           aconf->status = CONF_HUB;
+           break;
+       case 'I':                 /* Just plain normal irc client trying  */
+       case 'i':                 /* to connect me */
+           aconf->status = CONF_CLIENT;
+           break;
+       case 'K':                 /* Kill user line on irc.conf           */
+       case 'k':
+           aconf->status = CONF_KILL;
+           break;
+           /* Me. Host field is name used for this host */
+           /* and port number is the number of the port */
+       case 'M':
+       case 'm':
+           aconf->status = CONF_ME;
+           break;
+       case 'N':                 /* Server where I should NOT try to     */
+       case 'n':                 /* connect in case of lp failures     */
+           /* but which tries to connect ME        */
+           ++ncount;
+           aconf->status = CONF_NOCONNECT_SERVER;
+           break;
+       case 'O':
+           aconf->status = CONF_OPERATOR;
+           break;
+           /* Local Operator, (limited privs --SRB)
+            * Not anymore, OperFlag access levels. -Cabal95 */
+       case 'o':
+           aconf->status = CONF_OPERATOR;
+           break;
+       case 'P':                 /* listen port line */
+       case 'p':
+           aconf->status = CONF_LISTEN_PORT;
+           break;
+       case 'Q':                 /* reserved nicks */
+           aconf->status = CONF_QUARANTINED_NICK;
+           break;
+       case 'q':                 /* a server that you don't want in your */
+                                 /* network. USE WITH CAUTION! */
+           aconf->status = CONF_QUARANTINED_SERVER;
+           break;
+       case 'S':                 /* Service. Same semantics as   */
+       case 's':                 /* CONF_OPERATOR                */
+           aconf->status = CONF_SERVICE;
+           break;
+       case 'U':                 /* Underworld server, allowed to hack modes */
+       case 'u':                 /* *Every* server on the net must define the same !!! */
+           aconf->status = CONF_UWORLD;
+           break;
+       case 'Y':
+       case 'y':
+           aconf->status = CONF_CLASS;
+           break;
+       case 'Z':
+       case 'z':
+           aconf->status = CONF_ZAP;
+           break;
+       case 'X':
+       case 'x':
+           aconf->status = CONF_DRPASS;
+           break;
+       default:
+           Debug ((DEBUG_ERROR, "Error in config file: %s", line));
+           break;
+       }
+       if (IsIllegal (aconf))
+           continue;
+
+       for (;;) {                /* Fake loop, that I can use break here --msa */
+           /* Yes I know this could be much cleaner, but I did not
+            * want to put it into its own separate function, but  
+            * I believe the X:should be like this:
+            * X:restartpass:diepass
+            * which leaves this code untouched. This is already indented
+            * enough to justify that...
+            */
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           DupString (aconf->host, tmp);
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           DupString (aconf->passwd, tmp);
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           DupString (aconf->name, tmp);
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           if (aconf->status & CONF_OPS) {
+               int *i, flag;
+               char *m = "*";
+               /*
+                * Now we use access flags to define
+                * what an operator can do with their O.
+                */
+               for (m = (*tmp) ? tmp : m; *m; m++) {
+                   for (i = oper_access; (flag = *i); i += 2)
+                       if (*m == (char) (*(i + 1))) {
+                           aconf->port |= flag;
+                           break;
+                       }
+               }
+               if (!(aconf->port & OFLAG_ISGLOBAL))
+                   aconf->status = CONF_LOCOP;
+           }
+           else
+               aconf->port = atoi (tmp);
+           if ((tmp = getfield (NULL)) == NULL)
+               break;
+           Class (aconf) = find_class (atoi (tmp));
+           break;
+       }
+       /*
+          ** If conf line is a general config, just
+          ** see if we recognize the keyword, and set
+          ** the appropriate global.  We don't use a "standard"
+          ** config link here, because these are things which need
+          ** to be tested SO often that a simple global test
+          ** is much better!  -Aeto
+        */
+       if ((aconf->status & CONF_CONFIG) == CONF_CONFIG) {
+
+           continue;
+       }
+       /* Check if Z:line time    -taz */
+       if (aconf->status & CONF_ZTIME) {
+           if ((aconf->host) && isdigit (*aconf->host))
+               socks_zline_time = (atoi (aconf->host) * 60);
+           continue;
+       }
+       /* Check for bad Z-lines masks as they are *very* dangerous
+          if not correct!!! */
+       if (aconf->status == CONF_ZAP) {
+           char *tempc = aconf->host;
+           if (!tempc) {
+               free_conf (aconf);
+               aconf = NULL;
+               continue;
+           }
+           for (; *tempc; tempc++)
+               if ((*tempc >= '0') && (*tempc <= '9'))
+                   goto zap_safe;
+           free_conf (aconf);
+           aconf = NULL;
+           continue;
+         zap_safe:;
+       }
+       /*
+          ** If conf line is a class definition, create a class entry
+          ** for it and make the conf_line illegal and delete it.
+        */
+       if (aconf->status & CONF_CLASS) {
+           add_class (atoi (aconf->host), atoi (aconf->passwd),
+                      atoi (aconf->name), aconf->port, tmp ? atoi (tmp) : 0);
+           continue;
+       }
+       /*
+          ** associate each conf line with a class by using a pointer
+          ** to the correct class record. -avalon
+        */
+       if (aconf->status & (CONF_CLIENT_MASK | CONF_LISTEN_PORT)) {
+           if (Class (aconf) == 0)
+               Class (aconf) = find_class (0);
+           if (MaxLinks (Class (aconf)) < 0)
+               Class (aconf) = find_class (0);
+       }
+       if (aconf->status & (CONF_LISTEN_PORT | CONF_CLIENT)) {
+           aConfItem *bconf;
+
+           if ((bconf = find_conf_entry (aconf, aconf->status))) {
+               delist_conf (bconf);
+               bconf->status &= ~CONF_ILLEGAL;
+               if (aconf->status == CONF_CLIENT) {
+                   bconf->class->links -= bconf->clients;
+                   bconf->class = aconf->class;
+                   if (bconf->class)
+                       bconf->class->links += bconf->clients;
+               }
+               free_conf (aconf);
+               aconf = bconf;
+           }
+           else if (aconf->host && aconf->status == CONF_LISTEN_PORT)
+               (void) add_listener (aconf);
+       }
+       if (aconf->status & CONF_SERVER_MASK)
+           if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
+               !aconf->host || index (aconf->host, '*') ||
+               index (aconf->host, '?') || !aconf->name)
+               continue;
+
+       if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR))
+           if (!index (aconf->host, '@') && *aconf->host != '/') {
+               char *newhost;
+               int len = 3;    /* *@\0 = 3 */
+
+               len += strlen (aconf->host);
+               newhost = (char *) MyMalloc (len);
+               (void) sprintf (newhost, "*@%s", aconf->host);
+               MyFree (aconf->host);
+               aconf->host = newhost;
+           }
+       if (aconf->status & CONF_SERVER_MASK) {
+           if (BadPtr (aconf->passwd))
+               continue;
+           else if (!(opt & BOOT_QUICK))
+               (void) lookup_confhost (aconf);
+       }
+       /*
+          ** Own port and name cannot be changed after the startup.
+          ** (or could be allowed, but only if all links are closed
+          ** first).
+          ** Configuration info does not override the name and port
+          ** if previously defined. Note, that "info"-field can be
+          ** changed by "/rehash".
+        */
+       if (aconf->status == CONF_ME) {
+           strncpyzt (me.info, aconf->name, sizeof (me.info));
+           if (me.name[0] == '\0' && aconf->host[0])
+               strncpyzt (me.name, aconf->host, sizeof (me.name));
+
+           if (aconf->passwd[0] && (aconf->passwd[0] != '*'))
+               me.ip.s_addr = inet_addr (aconf->passwd);
+           else
+               me.ip.s_addr = INADDR_ANY;
+           if (portnum < 0 && aconf->port >= 0)
+               portnum = aconf->port;
+       }
+       if (aconf->status == CONF_KILL)
+           aconf->tmpconf = KLINE_PERM;
+       (void) collapse (aconf->host);
+       (void) collapse (aconf->name);
+       Debug ((DEBUG_NOTICE,
+               "Read Init: (%d) (%s) (%s) (%s) (%d) (%d)",
+               aconf->status, aconf->host, aconf->passwd,
+               aconf->name, aconf->port, Class (aconf)));
+       aconf->next = conf;
+       conf = aconf;
+       aconf = NULL;
+    }
+    if (aconf)
+       free_conf (aconf);
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    (void) close (fd);
+#ifdef M4_PREPROC
+    (void) wait (0);
+#endif
+    check_class ();
+    nextping = nextconnect = time (NULL);
+    return 0;
+}
+
+/*
+ * lookup_confhost
+ *   Do (start) DNS lookups of all hostnames in the conf line and convert
+ * an IP addresses in a.b.c.d number for to IP#s.
+ */
+static int lookup_confhost (aconf)
+     aConfItem *aconf;
+{
+    char *s;
+    struct hostent *hp;
+    Link ln;
+
+    if (BadPtr (aconf->host) || BadPtr (aconf->name))
+       goto badlookup;
+    if ((s = index (aconf->host, '@')))
+       s++;
+    else
+       s = aconf->host;
+    /*
+       ** Do name lookup now on hostnames given and store the
+       ** ip numbers in conf structure.
+     */
+    if (!isalpha (*s) && !isdigit (*s))
+       goto badlookup;
+
+    /*
+       ** Prepare structure in case we have to wait for a
+       ** reply which we get later and store away.
+     */
+    ln.value.aconf = aconf;
+    ln.flags = ASYNC_CONF;
+
+    if (isdigit (*s))
+       aconf->ipnum.s_addr = inet_addr (s);
+    else if ((hp = gethost_byname (s, &ln)))
+       bcopy (hp->h_addr, (char *) &(aconf->ipnum), sizeof (struct in_addr));
+
+    if (aconf->ipnum.s_addr == -1)
+       goto badlookup;
+    return 0;
+  badlookup:
+    if (aconf->ipnum.s_addr == -1)
+       bzero ((char *) &aconf->ipnum, sizeof (struct in_addr));
+    Debug ((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
+           aconf->host, aconf->name));
+    return -1;
+}
+
+int find_kill (cptr)
+     aClient *cptr;
+{
+    char *host, *name;
+    char ipaddy[HOSTLEN + 1];
+    aConfItem *tmp, *tmpexempt = NULL;
+
+    if (!cptr->user)
+       return 0;
+
+    host = cptr->sockhost;
+    name = cptr->user->username;
+    strncpy (ipaddy, inetntoa ((char *) &(cptr->ip)), HOSTLEN);
+
+    if (strlen (host) > (size_t) HOSTLEN ||
+       (name ? strlen (name) : 0) > (size_t) HOSTLEN)
+       return (0);
+
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       if (tmp->status != CONF_KILL)
+           continue;
+       if (!tmp->host || !tmp->name)
+           continue;
+       if (((match (tmp->host, host) == 0) ||         /* hostname */
+            (match (tmp->host, ipaddy) == 0)) &&      /* ip addy */
+           (!name || match (tmp->name, name) == 0) && /* username */
+           (!tmp->port || (tmp->port == cptr->acpt->port))) {  /* port (still used?) */
+           break; /* KLINE ! */
+       }
+    }
+    if (tmp) { 
+       /* A KLINE was found. Lets see if an exemption for this address exists */
+       for (tmpexempt = conf; tmpexempt; tmpexempt = tmpexempt->next) {
+           if (tmpexempt->status != CONF_KILLEXEMPT)
+               continue;
+           if (!tmpexempt->host || !tmpexempt->name)
+               continue;
+           if (((match (tmpexempt->host, host) == 0) ||         /* hostname */
+                (match (tmpexempt->host, ipaddy) == 0)) &&      /* ip addy */
+               (!name || match (tmpexempt->name, name) == 0) && /* username */
+               (!tmpexempt->port || (tmpexempt->port == cptr->acpt->port))) {  /* port (still used?) */
+               break; /* KLINE exemption ! */
+           }
+        }
+        if (tmp && !tmpexempt) {
+           if (BadPtr (tmp->passwd))
+               sendto_one (cptr,
+                       ":%s %d %s :*** You are not welcome on this server."
+                       "  Email " KLINE_ADDRESS " for more information.",
+                       me.name, ERR_YOUREBANNEDCREEP, cptr->name);
+           else
+#ifdef COMMENT_IS_FILE
+               m_killcomment (cptr, cptr->name, tmp->passwd);
+        }
+#else
+           {
+               if (tmp->tmpconf == KLINE_AKILL)
+                   sendto_one (cptr,
+                           ":%s %d %s :*** %s",
+                           me.name, ERR_YOUREBANNEDCREEP, cptr->name,
+                           tmp->passwd);
+               else
+                   sendto_one (cptr,
+                           ":%s %d %s :*** You are not welcome on this server: "
+                           "%s.  Email " KLINE_ADDRESS
+                           " for more information.", me.name,
+                           ERR_YOUREBANNEDCREEP, cptr->name, tmp->passwd);
+           }
+        }
+#endif /* COMMENT_IS_FILE */
+    }
+    return ((tmp && !tmpexempt) ? -1 : 0);
+}
+
+char *find_zap (aClient * cptr, int dokillmsg)
+{
+    aConfItem *tmp;
+    char *retval = NULL;
+    char ipaddy[HOSTLEN + 1];
+
+    strncpy (ipaddy, inetntoa ((char *) &cptr->ip), HOSTLEN);
+
+    for (tmp = conf; tmp; tmp = tmp->next)
+       if ((tmp->status == CONF_ZAP) && tmp->host
+           && !match (tmp->host, ipaddy)) {
+           retval = (tmp->passwd) ? tmp->passwd : "Reason unspecified";
+           break;
+       }
+    if (dokillmsg && retval)
+       sendto_one (cptr,
+                   ":%s %d %s :*** You are not welcome on this server: %s.",
+                   me.name, ERR_YOUREBANNEDCREEP, cptr->name, retval);
+    if (!dokillmsg && retval) {
+       sprintf (zlinebuf,
+                "ERROR :Closing Link: [%s] (You are not welcome on "
+                "this server: %s.\r\n", inetntoa ((char *) &cptr->ip),
+                retval);
+       retval = zlinebuf;
+    }
+    return retval;
+}
+
+int find_kill_exempt (host, name)
+     char *host, *name;
+{
+    aConfItem *tmp;
+
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       if ((tmp->status == CONF_KILLEXEMPT) && tmp->host && tmp->name &&
+           (match (tmp->host, host) == 0) &&
+           (!name || match (tmp->name, name) == 0))
+           return 1; /* KLINE exempt found! */
+    }
+
+    return 0;
+}
+
+int find_kill_byname (host, name)
+     char *host, *name;
+{
+    aConfItem *tmp;
+
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       if ((tmp->status == CONF_KILL) && tmp->host && tmp->name &&
+           (match (tmp->host, host) == 0) &&
+           (!name || match (tmp->name, name) == 0))
+              return 1;
+    }
+
+    return 0;
+}
+
+/* Find DCC block */
+int find_dccblock (char *file)
+{
+    aConfItem *tmp;
+
+    for (tmp = conf; tmp; tmp = tmp->next) {
+       if ((tmp->status == CONF_DCCBLOCK) && tmp->host
+           && !match (tmp->host, file))
+           return 1;
+    }
+    return 0;
+}
+
+/*
+   **  output the reason for being k lined from a file  - Mmmm
+   ** sptr is server    
+   ** parv is the sender prefix
+   ** filename is the file that is to be output to the K lined client
+ */
+int m_killcomment (sptr, parv, filename)
+     aClient *sptr;
+     char *parv, *filename;
+{
+    int fd;
+    char line[80];
+    char *tmp;
+    struct stat sb;
+    struct tm *tm;
+
+    /*
+     * stop NFS hangs...most systems should be able to open a file in
+     * 3 seconds. -avalon (curtesy of wumpus)
+     */
+    fd = open (filename, O_RDONLY);
+    if (fd == -1) {
+       sendto_one (sptr, err_str (ERR_NOMOTD), me.name, parv);
+       sendto_one (sptr,
+                   ":%s %d %s :*** You are not welcome to this server.",
+                   me.name, ERR_YOUREBANNEDCREEP, parv);
+       return 0;
+    }
+    (void) fstat (fd, &sb);
+    tm = localtime (&sb.st_mtime);
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    while (dgets (fd, line, sizeof (line) - 1) > 0) {
+       if ((tmp = (char *) index (line, '\n')))
+           *tmp = '\0';
+       if ((tmp = (char *) index (line, '\r')))
+           *tmp = '\0';
+       /* sendto_one(sptr,
+          ":%s %d %s : %s.",
+          me.name, ERR_YOUREBANNEDCREEP, parv,line); */
+       sendto_one (sptr, rpl_str (RPL_MOTD), me.name, parv, line);
+    }
+    sendto_one (sptr,
+               ":%s %d %s :*** You are not welcome to this server.",
+               me.name, ERR_YOUREBANNEDCREEP, parv);
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    (void) close (fd);
+    return 0;
+}
+
+/*
+   ** m_rakill;
+   **      parv[0] = sender prefix
+   **      parv[1] = hostmask
+   **      parv[2] = username
+   **      parv[3] = comment
+ */
+int m_rakill (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    if (check_registered (sptr))
+       return 0;
+
+    if (parc < 3) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "RAKILL");
+       return 0;
+    }
+    if (IsServer (cptr) &&
+       find_conf_host (cptr->confs, sptr->name, CONF_UWORLD)) {
+       if (find_kill_byname (parv[1], parv[2])) {
+#ifndef COMMENT_IS_FILE
+           del_temp_conf (CONF_KILL, parv[1], parv[3], parv[2], 0, 0, 1);
+#else
+           del_temp_conf (CONF_KILL, parv[1], NULL, parv[2], 0, 0, 1);
+#endif
+       }
+       if (parv[3])
+           sendto_serv_butone (cptr, ":%s RAKILL %s %s :%s",
+                               parv[0], parv[1], parv[2], parv[3]);
+       else
+           sendto_serv_butone (cptr, ":%s RAKILL %s %s",
+                               parv[0], parv[1], parv[2]);
+       check_pings (time (NULL), 1);
+    }
+    return 0;
+}
+
+/* ** m_akill;
+   **   parv[0] = sender prefix
+   **   parv[1] = hostmask
+   **   parv[2] = username
+   **   parv[3] = comment
+ */
+int m_akill (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    if (check_registered (sptr))
+       return 0;
+
+    if (parc < 3) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "AKILL");
+       return 0;
+    }
+    if (IsServer (cptr) &&
+       find_conf_host (cptr->confs, sptr->name, CONF_UWORLD)) {
+       if (!find_kill_byname (parv[1], parv[2])) {
+
+#ifndef COMMENT_IS_FILE
+           add_temp_conf (CONF_KILL, parv[1], parv[3], parv[2], 0, 0, 2);
+#else
+           add_temp_conf (CONF_KILL, parv[1], NULL, parv[2], 0, 0, 2);
+#endif
+       }
+       if (parv[3])
+           sendto_serv_butone (cptr, ":%s AKILL %s %s :%s", parv[0],
+                               parv[1], parv[2], parv[3]);
+       else
+           sendto_serv_butone (cptr, ":%s AKILL %s %s", parv[0], parv[1],
+                               parv[2]);
+       check_pings (time (NULL), 1);
+    }
+    return 0;
+}
+
+/*
+   ** m_rakillex;
+   **      parv[0] = sender prefix
+   **      parv[1] = hostmask
+   **      parv[2] = username
+   **      parv[3] = comment
+ */
+int m_rakillex (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    if (check_registered (sptr))
+       return 0;
+
+    if (parc < 3) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "RAKILLEX");
+       return 0;
+    }
+    if (IsServer (cptr) &&
+       find_conf_host (cptr->confs, sptr->name, CONF_UWORLD)) {
+       if (find_kill_exempt (parv[1], parv[2])) {
+#ifndef COMMENT_IS_FILE
+           del_temp_conf (CONF_KILLEXEMPT, parv[1], parv[3], parv[2], 0, 0, 1);
+#else
+           del_temp_conf (CONF_KILLEXEMPT, parv[1], NULL, parv[2], 0, 0, 1);
+#endif
+       }
+       if (parv[3])
+           sendto_serv_butone (cptr, ":%s RAKILLEX %s %s :%s",
+                               parv[0], parv[1], parv[2], parv[3]);
+       else
+           sendto_serv_butone (cptr, ":%s RAKILLEX %s %s",
+                               parv[0], parv[1], parv[2]);
+       check_pings (time (NULL), 1);
+    }
+    return 0;
+}
+
+/* ** m_akillex;
+   **   parv[0] = sender prefix
+   **   parv[1] = hostmask
+   **   parv[2] = username
+   **   parv[3] = comment
+ */
+int m_akillex (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    if (check_registered (sptr))
+       return 0;
+
+    if (parc < 3) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "AKILLEX");
+       return 0;
+    }
+    if (IsServer (cptr) &&
+       find_conf_host (cptr->confs, sptr->name, CONF_UWORLD)) {
+       if (!find_kill_exempt (parv[1], parv[2])) {
+
+#ifndef COMMENT_IS_FILE
+           add_temp_conf (CONF_KILLEXEMPT, parv[1], parv[3], parv[2], 0, 0, 2);
+#else
+           add_temp_conf (CONF_KILLEXEMPT, parv[1], NULL, parv[2], 0, 0, 2);
+#endif
+       }
+       if (parv[3])
+           sendto_serv_butone (cptr, ":%s AKILLEX %s %s :%s", parv[0],
+                               parv[1], parv[2], parv[3]);
+       else
+           sendto_serv_butone (cptr, ":%s AKILLEX %s %s", parv[0], parv[1],
+                               parv[2]);
+       check_pings (time (NULL), 1);
+    }
+    return 0;
+}
+
+
+/*    m_sqline
+   **   parv[0] = sender
+   **   parv[1] = nickmask
+   **   parv[2] = reason
+ */
+int m_sqline (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aSqlineItem *asqline;
+
+    if (!IsServer (sptr) || parc < 2)
+       return 0;
+
+    if (parv[2])
+       sendto_serv_butone (cptr, ":%s SQLINE %s :%s", parv[0], parv[1],
+                           parv[2]);
+    else
+       sendto_serv_butone (cptr, ":%s SQLINE %s", parv[0], parv[1]);
+
+    asqline = make_sqline ();
+
+    if (parv[2])
+       DupString (asqline->reason, parv[2]);
+    if (parv[1])
+       DupString (asqline->sqline, parv[1]);
+
+    if (!find_sqline_nick (parv[1])) {
+       asqline->next = sqline;
+       sqline = asqline;
+       asqline = NULL;
+    }
+    if (asqline)
+       free_sqline (asqline);
+    return 0;
+}
+
+/*    m_unsqline
+   **   parv[0] = sender
+   **   parv[1] = nickmask
+ */
+int m_unsqline (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aSqlineItem *asqline;
+
+    if (!IsServer (sptr) || parc < 1)
+       return 0;
+
+    sendto_serv_butone (cptr, ":%s UNSQLINE %s", parv[0], parv[1]);
+
+    if (!(asqline = find_sqline_nick (parv[1])))
+       return -1;
+
+    asqline->status = CONF_ILLEGAL;
+    return 0;
+}
+
+/*
+ *  m_jinx
+ *  parv[0] = sender;
+ *  parv[1] = usermask;
+ *  parv[2] = reason;
+ *
+ * Aug 1999 - Totally rewritten by Zaf
+ */
+int m_jinx (aClient * cptr, aClient * sptr, int parc, char *parv[])
+{
+    aClient *acptr;
+
+    if (!IsServer (sptr))
+       return 0;
+
+    if (parc < 2)
+       return 0;
+    
+    if (parv[2]) {
+       sendto_serv_butone (cptr, ":%s JINX %s :%s", parv[0], parv[1],
+                           parv[2]);
+    }
+    else {
+       sendto_serv_butone (cptr, ":%s JINX %s", parv[0], parv[1]);
+    }
+
+    /* Set jinx */
+    if (!(acptr = find_person (parv[1], NULL))) {
+        if (MyConnect (sptr))
+               sendto_one (sptr, err_str (ERR_NOSUCHNICK),
+                           me.name, parv[0], parv[1]);
+       return 0;
+    }
+    SetJinx (acptr);
+    return 0;
+}
+
+/*      
+ * m_unjinx
+ *  parv[0] = sender;
+ *  parv[1] = usermask;
+ *
+ * Aug 1999 - Totally rewritten by Zaf
+ */
+int m_unjinx (aClient * cptr, aClient * sptr, int parc, char *parv[])
+{
+    aClient *acptr;
+
+    if (!IsULine (cptr, sptr))
+       return 0;
+
+    if (parc < 2)
+       return 0;
+    
+    sendto_serv_butone (cptr, ":%s UNJINX %s", parv[0], parv[1]);
+
+    /* Remove jinx */
+    if (!(acptr = find_person (parv[1], NULL))) {
+       if (MyConnect (sptr))
+           sendto_one (sptr, err_str (ERR_NOSUCHNICK),
+                       me.name, parv[0], parv[1]);
+       return 0;
+       }
+    ClearJinx (acptr);
+    return 0;
+}
+
+
+/*
+   ** m_kline;
+   **   parv[0] = sender prefix
+   **   parv[1] = nickname
+   **   parv[2] = comment or filename
+ */
+int m_kline (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *host, *tmp, *hosttemp;
+    char uhost[80], name[80];
+    int ip1, ip2, ip3, temp;
+    aClient *acptr;
+
+    if (!MyClient (sptr) || !OPCanKline (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if (check_registered (sptr))
+       return 0;
+
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "KLINE");
+       return 0;
+    }
+    if (strlen (parv[1]) > 80) {
+       sendto_one (sptr, "NOTICE %s :This KLINE is too long", parv[0]);
+       return 0;
+    }
+    if (parc > 3) {
+       sendto_one (sptr,
+                   "NOTICE %s :Try \"/quote kline <user@host> :<reason>\" (note the ':')",
+                   parv[0]);
+       return 0;
+    }
+/* This patch allows opers to quote kline by address as well as nick
+ * --Russell
+ */
+    if ((hosttemp = (char *) strchr (parv[1], '@'))) {
+       temp = 0;
+       while (temp <= 20)
+           name[temp++] = 0;
+       strcpy (uhost, ++hosttemp);
+       strncpy (name, parv[1], hosttemp - 1 - parv[1]);
+       if (name[0] == '\0' || uhost[0] == '\0') {
+           Debug ((DEBUG_INFO, "KLINE: Bad field!"));
+           sendto_one (sptr,
+                       "NOTICE %s :If you're going to add a userhost, at LEAST specify both fields",
+                       parv[0]);
+           return 0;
+       }
+       if (!strcmp (uhost, "*") || !(char *) strchr (uhost, '.')) {
+           sendto_one (sptr, "NOTICE %s :You cannot add a KLINE that broad",
+                       parv[0]);
+           return 0;
+       }
+    }
+/* by nick */
+    else {
+       if (!(acptr = find_client (parv[1], NULL))) {
+           if (!(acptr = get_history (parv[1], (long) KILLCHASETIMELIMIT))) {
+               sendto_one (sptr,
+                           "NOTICE %s :Can't find user %s to add KLINE",
+                           parv[0], parv[1]);
+               return 0;
+           }
+       }
+       if (!acptr->user)
+           return 0;
+
+       strcpy (name, acptr->user->username);
+       if (MyClient (acptr))
+           host = acptr->sockhost;
+       else
+           host = acptr->user->host;
+
+       /* Sanity checks */
+
+       if (name == '\0' || host == '\0') {
+           Debug ((DEBUG_INFO, "KLINE: Bad field"));
+           sendto_one (sptr, "NOTICE %s :Bad field!", parv[0]);
+           return 0;
+       }
+       /* Add some wildcards */
+
+
+       strcpy (uhost, host);
+       if (isdigit (host[strlen (host) - 1])) {
+           if (sscanf (host, "%d.%d.%d.%*d", &ip1, &ip2, &ip3))
+               sprintf (uhost, "%d.%d.%d.*", ip1, ip2, ip3);
+       }
+       else if (sscanf (host, "%*[^.].%*[^.].%s", uhost)) {    /* Not really... */
+           tmp = (char *) strchr (host, '.');
+           sprintf (uhost, "*%s", tmp);
+       }
+    }
+
+    sendto_ops ("%s added a temp k:line for %s@%s %s", parv[0], name, uhost,
+               parv[2] ? parv[2] : "");
+    add_temp_conf (CONF_KILL, uhost, parv[2], name, 0, 0, 1);
+    check_pings (time (NULL), 1);
+    return 0;
+}
+
+/*
+ *  m_unkline
+ *    parv[0] = sender prefix
+ *    parv[1] = userhost
+ */
+
+int m_unkline (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+
+    int result, temp;
+    char *hosttemp = parv[1], host[80], name[80];
+
+    if (!MyClient (sptr) || !OPCanUnKline (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if (parc < 2) {
+       sendto_one (sptr, "NOTICE %s :Not enough parameters", parv[0]);
+       return 0;
+    }
+    if ((hosttemp = (char *) strchr (parv[1], '@'))) {
+       temp = 0;
+       while (temp <= 20)
+           name[temp++] = 0;
+       strcpy (host, ++hosttemp);
+       strncpy (name, parv[1], hosttemp - 1 - parv[1]);
+       if (name[0] == '\0' || host[0] == '\0') {
+           Debug ((DEBUG_INFO, "UNKLINE: Bad field"));
+           sendto_one (sptr,
+                       "NOTICE %s : Both user and host fields must be non-null",
+                       parv[0]);
+           return 0;
+       }
+       result = del_temp_conf (CONF_KILL, host, NULL, name, 0, 0, 0);
+       if (result == KLINE_RET_AKILL) {        /* akill - result = 3 */
+           sendto_one (sptr,
+                       "NOTICE %s :You may not remove autokills.  Only U:lined clients may.",
+                       parv[0]);
+           return 0;
+       }
+       if (result == KLINE_RET_PERM) { /* Not a temporary line - result =2 */
+           sendto_one (sptr,
+                       "NOTICE %s :You may not remove permanent K:Lines - talk to the admin",
+                       parv[0]);
+           return 0;
+       }
+       if (result == KLINE_RET_DELOK) {        /* Successful result = 1 */
+           sendto_one (sptr, "NOTICE %s :Temp K:Line %s@%s is now removed.",
+                       parv[0], name, host);
+           sendto_ops ("%s removed temp k:line %s@%s", parv[0], name, host);
+           return 0;
+       }
+       if (result == KLINE_DEL_ERR) {  /* Unsuccessful result = 0 */
+           sendto_one (sptr, "NOTICE %s :Temporary K:Line %s@%s not found",
+                       parv[0], name, host);
+           return 0;
+       }
+    }
+    /* This wasn't here before -- Barubary */
+    check_pings (time (NULL), 1);
+    return 0;
+}
+
+/*
+ *  m_zline                       add a temporary zap line
+ *    parv[0] = sender prefix
+ *    parv[1] = host
+ *    parv[2] = reason
+ */
+
+int m_zline (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char userhost[512 + 2] = "", *in;
+    int uline = 0, i = 0, propo = 0;
+    char *reason, *mask, *server, *person;
+    aClient *acptr;
+
+    reason = mask = server = person = NULL;
+
+    reason = ((parc >= 3) ? parv[parc - 1] : "Reason unspecified");
+    mask = ((parc >= 2) ? parv[parc - 2] : NULL);
+    server = ((parc >= 4) ? parv[parc - 1] : NULL);
+
+    if (parc == 4) {
+       mask = parv[parc - 3];
+       server = parv[parc - 2];
+       reason = parv[parc - 1];
+    }
+    uline = IsULine (cptr, sptr) ? 1 : 0;
+
+    if (!uline && (!MyConnect (sptr) || !IsOper (sptr))) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return -1;
+    }
+    if (uline) {
+       if (parc >= 4 && server) {
+           if (hunt_server (cptr, sptr, ":%s ZLINE %s %s :%s", 2, parc, parv)
+               != HUNTED_ISME)
+               return 0;
+           else;
+       }
+       else
+           propo = 1;
+    }
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "ZLINE");
+       return -1;
+    }
+    /* z-lines don't support user@host format, they only 
+       work with ip addresses and nicks */
+    if ((in = index (parv[1], '@')) && (*(in + 1) != '\0')) {
+       strcpy (userhost, in + 1);
+       in = &userhost[0];
+       while (*in) {
+           if (!isdigit (*in) && !ispunct (*in)) {
+               sendto_one (sptr,
+                           ":%s NOTICE %s :z:lines work only with ip addresses (you cannot specify ident either)",
+                           me.name, sptr->name);
+               return -1;
+           }
+           in++;
+       }
+    }
+    else if (in && !(*(in + 1))) {     /* sheesh not only specifying a ident@, but
+                                          omitting the ip...? */
+       sendto_one (sptr, ":%s NOTICE %s :Hey! z:lines need an ip address...",
+                   me.name, sptr->name);
+       return -1;
+    }
+    else if ((acptr = find_client (parv[1], NULL))) {
+       if (!MyConnect (acptr)) {
+           sendto_one (sptr,
+                       ":%s NOTICE %s :I don't know the IP address of that user...",
+                       me.name, sptr->name);
+           return -1;
+       }
+       strcpy (userhost, inetntoa ((char *) &acptr->ip));
+       person = &acptr->name[0];
+       acptr = NULL;
+    }
+    else {
+       strcpy (userhost, parv[1]);
+       in = &userhost[0];
+       while (*in) {
+           if (!isdigit (*in) && !ispunct (*in)) {
+               sendto_one (sptr,
+                           ":%s NOTICE %s :z:lines work only with ip addresses (you cannot specify ident either)",
+                           me.name, sptr->name);
+               return -1;
+           }
+           in++;
+       }
+    }
+
+    /* this'll protect against z-lining *.* or something */
+    if (advanced_check (userhost, TRUE) == FALSE) {
+       sendto_ops ("Bad z:line mask from %s *@%s [%s]", parv[0], userhost,
+                   reason ? reason : "");
+       if (MyClient (sptr))
+           sendto_one (sptr, ":%s NOTICE %s :*@%s is a bad z:line mask...",
+                       me.name, sptr->name, userhost);
+       return -1;
+    }
+    if (ZLineExists (userhost)) {
+       if (MyClient (sptr))
+           sendto_one (sptr,
+                       ":%s NOTICE %s :*@%s already exists in my zline list.",
+                       me.name, sptr->name, userhost);
+       return -1;
+    }
+    if (uline == 0) {
+       if (person)
+           sendto_ops ("%s added a temp z:line for %s (*@%s) [%s]", parv[0],
+                       person, userhost, reason ? reason : "");
+       else
+           sendto_ops ("%s added a temp z:line *@%s [%s]", parv[0], userhost,
+                       reason ? reason : "");
+       (void) add_temp_conf (CONF_ZAP, userhost, reason, NULL, 0, 0,
+                             KLINE_TEMP);
+    }
+    else {
+       if (person)
+           sendto_ops ("%s z:lined %s (*@%s) on %s [%s]", parv[0], person,
+                       userhost, server ? server : irc_network,
+                       reason ? reason : "");
+       else
+           sendto_ops ("%s z:lined *@%s on %s [%s]", parv[0], userhost,
+                       server ? server : irc_network, reason ? reason : "");
+       (void) add_temp_conf (CONF_ZAP, userhost, reason, NULL, 0, 0,
+                             KLINE_AKILL);
+    }
+
+    /* something's wrong if i'm
+       zapping the command source... */
+    if (find_zap (cptr, 0) || find_zap (sptr, 0)) {
+       sendto_failops_whoare_opers
+           ("z:line error: mask=%s parsed=%s I tried to zap cptr", mask,
+            userhost);
+       sendto_serv_butone (NULL,
+                           ":%s GLOBOPS :z:line error: mask=%s parsed=%s I tried to zap cptr",
+                           me.name, mask, userhost);
+       flush_connections (me.fd);
+       (void) rehash (&me, &me, 0);
+       return -1;
+    }
+    for (i = highest_fd; i > 0; i--) {
+       if (!(acptr = local[i]) || IsLog (acptr) || IsMe (acptr));
+       continue;
+       if (find_zap (acptr, 1)) {
+           if (!IsServer (acptr)) {
+               sendto_one (sptr, ":%s NOTICE %s :*** %s %s",
+                           me.name, sptr->name,
+                           IsPerson (acptr) ? "exiting" : "closing",
+                           acptr->name[0] ? acptr->name : "<unknown>");
+               exit_client (acptr, acptr, acptr, "z-lined");
+           }
+           else {
+               sendto_one (sptr, ":%s NOTICE %s :*** exiting %s",
+                           me.name, sptr->name, acptr->name);
+               sendto_ops ("dropping server %s (z-lined)", acptr->name);
+               sendto_serv_butone (cptr,
+                                   "GNOTICE :dropping server %s (z-lined)",
+                                   acptr->name);
+               exit_client (acptr, acptr, acptr, "z-lined");
+
+           }
+       }
+    }
+
+    if (propo == 1)              /* propo is if a ulined server is propagating a z-line
+                                    this should go after the above check */
+       sendto_serv_butone (cptr, ":%s ZLINE %s :%s", parv[0], parv[1],
+                           reason ? reason : "");
+
+    check_pings (time (NULL), 1);
+    return 0;
+}
+
+
+/*
+ *  m_unzline                        remove a temporary zap line
+ *    parv[0] = sender prefix
+ *    parv[1] = host
+ */
+
+int m_unzline (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char userhost[512 + 2] = "", *in;
+    int result = 0, uline = 0, akill = 0;
+/* Compiler says these are unused
+   aConfItem *aconf, *tmp;
+   aConfItem dummy;
+ */
+    char *mask, *server = NULL;
+
+    uline = IsULine (cptr, sptr) ? 1 : 0;
+
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "UNZLINE");
+       return -1;
+    }
+    if (parc < 3 || !uline) {
+       mask = parv[parc - 1];
+       server = NULL;
+    }
+    else if (parc == 3) {
+       mask = parv[parc - 2];
+       server = parv[parc - 1];
+    }
+    if (!uline && (!MyConnect (sptr) || !IsOper (sptr))) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return -1;
+    }
+    /* before we even check ourselves we need to do the uline checks
+       because we aren't supposed to add a z:line if the message is
+       destined to be passed on... */
+
+    if (uline) {
+       if (parc == 3 && server) {
+           if (hunt_server (cptr, sptr, ":%s UNZLINE %s %s", 2, parc, parv)
+               != HUNTED_ISME)
+               return 0;
+           else;
+       }
+       else
+           sendto_serv_butone (cptr, ":%s UNZLINE %s", parv[0], parv[1]);
+
+    }
+    /* parse the removal mask the same way so an oper can just use
+       the same thing to remove it if they specified *@ or something... */
+    if ((in = index (parv[1], '@'))) {
+       strcpy (userhost, in + 1);
+       in = &userhost[0];
+       while (*in) {
+           if (!isdigit (*in) && !ispunct (*in)) {
+               sendto_one (sptr,
+                           ":%s NOTICE %s :it's not possible to have a z:line that's not an ip addresss...",
+                           me.name, sptr->name);
+               return -1;
+           }
+           in++;
+       }
+    }
+    else {
+       strcpy (userhost, parv[1]);
+       in = &userhost[0];
+       while (*in) {
+           if (!isdigit (*in) && !ispunct (*in)) {
+               sendto_one (sptr,
+                           ":%s NOTICE %s :it's not possible to have a z:line that's not an ip addresss...",
+                           me.name, sptr->name);
+               return -1;
+           }
+           in++;
+       }
+    }
+
+    akill = 0;
+  retry_unzline:
+
+    if (uline == 0) {
+       result = del_temp_conf (CONF_ZAP, userhost, NULL, NULL, 0, 0, akill);
+       if ((result) == KLINE_RET_DELOK) {
+           sendto_one (sptr, ":%s NOTICE %s :temp z:line *@%s removed",
+                       me.name, parv[0], userhost);
+           sendto_ops ("%s removed temp z:line *@%s", parv[0], userhost);
+       }
+       else if (result == KLINE_RET_PERM)
+           sendto_one (sptr,
+                       ":%s NOTICE %s :You may not remove permanent z:lines talk to your admin...",
+                       me.name, sptr->name);
+
+       else if (result == KLINE_RET_AKILL && !(sptr->umodes & UMODE_SADMIN)) {
+           sendto_one (sptr,
+                       ":%s NOTICE %s :You may not remove z:lines placed by services...",
+                       me.name, sptr->name);
+       }
+       else if (result == KLINE_RET_AKILL && !akill) {
+           akill = 1;
+           goto retry_unzline;
+       }
+       else
+           sendto_one (sptr,
+                       ":%s NOTICE %s :Couldn't find/remove zline for *@%s",
+                       me.name, sptr->name, userhost);
+
+    }
+    else {                       /* services did it, services should be able to remove
+                                    both types...;> */
+       if (del_temp_conf (CONF_ZAP, userhost, NULL, NULL, 0, 0, 1) ==
+           KLINE_RET_DELOK
+           || del_temp_conf (CONF_ZAP, userhost, NULL, NULL, 0, 0,
+                             0) == KLINE_RET_DELOK) {
+           if (MyClient (sptr))
+               sendto_one (sptr, "NOTICE %s :temp z:line *@%s removed",
+                           parv[0], userhost);
+           sendto_ops ("%s removed temp z:line *@%s", parv[0], userhost);
+       }
+       else
+           sendto_one (sptr, ":%s NOTICE %s :ERROR Removing z:line", me.name,
+                       sptr->name);
+    }
+    return 0;
+}
+
+
+/* ok, given a mask, our job is to determine
+ * wether or not it's a safe mask to banish...
+ *
+ * userhost= mask to verify
+ * ipstat= TRUE  == it's an ip
+ *         FALSE == it's a hostname
+ *         UNSURE == we need to find out
+ * return value
+ *         TRUE  == mask is ok
+ *         FALSE == mask is not ok
+ *        UNSURE == [unused] something went wrong
+ */
+
+int advanced_check (char *userhost, int ipstat)
+{
+    register int retval = TRUE;
+    char *up = NULL, *p, *thisseg;
+    int numdots = 0, segno = 0, numseg, i = 0;
+    char *ipseg[10 + 2];
+    char safebuffer[512] = ""; /* buffer strtoken() can mess up to its heart's content...;> */
+
+    strcpy (safebuffer, userhost);
+
+#define userhost safebuffer
+#define IP_WILDS_OK(x) ((x)<2? 0 : 1)
+
+    if (ipstat == UNSURE) {
+       ipstat = TRUE;
+       for (; *up; up++) {
+           if (*up == '.')
+               numdots++;
+           if (!isdigit (*up) && !ispunct (*up)) {
+               ipstat = FALSE;
+               continue;
+           }
+       }
+       if (numdots != 3)
+           ipstat = FALSE;
+       if (numdots < 1 || numdots > 9)
+           return (0);
+    }
+    /* fill in the segment set */
+    {
+       int l = 0;
+       for (segno = 0, i = 0, thisseg = strtoken (&p, userhost, ".");
+            thisseg; thisseg = strtoken (&p, NULL, "."), i++) {
+
+           l = strlen (thisseg) + 2;
+           ipseg[segno] = calloc (1, l);
+           strncpy (ipseg[segno++], thisseg, l);
+       }
+    }
+    if (segno < 2 && ipstat == TRUE)
+       retval = FALSE;
+    numseg = segno;
+    if (ipstat == TRUE)
+       for (i = 0; i < numseg; i++) {
+           if (!IP_WILDS_OK (i)
+               && (index (ipseg[i], '*') || index (ipseg[i], '?')))
+               retval = FALSE;
+           MyFree (ipseg[i]);
+       }
+    else {
+       int wildsok = 0;
+
+       for (i = 0; i < numseg; i++) {
+           /* for hosts, let the mask extent all the way to 
+              the second-level domain... */
+           wildsok = 1;
+           if (i == numseg || (i + 1) == numseg)
+               wildsok = 0;
+           if (wildsok == 0
+               && (index (ipseg[i], '*') || index (ipseg[i], '?'))) {
+               retval = FALSE;
+           }
+           MyFree (ipseg[i]);
+       }
+
+
+    }
+
+    return (retval);
+#undef userhost
+#undef IP_WILDS_OK
+
+}
diff --git a/src/s_debug.c b/src/s_debug.c
new file mode 100644 (file)
index 0000000..fe61b13
--- /dev/null
@@ -0,0 +1,421 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_debug.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+/*
+ * Option string.  Must be before #ifdef DEBUGMODE.
+ */
+char serveropts[] = {
+#ifdef CHROOTDIR
+    'c',
+#endif
+#ifdef CMDLINE_CONFIG
+    'C',
+#endif
+#ifdef DEBUGMODE
+    'D',
+#endif
+#ifdef HUB
+    'H',
+#endif
+#ifdef SHOW_INVISIBLE_LUSERS
+    'i',
+#endif
+#ifndef        NO_DEFAULT_INVISIBLE
+    'I',
+#endif
+#ifdef M4_PREPROC
+    'm',
+#endif
+#ifdef IDLE_FROM_MSG
+    'M',
+#endif
+#ifdef CRYPT_OPER_PASSWORD
+    'p',
+#endif
+#ifdef NOSPOOF
+    'n',
+#endif
+#ifdef VALLOC
+    'V',
+#endif
+#ifdef USE_SYSLOG
+    'Y',
+#endif
+#ifdef ZIP_LINKS
+    'z',
+#endif
+    '\0'
+};
+
+#include "numeric.h"
+#include "common.h"
+#include "sys.h"
+#include "whowas.h"
+#include "hash.h"
+#include <sys/file.h>
+#ifdef HPUX
+#include <fcntl.h>
+#endif
+#if !defined(ULTRIX) && !defined(SGI) && !defined(sequent) && \
+    !defined(__convex__)
+#include <sys/param.h>
+#endif
+#ifdef HPUX
+#include <sys/syscall.h>
+#define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b)
+#endif
+#ifdef GETRUSAGE_2
+#ifdef SOL20
+#include <sys/time.h>
+#ifdef RUSAGEH
+#include <sys/rusage.h>
+#endif
+#endif
+#include <sys/resource.h>
+#else
+#ifdef TIMES_2
+#include <sys/times.h>
+#endif
+#endif
+#ifdef PCS
+#include <time.h>
+#endif
+#ifdef HPUX
+#include <unistd.h>
+#ifdef DYNIXPTX
+#include <sys/types.h>
+#include <time.h>
+#endif
+#endif
+#include "h.h"
+
+#ifndef ssize_t
+#define ssize_t unsigned int
+#endif
+
+#ifdef DEBUGMODE
+static char debugbuf[1024];
+
+void debug (level, form, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
+     int level;
+     char *form, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10;
+{
+    int err = errno;
+
+    if ((debuglevel >= 0) && (level <= debuglevel)) {
+       (void) sprintf (debugbuf, form, p1, p2, p3, p4, p5, p6, p7, p8, p9,
+                       p10);
+       if (local[2]) {
+           local[2]->sendM++;
+           local[2]->sendB += strlen (debugbuf);
+       }
+       (void) fprintf (stderr, "%s", debugbuf);
+       (void) fputc ('\n', stderr);
+    }
+    errno = err;
+}
+
+/*
+ * This is part of the STATS replies. There is no offical numeric for this
+ * since this isnt an official command, in much the same way as HASH isnt.
+ * It is also possible that some systems wont support this call or have
+ * different field names for "struct rusage".
+ * -avalon
+ */
+void send_usage (cptr, nick)
+     aClient *cptr;
+     char *nick;
+{
+
+#ifdef GETRUSAGE_2
+    struct rusage rus;
+    time_t secs, rup;
+#ifdef hz
+#define hzz hz
+#else
+#ifdef HZ
+#define hzz HZ
+#else
+    int hzz = 1;
+#ifdef HPUX
+    hzz = (int) sysconf (_SC_CLK_TCK);
+#endif
+#endif
+#endif
+
+    if (getrusage (RUSAGE_SELF, &rus) == -1) {
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__linux__) && !defined(__Darwin__)
+       extern char *sys_errlist[];
+#endif
+       sendto_one (cptr, ":%s NOTICE %s :Getruseage error: %s.",
+                   me.name, nick, sys_errlist[errno]);
+       return;
+    }
+    secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
+    rup = time (NULL) - me.since;
+    if (secs == 0)
+       secs = 1;
+
+    sendto_one (cptr,
+               ":%s %d %s :CPU Secs %d:%d User %d:%d System %d:%d",
+               me.name, RPL_STATSDEBUG, nick, secs / 60, secs % 60,
+               rus.ru_utime.tv_sec / 60, rus.ru_utime.tv_sec % 60,
+               rus.ru_stime.tv_sec / 60, rus.ru_stime.tv_sec % 60);
+    sendto_one (cptr, ":%s %d %s :RSS %d ShMem %d Data %d Stack %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_maxrss,
+               rus.ru_ixrss / (rup * hzz), rus.ru_idrss / (rup * hzz),
+               rus.ru_isrss / (rup * hzz));
+    sendto_one (cptr, ":%s %d %s :Swaps %d Reclaims %d Faults %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_nswap,
+               rus.ru_minflt, rus.ru_majflt);
+    sendto_one (cptr, ":%s %d %s :Block in %d out %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_inblock,
+               rus.ru_oublock);
+    sendto_one (cptr, ":%s %d %s :Msg Rcv %d Send %d", me.name,
+               RPL_STATSDEBUG, nick, rus.ru_msgrcv, rus.ru_msgsnd);
+    sendto_one (cptr, ":%s %d %s :Signals %d Context Vol. %d Invol %d",
+               me.name, RPL_STATSDEBUG, nick, rus.ru_nsignals, rus.ru_nvcsw,
+               rus.ru_nivcsw);
+#else
+#ifdef TIMES_2
+    struct tms tmsbuf;
+    time_t secs, mins;
+    int hzz = 1, ticpermin;
+    int umin, smin, usec, ssec;
+
+#ifdef HPUX
+    hzz = sysconf (_SC_CLK_TCK);
+#endif
+    ticpermin = hzz * 60;
+
+    umin = tmsbuf.tms_utime / ticpermin;
+    usec = (tmsbuf.tms_utime % ticpermin) / (float) hzz;
+    smin = tmsbuf.tms_stime / ticpermin;
+    ssec = (tmsbuf.tms_stime % ticpermin) / (float) hzz;
+    secs = usec + ssec;
+    mins = (secs / 60) + umin + smin;
+    secs %= hzz;
+
+    if (times (&tmsbuf) == -1) {
+       sendto_one (cptr, ":%s %d %s :times(2) error: %s.",
+                   me.name, RPL_STATSDEBUG, nick, strerror (errno));
+       return;
+    }
+    secs = tmsbuf.tms_utime + tmsbuf.tms_stime;
+
+    sendto_one (cptr,
+               ":%s %d %s :CPU Secs %d:%d User %d:%d System %d:%d",
+               me.name, RPL_STATSDEBUG, nick, mins, secs, umin, usec,
+               smin, ssec);
+#endif
+#endif
+    sendto_one (cptr, ":%s %d %s :Reads %d Writes %d",
+               me.name, RPL_STATSDEBUG, nick, readcalls, writecalls);
+    sendto_one (cptr, ":%s %d %s :DBUF alloc %d blocks %d",
+               me.name, RPL_STATSDEBUG, nick, dbufalloc, dbufblocks);
+    sendto_one (cptr,
+               ":%s %d %s :Writes:  <0 %d 0 %d <16 %d <32 %d <64 %d",
+               me.name, RPL_STATSDEBUG, nick,
+               writeb[0], writeb[1], writeb[2], writeb[3], writeb[4]);
+    sendto_one (cptr,
+               ":%s %d %s :<128 %d <256 %d <512 %d <1024 %d >1024 %d",
+               me.name, RPL_STATSDEBUG, nick,
+               writeb[5], writeb[6], writeb[7], writeb[8], writeb[9]);
+    return;
+}
+#endif
+
+void count_memory (cptr, nick)
+     aClient *cptr;
+     char *nick;
+{
+    extern aChannel *channel;
+    extern aClass *classes;
+    extern aConfItem *conf;
+    extern int flinks;
+    extern Link *freelink;
+
+    aClient *acptr;
+    Ban *ban;
+    Link *link;
+    aChannel *chptr;
+    aConfItem *aconf;
+    aClass *cltmp;
+
+    int lc = 0,           /* local clients */
+      ch = 0,     /* channels */
+      lcc = 0,    /* local client conf links */
+      rc = 0,     /* remote clients */
+      us = 0,     /* user structs */
+      chu = 0,    /* channel users */
+      chi = 0,    /* channel invites */
+      chb = 0,    /* channel bans */
+      wwu = 0,    /* whowas users */
+      fl = 0,     /* free links */
+      cl = 0,     /* classes */
+      co = 0;     /* conf lines */
+
+    int usi = 0,   /* users invited */
+      usc = 0,    /* users in channels */
+      aw = 0,     /* aways set */
+      wwa = 0,    /* whowas aways */
+      wlh = 0,    /* watchlist headers */
+      wle = 0;    /* watchlist entries */
+
+    u_long chm = 0,    /* memory used by channels */
+      chbm = 0,           /* memory used by channel bans */
+      lcm = 0,    /* memory used by local clients */
+      rcm = 0,    /* memory used by remote clients */
+      awm = 0,    /* memory used by aways */
+      wwam = 0,           /* whowas away memory used */
+      wwm = 0,    /* whowas array memory used */
+      com = 0,    /* memory used by conf lines */
+      wlhm = 0,           /* watchlist memory used */
+      db = 0,     /* memory used by dbufs */
+      rm = 0,     /* res memory used */
+      totcl = 0, totch = 0, totww = 0, tot = 0;
+
+    count_whowas_memory (&wwu, &wwa, &wwam);
+    count_watch_memory (&wlh, &wlhm);
+    wwm = sizeof (aName) * NICKNAMEHISTORYLENGTH;
+
+    for (acptr = client; acptr; acptr = acptr->next) {
+       if (MyConnect (acptr)) {
+           lc++;
+           for (link = acptr->confs; link; link = link->next)
+               lcc++;
+           wle += acptr->notifies;
+       }
+       else
+           rc++;
+       if (acptr->user) {
+           us++;
+           for (link = acptr->user->invited; link; link = link->next)
+               usi++;
+           for (link = acptr->user->channel; link; link = link->next)
+               usc++;
+           if (acptr->user->away) {
+               aw++;
+               awm += (strlen (acptr->user->away) + 1);
+           }
+       }
+    }
+    lcm = lc * CLIENT_LOCAL_SIZE;
+    rcm = rc * CLIENT_REMOTE_SIZE;
+
+    for (chptr = channel; chptr; chptr = chptr->nextch) {
+       ch++;
+       chm += (strlen (chptr->chname) + sizeof (aChannel));
+       for (link = chptr->members; link; link = link->next)
+           chu++;
+       for (link = chptr->invites; link; link = link->next)
+           chi++;
+       for (ban = chptr->banlist; ban; ban = ban->next) {
+           chb++;
+           chbm += (strlen (ban->banstr) + 1 +
+                    strlen (ban->who) + 1 + sizeof (Ban));
+       }
+    }
+
+    for (aconf = conf; aconf; aconf = aconf->next) {
+       co++;
+       com += aconf->host ? strlen (aconf->host) + 1 : 0;
+       com += aconf->passwd ? strlen (aconf->passwd) + 1 : 0;
+       com += aconf->name ? strlen (aconf->name) + 1 : 0;
+       com += sizeof (aConfItem);
+    }
+
+    for (cltmp = classes; cltmp; cltmp = cltmp->next)
+       cl++;
+
+    sendto_one (cptr, ":%s %d %s :Client Local %d(%d) Remote %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, lc, lcm, rc, rcm);
+    sendto_one (cptr, ":%s %d %s :Users %d(%d) Invites %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, us, us * sizeof (anUser), usi,
+               usi * sizeof (Link));
+    sendto_one (cptr, ":%s %d %s :User channels %d(%d) Aways %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, usc, usc * sizeof (Link),
+               aw, awm);
+    sendto_one (cptr, ":%s %d %s :WATCH headers %d(%d) entries %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, wlh, wlhm,
+               wle, wle * sizeof (Link));
+    sendto_one (cptr, ":%s %d %s :Attached confs %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, lcc, lcc * sizeof (Link));
+
+    totcl = lcm + rcm + us * sizeof (anUser) + usc * sizeof (Link) + awm;
+    totcl += lcc * sizeof (Link) + usi * sizeof (Link) + wlhm;
+    totcl += wle * sizeof (Link);
+
+    sendto_one (cptr, ":%s %d %s :Conflines %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, co, com);
+
+    sendto_one (cptr, ":%s %d %s :Classes %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, cl, cl * sizeof (aClass));
+
+    sendto_one (cptr, ":%s %d %s :Channels %d(%d) Bans %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, ch, chm, chb, chbm);
+    sendto_one (cptr, ":%s %d %s :Channel membrs %d(%d) invite %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, chu, chu * sizeof (Link),
+               chi, chi * sizeof (Link));
+
+    totch = chm + chbm + chu * sizeof (Link) + chi * sizeof (Link);
+
+    sendto_one (cptr, ":%s %d %s :Whowas users %d(%d) away %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, wwu, wwu * sizeof (anUser),
+               wwa, wwam);
+    sendto_one (cptr, ":%s %d %s :Whowas array %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, NICKNAMEHISTORYLENGTH, wwm);
+
+    totww = wwu * sizeof (anUser) + wwam + wwm;
+
+    sendto_one (cptr,
+               ":%s %d %s :Hash: client %d(%d) chan %d(%d) watch %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, HASHSIZE,
+               sizeof (aHashEntry) * HASHSIZE, CHANNELHASHSIZE,
+               sizeof (aHashEntry) * CHANNELHASHSIZE, NOTIFYHASHSIZE,
+               sizeof (aNotify *) * NOTIFYHASHSIZE);
+    db = dbufblocks * sizeof (dbufbuf);
+    sendto_one (cptr, ":%s %d %s :Dbuf blocks %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, dbufblocks, db);
+
+    link = freelink;
+    while ((link = link->next))
+       fl++;
+    fl++;
+    sendto_one (cptr, ":%s %d %s :Link blocks free %d(%d) total %d(%d)",
+               me.name, RPL_STATSDEBUG, nick, fl, fl * sizeof (Link),
+               flinks, flinks * sizeof (Link));
+
+    rm = cres_mem (cptr);
+
+    tot = totww + totch + totcl + com + cl * sizeof (aClass) + db + rm;
+    tot += fl * sizeof (Link);
+    tot += sizeof (aHashEntry) * HASHSIZE;
+    tot += sizeof (aHashEntry) * CHANNELHASHSIZE;
+    tot += sizeof (aNotify *) * NOTIFYHASHSIZE;
+
+    sendto_one (cptr, ":%s %d %s :Total: ww %d ch %d cl %d co %d db %d",
+               me.name, RPL_STATSDEBUG, nick, totww, totch, totcl, com, db);
+    sendto_one (cptr, ":%s %d %s :TOTAL: %d sbrk(0)-etext: %u",
+               me.name, RPL_STATSDEBUG, nick, tot,
+               (u_int) ((char *)sbrk ((size_t) 0) - (char *) sbrk0));
+    return;
+}
diff --git a/src/s_err.c b/src/s_err.c
new file mode 100644 (file)
index 0000000..f72d7f6
--- /dev/null
@@ -0,0 +1,785 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_err.c
+ *   Copyright (C) 1992 Darren Reed
+ *   Copyright (C) 1999 Maarten van den Bosch
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "numeric.h"
+#include "h.h"
+
+typedef struct
+{
+    int num_val;
+    char *num_form;
+}
+Numeric;
+
+static char *prepbuf PROTO ((char *, int, char *));
+static char numbuff[514];
+static char numbers[] = "0123456789";
+
+static Numeric local_replies[] = {
+/* 000 */
+    {0, (char *) NULL},
+/* 001 */
+    {RPL_WELCOME, ":Hello %s, Welcome to the " irc_network " IRC Network!"},
+/* 002 */
+    {RPL_YOURHOST, ":Your host is %s, running version %s"},
+/* 003 */
+    {RPL_CREATED, ":This server was created on %s"},
+/* 004 */
+    {RPL_MYINFO, "%s %s aAcfgiIjknNoOrRstxy bceHiklmnorRstv"},
+/* 005 */
+    {RPL_PROTOCTL, "%s :are available on this server"},
+/* 006 */
+    {RPL_MAP, ":%s%-*s (Users:%5d)  (%2d%%)"},
+/* 007 */
+    {RPL_MAPEND, ":End of /MAP"},
+    {0, (char *) NULL}
+};
+
+static Numeric numeric_errors[] = {
+/* 401 */
+    {ERR_NOSUCHNICK, "%s :No such nickname/channel"},
+/* 402 */
+    {ERR_NOSUCHSERVER, "%s :No such server"},
+/* 403 */
+    {ERR_NOSUCHCHANNEL, "%s :No such channel"},
+/* 404 */
+    {ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel (%s)"},
+/* 405 */
+    {ERR_TOOMANYCHANNELS, "%s :You have joined too many channels"},
+/* 406 */
+    {ERR_WASNOSUCHNICK, "%s :There was no such nickname"},
+/* 407 */
+    {ERR_TOOMANYTARGETS, "%s :Duplicate recipients. No message delivered"},
+/* 408 */
+    {ERR_NOSUCHSERVICE, (char *) NULL},
+/* 409 */
+    {ERR_NOORIGIN, ":No origin specified"},
+/* 410 */
+    {ERR_CANNOTKNOCK, ":Cannot knock on %s (%s)"},
+/* 411 */
+    {ERR_NORECIPIENT, ":No recipient given (%s)"},
+/* 412 */
+    {ERR_NOTEXTTOSEND, ":No text to send"},
+/* 413 */
+    {ERR_NOTOPLEVEL, "%s :No toplevel domain specified"},
+/* 414 */
+    {ERR_WILDTOPLEVEL, "%s :Wildcard in toplevel Domain"},
+/* 415 */
+    {ERR_SERVICESUP,
+     "%s :Denied, This command cannot be executed while services are online."},
+/* 416 */
+    {0, (char *) NULL},
+/* 417 */
+    {0, (char *) NULL},
+/* 418 */
+    {0, (char *) NULL},
+/* 419 */
+    {0, (char *) NULL},
+/* 420 */
+    {0, (char *) NULL},
+/* 421 */
+    {ERR_UNKNOWNCOMMAND, "%s :Unknown command"},
+/* 422 */
+    {ERR_NOMOTD, ":MOTD File is missing"},
+/* 423 */
+    {ERR_NOADMININFO, "%s :No administrative info available"},
+/* 424 */
+    {ERR_FILEERROR, ":File error doing %s on %s"},
+/* 425 */
+    {0, (char *) NULL},
+/* 426 */
+    {0, (char *) NULL},
+/* 427 */
+    {0, (char *) NULL},
+/* 428 */
+    {0, (char *) NULL},
+/* 429 */
+    {0, (char *) NULL},
+/* 430 */
+    {0, (char *) NULL},
+/* 431 */
+    {ERR_NONICKNAMEGIVEN, ":No nickname given"},
+/* 432 */
+    {ERR_ERRONEUSNICKNAME, "%s :Erroneous Nickname: \2%s\2"},
+/* 433 */
+    {ERR_NICKNAMEINUSE,
+     "%s :Nickname is already in use, please choose another."},
+/* 434 */
+    {ERR_SERVICENAMEINUSE, (char *) NULL},
+/* 435 */
+    {ERR_SERVICECONFUSED, (char *) NULL},
+/* 436 */
+    {ERR_NICKCOLLISION, "%s :Nickname collision KILL"},
+/* 437 */
+    {ERR_BANNICKCHANGE, "%s :Cannot change nickname while banned on channel"},
+/* 438 */
+    {ERR_NCHANGETOOFAST,
+     "%s :Nick change too fast. Please wait \2%d\2 seconds"},
+/* 439 */
+    {ERR_TARGETTOOFAST,
+     "%s :Message target change too fast. Please wait \2%d\2 seconds"},
+/* 440 */
+    {ERR_SERVICESDOWN,
+     "%s :Services is currently down. Please wait a few moments, and then try again."},
+/* 441 */
+    {ERR_USERNOTINCHANNEL, "%s %s :They aren't on that channel"},
+/* 442 */
+    {ERR_NOTONCHANNEL, "%s :You're not on that channel"},
+/* 443 */
+    {ERR_USERONCHANNEL, "%s :is already on channel %s"},
+/* 444 */
+    {ERR_NOLOGIN, "%s :User not logged in"},
+/* 445 */
+    {ERR_SUMMONDISABLED, ":SUMMON has been disabled"},
+/* 446 */
+    {ERR_USERSDISABLED, ":USERS has been disabled"},
+/* 447 */
+    {0, (char *) NULL},
+/* 448 */
+    {0, (char *) NULL},
+/* 449 */
+    {0, (char *) NULL},
+/* 450 */
+    {0, (char *) NULL},
+/* 451 */
+    {ERR_NOTREGISTERED, ":You have not registered"},
+/* 452 */
+    {0, (char *) NULL},
+/* 453 */
+    {0, (char *) NULL},
+/* 454 */
+    {0, (char *) NULL},
+/* 455 */
+    {ERR_HOSTILENAME,
+     ":Your username %s contained the invalid character(s) %s and has been changed "
+     "to %s. Please use only the characters 0-9 a-z A-Z _ - or . in your username."
+     "Your username is the part before the @ in your email address."},
+/* 456 */
+    {0, (char *) NULL},
+/* 457 */
+    {0, (char *) NULL},
+/* 458 */
+    {0, (char *) NULL},
+/* 459 */
+    {0, (char *) NULL},
+/* 460 */
+    {0, (char *) NULL},
+/* 461 */
+    {ERR_NEEDMOREPARAMS, "%s :Not enough parameters"},
+/* 462 */
+    {ERR_ALREADYREGISTRED, ":You may not reregister"},
+/* 463 */
+    {ERR_NOPERMFORHOST, ":Your host isn't among the privileged"},
+/* 464 */
+    {ERR_PASSWDMISMATCH, ":Password Incorrect"},
+/* 465 */
+    {ERR_YOUREBANNEDCREEP,
+     ":You are banned from this server.  Mail " KLINE_ADDRESS
+     " for more information"},
+/* 466 */
+    {ERR_YOUWILLBEBANNED, (char *) NULL},
+/* 467 */
+    {ERR_KEYSET, "%s :Channel key already set"},
+/* 468 */
+    {ERR_ONLYSERVERSCANCHANGE, "%s :Only servers can change that mode"},
+/* 469 */
+    {0, (char *) NULL},
+/* 470 */
+    {0, (char *) NULL},
+/* 471 */
+    {ERR_CHANNELISFULL, "%s :Cannot join channel because it is full (+l)"},
+/* 472 */
+    {ERR_UNKNOWNMODE, "%c :is an unknown mode char"},
+/* 473 */
+    {ERR_INVITEONLYCHAN,
+     "%s :Cannot join channel because it is invite only (+i)"},
+/* 474 */
+    {ERR_BANNEDFROMCHAN,
+     "%s :Cannot join channel because you are banned (+b)"},
+/* 475 */
+    {ERR_BADCHANNELKEY,
+     "%s :Cannot join channel as you need a correct key (+k)"},
+/* 476 */
+    {ERR_BADCHANMASK, "%s :Bad Channel Mask"},
+/* 477 */
+    {ERR_NEEDREGGEDNICK,
+     "%s :Your nickname must be registered and identified with NickServ in order to join this channel. For help with registering your nick, please join #help"},
+/* 478 */
+    {ERR_BANLISTFULL, "%s %s :Channel ban/ignore list is full"},
+/* 479 */
+    {ERR_NEEDREGGEDNICKTOMSG, "%s is only accepting messages from registered and identified to nicks. For help with registering your nick, please join #help"},
+/* 480 */
+    {0, (char *) NULL},
+/* 481 */
+    {ERR_NOPRIVILEGES,
+     ":Permission Denied- You do not have the correct IRC Operator privileges"},
+/* 482 */
+    {ERR_CHANOPRIVSNEEDED, "%s :Channel Operator status required."},
+/* 483 */
+    {ERR_CANTKILLSERVER, ":You cant kill a server!"},
+/* 484 */
+    {ERR_ISROOT, "%s %s :Cannot kick a Services Root Administrator"},
+/* 485 */
+    {0, (char *) NULL},
+/* 486 */
+    {0, (char *) NULL},
+/* 487 */
+    {0, (char *) NULL},
+/* 488 */
+    {0, (char *) NULL},
+/* 489 */
+    {0, (char *) NULL},
+/* 490 */
+    {0, (char *) NULL},
+/* 491 */
+    {ERR_NOOPERHOST, ":No O-lines for your host"},
+/* 492 */
+    {ERR_NOSERVICEHOST, (char *) NULL},
+/* 493 */
+    {0, (char *) NULL},
+/* 494 */
+    {0, (char *) NULL},
+/* 495 */
+    {0, (char *) NULL},
+/* 496 */
+    {0, (char *) NULL},
+/* 497 */
+    {0, (char *) NULL},
+/* 498 */
+    {0, (char *) NULL},
+/* 499 */
+    {0, (char *) NULL},
+/* 500 */
+    {0, (char *) NULL},
+/* 501 */
+    {ERR_UMODEUNKNOWNFLAG, ":Unknown MODE flag"},
+/* 502 */
+    {ERR_USERSDONTMATCH, ":Can not change mode for other users"},
+/* 503 */
+    {0, (char *) NULL},
+/* 504 */
+    {0, (char *) NULL},
+/* 505 */
+    {0, (char *) NULL},
+/* 506 */
+    {0, (char *) NULL},
+/* 507 */
+    {0, (char *) NULL},
+/* 508 */
+    {0, (char *) NULL},
+/* 509 */
+    {0, (char *) NULL},
+/* 510 */
+    {0, (char *) NULL},
+/* 511 */
+    {ERR_SILELISTFULL, "%s :Your silence list is full"},
+/* 512 */
+    {ERR_TOOMANYWATCH, "%s :Maximum size for WATCH-list is 128 entries"},
+/* 513 */
+    {ERR_NEEDPONG, ":To connect, type /QUOTE PONG %lX"},
+/* 514 */
+    {0, (char *) NULL},
+/* 515 */
+    {0, (char *) NULL},
+/* 516 */
+    {0, (char *) NULL},
+/* 517 */
+    {0, (char *) NULL},
+/* 518 */
+    {0, (char *) NULL},
+/* 519 */
+    {0, (char *) NULL},
+/* 520 */
+    {0, (char *) NULL},
+/* 521 */
+    {ERR_LISTSYNTAX, "Bad list syntax, type /quote list ? or /raw list ?"},
+    {0, (char *) NULL}
+};
+
+static Numeric numeric_replies[] = {
+/* 300 */
+    {RPL_NONE, (char *) NULL},
+/* 301 */
+    {RPL_AWAY, "%s :%s"},
+/* 302 */
+    {RPL_USERHOST, ":"},
+/* 303 */
+    {RPL_ISON, ":"},
+/* 304 */
+    {RPL_TEXT, (char *) NULL},
+/* 305 */
+    {RPL_UNAWAY, ":You are no longer marked as being away"},
+/* 306 */
+    {RPL_NOWAWAY, ":You have been marked as being away"},
+/* 307 */
+    {0, (char *) NULL},
+/* 308 */
+    {RPL_WHOISADMIN, "%s :is a Server Administrator"},
+/* 309 */
+    {RPL_WHOISSADMIN, "%s :is a Services Operator"},
+/* 310 */
+    {0, (char *) NULL},
+/* 311 */
+    {RPL_WHOISUSER, "%s %s %s * :%s"},
+/* 312 */
+    {RPL_WHOISSERVER, "%s %s :%s"},
+/* 313 */
+    {RPL_WHOISOPERATOR, "%s :is %s"},
+/* 314 */
+    {RPL_WHOWASUSER, "%s %s %s * :%s"},
+/* 315 */
+    {RPL_ENDOFWHO, "%s :End of /WHO list."},
+/* 316 */
+    {RPL_WHOISCHANOP, (char *) NULL},
+/* 317 */
+    {RPL_WHOISIDLE, "%s %ld %ld :second(s) idle, signon time"},
+/* 318 */
+    {RPL_ENDOFWHOIS, "%s :End of /WHOIS list."},
+/* 319 */
+    {RPL_WHOISCHANNELS, "%s :%s"},
+/* 320 */
+    {RPL_WHOISCONNECTION, "%s :Host: %s, Server: %s"},
+/* 321 */
+    {RPL_LISTSTART, "Channel :Users  Name"},
+/* 322 */
+    {RPL_LIST, "%s %d :%s"},
+/* 323 */
+    {RPL_LISTEND, ":End of /LIST"},
+/* 324 */
+    {RPL_CHANNELMODEIS, "%s %s %s"},
+/* 325 */
+    {0, (char *) NULL},
+/* 326 */
+    {0, (char *) NULL},
+/* 327 */
+    {0, (char *) NULL},
+/* 328 */
+    {0, (char *) NULL},
+/* 329 */
+    {RPL_CREATIONTIME, "%s %lu"},
+/* 330 */
+    {0, (char *) NULL},
+/* 331 */
+    {RPL_NOTOPIC, "%s :No topic is set."},
+/* 332 */
+    {RPL_TOPIC, "%s :%s"},
+/* 333 */
+    {RPL_TOPICWHOTIME, "%s %s %lu"},
+/* 334 */
+    {RPL_LISTSYNTAX, ":%s"},
+/* 335 */
+    {0, (char *) NULL},
+/* 336 */
+    {0, (char *) NULL},
+/* 337 */
+    {0, (char *) NULL},
+/* 338 */
+    {0, (char *) NULL},
+/* 339 */
+    {0, (char *) NULL},
+/* 340 */
+    {0, (char *) NULL},
+/* 341 */
+    {RPL_INVITING, "%s %s"},
+/* 342 */
+    {RPL_SUMMONING, "%s :User summoned to irc"},
+/* 343 */
+    {0, (char *) NULL},
+/* 344 */
+    {0, (char *) NULL},
+/* 345 */
+    {0, (char *) NULL},
+/* 346 */
+    {0, (char *) NULL},
+/* 347 */
+    {0, (char *) NULL},
+/* 348 */
+    {0, (char *) NULL},
+/* 349 */
+    {0, (char *) NULL},
+/* 350 */
+    {0, (char *) NULL},
+/* 351 */
+    {RPL_VERSION, "%s.%s %s :%s"},
+/* 352 */
+    {RPL_WHOREPLY, "%s %s %s %s %s %s :%d %s"},
+/* 353 */
+    {RPL_NAMREPLY, "%s"},
+/* 354 */
+    {0, (char *) NULL},
+/* 355 */
+    {0, (char *) NULL},
+/* 356 */
+    {0, (char *) NULL},
+/* 357 */
+    {0, (char *) NULL},
+/* 358 */
+    {0, (char *) NULL},
+/* 359 */
+    {0, (char *) NULL},
+/* 360 */
+    {0, (char *) NULL},
+/* 361 */
+    {RPL_KILLDONE, (char *) NULL},
+/* 362 */
+    {RPL_CLOSING, "%s :Closed. Status = %d"},
+/* 363 */
+    {RPL_CLOSEEND, "%d: Connections Closed"},
+/* 364 */
+    {RPL_LINKS, "%s %s :%d %s"},
+/* 365 */
+    {RPL_ENDOFLINKS, "%s :End of /LINKS list."},
+/* 366 */
+    {RPL_ENDOFNAMES, "%s :End of /NAMES list."},
+/* 367 */
+    {RPL_BANLIST, "%s %s %s %lu"},
+/* 368 */
+    {RPL_ENDOFBANLIST, "%s :End of Channel Ban List"},
+/* 369 */
+    {RPL_ENDOFWHOWAS, "%s :End of WHOWAS"},
+/* 370 */
+    {0, (char *) NULL},
+/* 371 */
+    {RPL_INFO, ":%s"},
+/* 372 */
+    {RPL_MOTD, ":- %s"},
+/* 373 */
+    {RPL_INFOSTART, ":Server INFO"},
+/* 374 */
+    {RPL_ENDOFINFO, ":End of /INFO list."},
+/* 375 */
+    {RPL_MOTDSTART, ":- %s Message of the Day - "},
+/* 376 */
+    {RPL_ENDOFMOTD, ":End of /MOTD command."},
+/* 377 */
+    {0, (char *) NULL},
+/* 378 */
+    {RPL_WHOISHOST, "%s %s :Real nick@host"},
+/* 379 */
+    {RPL_EXBANLIST, "%s %s %s %lu"},
+/* 380 */
+    {RPL_EXBANLISTEND, "%s :End of Channel Exception Ban List"},
+/* 381 */
+    {RPL_YOUREOPER, ":You are now an IRC Operator"},
+/* 382 */
+    {RPL_REHASHING, "%s :Rehashing"},
+/* 383 */
+    {RPL_YOURESERVICE, (char *) NULL},
+/* 384 */
+    {RPL_MYPORTIS, "%d :Port to local server is\r\n"},
+/* 385 */
+    {RPL_NOTOPERANYMORE, (char *) NULL},
+/* 386 */
+    {RPL_HOSTRESTRICTLIST, "%s %s %s %lu"},
+/* 387 */
+    {RPL_HOSTRESTRICTLISTEND, "%s :End of Channel Host Restrict List"},
+/* 388 */
+    {0, (char *) NULL},
+/* 389 */
+    {0, (char *) NULL},
+/* 390 */
+    {0, (char *) NULL},
+/* 391 */
+    {RPL_TIME, "%s :%s"},
+/* 392 */
+    {0, (char *) NULL},
+/* 393 */
+    {0, (char *) NULL},
+/* 394 */
+    {0, (char *) NULL},
+/* 395 */
+    {0, (char *) NULL},
+/* 396 */
+    {0, (char *) NULL},
+/* 397 */
+    {0, (char *) NULL},
+/* 398 */
+    {0, (char *) NULL},
+/* 399 */
+    {0, (char *) NULL},
+/* 400 */
+    {RPL_TRACELINK, "Link %s%s %s %s"},
+/* 201 */
+    {RPL_TRACECONNECTING, "Attempt %d %s"},
+/* 202 */
+    {RPL_TRACEHANDSHAKE, "Handshaking %d %s"},
+/* 203 */
+    {RPL_TRACEUNKNOWN, "???? %d %s"},
+/* 204 */
+    {RPL_TRACEOPERATOR, "Operator %d %s %ld"},
+/* 205 */
+    {RPL_TRACEUSER, "User %d %s %ld"},
+/* 206 */
+    {RPL_TRACESERVER, "Server %d %dS %dC %s %s!%s@%s %ld"},
+/* 207 */
+    {RPL_TRACESERVICE, "Service %d %s"},
+/* 208 */
+    {RPL_TRACENEWTYPE, "<newtype> 0 %s"},
+/* 209 */
+    {RPL_TRACECLASS, "Class %d %d"},
+/* 210 */
+    {0, (char *) NULL},
+/* 211 */
+    {RPL_STATSLINKINFO, (char *) NULL},
+#ifdef DEBUGMODE
+/* 212 */
+    {RPL_STATSCOMMANDS, "%s %u %u %u %u %u %u"},
+#else
+/* 212 */
+    {RPL_STATSCOMMANDS, "%s %u %u"},
+#endif
+/* 213 */
+    {RPL_STATSCLINE, "%c %s * %s %d %d"},
+/* 214 */
+    {RPL_STATSNLINE, "%c %s * %s %d %d"},
+/* 215 */
+    {RPL_STATSILINE, "%c %s * %s %d %d"},
+/* 216 */
+    {RPL_STATSKLINE, "%c %s %s %s %d %d"},
+/* 217 */
+    {RPL_STATSQLINE, "%c %s %s %s %d %d"},
+/* 218 */
+    {RPL_STATSYLINE, "%c %d %d %d %d %ld"},
+/* 219 */
+    {RPL_ENDOFSTATS, "%c :End of /STATS report"},
+/* 220 */
+    {RPL_STATSBLINE, "%c %s"},
+/* 221 */
+    {RPL_UMODEIS, "%s"},
+/* 222 */
+    {RPL_SQLINE_NICK, "%s :%s"},
+/* 223 */
+    {RPL_STATSFLINE, "%c %s * * * *"},
+/* 224 */
+    {0, (char *) NULL},
+/* 225 */
+    {0, (char *) NULL},
+/* 226 */
+    {0, (char *) NULL},
+/* 227 */
+    {0, (char *) NULL},
+/* 228 */
+    {0, (char *) NULL},
+/* 229 */
+    {0, (char *) NULL},
+/* 230 */
+    {0, (char *) NULL},
+/* 231 */
+    {RPL_SERVICEINFO, (char *) NULL},
+/* 232 */
+    {RPL_ENDOFSERVICES, (char *) NULL},
+/* 233 */
+    {RPL_SERVICE, (char *) NULL},
+/* 234 */
+    {RPL_SERVLIST, (char *) NULL},
+/* 235 */
+    {RPL_SERVLISTEND, (char *) NULL},
+/* 236 */
+    {RPL_JINX, "%s :%s"},
+    {RPL_STATSJINX, "J %s :%s"},
+    {0, (char *) NULL},
+    {0, (char *) NULL},
+    {0, (char *) NULL},
+/* 241 */
+    {RPL_STATSLLINE, "%c %s * %s %d %d"},
+/* 242 */
+    {RPL_STATSUPTIME, ":Server Up \2%d\2 day(s), %d:%02d:%02d"},
+/* 243 */
+    {RPL_STATSOLINE, "%c %s * %s %u %d"},
+/* 244 */
+    {RPL_STATSHLINE, "%c %s * %s %d %d"},
+/* 245 */
+    {RPL_STATSSLINE, "%c %s * %s %d %d"},
+    {0, (char *) NULL},
+/* 247 */
+    {RPL_STATSXLINE, "X %s %d"},
+/* 248 */
+    {RPL_STATSULINE, "%c %s * %s %d %d"},
+    {0, (char *) NULL},
+/* 250 */
+    {RPL_STATSCONN, ":Highest connection count: %d (%d client(s))"},
+/* 251 */
+    {RPL_LUSERCLIENT,
+     ":There are %d user(s) and %d invisible on %d server(s)"},
+/* 252 */
+    {RPL_LUSEROP, "%d :operator(s) online"},
+/* 253 */
+    {RPL_LUSERUNKNOWN, "%d :unknown connection(s)"},
+/* 254 */
+    {RPL_LUSERCHANNELS, "%d :channel(s) formed"},
+/* 255 */
+    {RPL_LUSERME, ":I have %d client(s) and %d server(s)"},
+/* 256 */
+    {RPL_ADMINME, ":Administrative info about %s"},
+/* 257 */
+    {RPL_ADMINLOC1, ":%s"},
+/* 258 */
+    {RPL_ADMINLOC2, ":%s"},
+/* 259 */
+    {RPL_ADMINEMAIL, ":%s"},
+    {0, (char *) NULL},
+/* 261 */
+    {RPL_TRACELOG, "File %s %d"},
+    {0, (char *) NULL},
+    {0, (char *) NULL},
+    {0, (char *) NULL},
+/* 265 */
+    {RPL_LOCALUSERS, ":Current local users: %d  Max: %d"},
+/* 266 */
+    {RPL_GLOBALUSERS, ":Current global users: %d  Max: %d"},
+    {0, (char *) NULL},
+    {0, (char *) NULL},
+    {0, (char *) NULL},
+    {0, (char *) NULL},
+/* 271 */
+    {RPL_SILELIST, "%s %s"},
+/* 272 */
+    {RPL_ENDOFSILELIST, ":End of Silence List"},
+    {0, (char *) NULL},
+    {0, (char *) NULL},
+/* 275 */
+    {RPL_STATSDLINE, "%c %s %s"},
+    {0, (char *) NULL}
+};
+
+/*
+ * NOTE: Unlike the others, this one goes strait through, 600-799
+ */
+static Numeric numeric_replies2[] = {
+/* 600 */
+    {RPL_LOGON, "%s %s %s %d :logged online"},
+/* 601 */
+    {RPL_LOGOFF, "%s %s %s %d :logged offline"},
+/* 602 */
+    {RPL_WATCHOFF, "%s %s %s %d :stopped watching"},
+/* 603 */
+    {RPL_WATCHSTAT, ":You have %d and are on %d WATCH entries"},
+/* 604 */
+    {RPL_NOWON, "%s %s %s %d :is online"},
+/* 605 */
+    {RPL_NOWOFF, "%s %s %s %d :is offline"},
+/* 606 */
+    {RPL_WATCHLIST, ":%s"},
+/* 607 */
+    {RPL_ENDOFWATCHLIST, ":End of WATCH %c"},
+/* 608 */
+    {RPL_WATCHCLEAR, ":Your WATCH list is now empty"},
+/* 609 */
+    {RPL_OMOTDSTART, ":%s IRC Operator MOTD"},
+/* 610 */
+    {RPL_OMOTD, ":- %s"},
+/* 611 */
+    {RPL_ENDOFOMOTD, ":End of IRC Operator MOTD."},
+/* 612 */
+    {RPL_WHOWASIP, ":%s was using IP %s."},
+/* 613 */
+    {RPL_STATSGZLINE, "%c %s %s %s %i %i %s"},
+/* 614 */
+    {RPL_WHOISMODES, ":%s (%s) is using modes: %s"},
+/* 615 */
+    {RPL_MAPMORE, ":%s%-*s --> *more*"},
+/* 616 */
+    {RPL_POLICY,
+     ":Welcome to the Serenity-IRC network \2%s\2! Using Serenity-IRC constitutes agreement with our Acceptable Use Policy. You may view our policy at http://www.serenity-irc.net/aup"},
+/* 617 */
+    {ERR_HTCTOOFAST,
+     ":You are sending too fast. Please wait %i seconds before sending new commands."},
+/* 618 */
+    {ERR_NEEDIDTODCC, ":Your DCC command to %s was blocked. In order to prevent abuse, both the initiating and the accepting party of any DCC related commands must have a registered nickname and be identified to that nickname."},
+    {0, (char *) NULL}
+};
+
+char *err_str (numeric)
+     int numeric;
+{
+    Numeric *nptr;
+    int num = numeric;
+
+    num -= numeric_errors[0].num_val;
+    if (num < 0 || num > ERR_NEEDPONG)
+       (void) sprintf (numbuff,
+                       ":%%s %d %%s :INTERNAL ERROR: BAD NUMERIC! %d",
+                       numeric, num);
+    else {
+       nptr = &numeric_errors[num];
+       if (!nptr->num_form || !nptr->num_val)
+           (void) sprintf (numbuff,
+                           ":%%s %d %%s :NO ERROR FOR NUMERIC ERROR %d",
+                           numeric, num);
+       else
+           (void) prepbuf (numbuff, nptr->num_val, nptr->num_form);
+    }
+    return numbuff;
+}
+
+
+char *rpl_str (numeric)
+     int numeric;
+{
+    Numeric *nptr;
+    int num = numeric;
+
+    if (num > 99)
+       num -= (num > 300) ? 300 : 100;
+
+    if ((num < 0 || num > 200) && (num < 300 || num > 499))
+       (void) sprintf (numbuff,
+                       ":%%s %d %%s :INTERNAL REPLY ERROR: BAD NUMERIC! %d",
+                       numeric, num);
+    else {
+       if (numeric > 599) {
+           num -= 300;
+           nptr = &numeric_replies2[num];
+       }
+       else if (numeric > 99)
+           nptr = &numeric_replies[num];
+       else
+           nptr = &local_replies[num];
+       Debug ((DEBUG_NUM, "rpl_str: numeric %d num %d nptr %x %d %x",
+               numeric, num, nptr, nptr->num_val, nptr->num_form));
+       if (!nptr->num_form || !nptr->num_val)
+           (void) sprintf (numbuff,
+                           ":%%s %d %%s :NO REPLY FOR NUMERIC ERROR %d",
+                           numeric, num);
+       else
+           (void) prepbuf (numbuff, nptr->num_val, nptr->num_form);
+    }
+    return numbuff;
+}
+
+static char *prepbuf (buffer, num, tail)
+     char *buffer;
+     int num;
+     char *tail;
+{
+    char *s;
+
+    (void) strcpy (buffer, ":%s ");
+    s = buffer + 4;
+
+    *s++ = numbers[num / 100];
+    num %= 100;
+    *s++ = numbers[num / 10];
+    *s++ = numbers[num % 10];
+    (void) strcpy (s, " %s ");
+    (void) strcpy (s + 4, tail);
+    return buffer;
+}
diff --git a/src/s_misc.c b/src/s_misc.c
new file mode 100644 (file)
index 0000000..a82273e
--- /dev/null
@@ -0,0 +1,938 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_misc.c (formerly ircd/date.c)
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/time.h>
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "userload.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#if !defined(ULTRIX) && !defined(SGI) && !defined(sequent) && \
+    !defined(__convex__)
+#include <sys/param.h>
+#endif
+#if defined(PCS) || defined(AIX) || defined(SVR3)
+#include <time.h>
+#endif
+#ifdef HPUX
+#include <unistd.h>
+#endif
+#ifdef DYNIXPTX
+#include <sys/types.h>
+#include <time.h>
+#endif
+#include "h.h"
+
+#define        _1MEG   (1024.0)
+#define        _1GIG   (1024.0*1024.0)
+#define        _1TER   (1024.0*1024.0*1024.0)
+#define        _GMKs(x)        ( (x > _1TER) ? "Terabytes" : ((x > _1GIG) ? "Gigabytes" : \
+                       ((x > _1MEG) ? "Megabytes" : "Kilobytes")))
+#define _GMKv(x)        ( (x > _1TER) ? (float)((float)x/(float)_1TER) : (((float)x > (float)_1GIG) ? \
+                        (float)((float)x/(float)_1GIG) : ((x > _1MEG) ? (float)((float)x/(float)_1MEG) : (float)x)))
+aEvent *EventList = NULL;
+
+static void exit_one_client PROTO ((aClient *, aClient *, aClient *, char *));
+static void exit_one_client_in_split
+PROTO ((aClient *, aClient *, aClient *, char *));
+
+static char *months[] = {
+    "January", "February", "March", "April",
+    "May", "June", "July", "August",
+    "September", "October", "November", "December"
+};
+
+static char *weekdays[] = {
+    "Sunday", "Monday", "Tuesday", "Wednesday",
+    "Thursday", "Friday", "Saturday"
+};
+
+/*
+ * stats stuff
+ */
+struct stats ircst, *ircstp = &ircst;
+
+char *date (clock)
+     time_t clock;
+{
+    static char buf[80], plus;
+    struct tm *lt, *gm;
+    struct tm gmbuf;
+    int minswest;
+
+    if (!clock)
+       time (&clock);
+    gm = gmtime (&clock);
+    bcopy ((char *) gm, (char *) &gmbuf, sizeof (gmbuf));
+    gm = &gmbuf;
+    lt = localtime (&clock);
+
+    if (lt->tm_yday == gm->tm_yday)
+       minswest =
+           (gm->tm_hour - lt->tm_hour) * 60 + (gm->tm_min - lt->tm_min);
+    else if (lt->tm_yday > gm->tm_yday)
+       minswest = (gm->tm_hour - (lt->tm_hour + 24)) * 60;
+    else
+       minswest = ((gm->tm_hour + 24) - lt->tm_hour) * 60;
+
+    plus = (minswest > 0) ? '-' : '+';
+    if (minswest < 0)
+       minswest = -minswest;
+
+    (void) sprintf (buf, "%s %s %d 19%02d -- %02d:%02d %c%02d:%02d",
+                   weekdays[lt->tm_wday], months[lt->tm_mon], lt->tm_mday,
+                   lt->tm_year, lt->tm_hour, lt->tm_min,
+                   plus, minswest / 60, minswest % 60);
+
+    return buf;
+}
+
+/*
+ *  Fixes a string so that the first white space found becomes an end of
+ * string marker (`\-`).  returns the 'fixed' string or "*" if the string
+ * was NULL length or a NULL pointer.
+ */
+char *check_string (s)
+     char *s;
+{
+    static char star[2] = "*";
+    char *str = s;
+
+    if (BadPtr (s))
+       return star;
+
+    for (; *s; s++)
+       if (isspace (*s)) {
+           *s = '\0';
+           break;
+       }
+    return (BadPtr (str)) ? star : str;
+}
+
+/*
+ * create a string of form "foo!bar@fubar" given foo, bar and fubar
+ * as the parameters.  If NULL, they become "*".
+ */
+char *make_nick_user_host (nick, name, host)
+     char *nick, *name, *host;
+{
+    static char namebuf[NICKLEN + USERLEN + HOSTLEN + 6];
+    char *s = namebuf;
+
+    bzero (namebuf, sizeof (namebuf));
+    nick = check_string (nick);
+    strncpyzt (namebuf, nick, NICKLEN + 1);
+    s += strlen (s);
+    *s++ = '!';
+    name = check_string (name);
+    strncpyzt (s, name, USERLEN + 1);
+    s += strlen (s);
+    *s++ = '@';
+    host = check_string (host);
+    strncpyzt (s, host, HOSTLEN + 1);
+    s += strlen (s);
+    *s = '\0';
+    return (namebuf);
+}
+
+/**
+ ** myctime()
+ **   This is like standard ctime()-function, but it zaps away
+ **   the newline from the end of that string. Also, it takes
+ **   the time value as parameter, instead of pointer to it.
+ **   Note that it is necessary to copy the string to alternate
+ **   buffer (who knows how ctime() implements it, maybe it statically
+ **   has newline there and never 'refreshes' it -- zapping that
+ **   might break things in other places...)
+ **
+ **/
+
+char *myctime (value)
+     time_t value;
+{
+    static char buf[28];
+    char *p;
+
+    (void) strcpy (buf, ctime (&value));
+    if ((p = (char *) index (buf, '\n')) != NULL)
+       *p = '\0';
+
+    return buf;
+}
+
+/*
+   ** check_registered_user is used to cancel message, if the
+   ** originator is a server or not registered yet. In other
+   ** words, passing this test, *MUST* guarantee that the
+   ** sptr->user exists (not checked after this--let there
+   ** be coredumps to catch bugs... this is intentional --msa ;)
+   **
+   ** There is this nagging feeling... should this NOT_REGISTERED
+   ** error really be sent to remote users? This happening means
+   ** that remote servers have this user registered, althout this
+   ** one has it not... Not really users fault... Perhaps this
+   ** error message should be restricted to local clients and some
+   ** other thing generated for remotes...
+ */
+int check_registered_user (sptr)
+     aClient *sptr;
+{
+    if (!IsRegisteredUser (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOTREGISTERED), me.name, "*");
+       return -1;
+    }
+    return 0;
+}
+
+/*
+   ** check_registered user cancels message, if 'x' is not
+   ** registered (e.g. we don't know yet whether a server
+   ** or user)
+ */
+int check_registered (sptr)
+     aClient *sptr;
+{
+    if (!IsRegistered (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOTREGISTERED), me.name, "*");
+       return -1;
+    }
+    return 0;
+}
+
+/*
+   ** get_client_name
+   **      Return the name of the client for various tracking and
+   **      admin purposes. The main purpose of this function is to
+   **      return the "socket host" name of the client, if that
+   **   differs from the advertised name (other than case).
+   **   But, this can be used to any client structure.
+   **
+   **   Returns:
+   **     "name[user@ip#.port]" if 'showip' is true;
+   **     "name[sockethost]", if name and sockhost are different and
+   **     showip is false; else
+   **     "name".
+   **
+   ** NOTE 1:
+   **   Watch out the allocation of "nbuf", if either sptr->name
+   **   or sptr->sockhost gets changed into pointers instead of
+   **   directly allocated within the structure...
+   **
+   ** NOTE 2:
+   **   Function return either a pointer to the structure (sptr) or
+   **   to internal buffer (nbuf). *NEVER* use the returned pointer
+   **   to modify what it points!!!
+ */
+char *get_client_name (sptr, showip)
+     aClient *sptr;
+     int showip;
+{
+    static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+
+    if (MyConnect (sptr)) {
+       if (IsUnixSocket (sptr)) {
+           if (showip)
+               (void) sprintf (nbuf, "%s[%s]", sptr->name, sptr->sockhost);
+           else
+               (void) sprintf (nbuf, "%s[%s]", sptr->name, me.sockhost);
+       }
+       else {
+           if (showip)
+               (void) sprintf (nbuf, "%s[%s@%s.%u]",
+                               sptr->name,
+                               sptr->username,
+                               inetntoa ((char *) &sptr->ip),
+                               (unsigned int) sptr->port);
+           else {
+               if (mycmp (sptr->name, sptr->sockhost))
+                   (void) sprintf (nbuf, "%s[%s]", sptr->name,
+                                   sptr->sockhost);
+               else
+                   return sptr->name;
+           }
+       }
+       return nbuf;
+    }
+    return sptr->name;
+}
+
+char *get_client_host (cptr)
+     aClient *cptr;
+{
+    static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+
+    if (!MyConnect (cptr))
+       return cptr->name;
+    if (!cptr->hostp)
+       return get_client_name (cptr, FALSE);
+    if (IsUnixSocket (cptr))
+       (void) sprintf (nbuf, "%s[%s]", cptr->name, me.name);
+    else
+       (void) sprintf (nbuf, "%s[%-.*s@%-.*s]",
+                       cptr->name, USERLEN, cptr->username,
+                       HOSTLEN, cptr->hostp->h_name);
+    return nbuf;
+}
+
+/*
+ * Form sockhost such that if the host is of form user@host, only the host
+ * portion is copied.
+ */
+void get_sockhost (cptr, host)
+     aClient *cptr;
+     char *host;
+{
+    char *s;
+    if ((s = (char *) index (host, '@')))
+       s++;
+    else
+       s = host;
+    strncpyzt (cptr->sockhost, s, sizeof (cptr->sockhost));
+}
+
+/*
+ * Return wildcard name of my server name according to given config entry
+ * --Jto
+ */
+char *my_name_for_link (name, aconf)
+     char *name;
+     aConfItem *aconf;
+{
+    static char namebuf[HOSTLEN];
+    register int count = aconf->port;
+    register char *start = name;
+
+    if (count <= 0 || count > 5)
+       return start;
+
+    while (count-- && name) {
+       name++;
+       name = (char *) index (name, '.');
+    }
+    if (!name)
+       return start;
+
+    namebuf[0] = '*';
+    (void) strncpy (&namebuf[1], name, HOSTLEN - 1);
+    namebuf[HOSTLEN - 1] = '\0';
+
+    return namebuf;
+}
+
+/*
+   ** exit_client
+   **   This is old "m_bye". Name  changed, because this is not a
+   **   protocol function, but a general server utility function.
+   **
+   **   This function exits a client of *any* type (user, server, etc)
+   **   from this server. Also, this generates all necessary prototol
+   **   messages that this exit may cause.
+   **
+   **   1) If the client is a local client, then this implicitly
+   **   exits all other clients depending on this connection (e.g.
+   **   remote clients having 'from'-field that points to this.
+   **
+   **   2) If the client is a remote client, then only this is exited.
+   **
+   ** For convenience, this function returns a suitable value for
+   ** m_funtion return value:
+   **
+   **   FLUSH_BUFFER    if (cptr == sptr)
+   **   0       if (cptr != sptr)
+ */
+int exit_client (cptr, sptr, from, comment)
+     aClient *cptr;    /*
+                          ** The local client originating the exit or NULL, if this
+                          ** exit is generated by this server for internal reasons.
+                          ** This will not get any of the generated messages.
+                        */
+     aClient *sptr;    /* Client exiting */
+     aClient *from;    /* Client firing off this Exit, never NULL! */
+     char *comment;    /* Reason for the exit */
+{
+    aClient *acptr;
+    aClient *next;
+#ifdef FNAME_USERLOG
+    time_t on_for;
+#endif
+    static char comment1[HOSTLEN + HOSTLEN + 2];
+    static int recurse = 0;
+
+
+    if (MyConnect (sptr)) {
+       sptr->flags |= FLAGS_CLOSING;
+       if (IsPerson (sptr))
+           sendto_umode (UMODE_OPER | UMODE_CLIENT,
+                         "*** Notice -- Client exiting: %s (%s@%s) [%s]",
+                         sptr->name, sptr->user->username, sptr->user->host,
+                         comment);
+       current_load_data.conn_count--;
+       if (IsPerson (sptr)) {
+           char mydom_mask[HOSTLEN + 1];
+           mydom_mask[0] = '*';
+           strncpy (&mydom_mask[1], DOMAINNAME, HOSTLEN - 1);
+           current_load_data.client_count--;
+           if (match (mydom_mask, sptr->sockhost) == 0)
+               current_load_data.local_count--;
+           /* Clean out list and watch structures -Donwulff */
+           hash_del_notify_list (sptr);
+           if (sptr->lopt) {
+               free_str_list (sptr->lopt->yeslist);
+               free_str_list (sptr->lopt->nolist);
+               MyFree (sptr->lopt);
+           }
+       }
+       update_load ();
+#ifdef FNAME_USERLOG
+       on_for = time (NULL) - sptr->firsttime;
+#if defined(USE_SYSLOG) && defined(SYSLOG_USERS)
+       if (IsPerson (sptr))
+           syslog (LOG_NOTICE, "%s (%3d:%02d:%02d): %s@%s (%s)\n",
+                   myctime (sptr->firsttime),
+                   on_for / 3600, (on_for % 3600) / 60,
+                   on_for % 60, sptr->user->username, sptr->sockhost,
+                   sptr->name);
+#else
+       {
+           char linebuf[160];
+           int logfile;
+
+           /*
+            * This conditional makes the logfile active only after
+            * it's been created - thus logging can be turned off by
+            * removing the file.
+            *
+            * stop NFS hangs...most systems should be able to open a
+            * file in 3 seconds. -avalon (curtesy of wumpus)
+            */
+           if (IsPerson (sptr) &&
+               (logfile = open (FNAME_USERLOG, O_WRONLY | O_APPEND)) != -1) {
+               (void) sprintf (linebuf,
+                               "%s (%3d:%02d:%02d): %s@%s [%s]\n",
+                               myctime (sptr->firsttime),
+                               (int) on_for / 3600,
+                               (int) (on_for % 3600) / 60, (int) on_for % 60,
+                               sptr->user->username, sptr->user->host,
+                               sptr->username);
+               (void) write (logfile, linebuf, strlen (linebuf));
+               (void) close (logfile);
+           }
+           /* Modification by stealth@caen.engin.umich.edu */
+       }
+#endif
+#endif
+       if (sptr->fd >= 0 && !IsConnecting (sptr)) {
+           if (cptr != NULL && sptr != cptr)
+               sendto_one (sptr, "ERROR :Closing Link: %s %s (%s)",
+                           get_client_name (sptr, FALSE), cptr->name,
+                           comment);
+           else
+               sendto_one (sptr, "ERROR :Closing Link: %s (%s)",
+                           get_client_name (sptr, FALSE), comment);
+       }
+       /*
+          ** Currently only server connections can have
+          ** depending remote clients here, but it does no
+          ** harm to check for all local clients. In
+          ** future some other clients than servers might
+          ** have remotes too...
+          **
+          ** Close the Client connection first and mark it
+          ** so that no messages are attempted to send to it.
+          ** (The following *must* make MyConnect(sptr) == FALSE!).
+          ** It also makes sptr->from == NULL, thus it's unnecessary
+          ** to test whether "sptr != acptr" in the following loops.
+        */
+       close_connection (sptr);
+    }
+    /*
+     * Recurse down the client list and get rid of clients who are no
+     * longer connected to the network (from my point of view)
+     * Only do this expensive stuff if exited==server -Donwulff
+     */
+
+    if (IsServer (sptr)) {
+       /*
+        * Is this right? Not recreateing the split message if
+        * we have been called recursivly? I hope so, cuz thats
+        * the only way I could make this give the right servers
+        * in the quit msg. -Cabal95
+        */
+       if (cptr && !recurse) {
+           (void) strcpy (comment1, sptr->srvptr->name);
+           (void) strcat (comment1, " ");
+           (void) strcat (comment1, sptr->name);
+       }
+       /*
+        * First, remove the clients on the server itself.
+        */
+       for (acptr = client; acptr; acptr = next) {
+           next = acptr->next;
+           if (IsClient (acptr) && (acptr->srvptr == sptr))
+               exit_one_client_in_split (NULL, acptr, &me, comment1);
+#ifdef DEBUGMODE
+           else if (IsClient (acptr) &&
+                    (find_server (acptr->user->server, NULL) == sptr)) {
+               sendto_ops ("WARNING, srvptr!=sptr but "
+                           "find_server did!  User %s on %s "
+                           "thought it was on %s while "
+                           "loosing %s.  Tell coding team.",
+                           acptr->name, acptr->user->server,
+                           acptr->srvptr ? acptr->srvptr->
+                           name : "<noserver>", sptr->name);
+               exit_one_client_in_split (NULL, acptr, &me, comment1);
+           }
+#endif
+       }
+
+       /*
+        * Now, go SQUIT off the servers which are down-stream of
+        * the one we just lost.
+        */
+       recurse++;
+       for (acptr = client; acptr; acptr = next) {
+           next = acptr->next;
+           if (IsServer (acptr) && acptr->srvptr == sptr)
+               exit_client (sptr, acptr,       /* RECURSION */
+                            sptr, comment1);
+           /*
+            * I am not masking SQUITS like I do QUITs.  This
+            * is probobly something we could easily do, but
+            * how much savings is there really in something
+            * like that?
+            */
+#ifdef DEBUGMODE
+           else if (IsServer (acptr) &&
+                    (find_server (acptr->serv->up, NULL) == sptr)) {
+               sendto_ops ("WARNING, srvptr!=sptr but "
+                           "find_server did!  Server %s on "
+                           "%s thought it was on %s while "
+                           "loosing %s.  Tell coding team.",
+                           acptr->name, acptr->serv->up,
+                           acptr->srvptr ? acptr->srvptr->
+                           name : "<noserver>", sptr->name);
+               exit_client (sptr, acptr, sptr, comment1);
+           }
+#endif
+       }
+       recurse--;
+    }
+    /*
+     * Finally, clear out the server we lost itself
+     */
+    exit_one_client (cptr, sptr, from, comment);
+    return cptr == sptr ? FLUSH_BUFFER : 0;
+}
+
+/*
+   ** Exit one client, local or remote. Assuming all dependants have
+   ** been already removed, and socket closed for local client.
+ */
+/* DANGER: Ugly hack follows. */
+/* Yeah :/ */
+static void exit_one_client_backend (cptr, sptr, from, comment, split)
+     aClient *sptr;
+     aClient *cptr;
+     aClient *from;
+     char *comment;
+     int split;
+{
+    aClient *acptr;
+    int i;
+    Link *lp;
+
+    /*
+       **  For a server or user quitting, propagage the information to
+       **  other servers (except to the one where is came from (cptr))
+     */
+    if (IsMe (sptr)) {
+       sendto_ops ("ERROR: tried to exit me! : %s", comment);
+       return;                   /* ...must *never* exit self!! */
+    }
+    else if (IsServer (sptr)) {
+       /*
+          ** Old sendto_serv_but_one() call removed because we now
+          ** need to send different names to different servers
+          ** (domain name matching)
+        */
+       for (i = 0; i <= highest_fd; i++) {
+           aConfItem *aconf;
+
+           if (!(acptr = local[i]) || !IsServer (acptr) ||
+               acptr == cptr || IsMe (acptr))
+               continue;
+           if ((aconf = acptr->serv->nline) &&
+               (match (my_name_for_link (me.name, aconf), sptr->name) == 0))
+               continue;
+           /*
+              ** SQUIT going "upstream". This is the remote
+              ** squit still hunting for the target. Use prefixed
+              ** form. "from" will be either the oper that issued
+              ** the squit or some server along the path that
+              ** didn't have this fix installed. --msa
+            */
+           if (sptr->from == acptr) {
+               sendto_one (acptr, ":%s SQUIT %s :%s",
+                           from->name, sptr->name, comment);
+#ifdef USE_SERVICES
+               check_services_butone (SERVICE_WANT_SQUIT, sptr,
+                                      ":%s SQUIT %s :%s",
+                                      from->name, sptr->name, comment);
+#endif
+           }
+           else {
+               sendto_one (acptr, "SQUIT %s :%s", sptr->name, comment);
+#ifdef USE_SERVICES
+               check_services_butone (SERVICE_WANT_SQUIT, sptr,
+                                      "SQUIT %s :%s", sptr->name, comment);
+#endif
+           }
+       }
+    }
+    else if (IsPing (sptr)) {
+       del_queries ((char *) sptr);
+       end_ping (sptr);
+       return;
+    }
+    else if (!(IsPerson (sptr) || IsService (sptr)))
+       /* ...this test is *dubious*, would need
+          ** some thougth.. but for now it plugs a
+          ** nasty hole in the server... --msa
+        */
+       ;                         /* Nothing */
+    else if (sptr->name[0]) {    /* ...just clean all others with QUIT... */
+
+       if (AskedPing (sptr))
+           cancel_ping (sptr, NULL);
+
+       /*
+          ** If this exit is generated from "m_kill", then there
+          ** is no sense in sending the QUIT--KILL's have been
+          ** sent instead.
+        */
+       if ((sptr->flags & FLAGS_KILLED) == 0) {
+           if (split == 0)
+               sendto_serv_butone (cptr, ":%s QUIT :%s", sptr->name,
+                                   comment);
+           else
+               /*
+                * Then this is a split, only old (stupid)
+                * clients need to get quit messages
+                */
+               sendto_serv_butone_quit (cptr, ":%s QUIT :%s", sptr->name,
+                                        comment);
+#ifdef USE_SERVICES
+           check_services_butone (SERVICE_WANT_QUIT,
+                                  ":%s QUIT :%s", sptr->name, comment);
+#endif
+       }
+       /*
+          ** If a person is on a channel, send a QUIT notice
+          ** to every client (person) on the same channel (so
+          ** that the client can show the "**signoff" message).
+          ** (Note: The notice is to the local clients *only*)
+        */
+       if (sptr->user) {
+           sendto_common_channels (sptr, ":%s QUIT :%s", sptr->name,
+                                   comment);
+
+           while ((lp = sptr->user->channel))
+               remove_user_from_channel (sptr, lp->value.chptr);
+
+           /* Clean up invitefield */
+           while ((lp = sptr->user->invited))
+               del_invite (sptr, lp->value.chptr);
+           /* again, this is all that is needed */
+
+           /* Clean up silencefield */
+           while ((lp = sptr->user->silence))
+               (void) del_silence (sptr, lp->value.cp);
+       }
+    }
+    /* Remove sptr from the client list */
+    if (del_from_client_hash_table (sptr->name, sptr) != 1)
+       Debug ((DEBUG_ERROR, "%#x !in tab %s[%s] %#x %#x %#x %d %d %#x",
+               sptr, sptr->name,
+               sptr->from ? sptr->from->sockhost : "??host",
+               sptr->from, sptr->next, sptr->prev, sptr->fd,
+               sptr->status, sptr->user));
+    if (IsRegisteredUser (sptr))
+       hash_check_notify (sptr, RPL_LOGOFF);
+    remove_client_from_list (sptr);
+    return;
+}
+
+static void exit_one_client (cptr, sptr, from, comment)
+     aClient *sptr, *cptr, *from;
+     char *comment;
+{
+    exit_one_client_backend (cptr, sptr, from, comment, 0);
+}
+
+static void exit_one_client_in_split (cptr, sptr, from, comment)
+     aClient *sptr, *cptr, *from;
+     char *comment;
+{
+    exit_one_client_backend (cptr, sptr, from, comment, 1);
+}
+
+
+void checklist ()
+{
+    aClient *acptr;
+    int i, j;
+
+    if (!(bootopt & BOOT_AUTODIE))
+       return;
+    for (j = i = 0; i <= highest_fd; i++)
+       if (!(acptr = local[i]))
+           continue;
+       else if (IsClient (acptr))
+           j++;
+    if (!j) {
+#ifdef USE_SYSLOG
+       syslog (LOG_WARNING, "ircd exiting: autodie");
+#endif
+       exit (0);
+    }
+    return;
+}
+
+void initstats ()
+{
+    bzero ((char *) &ircst, sizeof (ircst));
+}
+
+void tstats (cptr, name)
+     aClient *cptr;
+     char *name;
+{
+    aClient *acptr;
+    int i;
+    struct stats *sp;
+    struct stats tmp;
+    time_t now = time (NULL);
+
+    sp = &tmp;
+    bcopy ((char *) ircstp, (char *) sp, sizeof (*sp));
+    for (i = 0; i < MAXCONNECTIONS; i++) {
+       if (!(acptr = local[i]))
+           continue;
+       if (IsServer (acptr)) {
+           sp->is_sbs += acptr->sendB;
+           sp->is_sbr += acptr->receiveB;
+           sp->is_sks += acptr->sendK;
+           sp->is_skr += acptr->receiveK;
+           sp->is_sti += now - acptr->firsttime;
+           sp->is_sv++;
+           if (sp->is_sbs > 1023) {
+               sp->is_sks += (sp->is_sbs >> 10);
+               sp->is_sbs &= 0x3ff;
+           }
+           if (sp->is_sbr > 1023) {
+               sp->is_skr += (sp->is_sbr >> 10);
+               sp->is_sbr &= 0x3ff;
+           }
+       }
+       else if (IsClient (acptr)) {
+           sp->is_cbs += acptr->sendB;
+           sp->is_cbr += acptr->receiveB;
+           sp->is_cks += acptr->sendK;
+           sp->is_ckr += acptr->receiveK;
+           sp->is_cti += now - acptr->firsttime;
+           sp->is_cl++;
+           if (sp->is_cbs > 1023) {
+               sp->is_cks += (sp->is_cbs >> 10);
+               sp->is_cbs &= 0x3ff;
+           }
+           if (sp->is_cbr > 1023) {
+               sp->is_ckr += (sp->is_cbr >> 10);
+               sp->is_cbr &= 0x3ff;
+           }
+       }
+       else if (IsUnknown (acptr))
+           sp->is_ni++;
+    }
+
+    sendto_one (cptr, ":%s %d %s :accepts %u refused %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_ac, sp->is_ref);
+    sendto_one (cptr, ":%s %d %s :unknown commands %u prefixes %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_unco, sp->is_unpf);
+    sendto_one (cptr, ":%s %d %s :nick collisions %u unknown closes %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_kill, sp->is_ni);
+    sendto_one (cptr, ":%s %d %s :wrong direction %u empty %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_wrdi, sp->is_empt);
+    sendto_one (cptr, ":%s %d %s :numerics seen %u mode fakes %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_num, sp->is_fake);
+    sendto_one (cptr, ":%s %d %s :local connections %u udp packets %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_loc, sp->is_udp);
+    sendto_one (cptr, ":%s %d %s :Client Server",
+               me.name, RPL_STATSDEBUG, name);
+    sendto_one (cptr, ":%s %d %s :connected %u %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_cl, sp->is_sv);
+    sendto_one (cptr, ":%s %d %s :bytes sent %u.%uK %u.%uK",
+               me.name, RPL_STATSDEBUG, name,
+               sp->is_cks, sp->is_cbs, sp->is_sks, sp->is_sbs);
+    sendto_one (cptr, ":%s %d %s :bytes recv %u.%uK %u.%uK",
+               me.name, RPL_STATSDEBUG, name,
+               sp->is_ckr, sp->is_cbr, sp->is_skr, sp->is_sbr);
+    sendto_one (cptr, ":%s %d %s :time connected %u %u",
+               me.name, RPL_STATSDEBUG, name, sp->is_cti, sp->is_sti);
+}
+
+void AddEvent (void (*func) (char *), char *arg, unsigned int delay)
+{
+    aEvent *eptr;
+    eptr = make_event ();
+    if (arg != NULL)
+       strncpyzt (eptr->arg, arg, ARGLEN - 2);
+    eptr->func = func;
+    eptr->exectime = now + delay;
+    if (EventList == NULL)
+       EventList = eptr;
+    else {
+       EventList->prev = eptr;
+       eptr->next = EventList;
+       EventList = eptr;
+    }
+}
+
+void CheckEvents ()
+{
+    aEvent *eptr;
+    aEvent *eptr2;
+    if (EventList == NULL)
+       return;
+    for (eptr = EventList; eptr; eptr = eptr2) {
+       eptr2 = eptr->next;
+       if (now >= eptr->exectime) {
+           eptr->func (eptr->arg);
+           if (eptr->prev)
+               eptr->prev->next = eptr->next;
+           else
+               EventList = eptr->next;
+           if (eptr->next)
+               eptr->next->prev = eptr->prev;
+           free_event (eptr);
+       }
+    }
+}
+
+void serv_info (aClient * cptr, char *name)
+{
+    long SendQ, RecvQ, uptime, timeofday;
+    time_t t1;
+
+    timeofday = time (&t1);
+
+    SendQ = RecvQ = 0;
+
+    uptime = (timeofday - me.since);
+
+    sendto_one (cptr, ":%s %d %s :*** Bandwidth usage for server %s ***",
+               me.name, RPL_STATSDEBUG, name, me.name);
+    sendto_one (cptr, ":%s %d %s :Maximum send peak   : %7.2f K/s",
+               me.name, RPL_STATSDEBUG, name, (float) max_sendqs);
+    sendto_one (cptr, ":%s %d %s :Maximum recv peak   : %7.2f K/s",
+               me.name, RPL_STATSDEBUG, name, (float) max_recvqs);
+    sendto_one (cptr, ":%s %d %s :Current server send : %7.2f K/s",
+               me.name, RPL_STATSDEBUG, name, (float) me.deltaSQK);
+    sendto_one (cptr, ":%s %d %s :Current server recv : %7.2f K/s",
+               me.name, RPL_STATSDEBUG, name, (float) me.deltaRQK);
+    sendto_one (cptr,
+               ":%s %d %s :Server send         : %7.2f %s (%7.2f %s C.) (%4.1fK/s)",
+               me.name, RPL_STATSDEBUG, name, _GMKv (me.u_sendK),
+               _GMKs (me.u_sendK), _GMKv (me.sendK), _GMKs (me.sendK),
+               (float) ((float) me.sendK / (float) uptime));
+    sendto_one (cptr,
+               ":%s %d %s :Server recv         : %7.2f %s (%7.2f %s C.) (%4.1fK/s)",
+               me.name, RPL_STATSDEBUG, name, _GMKv (me.u_receiveK),
+               _GMKs (me.u_receiveK), _GMKv (me.receiveK),
+               _GMKs (me.receiveK),
+               (float) ((float) me.receiveK / (float) uptime));
+    sendto_one (cptr, ":%s %d %s :Send compression    : %7.2f percent.",
+               me.name, RPL_STATSDEBUG, name,
+               (me.u_sendK >
+                0) ? ((float) (100 -
+                               (((float) me.sendK / (float) me.u_sendK) *
+                                100))) : 0);
+    sendto_one (cptr, ":%s %d %s :Recv compression    : %7.2f percent.",
+               me.name, RPL_STATSDEBUG, name,
+               (me.u_receiveK >
+                0) ? ((float) (100 -
+                               (((float) me.receiveK /
+                                 (float) me.u_receiveK) * 100))) : 0);
+    return;
+}
+
+void get_max_users (void)
+{
+    int fd, nr;
+    char *tmp;
+    static char *parv[HOSTLEN + 1];
+    char line[80];
+    int parc;
+
+    fd = open (LUSERS, O_RDONLY);
+    if (fd == -1)
+       return;
+
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    while ((nr = dgets (fd, line, sizeof (line) - 1)) > 0) {
+       line[nr] = '\0';
+       if ((tmp = (char *) index (line, '\n')))
+           *tmp = '\0';
+       if ((tmp = (char *) index (line, '\r')))
+           *tmp = '\0';
+    }
+
+    parc = 0;
+
+    parc = str2array (parv, line, " ");
+
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    (void) close (fd);
+
+    if (parc < 2)
+       return;
+
+    lu_mglobalu = atoi (parv[0]);
+    lu_mlu = atoi (parv[1]);
+
+    return;
+}
+
+void CheckBandwidth (void)
+{
+    me.deltaRQK = (me.receiveK - me.previousRQK);
+    me.deltaSQK = (me.sendK - me.previousSQK);
+    if (me.deltaRQK > max_recvqs)
+       max_recvqs = me.deltaRQK;
+    if (me.deltaSQK > max_sendqs)
+       max_sendqs = me.deltaSQK;
+    me.previousRQK = me.receiveK;
+    me.previousSQK = me.sendK;
+    return;
+}
diff --git a/src/s_numeric.c b/src/s_numeric.c
new file mode 100644 (file)
index 0000000..032f685
--- /dev/null
@@ -0,0 +1,122 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_numeric.c
+ *   Copyright (C) 1990 Jarkko Oikarinen
+ *
+ *   Numerous fixes by Markku Savela
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "h.h"
+
+static char buffer[1024];
+
+/*
+   ** DoNumeric (replacement for the old do_numeric)
+   **
+   **   parc    number of arguments ('sender' counted as one!)
+   **   parv[0] pointer to 'sender' (may point to empty string) (not used)
+   **   parv[1]..parv[parc-1]
+   **       pointers to additional parameters, this is a NULL
+   **       terminated list (parv[parc] == NULL).
+   **
+   ** *WARNING*
+   **   Numerics are mostly error reports. If there is something
+   **   wrong with the message, just *DROP* it! Don't even think of
+   **   sending back a neat error message -- big danger of creating
+   **   a ping pong error message...
+ */
+int do_numeric (numeric, cptr, sptr, parc, parv)
+     int numeric;
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    aChannel *chptr;
+    char *nick, *p;
+    int i;
+
+    if (parc < 1 || !IsServer (sptr))
+       return 0;
+    /* Remap low number numerics. */
+    if (numeric < 100)
+       numeric += 100;
+    /*
+       ** Prepare the parameter portion of the message into 'buffer'.
+       ** (Because the buffer is twice as large as the message buffer
+       ** for the socket, no overflow can occur here... ...on current
+       ** assumptions--bets are off, if these are changed --msa)
+       ** Note: if buffer is non-empty, it will begin with SPACE.
+     */
+    buffer[0] = '\0';
+    if (parc > 2) {
+       for (i = 2; i < (parc - 1); i++) {
+           (void) strcat (buffer, " ");
+           (void) strcat (buffer, parv[i]);
+       }
+       (void) strcat (buffer, " :");
+       (void) strcat (buffer, parv[parc - 1]);
+    }
+    else
+       sendto_realops ("do_numeric( %i, %s, %s, %i, { %s, %s } )!",
+                       numeric, cptr->name, sptr->name, parc,
+                       parv[0], parv[1] ? parv[1] : "<null>");
+    for (; (nick = strtoken (&p, parv[1], ",")); parv[1] = NULL) {
+       if ((acptr = find_client (nick, (aClient *) NULL))) {
+           /*
+              ** Drop to bit bucket if for me...
+              ** ...one might consider sendto_ops
+              ** here... --msa
+              ** And so it was done. -avalon
+              ** And regretted. Dont do it that way. Make sure
+              ** it goes only to non-servers. -avalon
+              ** Check added to make sure servers don't try to loop
+              ** with numerics which can happen with nick collisions.
+              ** - Avalon
+            */
+           if (!IsMe (acptr) && IsPerson (acptr)) {
+               /* Added for .U3.2. drop remote 'You are not on
+                  ** that channel', we should be synced anyway,
+                  ** and this is an annoying message with TSpre7
+                  ** still on the net; would result in numeric 442 for
+                  ** every KICK... Can be removed when TSpre7 is gone.
+                  ** --Run
+                */
+               if (numeric == ERR_NOTONCHANNEL)
+                   return 0;
+
+               sendto_prefix_one (acptr, sptr, ":%s %d %s%s",
+                                  parv[0], numeric, nick, buffer);
+           }
+           else if (IsServer (acptr) && acptr->from != cptr)
+               sendto_prefix_one (acptr, sptr, ":%s %d %s%s",
+                                  parv[0], numeric, nick, buffer);
+       }
+       else if ((acptr = find_server (nick, (aClient *) NULL))) {
+           if (!IsMe (acptr) && acptr->from != cptr)
+               sendto_prefix_one (acptr, sptr, ":%s %d %s%s",
+                                  parv[0], numeric, nick, buffer);
+       }
+       else if ((chptr = find_channel (nick, (aChannel *) NULL)))
+           sendto_channel_butone (cptr, sptr, chptr, ":%s %d %s%s",
+                                  parv[0], numeric, chptr->chname, buffer);
+    }
+    return 0;
+}
diff --git a/src/s_ping.c b/src/s_ping.c
new file mode 100644 (file)
index 0000000..a0423ce
--- /dev/null
@@ -0,0 +1,487 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_ping.c
+ *   Copyright (C) 1994 Carlo K ( Run @ undernet.org )
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "patchlevel.h"
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#if defined(__hpux)
+#include "inet.h"
+#endif
+#include <fcntl.h>
+#include "sock.h"                /* If FD_ZERO isn't defined up to this point,  */
+                       /* define it (BSD4.2 needs this) */
+#include "h.h"
+
+#define UPINGBUFSIZE 2000        /* Lot bigger then 1024, bit smaller then 2048 */
+#define UPINGTIMEOUT 120         /* Timeout waitting for first ping response */
+
+
+/*
+ * sendto_one_notice
+ *
+ *      sendto_one_notice should be used for all NOTICEs that might be
+ *      addressed to a remote user, *and* are from this server
+ *
+ *  *** MAKE ME LOOK MORE PRETTY:
+ *  This really should not be here, but I'm too lazy to go change all
+ *  the notice calls below. Seeing as s_uping.c is the only place from
+ *  which it's called, and because it should really be removed, I've
+ *  put it here; instead of in send.c -TheShadow
+ */
+void sendto_one_notice (to, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+     aClient *to;
+{
+    char nbuf[1024];
+
+    (void) sprintf (nbuf, "NOTICE %s :", to->name);
+    (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+    sendto_one (to, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+
+    return;
+}
+
+extern u_long inet_addr ();
+
+void end_ping ();
+void cancel_ping ();
+
+/*
+ * start_ping
+ *
+ * As for now, I am abusing the client structure for a ping connection.
+ * Used members are:
+ * These are used by existing routines as well, and do have their own meaning:
+ *   fd       : The socket file descriptor.
+ *   status   : To flag that this IS one of these abused ping structures
+ *   sockhost : Name of requested server to ping (aconf->host).
+ *   name     : aconf->name
+ *   ip       : ip#
+ * These have more or less their own meaning,
+ * but are not used by existing routines:
+ *   flags    : To flag that a next ping is requested.
+ *   port     : Requested remote port.
+ * These are only used by the 'uping' routines
+ * and have totally different meanings:
+ *   buffer   : buffer hold pingtimes of received packets
+ *   confs    : recv/send (char *) buffer.
+ *   hopcount : Total number of requested pings
+ *   count    : Number of pings left to send.
+ *   hashv    : Number of pings left to be received.
+ *              NOTE: This is not used by any other part of DreamForge and is
+ *                      merely included for UPING to work. This is not the 
+ *                      ideal idea as we waste memory, albeit very little,
+ *                      simply for UPING.  -TheShadow
+ *   acpt     : client asking for this ping
+ *   lasttime : last time a ping was sent
+ *   firsttime: recvfrom timeout
+ *   since    : timeout in seconds to next recvfrom
+ *   receiveK : minimum in ms
+ *   sendM    : average in ms
+ *   receiveM : maximum in ms
+ */
+int start_ping (cptr)
+     aClient *cptr;
+{
+    struct sockaddr_in remote_addr;
+
+    Debug ((DEBUG_NOTICE, "start_ping(%x) status %d", cptr, cptr->status));
+
+    if (!(cptr->acpt))
+       return -1;
+
+    bcopy ((char *) &cptr->ip, (char *) &remote_addr.sin_addr,
+          sizeof (struct in_addr));
+    remote_addr.sin_port = htons (cptr->port);
+    remote_addr.sin_family = AF_INET;
+
+    sendto_one_notice (cptr->acpt,
+                      "Sending %d ping%s to %s[%s] port %d",
+                      cptr->hopcount,
+                      (cptr->hopcount == 1) ? "" : "s", cptr->name,
+                      inetntoa ((char *) &remote_addr.sin_addr),
+                      ntohs (remote_addr.sin_port));
+
+    cptr->firsttime = time (NULL) + UPINGTIMEOUT;
+    cptr->since = UPINGTIMEOUT;
+    cptr->flags |= (FLAGS_PING);
+
+    return 0;
+}
+
+/*
+ * send_ping
+ *
+ */
+void send_ping (cptr)
+     aClient *cptr;
+{
+    struct sockaddr_in remote_addr;
+    struct timeval tv;
+
+    bcopy ((char *) &cptr->ip, (char *) &remote_addr.sin_addr,
+          sizeof (struct in_addr));
+    remote_addr.sin_port = htons (cptr->port);
+    remote_addr.sin_family = AF_INET;
+
+    (void) gettimeofday (&tv, NULL);
+    (void) sprintf ((char *) cptr->confs, " %10lu%c%6lu",
+                   tv.tv_sec, '\0', tv.tv_usec);
+
+    Debug ((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
+           (char *) cptr->confs, (char *) cptr->confs + 12,
+           inetntoa ((char *) &remote_addr.sin_addr),
+           ntohs (remote_addr.sin_port), cptr->fd));
+
+    if (sendto (cptr->fd, (char *) cptr->confs, 1024, 0,
+               (struct sockaddr *) &remote_addr,
+               sizeof (struct sockaddr_in)) != 1024) {
+       if (cptr->acpt)
+           sendto_one_notice (cptr->acpt, "UPING: sendto() failed: %s",
+                              strerror (get_sockerr (cptr)));
+//    Debug((DEBUG_SEND, "send_ping: sendto failed on %d (%d)", cptr->fd, err));
+       (void) end_ping (cptr);
+    }
+    else if (--(cptr->count) <= 0) {
+       ClearPing (cptr);
+       if (cptr->hashv <= 0)
+           end_ping (cptr);
+    }
+    return;
+}
+
+/*
+ * read_ping
+ *
+ */
+void read_ping (cptr)
+     aClient *cptr;
+{
+    int addr_len = sizeof (struct sockaddr_in);
+    struct sockaddr_in remote_addr;
+    struct timeval tv;
+    int len;
+    unsigned long int pingtime;
+    char *s;
+
+    bcopy ((char *) &cptr->ip, (char *) &remote_addr.sin_addr,
+          sizeof (struct in_addr));
+    remote_addr.sin_port = htons (cptr->port);
+    remote_addr.sin_family = AF_INET;
+
+    (void) gettimeofday (&tv, NULL);
+
+    if ((len = recvfrom (cptr->fd, (char *) cptr->confs, UPINGBUFSIZE, 0,
+                        (struct sockaddr *) &remote_addr,
+                        &addr_len)) == -1) {
+       int err = errno;
+       sendto_one_notice (cptr->acpt,
+                          "UPING: recvfrom: %s",
+                          strerror (get_sockerr (cptr)));
+       Debug ((DEBUG_SEND, "read_ping: recvfrom: %d", err));
+       if (err != EAGAIN)
+           end_ping (cptr);
+       return;
+    }
+    if (len < 19)
+       return;                   /* Broken packet */
+
+    pingtime = (tv.tv_sec - atoi ((char *) cptr->confs + 1)) * 1000 +
+       (tv.tv_usec -
+        atoi ((char *) cptr->confs + strlen ((char *) cptr->confs) +
+              1)) / 1000;
+    cptr->sendM += pingtime;
+    if (!(cptr->receiveK) || (cptr->receiveK > pingtime))
+       cptr->receiveK = pingtime;
+    if (pingtime > cptr->receiveM)
+       cptr->receiveM = pingtime;
+    /* Wait at most 10 times the average pingtime for the next one: */
+    if ((cptr->since =
+        cptr->sendM / (100 * (cptr->hopcount - cptr->hashv + 1))) < 2)
+       cptr->since = 2;
+    cptr->firsttime = tv.tv_sec + cptr->since;
+
+    Debug (("read_ping: %d bytes, ti %lu: [%s %s] %u ms",
+           len, cptr->since, (char *) cptr->confs,
+           (char *) cptr->confs + strlen ((char *) cptr->confs) + 1,
+           pingtime));
+
+    s = cptr->buffer + strlen (cptr->buffer);
+    sprintf (s, " %lu", pingtime);
+
+    if ((--(cptr->hashv) <= 0 && !DoPing (cptr)) || !(cptr->acpt))
+       end_ping (cptr);
+
+    return;
+}
+
+int ping_server (cptr, hp)
+     aClient *cptr;
+     struct hostent *hp;
+{
+    if ((!cptr->ip.s_addr)) {
+       struct hostent *hp;
+       char *s;
+       Link lin;
+
+       if (!(cptr->acpt))
+           return -1;            /* Oper left already */
+
+       lin.flags = ASYNC_PING;
+       lin.value.cptr = cptr;
+       nextdnscheck = 1;
+       s = (char *) index (cptr->sockhost, '@');
+       s++;                      /* should never be NULL; cptr->sockhost is actually a conf->host */
+       if ((cptr->ip.s_addr = inet_addr (s)) == -1) {
+           cptr->ip.s_addr = 0;
+           hp = gethost_byname (s, &lin);
+           Debug ((DEBUG_NOTICE, "ping_sv: hp %x ac %x ho %s", hp, cptr, s));
+           if (!hp)
+               return 0;
+           bcopy (hp->h_addr, (char *) &cptr->ip, sizeof (struct in_addr));
+       }
+    }
+    return start_ping (cptr);
+}
+
+/*
+   ** m_uping  -- by Run
+   **
+   **   parv[0] = sender prefix
+   **   parv[1] = pinged server
+   **      parv[2] = port
+   **   parv[3] = hunted server
+   **      parv[4] = number of requested pings
+ */
+int m_uping (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aConfItem *aconf;
+    int fd, opt;
+    int port = atoi (UDPPORT);
+
+    if (!IsPrivileged (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return -1;
+    }
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "UPING");
+       return 0;
+    }
+    if (MyClient (sptr)) {
+       if (parc == 2) {
+           parv[parc++] = UDPPORT;
+           parv[parc++] = me.name;
+           parv[parc++] = "5";
+       }
+       else if (parc == 3) {
+           if (isdigit (*parv[2])) {
+               parv[parc++] = me.name;
+           }
+           else {
+               parv[parc++] = parv[2];
+               parv[2] = UDPPORT;
+           }
+           parv[parc++] = "5";
+       }
+       else if (parc == 4) {
+           if (isdigit (*parv[2])) {
+               if (isdigit (*parv[3])) {
+                   parv[parc++] = parv[3];
+                   parv[3] = me.name;
+               }
+               else
+                   parv[parc++] = "5";
+           }
+           else {
+               parv[parc++] = parv[3];
+               parv[3] = parv[2];
+               parv[2] = UDPPORT;
+           }
+       }
+    }
+    if (hunt_server (cptr, sptr, ":%s UPING %s %s %s %s", 3, parc, parv) !=
+       HUNTED_ISME)
+       return 0;
+
+    if (BadPtr (parv[4]) || atoi (parv[4]) <= 0) {
+       sendto_one_notice (sptr, "UPING: Illegal number of packets: %s",
+                          parv[4]);
+       return 0;
+    }
+    /* Check if a CONNECT would be possible at all (adapted from m_connect) */
+    for (aconf = conf; aconf; aconf = aconf->next)
+       if (aconf->status == CONF_CONNECT_SERVER &&
+           match (parv[1], aconf->name) == 0)
+           break;
+    if (!aconf)
+       for (aconf = conf; aconf; aconf = aconf->next)
+           if (aconf->status == CONF_CONNECT_SERVER &&
+               (match (parv[1], aconf->host) == 0 ||
+                match (parv[1], index (aconf->host, '@') + 1) == 0))
+               break;
+    if (!aconf) {
+       sendto_one_notice (sptr, "UPING: Host %s not listed in ircd.conf",
+                          parv[1]);
+       return 0;
+    }
+    if (AskedPing (sptr))
+       (void) cancel_ping (sptr, sptr);        /* Cancel previous ping request */
+
+    /*
+     * Determine port: First user supplied, then default : 7007
+     */
+    if (!BadPtr (parv[2]) && (port = atoi (parv[2])) <= 0)
+       port = atoi (UDPPORT);
+
+    (void) alarm (2);
+    if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) == -1) {
+       int err = errno;
+       (void) alarm (0);
+       sendto_ops ("m_uping: socket: %s", (err != EAGAIN) ?
+                   strerror (err) : "No more sockets");
+       sendto_one_notice (sptr, "UPING: Unable to create udp ping socket");
+#ifdef USE_SYSLOG
+       syslog (LOG_ERR, "Unable to create udp ping socket");
+#endif
+       return 0;
+    }
+    (void) alarm (0);
+
+    if (fcntl (fd, F_SETFL, FNDELAY) == -1) {
+       sendto_ops ("m_uping: fcntl FNDELAY: %s", strerror (errno));
+       sendto_one_notice (sptr, "UPING: Can't set fd non-blocking");
+       close (fd);
+       return 0;
+    }
+    /*
+       ** On some systems, receive and send buffers must be equal in size.
+       ** Others block select() when the buffers are too small
+       ** (Linux 1.1.50 blocks when < 2048) --Run
+     */
+    opt = 2048;
+    if (setsockopt
+       (fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *) & opt, sizeof (opt))
+       < 0
+       || setsockopt (fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *) & opt,
+                      sizeof (opt)) < 0) {
+       int err = errno;
+       sendto_ops ("m_uping: setsockopt SO_SNDBUF|SO_RCVBUF: %s",
+                   strerror (err));
+       sendto_one_notice (sptr, "UPING: error in setsockopt: %s",
+                          strerror (err));
+       close (fd);
+       return 0;
+    }
+    if (fd >= MAXCONNECTIONS) {
+       sendto_ops ("Can't allocate fd for uping (all connections in use)");
+       sendto_one_notice (sptr, "UPING: All connections in use");
+       close (fd);
+       return 0;
+    }
+    if (fd > highest_fd)
+       highest_fd = fd;
+    local[fd] = cptr = make_client (NULL, NULL);
+    cptr->confs = (Link *) MyMalloc (UPINGBUFSIZE);    /* Really a (char *) */
+    cptr->fd = fd;
+    cptr->port = port;
+    cptr->hopcount = cptr->hashv = cptr->count = MIN (20, atoi (parv[4]));
+    strcpy (cptr->sockhost, aconf->host);
+    cptr->acpt = sptr;
+    SetAskedPing (sptr);
+    bcopy ((void *) &aconf->ipnum, (void *) &cptr->ip,
+          sizeof (struct in_addr));
+    strcpy (cptr->name, aconf->name);
+    cptr->firsttime = 0;
+    SetPing (cptr);
+
+    switch (ping_server (cptr, NULL)) {
+    case 0:
+       break;
+    case -1:
+       del_queries ((char *) cptr);
+       end_ping (cptr);
+       break;
+    }
+    return 0;
+}
+
+void end_ping (cptr)
+     register aClient *cptr;
+{
+    Debug ((DEBUG_DEBUG, "end_ping: %x", cptr));
+    if (cptr->acpt) {
+       if (cptr->firsttime) {    /* Started at all ? */
+           if (cptr->hashv != cptr->hopcount) {        /* Received any pings at all ? */
+               sendto_one_notice (cptr->acpt, "UPING %s%s", cptr->name,
+                                  cptr->buffer);
+               sendto_one_notice (cptr->acpt,
+                                  "UPING Stats: sent %d recvd %d ; min/avg/max = %lu/%lu/%lu ms",
+                                  cptr->hopcount - cptr->count,
+                                  cptr->hopcount - cptr->hashv,
+                                  cptr->receiveK,
+                                  (2 * cptr->sendM + cptr->hopcount -
+                                   cptr->hashv) / (2 * (cptr->hopcount -
+                                                        cptr->hashv)),
+                                  cptr->receiveM);
+           }
+           else
+               sendto_one_notice (cptr->acpt,
+                                  "UPING: no response from %s within %d seconds",
+                                  cptr->name,
+                                  time (NULL) + cptr->since -
+                                  cptr->firsttime);
+       }
+       else
+           sendto_one_notice (cptr->acpt,
+                              "UPING: Could not start ping to %s %d",
+                              cptr->name, cptr->port);
+    }
+    (void) close (cptr->fd);
+    local[cptr->fd] = NULL;
+    if (cptr->acpt)
+       ClearAskedPing (cptr->acpt);
+    MyFree ((char *) cptr->confs);
+    free_client (cptr);
+}
+
+void cancel_ping (sptr, acptr)
+     aClient *sptr, *acptr;
+{
+    int i;
+    aClient *cptr;
+
+    Debug ((DEBUG_DEBUG, "Cancelling uping for %x (%s)", sptr, sptr->name));
+    for (i = highest_fd; i >= 0; i--)
+       if ((cptr = local[i]) && IsPing (cptr) && cptr->acpt == sptr) {
+           cptr->acpt = acptr;
+           del_queries ((char *) cptr);
+           end_ping (cptr);
+           break;
+       }
+    ClearAskedPing (sptr);
+}
diff --git a/src/s_serv.c b/src/s_serv.c
new file mode 100644 (file)
index 0000000..b4e39ec
--- /dev/null
@@ -0,0 +1,3235 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_serv.c (formerly ircd/s_msg.c)
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers. 
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "config.h"
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include "userload.h"
+#if defined(PCS) || defined(AIX) || defined(DYNIXPTX) || defined(SVR3)
+#include <time.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <time.h>
+#include "h.h"
+
+static void report_jinx (aClient * sptr);
+
+static char buf[BUFSIZE];
+VOIDSIG s_die ();
+/* I guess we can move this to somewhere else ... */
+
+/*
+** m_functions execute protocol messages on this server:
+**
+**   cptr    is always NON-NULL, pointing to a *LOCAL* client
+**       structure (with an open socket connected!). This
+**       identifies the physical socket where the message
+**       originated (or which caused the m_function to be
+**       executed--some m_functions may call others...).
+**
+**   sptr    is the source of the message, defined by the
+**       prefix part of the message if present. If not
+**       or prefix not found, then sptr==cptr.
+**
+**       (!IsServer(cptr)) => (cptr == sptr), because
+**       prefixes are taken *only* from servers...
+**
+**       (IsServer(cptr))
+**           (sptr == cptr) => the message didn't
+**           have the prefix.
+**
+**           (sptr != cptr && IsServer(sptr) means
+**           the prefix specified servername. (?)
+**
+**           (sptr != cptr && !IsServer(sptr) means
+**           that message originated from a remote
+**           user (not local).
+**
+**       combining
+**
+**       (!IsServer(sptr)) means that, sptr can safely
+**       taken as defining the target structure of the
+**       message in this server.
+**
+**   *Always* true (if 'parse' and others are working correct):
+**
+**   1)  sptr->from == cptr  (note: cptr->from == cptr)
+**
+**   2)  MyConnect(sptr) <=> sptr == cptr (e.g. sptr
+**       *cannot* be a local connection, unless it's
+**       actually cptr!). [MyConnect(x) should probably
+**       be defined as (x == x->from) --msa ]
+**
+**   parc    number of variable parameter strings (if zero,
+**       parv is allowed to be NULL)
+**
+**   parv    a NULL terminated list of parameter pointers,
+**
+**           parv[0], sender (prefix string), if not present
+**               this points to an empty string.
+**           parv[1]...parv[parc-1]
+**               pointers to additional parameters
+**           parv[parc] == NULL, *always*
+**
+**       note:   it is guaranteed that parv[0]..parv[parc-1] are all
+**           non-NULL pointers.
+*/
+
+/*
+** m_version
+**   parv[0] = sender prefix
+**   parv[1] = remote server
+*/
+int m_version (cptr, sptr, parc, parv)
+     aClient *sptr, *cptr;
+     int parc;
+     char *parv[];
+{
+    extern char serveropts[];
+
+    /* Make sure they stick to the local server if not registered.
+     *  -Studded */
+    if ((!IsRegistered (cptr)) && (!BadPtr (parv[1]))) {
+       sendto_one (cptr, ":%s %d VERSION :You have not registered",
+                   me.name, ERR_NOTREGISTERED);
+       return -1;
+    }
+    if (hunt_server (cptr, sptr, ":%s VERSION :%s", 1, parc, parv) ==
+       HUNTED_ISME)
+       sendto_one (sptr, rpl_str (RPL_VERSION), me.name, parv[0], version,
+                   debugmode, me.name, serveropts);
+    return 0;
+}
+
+/*
+   ** m_squit
+   **   parv[0] = sender prefix
+   **   parv[1] = server name
+   **   parv[parc-1] = comment
+ */
+int m_squit (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aConfItem *aconf;
+    char *server;
+    aClient *acptr;
+    char *comment = (parc > 2
+                    && parv[parc - 1]) ? parv[parc - 1] : cptr->name;
+
+    if (check_registered (sptr))
+       return 0;
+
+    if (!IsPrivileged (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if (parc > 1) {
+       server = parv[1];
+       /*
+          ** To accomodate host masking, a squit for a masked server
+          ** name is expanded if the incoming mask is the same as
+          ** the server name for that link to the name of link.
+        */
+       while ((*server == '*') && IsServer (cptr)) {
+           aconf = cptr->serv->nline;
+           if (!aconf)
+               break;
+           if (!mycmp (server, my_name_for_link (me.name, aconf)))
+               server = cptr->name;
+           break;                /* WARNING is normal here */
+       }
+       /*
+          ** The following allows wild cards in SQUIT. Only usefull
+          ** when the command is issued by an oper.
+        */
+       for (acptr = client; (acptr = next_client (acptr, server));
+            acptr = acptr->next)
+           if (IsServer (acptr) || IsMe (acptr))
+               break;
+       if (acptr && IsMe (acptr)) {
+           acptr = cptr;
+           server = cptr->sockhost;
+       }
+    }
+    else {
+       /*
+          ** This is actually protocol error. But, well, closing
+          ** the link is very proper answer to that...
+        */
+       server = cptr->sockhost;
+       acptr = cptr;
+    }
+
+    /*
+       ** SQUIT semantics is tricky, be careful...
+       **
+       ** The old (irc2.2PL1 and earlier) code just cleans away the
+       ** server client from the links (because it is never true
+       ** "cptr == acptr".
+       **
+       ** This logic here works the same way until "SQUIT host" hits
+       ** the server having the target "host" as local link. Then it
+       ** will do a real cleanup spewing SQUIT's and QUIT's to all
+       ** directions, also to the link from which the orinal SQUIT
+       ** came, generating one unnecessary "SQUIT host" back to that
+       ** link.
+       **
+       ** One may think that this could be implemented like
+       ** "hunt_server" (e.g. just pass on "SQUIT" without doing
+       ** nothing until the server having the link as local is
+       ** reached). Unfortunately this wouldn't work in the real life,
+       ** because either target may be unreachable or may not comply
+       ** with the request. In either case it would leave target in
+       ** links--no command to clear it away. So, it's better just
+       ** clean out while going forward, just to be sure.
+       **
+       ** ...of course, even better cleanout would be to QUIT/SQUIT
+       ** dependant users/servers already on the way out, but
+       ** currently there is not enough information about remote
+       ** clients to do this...   --msa
+     */
+    if (!acptr) {
+       sendto_one (sptr, err_str (ERR_NOSUCHSERVER), me.name, parv[0],
+                   server);
+       return 0;
+    }
+    if (MyClient (sptr) && ((!OPCanGRoute (sptr) && !MyConnect (acptr)) ||
+                           (!OPCanLRoute (sptr) && MyConnect (acptr)))) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    /*
+       **  Notify all opers, if my local link is remotely squitted
+     */
+    if (MyConnect (acptr) && !IsAnOper (cptr)) {
+       sendto_locfailops ("Received SQUIT %s from %s (%s)",
+                          acptr->name, get_client_name (sptr, FALSE),
+                          comment);
+       sendto_serv_butone (&me,
+                           ":%s GLOBOPS :Received SQUIT %s from %s (%s)",
+                           me.name, server, get_client_name (sptr, FALSE),
+                           comment);
+#if defined(USE_SYSLOG) && defined(SYSLOG_SQUIT)
+       syslog (LOG_DEBUG, "SQUIT From %s : %s (%s)", parv[0], server,
+               comment);
+#endif
+    }
+    else if (MyConnect (acptr)) {
+       sendto_locfailops ("Received SQUIT %s from %s (%s)",
+                          acptr->name, get_client_name (sptr, FALSE),
+                          comment);
+       sendto_serv_butone (&me,
+                           ":%s GLOBOPS :Received SQUIT %s from %s (%s)",
+                           me.name, acptr->name, get_client_name (sptr,
+                                                                  FALSE),
+                           comment);
+    }
+    if (IsAnOper (sptr)) {
+       /*
+        * It was manually /squit'ed by a human being(we hope),
+        * there is a very good chance they don't want us to
+        * reconnect right away.  -Cabal95
+        */
+       acptr->flags |= FLAGS_SQUIT;
+    }
+    return exit_client (cptr, acptr, sptr, comment);
+}
+
+/*
+ * m_protoctl
+ *  parv[0] = Sender prefix
+ *  parv[1+] = Options
+ */
+int m_protoctl (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int i;
+    char proto[128], *options, *equal;
+    static char *dummyblank = "";      /* Yes, it is kind of ugly */
+
+
+    if (GotProtoctl (sptr)) {
+       /*
+        * But we already GOT a protoctl msg!
+        */
+       sendto_one (cptr, "ERROR :Already got a PROTOCTL from you.");
+       return 0;
+    }
+    cptr->flags |= FLAGS_PROTOCTL;
+
+    for (i = 1; i < parc; i++) {
+       strncpy (proto, parv[i], 127);
+       proto[127] = '\0';        /* Just to be safe... */
+       equal = (char *) index (proto, '=');
+       if (equal == NULL)
+           options = dummyblank;
+       else {
+           options = &equal[1];  /* Variable-byte-size safe */
+           equal[0] = '\0';
+       }
+
+       if (strcmp (proto, "NOQUIT") == 0) {
+           SetNoQuit (cptr);
+       }
+       else if (strcmp (proto, "TOKEN") == 0) {
+           SetToken (cptr);
+       }
+       /*
+        * Add other protocol extensions here, with proto
+        * containing the base option, and options containing
+        * what it equals, if anything.
+        *
+        * DO NOT error or warn on unknown proto; we just don't
+        * support it.
+        */
+    }
+
+    return 0;
+}
+
+/*
+   ** m_server (changed 05/10/99 to send server-version with it too -GZ)
+   **
+   **   parv[0] = sender prefix
+   **   parv[1] = servername
+   **   parv[2] = hopcount
+   **   parv[3] = timestamp / version
+   **   parv[4] = version
+   **   parv[5] = info
+ */
+
+int m_server (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *ch;
+    int i;
+    char info[REALLEN + 1], *inpath, *host, *encr;
+    char s_version[REALLEN + 1];
+    aClient *acptr, *bcptr;
+    aConfItem *aconf, *cconf;
+    int hop, ts = 0;
+
+    info[0] = '\0';
+    s_version[0] = '\0';
+    inpath = get_client_name (cptr, FALSE);
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (cptr, "ERROR :No servername");
+       return 0;
+    }
+    hop = 0;
+    host = parv[1];
+
+    if (parc > 5) {
+       ts = atoi (parv[3]);
+       hop = atoi (parv[2]);
+       (void) strncpy (info, parv[5], REALLEN);
+       info[REALLEN] = '\0';
+       (void) strncpy (s_version, parv[4], REALLEN);
+       s_version[REALLEN] = '\0';
+    }
+    else if (parc > 4 && atoi (parv[2])) {
+       hop = atoi (parv[2]);
+       (void) strncpy (info, parv[4], REALLEN);
+       info[REALLEN] = '\0';
+       (void) strncpy (s_version, parv[3], REALLEN);
+       s_version[REALLEN] = '\0';
+    }
+    else if (parc > 3) {
+       (void) strncpy (info, parv[3], REALLEN);
+       info[REALLEN] = '\0';
+       (void) strncpy (s_version, parv[2], REALLEN);
+       s_version[REALLEN] = '\0';
+    }
+
+    /*
+       ** Check for "FRENCH " infection ;-) (actually this should
+       ** be replaced with routine to check the hostname syntax in
+       ** general). [ This check is still needed, even after the parse
+       ** is fixed, because someone can send "SERVER :foo bar " ].
+       ** Also, changed to check other "difficult" characters, now
+       ** that parse lets all through... --msa
+     */
+    if (strlen (host) > HOSTLEN)
+       host[HOSTLEN] = '\0';
+    for (ch = host; *ch; ch++)
+       if (*ch <= ' ' || *ch > '~')
+           break;
+    if (*ch || !index (host, '.')) {
+       sendto_one (sptr, "ERROR :Bogus server name (%s)", sptr->name, host);
+       sendto_ops ("Bogus server name (%s) from %s", host,
+                   get_client_name (cptr, TRUE));
+       sptr->since += 7;
+       return 0;
+    }
+    if (IsPerson (cptr)) {
+       /*
+          ** A local link that has been identified as a USER
+          ** tries something fishy... ;-)
+        */
+       sendto_one (cptr, err_str (ERR_ALREADYREGISTRED), me.name, parv[0]);
+       sendto_one (cptr, ":%s NOTICE %s :Sorry, but your IRC "
+                   "program doesn't appear to support changing "
+                   "servers.", me.name, cptr->name);
+       sendto_ops ("User %s trying to become a server %s",
+                   get_client_name (cptr, TRUE), host);
+       sptr->since += 7;
+       return 0;
+    }
+    /* *WHEN* can it be that "cptr != sptr" ????? --msa */
+    /* When SERVER command (like now) has prefix. -avalon */
+
+    /* Get a pre-peek at the password... */
+    if (IsUnknown (cptr)) {
+       aconf = find_conf_servern (host);
+       if (!aconf) {
+           sendto_one (cptr, "ERROR :No Access (No N line) %s", inpath);
+           sendto_locfailops ("Access denied (No N line) %s", inpath);
+           return exit_client (cptr, cptr, cptr, "No N line");
+       }
+       encr = cptr->passwd;
+       if (*aconf->passwd && !StrEq (aconf->passwd, encr)) {
+           sendto_one (cptr, "ERROR :No Access (passwd mismatch) %s",
+                       inpath);
+           sendto_locfailops ("Access denied (passwd mismatch) %s", inpath);
+           return exit_client (cptr, cptr, cptr, "Bad Password");
+       }
+       /*  bzero(cptr->passwd, sizeof(cptr->passwd)); */
+    }
+    if ((acptr = find_name (host, NULL))) {
+       aClient *ocptr;
+
+       /*
+        * This link is trying feed me a server that I already have
+        * access through another path -- multiple paths not accepted
+        * currently, kill this link immeatedly!!
+        *
+        * Rather than KILL the link which introduced it, KILL the
+        * youngest of the two links. -avalon
+        */
+       acptr = acptr->from;
+       ocptr = (cptr->firsttime > acptr->firsttime) ? acptr : cptr;
+       acptr = (cptr->firsttime > acptr->firsttime) ? cptr : acptr;
+       sendto_one (acptr, "ERROR :Server %s already exists from %s",
+                   host, (ocptr->from ? ocptr->from->name : "<nobody>"));
+       sendto_ops ("Link %s cancelled, server %s already exists from %s",
+                   get_client_name (acptr, TRUE), host,
+                   (ocptr->from ? ocptr->from->name : "<nobody>"));
+       return exit_client (acptr, acptr, acptr, "Server Exists");
+    }
+    if ((acptr = find_client (host, NULL))) {
+       /*
+          ** Server trying to use the same name as a person. Would
+          ** cause a fair bit of confusion. Enough to make it hellish
+          ** for a while and servers to send stuff to the wrong place.
+        */
+       sendto_one (cptr, "ERROR :Nickname %s already exists!", host);
+       sendto_locfailops ("Link %s cancelled: Server/nick collision on %s",
+                          inpath, host);
+       sendto_serv_butone (&me,
+                           ":%s GLOBOPS : Link %s cancelled: Server/nick collision on %s",
+                           parv[0], inpath, host);
+       return exit_client (cptr, cptr, cptr, "Nick as Server");
+    }
+    if (IsServer (cptr)) {
+       /*
+          ** Server is informing about a new server behind
+          ** this link. Create REMOTE server structure,
+          ** add it to list and propagate word to my other
+          ** server links...
+        */
+       if (parc == 1 || info[0] == '\0') {
+           sendto_one (cptr, "ERROR :No server info specified for %s", host);
+           return 0;
+       }
+       /*
+          **
+        */
+       if (!(aconf = find_conf_host (cptr->confs, host, CONF_HUB)) ||
+           (aconf->port && (hop > aconf->port))) {
+           sendto_ops ("Non-Hub link %s introduced %s(%s).",
+                       get_client_name (cptr, TRUE), host,
+                       aconf ? (aconf->host ? aconf->host : "*") : "!");
+           return exit_client (cptr, cptr, cptr, "Too many servers");
+       }
+       /*
+          ** See if the newly found server has a Q line for it in
+          ** our conf. If it does, lose the link that brought it
+          ** into our network. Format:
+          **
+          ** Q:<unused>:<reason>:<servername>
+          **
+          ** Example:  Q:*:for the hell of it:eris.Berkeley.EDU
+        */
+       if ((aconf = find_conf_name (host, CONF_QUARANTINED_SERVER))) {
+           sendto_one (cptr,
+                       "ERROR :%s is not welcome: %s. %s",
+                       host, BadPtr (aconf->passwd) ?
+                       "reason unspecified" : aconf->passwd,
+                       "Try another network");
+
+           return exit_client (cptr, cptr, cptr, "Q-Lined Server");
+       }
+       acptr = make_client (cptr, find_server (parv[0], NULL));
+       (void) make_server (acptr);
+       acptr->hopcount = hop;
+
+       strncpyzt (acptr->name, host, sizeof (acptr->name));
+       strncpyzt (acptr->info, info, sizeof (acptr->info));
+       strncpyzt (acptr->version, s_version, REALLEN);
+       strncpyzt (acptr->serv->up, parv[0], sizeof (acptr->serv->up));
+       SetServer (acptr);
+       acptr->flags |= FLAGS_TS8;
+       add_client_to_list (acptr);
+       (void) add_to_client_hash_table (acptr->name, acptr);
+       /*
+          ** Old sendto_serv_but_one() call removed because we now
+          ** need to send different names to different servers
+          ** (domain name matching)
+        */
+       for (i = 0; i <= highest_fd; i++) {
+           if (!(bcptr = local[i]) || !IsServer (bcptr) ||
+               bcptr == cptr || IsMe (bcptr))
+               continue;
+           if (!(aconf = bcptr->serv->nline)) {
+               sendto_ops ("Lost N-line for %s on %s. Closing",
+                           get_client_name (cptr, TRUE), host);
+               return exit_client (cptr, cptr, cptr, "Lost N line");
+           }
+           if (match (my_name_for_link (me.name, aconf), acptr->name) == 0)
+               continue;
+           if (ts)
+               sendto_one (bcptr, ":%s SERVER %s %d %d %s :%s",
+                           parv[0], acptr->name, hop + 1, ts,
+                           acptr->version, acptr->info);
+           else
+               sendto_one (bcptr, ":%s SERVER %s %d %s :%s",
+                           parv[0], acptr->name, hop + 1,
+                           acptr->version, acptr->info);
+       }
+       /* Check for U-line status -- Barubary */
+       if (find_conf_host (cptr->confs, acptr->name, CONF_UWORLD))
+           acptr->flags |= FLAGS_ULINE;
+#ifdef USE_SERVICES
+       check_services_butone (SERVICE_WANT_SERVER, sptr,
+                              ":%s SERVER %s %d %s :%s", parv[0],
+                              acptr->name, hop + 1, acptr->version,
+                              acptr->info);
+#endif
+       return 0;
+    }
+    if (!IsUnknown (cptr) && !IsHandshake (cptr))
+       return 0;
+    /*
+       ** A local link that is still in undefined state wants
+       ** to be a SERVER. Check if this is allowed and change
+       ** status accordingly...
+     */
+    strncpyzt (cptr->name, host, sizeof (cptr->name));
+    strncpyzt (cptr->info, info[0] ? info : me.name, sizeof (cptr->info));
+    strncpyzt (cptr->version, s_version, REALLEN);
+    cptr->hopcount = hop;
+    
+    switch (check_server_init (cptr)) {
+    case 0:
+       return m_server_estab (cptr);
+    case 1:
+       sendto_ops ("Access check for %s in progress",
+                   get_client_name (cptr, TRUE));
+       return 1;
+    default:
+       ircstp->is_ref++;
+       sendto_ops ("Received unauthorized connection from %s.",
+                   get_client_host (cptr));
+       sendto_serv_butone (&me,
+                           ":%s GLOBOPS :Recieved unauthorized connection from %s.",
+                           parv[0], get_client_host (cptr));
+       return exit_client (cptr, cptr, cptr, "No C/N conf lines");
+    }
+
+}
+
+int m_server_estab (cptr)
+     aClient *cptr;
+{
+    aClient *acptr;
+    aConfItem *aconf, *bconf;
+    aChannel *chptr;
+    aJinxItem *ajinx;
+    aSqlineItem *asqline;
+
+    char *inpath, *host, *s, *encr, *ver;
+    int split, i, verflags;
+    char link_caps[200] = "";
+
+    inpath = get_client_name (cptr, TRUE);     /* "refresh" inpath with host */
+    split = mycmp (cptr->name, cptr->sockhost);
+    host = cptr->name;
+
+    current_load_data.conn_count++;
+    update_load ();
+
+    if (!(aconf = find_conf (cptr->confs, host, CONF_NOCONNECT_SERVER |
+                            CONF_NZCONNECT_SERVER))) {
+       ircstp->is_ref++;
+       sendto_one (cptr,
+                   "ERROR :Access denied. No N line for server %s", inpath);
+       sendto_ops ("Access denied. No N line for server %s", inpath);
+       return exit_client (cptr, cptr, cptr, "No N line for server");
+    }
+    if (!(bconf = find_conf (cptr->confs, host, CONF_CONNECT_SERVER |
+                            CONF_NZCONNECT_SERVER))) {
+       ircstp->is_ref++;
+       sendto_one (cptr, "ERROR :Only N (no C) field for server %s", inpath);
+       sendto_ops ("Only N (no C) field for server %s", inpath);
+       return exit_client (cptr, cptr, cptr, "No C line for server");
+    }
+    encr = cptr->passwd;
+    if (*aconf->passwd && !StrEq (aconf->passwd, encr)) {
+       ircstp->is_ref++;
+       sendto_one (cptr, "ERROR :No Access (passwd mismatch) %s", inpath);
+       sendto_ops ("Access denied (passwd mismatch) %s", inpath);
+       return exit_client (cptr, cptr, cptr, "Bad Password");
+    }
+    bzero (cptr->passwd, sizeof (cptr->passwd));
+
+#ifndef        HUB
+    for (i = 0; i <= highest_fd; i++)
+       if (local[i] && IsServer (local[i])) {
+           ircstp->is_ref++;
+           sendto_one (cptr, "ERROR :I'm a leaf not a hub");
+           return exit_client (cptr, cptr, cptr, "I'm a leaf");
+       }
+#endif
+    synchmode = 1;
+
+    /* Check for bitflags here */
+
+    if ((ver = (char *) strchr (cptr->version, '_'))) {
+       if (strlen (ver) > 4 && !strncmp (ver, "_cap", 4)) {
+           verflags = atoi (ver + 4);
+
+           if (verflags & VERSION_ZIPLINKS)
+               cptr->flags2 |= FLAGS2_CAPAB_ZIP;
+
+           sendto_ops
+               ("Synching with %s using bitflag protocol. Bitflag is %i.",
+                cptr->name, verflags);
+       }
+       else {
+           sendto_ops ("Suspicious version from server %s (%s)", cptr->name,
+                       cptr->version);
+       }
+    }
+
+    if (IsUnknown (cptr)) {
+
+       sendto_one (cptr, "PROTOCTL %s", PROTOCTL_SUPPORTED);
+
+       if (bconf->passwd[0])
+           sendto_one (cptr, "PASS :%s", bconf->passwd);
+       /*
+        * Pass my info to the new server
+        */
+
+
+       sendto_one (cptr, "SERVER %s 1 %s_cap%i :%s",
+                   my_name_for_link (me.name, aconf), version, VERSION_SEND,
+                   (me.info[0]) ? (me.info) : "IRCers United");
+
+    }
+    else {
+       s = (char *) index (aconf->host, '@');
+       *s = '\0';                /* should never be NULL */
+       Debug ((DEBUG_INFO, "Check Usernames [%s]vs[%s]",
+               aconf->host, cptr->username));
+       if (match (aconf->host, cptr->username)) {
+           *s = '@';
+           ircstp->is_ref++;
+           sendto_ops ("Username mismatch [%s]v[%s] : %s",
+                       aconf->host, cptr->username, get_client_name (cptr,
+                                                                     TRUE));
+           sendto_one (cptr, "ERROR :No Username Match");
+           return exit_client (cptr, cptr, cptr, "Bad User");
+       }
+       *s = '@';
+    }
+
+#ifdef ZIP_LINKS
+
+    if ((cptr->flags2 & FLAGS2_CAPAB_ZIP) &&
+       (bconf->status != CONF_NZCONNECT_SERVER)) {
+       strcat (link_caps, " ZIP_LINKS");
+       if (zip_init (cptr) == -1) {
+           zip_free (cptr);
+           sendto_realops ("Unable to setup compressed link for %s",
+                           get_client_name (cptr, TRUE));
+           return exit_client (cptr, cptr, &me, "zip_init() failed");
+       }
+
+       cptr->flags2 |= (FLAGS2_ZIP | FLAGS2_ZIPFIRST);
+    }
+    else
+       cptr->flags2 &= ~FLAGS2_CAPAB_ZIP;
+#endif
+
+    det_confs_butmask (cptr,
+                      CONF_HUB | CONF_NOCONNECT_SERVER | CONF_UWORLD);
+    /*
+       ** *WARNING*
+       **   In the following code in place of plain server's
+       **   name we send what is returned by get_client_name
+       **   which may add the "sockhost" after the name. It's
+       **   *very* *important* that there is a SPACE between
+       **   the name and sockhost (if present). The receiving
+       **   server will start the information field from this
+       **   first blank and thus puts the sockhost into info.
+       **   ...a bit tricky, but you have been warned, besides
+       **   code is more neat this way...  --msa
+     */
+    SetServer (cptr);
+    if (find_conf_host (cptr->confs, cptr->name, CONF_UWORLD))
+       cptr->flags |= FLAGS_ULINE;
+    cptr->flags |= FLAGS_TS8;
+    nextping = time (NULL);
+#ifdef HUB
+    sendto_serv_butone (&me, ":%s GLOBOPS :Link with %s established.",
+                       me.name, inpath);
+#endif
+    sendto_locfailops ("Link with %s established.", inpath);
+    /* Insert here */
+    (void) add_to_client_hash_table (cptr->name, cptr);
+    /* doesnt duplicate cptr->serv if allocted this struct already */
+    (void) make_server (cptr);
+    (void) strcpy (cptr->serv->up, me.name);
+    cptr->srvptr = &me;
+    cptr->serv->nline = aconf;
+    if (find_conf_host (cptr->confs, cptr->name, CONF_UWORLD))
+       cptr->flags |= FLAGS_ULINE;
+
+#ifdef ZIP_LINKS
+    cptr->flags2 |= FLAGS2_CBURST;
+#endif
+
+    strcpy (cptr->sockhost, "0.0.0.0");
+
+#ifdef USE_SERVICES
+    check_services_butone (SERVICE_WANT_SERVER, sptr,
+                          ":%s SERVER %s %d %s :%s", parv[0],
+                          cptr->name, hop + 1, cptr->version, cptr->info);
+#endif
+    /*
+       ** Old sendto_serv_but_one() call removed because we now
+       ** need to send different names to different servers
+       ** (domain name matching) Send new server to other servers.
+     */
+    for (i = 0; i <= highest_fd; i++) {
+       if (!(acptr = local[i]) || !IsServer (acptr) ||
+           acptr == cptr || IsMe (acptr))
+           continue;
+       if ((aconf = acptr->serv->nline) &&
+           !match (my_name_for_link (me.name, aconf), cptr->name))
+           continue;
+
+       sendto_one (acptr, ":%s SERVER %s 2 %s :%s",
+                   me.name, cptr->name, cptr->version, cptr->info);
+    }
+
+    /*
+       ** Pass on my client information to the new server
+       **
+       ** First, pass only servers (idea is that if the link gets
+       ** cancelled beacause the server was already there,
+       ** there are no NICK's to be cancelled...). Of course,
+       ** if cancellation occurs, all this info is sent anyway,
+       ** and I guess the link dies when a read is attempted...? --msa
+       ** 
+       ** Note: Link cancellation to occur at this point means
+       ** that at least two servers from my fragment are building
+       ** up connection this other fragment at the same time, it's
+       ** a race condition, not the normal way of operation...
+       **
+       ** ALSO NOTE: using the get_client_name for server names--
+       **   see previous *WARNING*!!! (Also, original inpath
+       **   is destroyed...)
+     */
+
+    aconf = cptr->serv->nline;
+    for (acptr = &me; acptr; acptr = acptr->prev) {
+       /* acptr->from == acptr for acptr == cptr */
+       if (acptr->from == cptr)
+           continue;
+       if (IsServer (acptr)) {
+           if (match (my_name_for_link (me.name, aconf), acptr->name) == 0)
+               continue;
+
+           sendto_one (cptr, ":%s SERVER %s %d %s :%s",
+                       acptr->serv->up, acptr->name,
+                       acptr->hopcount + 1, acptr->version, acptr->info);
+       }
+    }
+
+    for (acptr = &me; acptr; acptr = acptr->prev) {
+       if (acptr->from == cptr)
+            continue;
+       if (IsPerson (acptr)) {
+           /*
+           ** IsPerson(x) is true only when IsClient(x) is true.
+           ** These are only true when *BOTH* NICK and USER have
+           ** been received. -avalon
+           ** Apparently USER command was forgotten... -Donwulff
+           */
+           sendto_one (cptr, "%s %s %d %d %s %s %s %lu %s :%s",
+                       (IsToken (cptr) ? TOK_SNICK : MSG_SNICK),
+                       acptr->name, acptr->hopcount + 1, acptr->lastnick,
+                       acptr->user->username, acptr->user->host,
+                       acptr->user->server, acptr->user->servicestamp,
+                       get_mode_str (acptr), acptr->info);
+           if (acptr->user->away)
+               sendto_one (cptr, ":%s %s :%s", acptr->name,
+                           (IsToken (cptr) ? TOK_AWAY : MSG_AWAY),
+                           acptr->user->away);
+           }
+       else if (IsService (acptr)) {
+           sendto_one (cptr, "NICK %s :%d", acptr->name,
+                       acptr->hopcount + 1);
+           sendto_one (cptr, ":%s SERVICE * * :%s", acptr->name,
+                       acptr->info);
+       }
+    }
+
+    {
+       sendto_ops ("Synching with %s using link options:%s", cptr->name,
+                   link_caps);
+
+       for (acptr = &me; acptr; acptr = acptr->prev) {
+           if (acptr->from == cptr)
+               continue;
+           if (IsPerson (acptr)) {
+               send_user_joins (cptr, acptr);
+           }
+       }
+
+    }
+
+    for (chptr = channel; chptr; chptr = chptr->nextch) {
+         send_channel_modes_sts (cptr, chptr);
+
+         if (chptr->topic_time)
+             sendto_one (cptr, ":%s %s %s %s %lu :%s",
+                        me.name,
+                        (IsToken (cptr) ? TOK_TOPIC : MSG_TOPIC),
+                        chptr->chname, chptr->topic_nick,
+                        chptr->topic_time, chptr->topic);
+    }
+
+    synchmode = 0;
+
+    /*
+       ** Pass on all services based q-lines
+     */
+
+    for (asqline = sqline; asqline; asqline = asqline->next) {
+       if (asqline->status != CONF_ILLEGAL) {
+           if (asqline->reason) {
+               sendto_one (cptr, ":%s SQLINE %s :%s", me.name,
+                           asqline->sqline, asqline->reason);
+           }
+           else {
+               sendto_one (cptr, ":%s SQLINE %s", me.name, asqline->sqline);
+           }
+       }
+    }
+    /*
+     ** Pass on all services based jinxlines
+     */
+    for (ajinx = jinx; ajinx; ajinx = ajinx->next) {
+       if (ajinx->status != CONF_ILLEGAL) {
+           if (ajinx->reason) {
+               sendto_one (cptr, ":%s JINX %s :%s", me.name,
+                           ajinx->userhost, ajinx->reason);
+           }
+           else {
+               sendto_one (cptr, ":%s JINX %s", me.name, ajinx->userhost);
+           }
+       }
+    }
+
+#ifdef ZIP_LINKS
+    if ((cptr->flags2 & FLAGS2_ZIP) && cptr->zip->out->total_in)
+       sendto_realops ("Connect burst to %s: %lu, compressed: %lu (%3.1f%%)",
+                       get_client_name (cptr, TRUE),
+                       cptr->zip->out->total_in, cptr->zip->out->total_out,
+                       (100.0 * (float) cptr->zip->out->total_out) /
+                       (float) cptr->zip->out->total_in);
+#endif
+    return 0;
+}
+
+/*
+   ** m_info
+   **   parv[0] = sender prefix
+   **   parv[1] = servername
+ */
+int m_info (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char **text = infotext;
+
+/* Not needed re change to parse.c
+   if (check_registered(sptr))
+   return 0;
+ */
+
+#ifdef SEEUSERSTATS
+    if (!IsAnOper (sptr)) {
+       if (parc > 1 && strlen (parv[1]) > USERSTATMAX) {
+           sendto_umode (UMODE_OPER | UMODE_STATS,
+                         "*** Notice -- INFO [truncated] requested by %s (%s@%s)",
+                         sptr->name,
+                         (sptr->user) ? sptr->user->username : "",
+                         (sptr->user) ? sptr->user->host : "");
+       }
+       else {
+           sendto_umode (UMODE_OPER | UMODE_STATS,
+                         "*** Notice -- INFO %s%srequested by %s (%s@%s)",
+                         (parc > 1) ? parv[1] : "", (parc > 1) ? " " : "",
+                         sptr->name,
+                         (sptr->user) ? sptr->user->username : "",
+                         (sptr->user) ? sptr->user->host : "");
+       }
+    }
+#endif
+
+/* Send users an error if they try to do /info <something>. -Studded */
+    if (parc > 1) {
+       sendto_one (sptr, ":%s NOTICE %s :The /info command "
+                   "displays the file which lists the names of "
+                   "the developers. If you wish to view info on a "
+                   "specific nick or channel, use /nickserv info nick or "
+                   "/chanserv info #Channel as appropriate.", me.name,
+                   parv[0]);
+       return -1;
+    }
+/* Prevent remote /info. It's useless, and could be used to flood the
+ * server <> server connections. -Studded
+ if (hunt_server(cptr,sptr,":%s INFO :%s",1,parc,parv) == HUNTED_ISME)
+ {
+ */
+    while (*text)
+       sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], *text++);
+
+    sendto_one (sptr, rpl_str (RPL_INFO), me.name, parv[0], "");
+    sendto_one (sptr,
+               ":%s %d %s :Birth Date: %s, compile # %s",
+               me.name, RPL_INFO, parv[0], creation, generation);
+    sendto_one (sptr, ":%s %d %s :On-line since %s",
+               me.name, RPL_INFO, parv[0], myctime (me.firsttime));
+    sendto_one (sptr, rpl_str (RPL_ENDOFINFO), me.name, parv[0]);
+/*      } */
+
+    return 0;
+}
+
+/*
+ * RPL_NOWON  - Online at the moment (Succesfully added to WATCH-list)
+ * RPL_NOWOFF   - Offline at the moement (Succesfully added to WATCH-list)
+ * RPL_WATCHOFF - Succesfully removed from WATCH-list.
+ * ERR_TOOMANYWATCH - Take a guess :>  Too many WATCH entries.
+ */
+static void show_watch (cptr, name, rpl1, rpl2)
+     aClient *cptr;
+     char *name;
+     int rpl1, rpl2;
+{
+    aClient *acptr;
+
+
+    if ((acptr = find_person (name, NULL)))
+       sendto_one (cptr, rpl_str (rpl1), me.name, cptr->name,
+                   acptr->name, acptr->user->username,
+                   MaskHost (acptr), acptr->lastnick);
+    else
+       sendto_one (cptr, rpl_str (rpl2), me.name, cptr->name, name, "*", "*",
+                   0);
+}
+
+/*
+ * m_watch
+ */
+int m_watch (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    char *s, **pav = parv, *user;
+    char *p = NULL, *def = "l";
+
+/* Not needed re change to parse.c
+   if (check_registered(sptr))
+   return 0;
+ */
+    if (parc < 2) {
+       /*
+        * Default to 'l' - list who's currently online
+        */
+       parc = 2;
+       parv[1] = def;
+    }
+    for (s = (char *) strtoken (&p, *++pav, " "); s;
+        s = (char *) strtoken (&p, NULL, " ")) {
+       if ((user = (char *) index (s, '!')))
+           *user++ = '\0';       /* Not used */
+
+       /*
+        * Prefix of "+", they want to add a name to their WATCH
+        * list.
+        */
+       if (*s == '+') {
+           if (do_nick_name (s + 1)) {
+               if (sptr->notifies >= MAXWATCH) {
+                   sendto_one (sptr, err_str (ERR_TOOMANYWATCH),
+                               me.name, cptr->name, s + 1);
+
+                   continue;
+               }
+               add_to_notify_hash_table (s + 1, sptr);
+           }
+           show_watch (sptr, s + 1, RPL_NOWON, RPL_NOWOFF);
+           continue;
+       }
+       /*
+        * Prefix of "-", coward wants to remove somebody from their
+        * WATCH list.  So do it. :-)
+        */
+       if (*s == '-') {
+           del_from_notify_hash_table (s + 1, sptr);
+           show_watch (sptr, s + 1, RPL_WATCHOFF, RPL_WATCHOFF);
+
+           continue;
+       }
+       /*
+        * Fancy "C" or "c", they want to nuke their WATCH list and start
+        * over, so be it.
+        */
+       if (*s == 'C' || *s == 'c') {
+           hash_del_notify_list (sptr);
+           sendto_one (sptr, rpl_str (RPL_WATCHCLEAR), me.name, parv[0]);
+
+           continue;
+       }
+       /*
+        * Now comes the fun stuff, "S" or "s" returns a status report of
+        * their WATCH list.  I imagine this could be CPU intensive if its
+        * done alot, perhaps an auto-lag on this?
+        */
+       if (*s == 'S' || *s == 's') {
+           Link *lp;
+           aNotify *anptr;
+           int count = 0;
+
+           /*
+            * Send a list of how many users they have on their WATCH list
+            * and how many WATCH lists they are on.
+            */
+           anptr = hash_get_notify (sptr->name);
+           if (anptr)
+               for (lp = anptr->notify, count = 1; (lp = lp->next); count++);
+           sendto_one (sptr, rpl_str (RPL_WATCHSTAT), me.name, parv[0],
+                       sptr->notifies, count);
+
+           /*
+            * Send a list of everybody in their WATCH list. Be careful
+            * not to buffer overflow.
+            */
+           if ((lp = sptr->notify) == NULL) {
+               sendto_one (sptr, rpl_str (RPL_ENDOFWATCHLIST), me.name,
+                           parv[0], *s);
+               continue;
+           }
+           *buf = '\0';
+           strcpy (buf, lp->value.nptr->nick);
+           count = strlen (parv[0]) + strlen (me.name) + 10 + strlen (buf);
+           while ((lp = lp->next)) {
+               if (count + strlen (lp->value.nptr->nick) + 1 > BUFSIZE - 2) {
+                   sendto_one (sptr, rpl_str (RPL_WATCHLIST), me.name,
+                               parv[0], buf);
+                   *buf = '\0';
+                   count = strlen (parv[0]) + strlen (me.name) + 10;
+               }
+               strcat (buf, " ");
+               strcat (buf, lp->value.nptr->nick);
+               count += (strlen (lp->value.nptr->nick) + 1);
+           }
+           sendto_one (sptr, rpl_str (RPL_WATCHLIST), me.name, parv[0], buf);
+
+           sendto_one (sptr, rpl_str (RPL_ENDOFWATCHLIST), me.name, parv[0],
+                       *s);
+           continue;
+       }
+       /*
+        * Well that was fun, NOT.  Now they want a list of everybody in
+        * their WATCH list AND if they are online or offline? Sheesh,
+        * greedy arn't we?
+        */
+       if (*s == 'L' || *s == 'l') {
+           Link *lp = sptr->notify;
+
+           while (lp) {
+               if ((acptr = find_person (lp->value.nptr->nick, NULL)))
+                   sendto_one (sptr, rpl_str (RPL_NOWON), me.name, parv[0],
+                               acptr->name, acptr->user->username,
+                               MaskHost (acptr), acptr->lastnick);
+               /*
+                * But actually, only show them offline if its a capital
+                * 'L' (full list wanted).
+                */
+               else if (isupper (*s))
+                   sendto_one (sptr, rpl_str (RPL_NOWOFF), me.name, parv[0],
+                               lp->value.nptr->nick, "*", "*",
+                               lp->value.nptr->lasttime);
+               lp = lp->next;
+           }
+
+           sendto_one (sptr, rpl_str (RPL_ENDOFWATCHLIST), me.name, parv[0],
+                       *s);
+
+           continue;
+       }
+       /*
+        * Hmm.. unknown prefix character.. Ignore it. :-)
+        */
+    }
+
+    return 0;
+}
+
+
+
+/*
+   ** m_links
+   **   parv[0] = sender prefix
+   **   parv[1] = servername mask
+   ** or
+   **   parv[0] = sender prefix
+   **   parv[1] = server to query 
+   **      parv[2] = servername mask
+ */
+int m_links (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *mask;
+    aClient *acptr;
+
+    if (parc > 2) {
+       if (hunt_server (cptr, sptr, ":%s LINKS %s :%s", 1, parc, parv)
+           != HUNTED_ISME)
+           return 0;
+       mask = parv[2];
+    }
+    else
+       mask = parc < 2 ? NULL : parv[1];
+
+    for (acptr = client, (void) collapse (mask); acptr; acptr = acptr->next) {
+       if (!IsServer (acptr) && !IsMe (acptr))
+           continue;
+       if (!BadPtr (mask) && match (mask, acptr->name))
+           continue;
+       sendto_one (sptr, rpl_str (RPL_LINKS),
+                   me.name, parv[0], acptr->name, acptr->serv->up,
+                   acptr->hopcount, (acptr->info[0] ? acptr->info :
+                                     "(Unknown Location)"));
+    }
+
+    sendto_one (sptr, rpl_str (RPL_ENDOFLINKS), me.name, parv[0],
+               BadPtr (mask) ? "*" : mask);
+    return 0;
+}
+
+/*
+   ** m_stats
+   **   parv[0] = sender prefix
+   **   parv[1] = statistics selector (defaults to Message frequency)
+   **   parv[2] = server name (current server defaulted, if omitted)
+   **
+   **   Currently supported are:
+   **       M = Message frequency (the old stat behaviour)
+   **       L = Local Link statistics
+   **              C = Report C and N configuration lines
+ */
+/*
+   ** m_stats/stats_conf
+   **    Report N/C-configuration lines from this server. This could
+   **    report other configuration lines too, but converting the
+   **    status back to "char" is a bit akward--not worth the code
+   **    it needs...
+   **
+   **    Note:   The info is reported in the order the server uses
+   **            it--not reversed as in ircd.conf!
+ */
+
+static int report_array[19][3] = {
+    {CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
+    {CONF_NZCONNECT_SERVER, RPL_STATSCLINE, 'c'},
+    {CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
+    {CONF_CLIENT, RPL_STATSILINE, 'I'},
+    {CONF_KILL, RPL_STATSKLINE, 'K'},
+    {CONF_ZAP, RPL_STATSKLINE, 'Z'},
+    {CONF_QUARANTINED_NICK, RPL_STATSQLINE, 'Q'},
+    {CONF_OPERATOR, RPL_STATSOLINE, 'O'},
+    {CONF_HUB, RPL_STATSHLINE, 'H'},
+    {CONF_LOCOP, RPL_STATSOLINE, 'o'},
+    {CONF_SERVICE, RPL_STATSSLINE, 'S'},
+    {CONF_UWORLD, RPL_STATSULINE, 'U'},
+    {CONF_MISSING, RPL_STATSXLINE, 'X'},
+    {CONF_ZTIME, RPL_STATSFLINE, 'F'},
+    {CONF_DCCBLOCK, RPL_STATSBLINE, 'E'},
+    {0, 0}
+};
+
+static void report_sqlined_nicks (sptr)
+     aClient *sptr;
+{
+    aSqlineItem *tmp;
+    char *nickmask, *reason;
+
+    for (tmp = sqline; tmp; tmp = tmp->next) {
+       if (tmp->status != CONF_ILLEGAL) {
+           nickmask = BadPtr (tmp->sqline) ? "<NULL>" : tmp->sqline;
+           reason = BadPtr (tmp->reason) ? "<NULL>" : tmp->reason;
+           sendto_one (sptr, rpl_str (RPL_SQLINE_NICK), me.name,
+                       sptr->name, nickmask, reason);
+       }
+    }
+}
+static void report_configured_links (sptr, mask)
+     aClient *sptr;
+     int mask;
+{
+    static char null[] = "<NULL>";
+    aConfItem *tmp;
+    int *p, port, tmpmask;
+    char c, *host, *pass, *name;
+
+    tmpmask = (mask == CONF_MISSING) ? CONF_CONNECT_SERVER : mask;
+
+    for (tmp = conf; tmp; tmp = tmp->next)
+       if (tmp->status & tmpmask) {
+           for (p = &report_array[0][0]; *p; p += 3)
+               if (*p == tmp->status)
+                   break;
+           if (!*p)
+               continue;
+           c = (char) *(p + 2);
+           host = BadPtr (tmp->host) ? null : tmp->host;
+           pass = BadPtr (tmp->passwd) ? null : tmp->passwd;
+           name = BadPtr (tmp->name) ? null : tmp->name;
+           port = (int) tmp->port;
+           /*
+            * On K line the passwd contents can be
+            * displayed on STATS reply.    -Vesa
+            */
+           /* Same with Z-lines and q/Q-lines -- Barubary */
+           if ((tmp->status == CONF_KILL) || (tmp->status & CONF_QUARANTINE)
+               || (tmp->status == CONF_ZAP)) {
+/* These mods are to tell the difference between the different kinds
+ * of klines.  the only effect it has is in the display.  --Russell
+ */
+/* Now translates spaces to _'s to show comments in klines -- Barubary */
+               char *temp;
+               if (!pass)
+                   strcpy (buf, "<NULL>");
+               else {
+                   strcpy (buf, pass);
+                   for (temp = buf; *temp; temp++)
+                       if (*temp == ' ')
+                           *temp = '_';
+               }
+               /* semicolon intentional -- Barubary */
+               if (tmp->status == CONF_QUARANTINED_NICK);
+               /* Hide password for servers -- Barubary */
+               else if (tmp->status & CONF_QUARANTINE)
+                   strcpy (buf, "*");
+               else {
+/* This wasn't documented before - comments aren't displayed for akills
+   because they are all the same. -- Barubary */
+                   if (tmp->tmpconf == KLINE_AKILL)
+                       strcpy (buf, "*");
+                   /* KLINE_PERM == 0 - watch out when doing
+                      Z-lines. -- Barubary */
+                   if (tmp->status != CONF_ZAP) {
+                       if (tmp->tmpconf == KLINE_PERM)
+                           c = 'K';
+                       if (tmp->tmpconf == KLINE_TEMP)
+                           c = 'k';
+                       if (tmp->tmpconf == KLINE_AKILL)
+                           c = 'A';
+                   }
+                   else {
+                       if (tmp->tmpconf == KLINE_PERM)
+                           c = 'Z';
+                       if (tmp->tmpconf == KLINE_TEMP)
+                           c = 'z';
+                       if (tmp->tmpconf == KLINE_AKILL)
+                           c = 'S';
+                   }
+               }
+               sendto_one (sptr, rpl_str (p[1]), me.name,
+                           sptr->name, c, host,
+                           buf, name, port, get_conf_class (tmp));
+           }
+           /* z-line times     -taz */
+           else if (tmp->status & CONF_ZTIME)
+               sendto_one (sptr, rpl_str (p[1]), me.name, sptr->name, host);
+           /* Only display on X if server is missing */
+           else if (mask == CONF_MISSING) {
+               if (!find_server (name, NULL))
+                   sendto_one (sptr, rpl_str (RPL_STATSXLINE), me.name,
+                               sptr->name, name, port);
+           }
+           else {
+               if (!IsOper (sptr) && (mask & CONF_NOCONNECT_SERVER ||
+                                      mask & CONF_CONNECT_SERVER))
+                   sendto_one (sptr, rpl_str (p[1]), me.name,
+                               sptr->name, c, "*", name, port,
+                               get_conf_class (tmp));
+               else
+                   sendto_one (sptr, rpl_str (p[1]), me.name,
+                               sptr->name, c, host, name, port,
+                               get_conf_class (tmp));
+           }
+       }
+    return;
+}
+
+static void report_jinx (aClient * sptr)
+{
+    aJinxItem *ajinx;
+    if (!IsAnOper (sptr))
+       return;
+    for (ajinx = jinx; ajinx; ajinx = ajinx->next) {
+       sendto_one (sptr, rpl_str (RPL_STATSJINX), me.name,
+                   sptr->name, ajinx->userhost,
+                   ajinx->reason ? ajinx->reason : "*");
+    }
+}
+
+
+/* Used to blank out ports -- Barubary */
+char *get_client_name2 (aClient * acptr, int showports)
+{
+    char *pointer = get_client_name (acptr, TRUE);
+
+    if (!pointer)
+       return NULL;
+    if (showports)
+       return pointer;
+    if (!(char *) strrchr (pointer, '.'))
+       return NULL;
+    strcpy ((char *) strrchr (pointer, '.'), ".0]");
+
+    return pointer;
+}
+
+int m_stats (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+#ifndef DEBUGMODE
+    static char Sformat[] =
+       ":%s %d %s SendQ SendM SendBytes RcveM RcveBytes Open_since :Idle";
+    static char Lformat[] = ":%s %d %s %s %u %u %u %u %u %u :%u";
+#else
+    static char Sformat[] =
+       ":%s %d %s SendQ SendM SendBytes RcveM RcveBytes Open_since CPU :Idle";
+    static char Lformat[] = ":%s %d %s %s %u %u %u %u %u %u %s";
+    char pbuf[96]; /* Should be enough for to ints */
+#endif
+    struct Message *mptr;
+    aClient *acptr;
+    char stat = parc > 1 ? parv[1][0] : '\0';
+    int i;
+    int doall = 0, wilds = 0, showports = IsAnOper (sptr), remote = 0;
+    char *name;
+
+/* Not needed re change to parse.c
+   if (check_registered(sptr))
+   return 0;
+ */
+    if (hunt_server (cptr, sptr, ":%s STATS %s :%s", 2, parc, parv) !=
+       HUNTED_ISME)
+       return 0;
+
+#ifdef SEEUSERSTATS
+    if (!IsAnOper (sptr)) {
+       if (parc > 1 && strlen (parv[1]) > USERSTATMAX) {
+           sendto_umode (UMODE_OPER | UMODE_STATS,
+                         "*** Notice -- STATS [truncated] requested by %s (%s@%s)",
+                         sptr->name,
+                         (sptr->user) ? sptr->user->username : "",
+                         (sptr->user) ? sptr->user->host : "");
+       }
+       else {
+           sendto_umode (UMODE_OPER | UMODE_STATS,
+                         "*** Notice -- STATS %s%srequested by %s (%s@%s)",
+                         (parc > 1) ? parv[1] : "", (parc > 1) ? " " : "",
+                         sptr->name,
+                         (sptr->user) ? sptr->user->username : "",
+                         (sptr->user) ? sptr->user->host : "");
+       }
+    }
+#endif
+
+    if (parc > 2) {
+       name = parv[2];
+       if (!mycmp (name, me.name))
+           doall = 2;
+       else if (match (name, me.name) == 0)
+           doall = 1;
+       if (index (name, '*') || index (name, '?'))
+           wilds = 1;
+    }
+    else
+       name = me.name;
+
+/* Add better error checking for common user mistakes. Use a
+ * NOTICE instead of a numeric because more clients can see
+ * and display them properly. -Studded */
+
+    if (!(stat)) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "STATS");
+       return -1;
+    }
+    if ((parc == 2) && (parv[1][1])) {
+       sendto_one (sptr, ":%s NOTICE %s :You have requested a stat "
+                   "(%s) that does not exist.", me.name, parv[0], parv[1]);
+       return -1;
+    }
+    switch (stat) {
+    case 'L':
+    case 'l':
+       /*
+        * send info about connections which match, or all if the
+        * mask matches me.name.  Only restrictions are on those who
+        * are invisible not being visible to 'foreigners' who use
+        * a wild card based search to list it.
+        */
+
+       if (!IsAnOper (sptr))
+           break;
+
+       sendto_one (sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
+       if (IsServer (cptr)) {
+           remote = 1;
+           wilds = 0;
+       }
+       for (i = 0; i <= highest_fd; i++) {
+           if (!(acptr = local[i]))
+               continue;
+           if (IsInvisible (acptr) && (doall || wilds) &&
+               !(MyConnect (sptr) && IsOper (sptr)) &&
+               !IsAnOper (acptr) && (acptr != sptr))
+               continue;
+           if (remote && doall && !IsServer (acptr) && !IsMe (acptr))
+               continue;
+           if (remote && !doall && IsServer (acptr))
+               continue;
+           if (!doall && wilds && match (name, acptr->name))
+               continue;
+           if (!(parc == 2 && IsServer (acptr)) &&
+               !(doall || wilds) && mycmp (name, acptr->name))
+               continue;
+#ifdef DEBUGMODE
+           sprintf (pbuf, "%d :%d", acptr->cputime,
+                    (acptr->user && MyConnect (acptr)) ?
+                    time (NULL) - acptr->user->last : 0);
+#endif
+           if (IsOper (sptr))
+               sendto_one (sptr, Lformat, me.name,
+                           RPL_STATSLINKINFO, parv[0],
+                           (isupper (stat)) ?
+                           get_client_name2 (acptr, showports) :
+                           get_client_name (acptr, FALSE),
+                           (int) DBufLength (&acptr->sendQ),
+                           (int) acptr->sendM, (int) acptr->sendK,
+                           (int) acptr->receiveM, (int) acptr->receiveK,
+                           time (NULL) - acptr->firsttime,
+#ifndef DEBUGMODE
+                           (acptr->user && MyConnect (acptr)) ?
+                           time (NULL) - acptr->user->last : 0);
+#else
+                           pbuf);
+#endif
+           else if (!strchr (acptr->name, '.'))
+               sendto_one (sptr, Lformat, me.name,
+                           RPL_STATSLINKINFO, parv[0],
+                           (isupper (stat)) ?
+                           get_client_name2 (acptr, showports) :
+                           get_client_name (acptr, FALSE),
+                           (int) DBufLength (&acptr->sendQ),
+                           (int) acptr->sendM, (int) acptr->sendK,
+                           (int) acptr->receiveM, (int) acptr->receiveK,
+                           time (NULL) - acptr->firsttime,
+#ifndef DEBUGMODE
+                           (acptr->user && MyConnect (acptr)) ?
+                           time (NULL) - acptr->user->last : 0);
+#else
+                           pbuf);
+#endif
+       }
+       break;
+    case 'C':
+    case 'c':
+       if (IsOper (sptr))
+           report_configured_links (sptr, CONF_CONNECT_SERVER |
+                                    CONF_NOCONNECT_SERVER |
+                                    CONF_NZCONNECT_SERVER);
+       break;
+    case 'H':
+    case 'h':
+       if (IsOper (sptr))
+           report_configured_links (sptr, CONF_HUB);
+       break;
+    case 'I':
+    case 'i':
+       report_configured_links (sptr, CONF_CLIENT);
+       break;
+    case 'K':
+       if (IsOper (sptr))
+            report_configured_links (sptr, CONF_KILL | CONF_ZAP | CONF_KILLEXEMPT);
+       else
+            report_configured_links (sptr, CONF_KILL | CONF_ZAP);
+        break;
+    case 'k':
+       if (IsOper (sptr))
+           report_configured_links (sptr, CONF_KILL | CONF_KILLEXEMPT);
+       else
+           report_configured_links (sptr, CONF_KILL);
+           
+       break;
+    case 'M':
+    case 'm':
+       if (IsOper (sptr)) {
+           for (mptr = msgtab; mptr->cmd; mptr++)
+               if (mptr->count)
+#ifndef DEBUGMODE
+                   sendto_one (sptr, rpl_str (RPL_STATSCOMMANDS),
+                               me.name, parv[0], mptr->cmd, mptr->count,
+                               mptr->bytes);
+#else
+                   sendto_one (sptr, rpl_str (RPL_STATSCOMMANDS),
+                               me.name, parv[0], mptr->cmd,
+                               mptr->count, mptr->bytes,
+                               mptr->lticks,
+                               mptr->lticks / CLOCKS_PER_SEC,
+                               mptr->rticks, mptr->rticks / CLOCKS_PER_SEC);
+#endif
+       }
+       else {
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       }
+       break;
+    case 'o':
+    case 'O':
+       if (IsOper (sptr))
+           report_configured_links (sptr, CONF_OPS);
+       else
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       break;
+    case 'Q':
+       report_configured_links (sptr, CONF_QUARANTINE);
+       break;
+    case 'q':
+       report_sqlined_nicks (sptr);
+       break;
+    case 'R':
+    case 'r':
+#ifdef DEBUGMODE
+       send_usage (sptr, parv[0]);
+#endif
+       break;
+    case 'S':
+    case 's':
+       report_configured_links (sptr, CONF_SERVICE);
+       break;
+    case 'T':
+    case 't':
+       tstats (sptr, parv[0]);
+       break;
+    case 'U':
+       if (IsOper (sptr))
+           report_configured_links (sptr, CONF_UWORLD);
+       else
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       break;
+    case 'f':
+    case 'F':
+       report_configured_links (sptr, CONF_ZTIME);
+       break;
+    case 'u':
+       {
+           register time_t tmpnow;
+
+           tmpnow = time (NULL) - me.since;
+           sendto_one (sptr, rpl_str (RPL_STATSUPTIME), me.name, parv[0],
+                       tmpnow / 86400, (tmpnow / 3600) % 24,
+                       (tmpnow / 60) % 60, tmpnow % 60);
+           sendto_one (sptr, rpl_str (RPL_STATSCONN), me.name, parv[0],
+                       max_connection_count, max_client_count);
+           break;
+       }
+    case 'W':
+    case 'w':
+       if (IsOper (sptr))
+           calc_load (sptr, parv[0]);
+       else
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       break;
+    case 'X':
+    case 'x':
+       if (IsOper (sptr))
+           report_configured_links (sptr, CONF_MISSING);
+       else
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       break;
+    case 'Y':
+    case 'y':
+       report_classes (sptr);
+       break;
+    case 'Z':
+        report_configured_links (sptr, CONF_ZAP);
+        break;
+    case 'z':
+       count_memory (sptr, parv[0]);
+       break;
+    case 'J':
+    case 'j':
+       if (IsOper (sptr))
+           report_jinx (sptr);
+       else
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       break;
+    case '?':
+       serv_info (sptr, parv[0]);
+       break;
+    case 'E':
+    case 'e':
+       if (IsOper (sptr))
+           report_configured_links (sptr, CONF_DCCBLOCK);
+       else
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       break;
+    default:
+       sendto_one (sptr, ":%s NOTICE %s :You have requested a stat "
+                   "(%c) that does not exist.", me.name, parv[0], stat);
+       break;
+    }
+    sendto_one (sptr, rpl_str (RPL_ENDOFSTATS), me.name, parv[0], stat);
+    return 0;
+}
+
+/*
+   ** Note: At least at protocol level ERROR has only one parameter,
+   ** although this is called internally from other functions
+   ** --msa
+   **
+   **   parv[0] = sender prefix
+   **   parv[*] = parameters
+ */
+int m_error (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *para;
+
+    para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>";
+
+    Debug ((DEBUG_ERROR, "Received ERROR message from %s: %s",
+           sptr->name, para));
+    /*
+       ** Ignore error messages generated by normal user clients
+       ** (because ill-behaving user clients would flood opers
+       ** screen otherwise). Pass ERROR's from other sources to
+       ** the local operator...
+     */
+    if (IsPerson (cptr) || IsUnknown (cptr) || IsService (cptr))
+       return 0;
+    if (cptr == sptr) {
+       sendto_serv_butone (&me, ":%s GLOBOPS :ERROR from %s -- %s",
+                           me.name, get_client_name (cptr, FALSE), para);
+       sendto_locfailops ("ERROR :from %s -- %s",
+                          get_client_name (cptr, FALSE), para);
+    }
+    else {
+       sendto_serv_butone (&me, ":%s GLOBOPS :ERROR from %s via %s -- %s",
+                           me.name, sptr->name, get_client_name (cptr,
+                                                                 FALSE),
+                           para);
+       sendto_ops ("ERROR :from %s via %s -- %s", sptr->name,
+                   get_client_name (cptr, FALSE), para);
+    }
+    return 0;
+}
+
+/*
+   ** m_help
+   **      parv[0] = sender prefix
+ */
+int m_help (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int i;
+    if (MyClient (sptr)) {
+#ifdef SEEUSERSTATS
+       if (!IsAnOper (sptr)) {
+           if (parc > 1 && strlen (parv[1]) > USERSTATMAX) {
+               sendto_umode (UMODE_OPER | UMODE_STATS,
+                             "*** Notice -- HELP [truncated] requested by %s (%s@%s)",
+                             sptr->name,
+                             (sptr->user) ? sptr->user->username : "",
+                             (sptr->user) ? sptr->user->host : "");
+           }
+           else {
+               sendto_umode (UMODE_OPER | UMODE_STATS,
+                             "*** Notice -- HELP %s%srequested by %s (%s@%s)",
+                             (parc > 1) ? parv[1] : "",
+                             (parc > 1) ? " " : "", sptr->name,
+                             (sptr->user) ? sptr->user->username : "",
+                             (sptr->user) ? sptr->user->host : "");
+           }
+       }
+#endif
+       sendto_one (sptr, ":%s NOTICE %s :\ 2Full command list\ 2:",
+                   me.name, parv[0]);
+       sendto_one (sptr, ":%s NOTICE %s :command token", me.name, parv[0]);
+       sendto_one (sptr, ":%s NOTICE %s :=============", me.name, parv[0]);
+       for (i = 0; msgtab[i].cmd; i++)
+           sendto_one (sptr, ":%s NOTICE %s :%s %s",
+                       me.name, parv[0], msgtab[i].cmd, msgtab[i].token);
+       sendto_one (sptr, ":%s NOTICE %s :=============", me.name, parv[0]);
+       return 0;
+    }
+    return -1;
+}
+
+/*
+ * parv[0] = sender
+ * parv[1] = host/server mask.
+ * parv[2] = server to query
+ */
+int m_lusers (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int s_count, c_count, u_count, i_count;
+    int o_count, m_client, m_client_local, m_server;
+    char mydom_mask[HOSTLEN + 1];
+    aClient *acptr;
+
+/* Not needed re change to parse.c
+   if (check_registered_user(sptr))
+   return 0;
+ */
+    if (parc > 2)
+       if (hunt_server (cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv)
+           != HUNTED_ISME)
+           return 0;
+
+    mydom_mask[0] = '*';
+    strncpy (&mydom_mask[1], DOMAINNAME, HOSTLEN - 1);
+
+    if (parc == 1) {
+       s_count = lu_serv;
+       c_count = lu_noninv;
+       u_count = lu_unknown;
+       i_count = lu_inv;
+       o_count = lu_oper;
+       m_client = lu_lu;
+       m_client_local = lu_lulocal;
+       m_server = lu_lserv;
+       global_count = lu_cglobalu;
+    }
+    else {
+       s_count = c_count = u_count = i_count = o_count = m_client =
+           m_client_local = m_server = 0;
+       (void) collapse (parv[1]);
+       for (acptr = client; acptr; acptr = acptr->next) {
+           if (parc > 1) {
+               if (!IsServer (acptr) && acptr->user) {
+                   if (match (parv[1], acptr->user->server))
+                       continue;
+               }
+               else {
+                   if (match (parv[1], acptr->name))
+                       continue;
+               }
+           }
+           switch (acptr->status) {
+           case STAT_SERVER:
+               if (MyConnect (acptr))
+                   m_server++;
+           case STAT_ME:
+               s_count++;
+               break;
+           case STAT_CLIENT:
+               if (IsOper (acptr))
+                   o_count++;
+               if (MyConnect (acptr)) {
+                   m_client++;
+                   if (match (mydom_mask, acptr->sockhost) == 0)
+                       m_client_local++;
+               }
+               if (!IsInvisible (acptr))
+                   c_count++;
+               else
+                   i_count++;
+               break;
+           default:
+               u_count++;
+               break;
+           }
+       }
+    }
+    sendto_one (sptr, rpl_str (RPL_LUSERCLIENT), me.name, parv[0],
+               c_count, i_count, s_count);
+    max_client_count = lu_mlu;
+    max_global_count = lu_mglobalu;
+
+    if (o_count)
+       sendto_one (sptr, rpl_str (RPL_LUSEROP), me.name, parv[0], o_count);
+    if (u_count > 0)
+       sendto_one (sptr, rpl_str (RPL_LUSERUNKNOWN), me.name, parv[0],
+                   u_count);
+    if ((c_count = count_channels (sptr)) > 0)
+       sendto_one (sptr, rpl_str (RPL_LUSERCHANNELS),
+                   me.name, parv[0], lu_channel);
+    sendto_one (sptr, rpl_str (RPL_LUSERME),
+               me.name, parv[0], m_client, m_server);
+    sendto_one (sptr, rpl_str (RPL_LOCALUSERS),
+               me.name, parv[0], m_client, max_client_count);
+    sendto_one (sptr, rpl_str (RPL_GLOBALUSERS),
+               me.name, parv[0], global_count, max_global_count);
+    sendto_one (sptr, rpl_str (RPL_STATSCONN),
+               me.name, parv[0], max_connection_count, max_client_count);
+    if ((m_client + m_server) > max_connection_count) {
+       max_connection_count = m_client + m_server;
+       if (max_connection_count % 10 == 0)     /* only send on even tens */
+           sendto_ops ("Maximum connections: %d (%d clients)",
+                       max_connection_count, max_client_count);
+    }
+    current_load_data.local_count = m_client_local;
+    current_load_data.client_count = m_client;
+    current_load_data.conn_count = m_client + m_server;
+    return 0;
+}
+
+
+/***********************************************************************
+ * m_connect() - Added by Jto 11 Feb 1989
+ ***********************************************************************/
+
+/*
+   ** m_connect
+   **   parv[0] = sender prefix
+   **   parv[1] = servername
+   **   parv[2] = port number
+   **   parv[3] = remote server
+ */
+int m_connect (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int port, tmpport, retval;
+    aConfItem *aconf, *cconf;
+    aClient *acptr;
+
+    if (check_registered (sptr))
+       return 0;
+
+    if (!IsPrivileged (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return -1;
+    }
+    if (MyClient (sptr) && !OPCanGRoute (sptr) && parc > 3) {  /* Only allow LocOps to make */
+       /* local CONNECTS --SRB      */
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if (MyClient (sptr) && !OPCanLRoute (sptr) && parc <= 3) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if (hunt_server (cptr, sptr, ":%s CONNECT %s %s :%s",
+                    3, parc, parv) != HUNTED_ISME)
+       return 0;
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "CONNECT");
+       return -1;
+    }
+    if ((acptr = find_server (parv[1], NULL))) {
+       sendto_one (sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
+                   me.name, parv[0], parv[1], "already exists from",
+                   acptr->from->name);
+       return 0;
+    }
+    for (aconf = conf; aconf; aconf = aconf->next)
+       if ((aconf->status == CONF_CONNECT_SERVER ||
+            aconf->status == CONF_NZCONNECT_SERVER) &&
+           match (parv[1], aconf->name) == 0)
+           break;
+    /* Checked first servernames, then try hostnames. */
+    if (!aconf)
+       for (aconf = conf; aconf; aconf = aconf->next)
+           if (aconf->status == CONF_CONNECT_SERVER &&
+               (match (parv[1], aconf->host) == 0 ||
+                match (parv[1], index (aconf->host, '@') + 1) == 0))
+               break;
+
+    if (!aconf) {
+       sendto_one (sptr,
+                   "NOTICE %s :Connect: Host %s not listed in ircd.conf",
+                   parv[0], parv[1]);
+       return 0;
+    }
+    /*
+       ** Get port number from user, if given. If not specified,
+       ** use the default form configuration structure. If missing
+       ** from there, then use the precompiled default.
+     */
+    tmpport = port = aconf->port;
+    if (parc > 2 && !BadPtr (parv[2])) {
+       if ((port = atoi (parv[2])) <= 0) {
+           sendto_one (sptr, "NOTICE %s :Connect: Illegal port number",
+                       parv[0]);
+           return 0;
+       }
+    }
+    else if (port <= 0 && (port = PORTNUM) <= 0) {
+       sendto_one (sptr, ":%s NOTICE %s :Connect: missing port number",
+                   me.name, parv[0]);
+       return 0;
+    }
+    /*
+       ** Notify all operators about remote connect requests
+     */
+    if (!IsAnOper (cptr)) {
+       sendto_serv_butone (&me,
+                           ":%s GLOBOPS :Remote CONNECT %s %s from %s",
+                           me.name, parv[1], parv[2] ? parv[2] : "",
+                           get_client_name (sptr, FALSE));
+#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECT)
+       syslog (LOG_DEBUG, "CONNECT From %s : %s %d", parv[0], parv[1],
+               parv[2] ? parv[2] : "");
+#endif
+    }
+    aconf->port = port;
+    switch (retval = connect_server (aconf, sptr, NULL)) {
+    case 0:
+       sendto_one (sptr,
+                   ":%s NOTICE %s :*** Connecting to %s[%s].",
+                   me.name, parv[0], aconf->host, aconf->name);
+       break;
+    case -1:
+       sendto_one (sptr, ":%s NOTICE %s :*** Couldn't connect to %s.",
+                   me.name, parv[0], aconf->host);
+       break;
+    case -2:
+       sendto_one (sptr, ":%s NOTICE %s :*** Host %s is unknown.",
+                   me.name, parv[0], aconf->host);
+       break;
+    default:
+       sendto_one (sptr,
+                   ":%s NOTICE %s :*** Connection to %s failed: %s",
+                   me.name, parv[0], aconf->host, strerror (retval));
+    }
+    aconf->port = tmpport;
+    return 0;
+}
+
+/* m_gnotice  (Russell) sort of like wallop, but only to +g clients on 
+   ** this server.
+   **   parv[0] = sender prefix
+   **   parv[1] = message text
+ */
+int m_gnotice (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *message;
+/*  char *pv[4]; Compiler says this is not used */
+
+    if (check_registered (sptr))
+       return 0;
+
+    message = parc > 1 ? parv[1] : NULL;
+
+    if (BadPtr (message)) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "GNOTICE");
+       return 0;
+    }
+    if (!IsServer (sptr) && MyConnect (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    sendto_serv_butone (IsServer (cptr) ? cptr : NULL, ":%s GNOTICE :%s",
+                       parv[0], message);
+    sendto_failops ("from %s: %s", parv[0], message);
+    return 0;
+}
+
+/*
+   ** m_globops (write to opers who are +g currently online)
+   **      parv[0] = sender prefix
+   **      parv[1] = message text
+ */
+int m_globops (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *message;
+/*  char    *pv[4]; Compiler says this is not used */
+
+    if (check_registered (sptr))
+       return 0;
+
+    message = parc > 1 ? parv[1] : NULL;
+
+    if (BadPtr (message)) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "GLOBOPS");
+       return 0;
+    }
+    if (MyClient (sptr) && !OPCanGlobOps (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    sendto_serv_butone (IsServer (cptr) ? cptr : NULL,
+                       ":%s GLOBOPS :%s", parv[0], message);
+    sendto_failops_whoare_opers ("from %s: %s", parv[0], message);
+
+    return 0;
+}
+
+/*
+   ** m_locops (write to opers who are +g currently online *this* server)
+   **      parv[0] = sender prefix
+   **      parv[1] = message text
+ */
+int m_locops (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *message;
+/*  char    *pv[4]; Compiler says this is not used */
+
+    if (check_registered_user (cptr))
+       return 0;
+
+    message = parc > 1 ? parv[1] : NULL;
+
+    if (BadPtr (message)) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "LOCOPS");
+       return 0;
+    }
+    if (MyClient (sptr) && !OPCanLocOps (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    sendto_locfailops ("from %s: %s", parv[0], message);
+
+    return 0;
+}
+
+/* m_goper  (Russell) sort of like wallop, but only to ALL +o clients on
+   ** every server.
+   **      parv[0] = sender prefix
+   **      parv[1] = message text
+ */
+int m_goper (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *message;
+/*  char *pv[4]; Compiler says this is not used */
+
+    if (check_registered (sptr))
+       return 0;
+
+    message = parc > 1 ? parv[1] : NULL;
+
+    if (BadPtr (message)) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "GOPER");
+       return 0;
+    }
+/*      if (!IsServer(sptr) && MyConnect(sptr) && !IsAnOper(sptr)) */
+    if (!IsServer (sptr) || !IsULine (cptr, sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    sendto_serv_butone (IsServer (cptr) ? cptr : NULL, ":%s GOPER :%s",
+                       parv[0], message);
+    sendto_opers ("from %s: %s", parv[0], message);
+    return 0;
+}
+
+/*
+   ** m_time
+   **   parv[0] = sender prefix
+   **   parv[1] = servername
+ */
+int m_time (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+
+    time_t t1;
+    int ticks;
+
+    ticks = time (&t1);
+
+    if (hunt_server (cptr, sptr, ":%s TIME :%s", 1, parc, parv) ==
+       HUNTED_ISME)
+       sendto_one (sptr, rpl_str (RPL_TIME), me.name, parv[0], me.name,
+                   myctime (ticks));
+    return 0;
+}
+
+/*
+   ** m_svskill
+   **   parv[0] = servername
+   **   parv[1] = client
+   **   parv[2] = kill message
+ */
+int m_svskill (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    char *comment = (parc > 2 && parv[2]) ? parv[2] : "SVS Killed";
+
+    if (!IsULine (cptr, sptr))
+       return -1;
+
+    if (hunt_server (cptr, sptr, ":%s SVSKILL %s :%s", 1, parc, parv) !=
+       HUNTED_ISME)
+       return 0;
+
+    if (parc < 1 || (!(acptr = find_client (parv[1], NULL))))
+       return 0;
+
+    sendto_locfailops ("I'm killing %s because he is local!", parv[1]);
+    return exit_client (acptr, acptr, acptr, comment);
+}
+
+/*
+   ** m_admin
+   **   parv[0] = sender prefix
+   **   parv[1] = servername
+ */
+int m_admin (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aConfItem *aconf;
+
+    /* Make sure they stick to the local server if not registered.
+     *  -Studded */
+    if ((!IsRegistered (cptr)) && (!BadPtr (parv[1]))) {
+       sendto_one (cptr, ":%s %d ADMIN :You have not registered",
+                   me.name, ERR_NOTREGISTERED);
+       return -1;
+    }
+    /* This probably isn't needed anymore, but I don't
+     * understand it. -Studded 23 May 1998 */
+
+    /* Only allow remote ADMINs if registered -- Barubary */
+    if (IsPerson (sptr) || IsServer (cptr))
+       if (hunt_server (cptr, sptr, ":%s ADMIN :%s", 1, parc, parv) !=
+           HUNTED_ISME)
+           return 0;
+
+#ifdef SEEUSERSTATS
+    if (!IsAnOper (sptr)) {
+       if (parc > 1 && strlen (parv[1]) > USERSTATMAX) {
+           sendto_umode (UMODE_OPER | UMODE_STATS,
+                         "*** Notice -- ADMIN [truncated] requested by %s (%s@%s)",
+                         sptr->name,
+                         (sptr->user) ? sptr->user->username : "",
+                         (sptr->user) ? sptr->user->host : "");
+       }
+       else {
+           sendto_umode (UMODE_OPER | UMODE_STATS,
+                         "*** Notice -- ADMIN %s%srequested by %s (%s@%s)",
+                         (parc > 1) ? parv[1] : "", (parc > 1) ? " " : "",
+                         sptr->name,
+                         (sptr->user) ? sptr->user->username : "",
+                         (sptr->user) ? sptr->user->host : "");
+       }
+    }
+#endif
+
+    if ((aconf = find_admin ())) {
+       sendto_one (sptr, rpl_str (RPL_ADMINME), me.name, parv[0], me.name);
+       sendto_one (sptr, rpl_str (RPL_ADMINLOC1),
+                   me.name, parv[0], (aconf->host ? aconf->host : "-"));
+       sendto_one (sptr, rpl_str (RPL_ADMINLOC2),
+                   me.name, parv[0], (aconf->passwd ? aconf->passwd : "-"));
+       sendto_one (sptr, rpl_str (RPL_ADMINEMAIL),
+                   me.name, parv[0], (aconf->name ? aconf->name : "-"));
+    }
+    else
+       sendto_one (sptr, err_str (ERR_NOADMININFO), me.name, parv[0],
+                   me.name);
+    return 0;
+}
+
+/*
+   ** m_rehash
+   **
+ */
+int m_rehash (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    if (!MyClient (sptr) || !OPCanRehash (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    sendto_one (sptr, rpl_str (RPL_REHASHING), me.name, parv[0], configfile);
+    sendto_ops ("%s is rehashing Server config file", parv[0]);
+#ifdef USE_SYSLOG
+    syslog (LOG_INFO, "REHASH From %s\n", get_client_name (sptr, FALSE));
+#endif
+    return rehash (cptr, sptr, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0);
+}
+
+/*
+   ** m_restart
+   **
+   ** parv[1] - password *OR* reason if no X:line
+   ** parv[2] - reason for restart (optional & only if X:line exists)
+   **
+   ** The password is only valid if there is a matching X line in the
+   ** config file. If it is not,  then it becomes the 
+ */
+int m_restart (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *pass = NULL;
+
+    if (check_registered (sptr))
+       return 0;
+
+    if (!MyClient (sptr) || !OPCanRestart (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if ((pass = find_restartpass ())) {
+       if (parc < 2) {
+           sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                       "RESTART");
+           return 0;
+       }
+       if (strcmp (pass, parv[1])) {
+           sendto_one (sptr, err_str (ERR_PASSWDMISMATCH), me.name, parv[0]);
+           return 0;
+       }
+       /* Hack to make the code after this if { } easier: we assign the comment to the
+        * first param, as if we had not had an X:line. We do not need the password
+        * now anyways. Accordingly we decrement parc ;)    -- NikB
+        */
+       parv[1] = parv[2];
+       parc--;
+    }
+#ifdef USE_SYSLOG
+    syslog (LOG_WARNING, "Server RESTART by %s - %s\n",
+           get_client_name (sptr, FALSE),
+           (parc > 1 ? parv[1] : "No reason"));
+#endif
+    sendto_ops ("Server is Restarting by request of %s", parv[0]);
+    server_reboot ((parv[1] ? parv[1] : "No Reason"));
+    return 0;
+}
+
+/*
+   ** m_trace
+   **   parv[0] = sender prefix
+   **   parv[1] = servername
+ */
+int m_trace (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int i;
+    aClient *acptr;
+    aClass *cltmp;
+    char *tname;
+    int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
+    int cnt = 0, wilds, dow;
+    time_t now;
+
+    if (check_registered (sptr))
+       return 0;
+
+    if (parc > 2)
+       if (hunt_server (cptr, sptr, ":%s TRACE %s :%s", 2, parc, parv))
+           return 0;
+
+    if (parc > 1)
+       tname = parv[1];
+    else
+       tname = me.name;
+
+    if (!IsOper (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    switch (hunt_server (cptr, sptr, ":%s TRACE :%s", 1, parc, parv)) {
+    case HUNTED_PASS:            /* note: gets here only if parv[1] exists */
+       {
+           aClient *ac2ptr;
+
+           ac2ptr = next_client (client, tname);
+           sendto_one (sptr, rpl_str (RPL_TRACELINK), me.name, parv[0],
+                       version, debugmode, tname, ac2ptr->from->name);
+           return 0;
+       }
+    case HUNTED_ISME:
+       break;
+    default:
+       return 0;
+    }
+
+    doall = (parv[1] && (parc > 1)) ? !match (tname, me.name) : TRUE;
+    wilds = !parv[1] || index (tname, '*') || index (tname, '?');
+    dow = wilds || doall;
+
+    for (i = 0; i < MAXCONNECTIONS; i++)
+       link_s[i] = 0, link_u[i] = 0;
+
+    if (doall)
+       for (acptr = client; acptr; acptr = acptr->next)
+#ifdef SHOW_INVISIBLE_LUSERS
+
+       {
+           if (IsPerson (acptr) && (!IsHidden (acptr) || IsOper (sptr)))
+               link_u[acptr->from->fd]++;
+#else
+       {
+           if (IsPerson (acptr) &&
+               ((!IsHidden (acptr) && !IsInvisible (acptr))
+                || IsOper (sptr)))
+               link_u[acptr->from->fd]++;
+#endif
+
+           else if (IsServer (acptr))
+               link_s[acptr->from->fd]++;
+       }
+    /* report all direct connections */
+
+    now = time (NULL);
+    for (i = 0; i <= highest_fd; i++) {
+       char *name;
+       int class;
+
+       if (!(acptr = local[i]))  /* Local Connection? */
+           continue;
+/* More bits of code to allow oers to see all users on remote traces
+ *        if (IsInvisible(acptr) && dow &&
+ *      if (dow &&
+ *          !(MyConnect(sptr) && IsOper(sptr)) && */
+       if (!IsOper (sptr) && !IsAnOper (acptr) && (acptr != sptr))
+           continue;
+       if (!doall && wilds && match (tname, acptr->name))
+           continue;
+       if (!dow && mycmp (tname, acptr->name))
+           continue;
+       name = get_client_name (acptr, FALSE);
+       class = get_client_class (acptr);
+
+       switch (acptr->status) {
+       case STAT_CONNECTING:
+           sendto_one (sptr, rpl_str (RPL_TRACECONNECTING), me.name,
+                       parv[0], class, name);
+           cnt++;
+           break;
+       case STAT_HANDSHAKE:
+           sendto_one (sptr, rpl_str (RPL_TRACEHANDSHAKE), me.name,
+                       parv[0], class, name);
+           cnt++;
+           break;
+       case STAT_ME:
+           break;
+       case STAT_UNKNOWN:
+           sendto_one (sptr, rpl_str (RPL_TRACEUNKNOWN),
+                       me.name, parv[0], class, name);
+           cnt++;
+           break;
+       case STAT_CLIENT:
+           /* Only opers see users if there is a wildcard
+            * but anyone can see all the opers.
+            */
+/*          if (IsOper(sptr) &&
+ * Allow opers to see invisible users on a remote trace or wildcard 
+ * search  ... sure as hell  helps to find clonebots.  --Russell
+ *              (MyClient(sptr) || !(dow && IsInvisible(acptr)))
+ *                           || !dow || IsAnOper(acptr)) */
+           if (IsOper (sptr) || (IsAnOper (acptr) && !IsInvisible (acptr))) {
+               if (IsAnOper (acptr))
+                   sendto_one (sptr,
+                               rpl_str (RPL_TRACEOPERATOR),
+                               me.name, parv[0], class, name,
+                               now - acptr->lasttime);
+               else
+                   sendto_one (sptr, rpl_str (RPL_TRACEUSER),
+                               me.name, parv[0], class, name,
+                               now - acptr->lasttime);
+               cnt++;
+           }
+           break;
+       case STAT_SERVER:
+           if (acptr->serv->user)
+               sendto_one (sptr, rpl_str (RPL_TRACESERVER),
+                           me.name, parv[0], class, link_s[i],
+                           link_u[i], name, acptr->serv->by,
+                           acptr->serv->user->username,
+                           acptr->serv->user->host, now - acptr->lasttime);
+           else
+               sendto_one (sptr, rpl_str (RPL_TRACESERVER),
+                           me.name, parv[0], class, link_s[i],
+                           link_u[i], name, *(acptr->serv->by) ?
+                           acptr->serv->by : "*", "*", me.name,
+                           now - acptr->lasttime);
+           cnt++;
+           break;
+       case STAT_SERVICE:
+           sendto_one (sptr, rpl_str (RPL_TRACESERVICE),
+                       me.name, parv[0], class, name);
+           cnt++;
+           break;
+       case STAT_LOG:
+           sendto_one (sptr, rpl_str (RPL_TRACELOG), me.name,
+                       parv[0], LOGFILE, acptr->port);
+           cnt++;
+           break;
+       default:                  /* ...we actually shouldn't come here... --msa */
+           sendto_one (sptr, rpl_str (RPL_TRACENEWTYPE), me.name, parv[0],
+                       name);
+           cnt++;
+           break;
+       }
+    }
+    /*
+     * Add these lines to summarize the above which can get rather long
+     * and messy when done remotely - Avalon
+     */
+    if (!IsAnOper (sptr) || !cnt) {
+       if (cnt)
+           return 0;
+       /* let the user have some idea that its at the end of the
+        * trace
+        */
+       sendto_one (sptr, rpl_str (RPL_TRACESERVER),
+                   me.name, parv[0], 0, link_s[me.fd],
+                   link_u[me.fd], me.name, "*", "*", me.name);
+       return 0;
+    }
+    for (cltmp = FirstClass (); doall && cltmp; cltmp = NextClass (cltmp))
+       if (Links (cltmp) > 0)
+           sendto_one (sptr, rpl_str (RPL_TRACECLASS), me.name,
+                       parv[0], Class (cltmp), Links (cltmp));
+    return 0;
+}
+
+/*
+   ** m_motd
+   **   parv[0] = sender prefix
+   **   parv[1] = servername
+ */
+int m_motd (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int fd, nr;
+    char line[80];
+    char *tmp;
+    struct stat sb;
+    struct tm *tm;
+
+
+    if (hunt_server (cptr, sptr, ":%s MOTD :%s", 1, parc, parv) !=
+       HUNTED_ISME)
+       return 0;
+    /*
+     * stop NFS hangs...most systems should be able to open a file in
+     * 3 seconds. -avalon (curtesy of wumpus)
+     */
+    fd = open (MOTD, O_RDONLY);
+    if (fd == -1) {
+       sendto_one (sptr, err_str (ERR_NOMOTD), me.name, parv[0]);
+       return 0;
+    }
+    (void) fstat (fd, &sb);
+    sendto_one (sptr, rpl_str (RPL_MOTDSTART), me.name, parv[0], me.name);
+    tm = localtime (&sb.st_mtime);
+    sendto_one (sptr, ":%s %d %s :- %d/%d/%d %d:%02d", me.name, RPL_MOTD,
+               parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
+               tm->tm_hour, tm->tm_min);
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    while ((nr = dgets (fd, line, sizeof (line) - 1)) > 0) {
+       line[nr] = '\0';
+       if ((tmp = (char *) index (line, '\n')))
+           *tmp = '\0';
+       if ((tmp = (char *) index (line, '\r')))
+           *tmp = '\0';
+       sendto_one (sptr, rpl_str (RPL_MOTD), me.name, parv[0], line);
+    }
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    sendto_one (sptr, rpl_str (RPL_ENDOFMOTD), me.name, parv[0]);
+    (void) close (fd);
+    return 0;
+}
+
+/*
+   ** m_opermotd (added by GZ 12/04/99)
+   **   parv[0] = sender prefix
+   **   parv[1] = servername
+ */
+int m_opermotd (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int fd, nr;
+    char line[80];
+    char *tmp;
+    struct stat sb;
+
+    if (!MyClient (sptr) || !IsAnOper (sptr))
+       return 0;
+    /*
+     * stop NFS hangs...most systems should be able to open a file in
+     * 3 seconds. -avalon (curtesy of wumpus)
+     */
+    fd = open (OMOTD, O_RDONLY);
+    if (fd == -1) {
+       return 0;
+    }
+    (void) fstat (fd, &sb);
+    sendto_one (sptr, rpl_str (RPL_OMOTDSTART), me.name, parv[0], me.name);
+/*  tm = localtime(&sb.st_mtime);
+   sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", me.name, RPL_OMOTD,
+   parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
+   tm->tm_hour, tm->tm_min);
+ */
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    while ((nr = dgets (fd, line, sizeof (line) - 1)) > 0) {
+       line[nr] = '\0';
+       if ((tmp = (char *) index (line, '\n')))
+           *tmp = '\0';
+       if ((tmp = (char *) index (line, '\r')))
+           *tmp = '\0';
+       sendto_one (sptr, rpl_str (RPL_OMOTD), me.name, parv[0], line);
+    }
+    (void) dgets (-1, NULL, 0);          /* make sure buffer is at empty pos */
+    sendto_one (sptr, rpl_str (RPL_ENDOFOMOTD), me.name, parv[0]);
+    (void) close (fd);
+    return 0;
+}
+
+/*
+   ** m_close - added by Darren Reed Jul 13 1992.
+ */
+int m_close (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    int i;
+    int closed = 0;
+
+    if (check_registered (sptr))
+       return 0;
+
+    if (!MyOper (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    for (i = highest_fd; i; i--) {
+       if (!(acptr = local[i]))
+           continue;
+       if (!IsUnknown (acptr) && !IsConnecting (acptr)
+           && !IsHandshake (acptr))
+           continue;
+       sendto_one (sptr, rpl_str (RPL_CLOSING), me.name, parv[0],
+                   get_client_name (acptr, TRUE), acptr->status);
+       (void) exit_client (acptr, acptr, acptr, "Oper Closing");
+       closed++;
+    }
+    sendto_one (sptr, rpl_str (RPL_CLOSEEND), me.name, parv[0], closed);
+    sendto_ops ("%s!%s@%s closed %d unknown connections", sptr->name,
+               sptr->user->username, sptr->user->host, closed);
+    return 0;
+}
+
+/* m_die, this terminates the server, and it intentionally does not
+ * have a reason. If you use it you should first do a GLOBOPS and 
+ * then a server notice to let everyone know what is going down...
+ */
+int m_die (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    int i;
+    char *pass = NULL;
+
+    if (check_registered (sptr))
+       return 0;
+
+    if (!MyClient (sptr) || !OPCanDie (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if ((pass = find_diepass ())) {    /* See if we have and DIE/RESTART password */
+       if (parc < 2) {           /* And if so, require a password :) */
+           sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                       "DIE");
+           return 0;
+       }
+       if (strcmp (pass, parv[1])) {
+           sendto_one (sptr, err_str (ERR_PASSWDMISMATCH), me.name, parv[0]);
+           return 0;
+       }
+    }
+    /* Let the +s know what is going on */
+    sendto_ops ("Server Terminating by request of %s", parv[0]);
+
+    for (i = 0; i <= highest_fd; i++) {
+       if (!(acptr = local[i]))
+           continue;
+       if (IsClient (acptr))
+           sendto_one (acptr,
+                       ":%s NOTICE %s :Server Terminating. %s",
+                       me.name, acptr->name, get_client_name (sptr, TRUE));
+       else if (IsServer (acptr))
+           sendto_one (acptr, ":%s ERROR :Terminated by %s",
+                       me.name, get_client_name (sptr, TRUE));
+    }
+    (void) s_die ();
+    return 0;
+}
+
+
+char *militime (char *sec, char *usec)
+{
+    struct timeval tv;
+    static char timebuf[18];
+
+    gettimeofday (&tv, NULL);
+
+    if (sec && usec)
+#if defined(__sun__) || defined(__bsdi__)
+       sprintf (timebuf, "%ld",
+                (tv.tv_sec - atoi (sec)) * 1000 + (tv.tv_usec -
+                                                   atoi (usec)) / 1000);
+#else
+       sprintf (timebuf, "%ld",
+                (tv.tv_sec - atoi (sec)) * 1000 + (tv.tv_usec -
+                                                   atoi (usec)) / 1000);
+#endif
+    else
+#if defined(__sun__) || defined(__bsdi__)
+       sprintf (timebuf, "%ld %ld", tv.tv_sec, tv.tv_usec);
+#else
+       sprintf (timebuf, "%ld %ld", tv.tv_sec, tv.tv_usec);
+#endif
+    return timebuf;
+}
+
+/*
+ *  m_rping  -- by Run
+ * 
+ *    parv[0] = sender prefix
+ *    parv[1] = pinged server
+ *    parv[2] = from person: start server ; from server: sender
+ *    parv[3] = start time in s  ;from person: Optional remark
+ *    parv[4] = start time in us
+ */
+int m_rping (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+
+    if (!IsPrivileged (sptr))
+       return 0;
+
+    if (parc < (IsAnOper (sptr) ? (MyConnect (sptr) ? 2 : 3) : 6)) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name,
+                   parv[0], "RPING");
+       return 0;
+    }
+    if (MyClient (sptr)) {
+       if (parc == 2)
+           parv[parc++] = me.name;
+       else if (!(acptr = find_server_wildcard (parv[2])))
+           /* Should be replacd with a WILDCARD search routine for servers! */
+           /* Done. -GZ */
+
+       {
+           parv[3] = parv[2];
+           parv[2] = me.name;
+           parc++;
+       }
+       else
+           parv[2] = acptr->name;
+
+       if (parc == 3)
+           parv[parc++] = "<No client start time>";
+    }
+    if (IsAnOper (sptr)) {
+       if (hunt_server (cptr, sptr, ":%s RPING %s %s :%s", 2, parc, parv) !=
+           HUNTED_ISME)
+           return 0;
+       if (!(acptr = find_server_wildcard (parv[1])) || !IsServer (acptr))
+           /* Should be replaced with a WILDCARD search routine for servers! */
+           /* Done. -GZ */
+       {
+           sendto_one (sptr, err_str (ERR_NOSUCHSERVER), me.name,
+                       parv[0], parv[1]);
+           return 0;
+       }
+       sendto_one (acptr, ":%s RPING %s %s %s :%s",
+                   me.name, acptr->name, sptr->name, militime (NULL, NULL),
+                   parv[3]);
+    }
+    else {
+       if (hunt_server
+           (cptr, sptr, ":%s RPING %s %s %s %s :%s", 1, parc, parv)
+           != HUNTED_ISME)
+           return 0;
+       sendto_one (cptr, ":%s RPONG %s %s %s %s :%s", me.name, parv[0],
+                   parv[2], parv[3], parv[4], parv[5]);
+    }
+
+    return 0;
+}
+
+/*
+ *  m_rpong  -- by Run too :)
+ * 
+ *    parv[0] = sender prefix
+ *    parv[1] = from pinged server: start server; from start server: sender
+ *    parv[2] = from pinged server: sender; from start server: pinged server
+ *    parv[3] = pingtime in ms
+ *    parv[4] = client info (for instance start time)
+ */
+int m_rpong (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+
+{
+    aClient *acptr;
+
+    if (!IsServer (sptr))
+       return 0;
+
+    if (parc < 5) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "RPING");
+       return 0;
+    }
+    if (!(acptr = find_client (parv[1], (aClient *) NULL)))
+       return 0;
+
+    if (!IsMe (acptr)) {
+       if (IsServer (acptr) && parc > 5) {
+           sendto_one (acptr, ":%s RPONG %s %s %s %s :%s",
+                       parv[0], parv[1], parv[2], parv[3], parv[4], parv[5]);
+           return 0;
+       }
+    }
+    else {
+       parv[1] = parv[2];
+       parv[2] = sptr->name;
+       parv[0] = me.name;
+       parv[3] = militime (parv[3], parv[4]);
+       parv[4] = parv[5];
+       if (!(acptr = find_person (parv[1], (aClient *) NULL)))
+           return 0;             /* No bouncing between servers ! */
+    }
+
+    sendto_one (acptr, ":%s RPONG %s %s %s :%s", parv[0], parv[1], parv[2],
+               parv[3], parv[4]);
+    return 0;
+}
+
+/*
+ * dump_map (used by m_map)
+ */
+void
+dump_map (aClient * cptr, aClient * server, char *mask, int prompt_length,
+         int length)
+{
+    static char prompt[64];
+    char *p = &prompt[prompt_length];
+    int cnt = 0, local = 0;
+    aClient *acptr;
+
+    *p = '\0';
+
+    if (prompt_length > 60)
+       sendto_one (cptr, rpl_str (RPL_MAPMORE), me.name, cptr->name, prompt,
+                   server->name);
+    else {
+       for (acptr = client; acptr; acptr = acptr->next) {
+           if (IsPerson (acptr)) {
+               ++cnt;
+               if (!strcmp (acptr->user->server, server->name))
+                   ++local;
+           }
+       }
+
+       sendto_one (cptr, rpl_str (RPL_MAP), me.name, cptr->name, prompt,
+                   length, server->name, local, (local * 100) / cnt);
+       cnt = 0;
+    }
+
+    if (prompt_length > 0) {
+       p[-1] = ' ';
+       if (p[-2] == '`')
+           p[-2] = ' ';
+    }
+    if (prompt_length > 60)
+       return;
+
+    strcpy (p, "|-");
+
+
+    for (acptr = client; acptr; acptr = acptr->next) {
+       if (!IsServer (acptr) || strcmp (acptr->serv->up, server->name))
+           continue;
+
+       if (match (mask, acptr->name))
+           acptr->flags &= ~FLAGS_MAP;
+       else {
+           acptr->flags |= FLAGS_MAP;
+           cnt++;
+       }
+    }
+
+    for (acptr = client; acptr; acptr = acptr->next) {
+       if (!(acptr->flags & FLAGS_MAP) ||      /* != */
+           !IsServer (acptr) || strcmp (acptr->serv->up, server->name))
+           continue;
+       if (--cnt == 0)
+           *p = '`';
+       dump_map (cptr, acptr, mask, prompt_length + 2, length - 2);
+    }
+
+    if (prompt_length > 0)
+       p[-1] = '-';
+}
+
+/*
+   ** m_map
+   **
+   **      parv[0] = sender prefix
+   **      parv[1] = server mask
+   * */
+int m_map (aClient * cptr, aClient * sptr, int parc, char *parv[])
+{
+
+    aClient *acptr;
+    int longest = strlen (me.name);
+
+    if (check_registered (sptr))
+       return 0;
+
+    if (parc < 2)
+       parv[1] = "*";
+
+    for (acptr = client; acptr; acptr = acptr->next)
+       if (IsServer (acptr)
+           && (strlen (acptr->name) + acptr->hopcount * 2) > longest)
+           longest = strlen (acptr->name) + acptr->hopcount * 2;
+
+    if (longest > 60)
+       longest = 60;
+    longest += 2;
+    dump_map (sptr, &me, parv[1], 0, longest);
+    sendto_one (sptr, rpl_str (RPL_MAPEND), me.name, parv[0]);
+
+    return 0;
+}
+
+int find_services (void)
+{
+
+    aClient *cptr;
+
+    if ((cptr = find_server (SERVICES_NAME, (aClient *) NULL)))
+       return 1;
+
+    return 0;
+
+}
+
+/*
+ *     m_snick added by GZ November '99
+ * 
+ *     parv[0] = source
+ *     parv[1] = nick
+ *     parv[2] = hopcount
+ *     parv[3] = lastnick
+ *     parv[4] = username
+ *     parv[5] = hostname
+ *     parv[6] = server
+ *     parv[7] = services stamp
+ *     parv[8] = modes
+ *     parv[9] = real name
+ */
+
+int m_snick (aClient * cptr, aClient * sptr, int parc, char *parv[])
+{
+    aClient *acptr;
+
+    if (!IsServer (sptr) || parc < 9)
+       return 1;
+
+    /* New KILL in case something messes up */
+
+    if (do_snick (cptr, sptr, parc, parv)) {
+       sendto_one (cptr, ":%s KILL %s :%s (Synchronizing error. (SNICK))",
+                   me.name, parv[1], me.name);
+       return 1;
+    }
+
+    if ((acptr = find_client (parv[1], NULL))) {
+
+       sendto_SNICK_butone (sptr, "SNICK %s %d %d %s %s %s %lu %s :%s",
+                            acptr->name, acptr->hopcount + 1,
+                            acptr->lastnick, acptr->user->username,
+                            acptr->user->host, acptr->user->server,
+                            acptr->user->servicestamp, parv[8], acptr->info);
+       return 0;
+    }
+
+    sendto_serv_butone (&me, ":%s GLOBOPS :ERROR with SNICK for %s", me.name,
+                       parv[1]);
+
+    return 1;
+}
+
+/*
+ * do_snick - added November 99 by GZ
+ * 
+ * This will make a client-structure out of the parameters
+ * that SNICK gives us. It's basicly a stripped down merge
+ * of m_nick, m_user and register_user.
+ *
+ *     parv[0] = source
+ *     parv[1] = nick
+ *     parv[2] = hopcount
+ *     parv[3] = lastnick
+ *     parv[4] = username
+ *     parv[5] = hostname
+ *     parv[6] = server
+ *     parv[7] = services stamp
+ *     parv[8] = modes
+ *     parv[9] = real name
+ *
+ */
+
+int do_snick (aClient * cptr, aClient * sptr, int parc, char *parv[])
+{
+
+    aClient *acptr, *serv = NULL;
+    anUser *user;
+    aConfItem *aconf;
+    aSqlineItem *asqline;
+    int keep, sameguy;
+    int ourts, theirts;
+
+
+    if (!(serv = find_server (parv[6], NULL)) || serv->from != cptr->from) {
+       return 1;
+    }
+
+    if ((acptr = find_client (parv[1], NULL))) {
+       keep = sameguy = 0;
+       ourts = acptr->lastnick;
+       theirts = atoi (parv[3]);
+       if (ourts < theirts)
+           keep = 1;
+       sameguy = !(strcmp (acptr->user->host, parv[5]));
+
+       if (sameguy)
+           keep = !keep;
+
+       if (!keep) {
+           if (sameguy) {
+               sendto_serv_butone (cptr,
+                                   ":%s KILL %s :%s (Nick collision (old nick killed))",
+                                   me.name, acptr->name, me.name);
+               acptr->flags |= FLAGS_KILLED;
+               (void) exit_client (NULL, acptr, &me,
+                                   "Nick collision (old nick killed)");
+           }
+           else {
+               sendto_serv_butone (cptr,
+                                   ":%s KILL %s :%s (Nick collision (new nick killed))",
+                                   me.name, acptr->name, me.name);
+               acptr->flags |= FLAGS_KILLED;
+               (void) exit_client (NULL, acptr, &me,
+                                   "Nick collision (new nick killed)");
+           }
+       }
+    }
+
+    sptr = make_client (cptr, serv);
+    add_client_to_list (sptr);
+
+    strncpyzt (sptr->name, parv[1], sizeof (sptr->name));
+
+    if (parc > 2)
+       sptr->hopcount = atoi (parv[2]);
+    if (parc > 3)
+       sptr->lastnick = atoi (parv[3]);
+    else
+       sptr->lastnick = time (NULL);
+
+    (void) add_to_client_hash_table (parv[1], sptr);
+    user = make_user (sptr);
+
+    strncpyzt (user->server, parv[6], sizeof (user->server));
+    strncpyzt (user->host, parv[5], sizeof (user->host));
+    user->servicestamp = atol (parv[7]);
+    strncpyzt (sptr->info, parv[9], sizeof (sptr->info));
+    strncpyzt (user->username, parv[4], USERLEN + 1);
+
+    SetClient (sptr);
+
+    if ((aconf = find_conf_name (parv[1], CONF_QUARANTINED_NICK))
+       || (asqline = find_sqline_match (parv[1]))) {
+// fixme
+//              char *qrsn = (aconf) ? aconf->passwd : asqline->reason;
+       char *qtyp = (aconf) ? "Q:Lined" : "SQLined";
+
+       if (!find_conf_host (cptr->confs, sptr->user->server, CONF_UWORLD))
+           sendto_realops ("%s nick %s from %s@%s on %s.", qtyp, parv[1],
+                           sptr->user->username, sptr->user->host,
+                           sptr->user->server);
+    }
+
+    /* Check for services */
+
+    if (find_conf_host (cptr->confs, sptr->name, CONF_UWORLD)
+       || (sptr->user
+           && find_conf_host (cptr->confs, sptr->user->server, CONF_UWORLD)))
+       sptr->flags |= FLAGS_ULINE;
+
+    /* Change modes for the user here */
+
+    mode_snick (sptr, parv[8]);
+
+    /* If user is masked - then update mask accordingly here */
+
+    calc_mask(sptr) ;
+
+    /* Do a WATCH notify - user should be masked by now, so it's safe */
+
+    hash_check_notify (sptr, RPL_LOGON);
+
+    return 0;
+}
+
+/*
+ * mode_snick - sets modes given by SNICK
+ * added by GZ Nov '99
+ */
+
+int mode_snick (aClient * acptr, char *modes)
+{
+    char *p;
+    int what;
+
+    p = modes;
+
+    what = MODE_ADD;
+
+    for (p = modes; *p; p++) {
+       switch (*p) {
+       case '+':
+           what = MODE_ADD;
+           break;
+       case '-':
+           what = MODE_DEL;
+           break;
+       case 'n':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_SROOT;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_SROOT;
+           }
+           break;
+       case 'o':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_OPER;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_OPER;
+           }
+           break;
+       case 'i':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_INVISIBLE;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_INVISIBLE;
+           }
+           break;
+       case 'g':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_FAILOP;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_FAILOP;
+           }
+           break;
+       case 'k':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_KILLS;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_KILLS;
+           }
+           break;
+       case 'a':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_SADMIN;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_SADMIN;
+           }
+           break;
+       case 'N':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_NETADMIN;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_NETADMIN;
+           }
+           break;
+       case 'A':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_ADMIN;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_ADMIN;
+           }
+           break;
+       case 'c':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_CLIENT;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_CLIENT;
+           }
+           break;
+           break;
+       case 'x':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_HIDE;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_HIDE;
+           }
+           break;
+       case 'y':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_WHOIS;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_WHOIS;
+           }
+           break;
+       case 'I':
+           if (what == MODE_ADD) {
+               acptr->umodes |= UMODE_IDENTIFY;
+           }
+           else if (what == MODE_DEL) {
+               acptr->umodes &= ~UMODE_IDENTIFY;
+           }
+           break;
+       }
+    }
+    return 0;
+
+}
diff --git a/src/s_user.c b/src/s_user.c
new file mode 100644 (file)
index 0000000..7780c70
--- /dev/null
@@ -0,0 +1,3818 @@
+ /************************************************************************
+ *   IRC - Internet Relay Chat, ircd/s_user.c (formerly ircd/s_msg.c)
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                      University of Oulu, Computing Center
+ *
+ *   See file AUTHORS in IRC package for additional names of
+ *   the programmers. 
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "config.h"              /* Get the value of THROTTLE -Studded */
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "msg.h"
+#include "channel.h"
+#include "userload.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include "h.h"
+
+void send_umode_out PROTO ((aClient *, aClient *, int));
+void send_svsmode_out PROTO ((aClient *, aClient *, aClient *, int));
+void send_umode PROTO ((aClient *, aClient *, int, int, char *));
+static int is_silenced PROTO ((aClient *, aClient *));
+
+static char buf[BUFSIZE], buf2[BUFSIZE];
+
+#if defined(THROTTLE)
+int ZLineExists (char *);
+#endif
+
+#ifdef NOSPOOF
+/* From md5.c */
+void MD5Init (u_int32_t[]);
+void MD5Transform (u_int32_t[], u_int32_t[]);
+#endif
+
+/*
+   ** m_functions execute protocol messages on this server:
+   **
+   **   cptr    is always NON-NULL, pointing to a *LOCAL* client
+   **       structure (with an open socket connected!). This
+   **       identifies the physical socket where the message
+   **       originated (or which caused the m_function to be
+   **       executed--some m_functions may call others...).
+   **
+   **   sptr    is the source of the message, defined by the
+   **       prefix part of the message if present. If not
+   **       or prefix not found, then sptr==cptr.
+   **
+   **       (!IsServer(cptr)) => (cptr == sptr), because
+   **       prefixes are taken *only* from servers...
+   **
+   **       (IsServer(cptr))
+   **           (sptr == cptr) => the message didn't
+   **           have the prefix.
+   **
+   **           (sptr != cptr && IsServer(sptr) means
+   **           the prefix specified servername. (?)
+   **
+   **           (sptr != cptr && !IsServer(sptr) means
+   **           that message originated from a remote
+   **           user (not local).
+   **
+   **       combining
+   **
+   **       (!IsServer(sptr)) means that, sptr can safely
+   **       taken as defining the target structure of the
+   **       message in this server.
+   **
+   **   *Always* true (if 'parse' and others are working correct):
+   **
+   **   1)  sptr->from == cptr  (note: cptr->from == cptr)
+   **
+   **   2)  MyConnect(sptr) <=> sptr == cptr (e.g. sptr
+   **       *cannot* be a local connection, unless it's
+   **       actually cptr!). [MyConnect(x) should probably
+   **       be defined as (x == x->from) --msa ]
+   **
+   **   parc    number of variable parameter strings (if zero,
+   **       parv is allowed to be NULL)
+   **
+   **   parv    a NULL terminated list of parameter pointers,
+   **
+   **           parv[0], sender (prefix string), if not present
+   **               this points to an empty string.
+   **           parv[1]...parv[parc-1]
+   **               pointers to additional parameters
+   **           parv[parc] == NULL, *always*
+   **
+   **       note:   it is guaranteed that parv[0]..parv[parc-1] are all
+   **           non-NULL pointers.
+ */
+
+/*
+   ** next_client
+   **   Local function to find the next matching client. The search
+   **   can be continued from the specified client entry. Normal
+   **   usage loop is:
+   **
+   **   for (x = client; x = next_client(x,mask); x = x->next)
+   **       HandleMatchingClient;
+   **         
+ */
+aClient *next_client (next, ch)
+     aClient *next;    /* First client to check */
+     char *ch; /* search string (may include wilds) */
+{
+    aClient *tmp = next;
+
+    next = find_client (ch, tmp);
+    if (tmp && tmp->prev == next)
+       return NULL;
+    if (next != tmp)
+       return next;
+    for (; next; next = next->next) {
+       if (IsService (next))
+           continue;
+       if (!match (ch, next->name) || !match (next->name, ch))
+           break;
+    }
+    return next;
+}
+
+/*
+   ** hunt_server
+   **
+   **   Do the basic thing in delivering the message (command)
+   **   across the relays to the specific server (server) for
+   **   actions.
+   **
+   **   Note:   The command is a format string and *MUST* be
+   **       of prefixed style (e.g. ":%s COMMAND %s ...").
+   **       Command can have only max 8 parameters.
+   **
+   **   server  parv[server] is the parameter identifying the
+   **       target server.
+   **
+   **   *WARNING*
+   **       parv[server] is replaced with the pointer to the
+   **       real servername from the matched client (I'm lazy
+   **       now --msa).
+   **
+   **   returns: (see #defines)
+ */
+int hunt_server (cptr, sptr, command, server, parc, parv)
+     aClient *cptr, *sptr;
+     char *command, *parv[];
+     int server, parc;
+{
+    aClient *acptr;
+
+    /*
+       ** Assume it's me, if no server
+     */
+    if (parc <= server || BadPtr (parv[server]) ||
+       match (me.name, parv[server]) == 0 ||
+       match (parv[server], me.name) == 0)
+       return (HUNTED_ISME);
+    /*
+       ** These are to pickup matches that would cause the following
+       ** message to go in the wrong direction while doing quick fast
+       ** non-matching lookups.
+     */
+    if ((acptr = find_client (parv[server], NULL)))
+       if (acptr->from == sptr->from && !MyConnect (acptr))
+           acptr = NULL;
+    if (!acptr && (acptr = find_server (parv[server], NULL)))
+       if (acptr->from == sptr->from && !MyConnect (acptr))
+           acptr = NULL;
+    if (!acptr)
+       for (acptr = client, (void) collapse (parv[server]);
+            (acptr = next_client (acptr, parv[server]));
+            acptr = acptr->next) {
+           if (acptr->from == sptr->from && !MyConnect (acptr))
+               continue;
+           /*
+            * Fix to prevent looping in case the parameter for
+            * some reason happens to match someone from the from
+            * link --jto
+            */
+           if (IsRegistered (acptr) && (acptr != cptr))
+               break;
+       }
+    if (acptr) {
+       if (IsMe (acptr) || MyClient (acptr))
+           return HUNTED_ISME;
+       if (match (acptr->name, parv[server]))
+           parv[server] = acptr->name;
+       sendto_one (acptr, command, parv[0],
+                   parv[1], parv[2], parv[3], parv[4],
+                   parv[5], parv[6], parv[7], parv[8]);
+       return (HUNTED_PASS);
+    }
+    sendto_one (sptr, err_str (ERR_NOSUCHSERVER), me.name,
+               parv[0], parv[server]);
+    return (HUNTED_NOSUCH);
+}
+
+/*
+   ** check_for_target_limit
+   **
+   ** Return Values:
+   ** True(1) == too many targets are addressed
+   ** False(0) == ok to send message
+   **
+ */
+int check_for_target_limit (aClient * sptr, void *target, const char *name)
+{
+    u_char *p;
+#ifdef __alpha
+    u_long tmp = ((u_long) target & 0xffff00) >> 8;
+#else
+    u_int tmp = ((u_int) target & 0xffff00) >> 8;
+#endif
+    u_char hash = (tmp * tmp) >> 12;
+
+    if (IsAnOper (sptr))
+       return 0;
+    if (sptr->targets[0] == hash)
+       return 0;
+
+    for (p = sptr->targets; p < &sptr->targets[MAXTARGETS - 1];)
+       if (*++p == hash) {
+           memmove (&sptr->targets[1], &sptr->targets[0], p - sptr->targets);
+           sptr->targets[0] = hash;
+           return 0;
+       }
+    if (now < sptr->nexttarget) {
+       if (sptr->nexttarget - now < TARGET_DELAY + 8) {
+           sptr->nexttarget += 2;
+           sendto_one (sptr, err_str (ERR_TARGETTOOFAST),
+                       me.name, sptr->name, name, sptr->nexttarget - now);
+       }
+       return 1;
+    }
+    else {
+       sptr->nexttarget += TARGET_DELAY;
+       if (sptr->nexttarget < now - (TARGET_DELAY * (MAXTARGETS - 1)))
+           sptr->nexttarget = now - (TARGET_DELAY * (MAXTARGETS - 1));
+    }
+    memmove (&sptr->targets[1], &sptr->targets[0], MAXTARGETS - 1);
+    sptr->targets[0] = hash;
+    return 0;
+}
+
+#ifdef THROTTLE
+
+/*
+ * check_clones
+ *
+ * This function counts the number of clients with the same IP number
+ * as cptr that connected less than CHECK_CLONE_PERIOD seconds ago. 
+ * return value
+ * -1: reject connections
+ * 0: permit connections
+ *
+ * Based on the original (ircu) from Seks, Record and Run
+ * fixed up by nikb <nikb@dal.net>
+ */
+
+struct aThrottle
+{                 /* this is a throttle record */
+    struct in_addr ip;
+    time_t last;
+    struct aThrottle *next;
+    char *connected;
+    short count;
+};
+
+#define THROTTLE_HITLIMIT -1
+#define THROTTLE_SENTWARN -2
+
+/* this is our hash table -- statically initialized to 0 */
+static struct aThrottle *throttles[256];
+
+static void remove_clone_check (aClient * cptr)
+{
+    register unsigned char *p = (unsigned char *) &cptr->ip.s_addr;
+    register struct aThrottle *ptr =
+       throttles[(p[0] + p[1] + p[2] + p[3]) & 0xff];
+
+    if (cptr->ip.s_addr == 0)
+       return;
+
+    while (ptr && (cptr->ip.s_addr != ptr->ip.s_addr))
+       ptr = ptr->next;
+
+    if (ptr && (ptr->count > 1))
+       ptr->count--;
+}
+
+int check_clones (aClient * cptr, const char *remote)
+{
+    struct aThrottle **tscn, *tptr;
+    unsigned char *p = (unsigned char *) &cptr->ip.s_addr;
+    unsigned char hval = (unsigned char) (p[0] ^ p[1] ^ p[2] ^ p[3]) & 0xFF;
+    tscn = &throttles[hval];
+
+    if (cptr->ip.s_addr == 0)
+       return 0;
+
+    while (*tscn && (cptr->ip.s_addr != (*tscn)->ip.s_addr)) {
+       if ((*tscn)->last + CHECK_CLONE_PERIOD < now) {
+           tptr = *tscn;
+           *tscn = tptr->next;
+           free (tptr->connected);
+           free (tptr);
+       }
+       else
+           tscn = &(*tscn)->next;
+    }
+
+    if ((tptr = *tscn)) {        /* There is a record for this host */
+       if (!(tptr->last + CHECK_CLONE_PERIOD < now)) { /* not expired */
+           if (remote && !tptr->connected
+               && (tptr->connected =
+                   (char *) MyMalloc (strlen (remote) + 1)))
+               strcpy (tptr->connected, remote);
+
+           tptr->last = now;
+
+           if (tptr->count >= 0) {
+               if (++tptr->count == CHECK_CLONE_LIMIT)
+                   tptr->count = THROTTLE_HITLIMIT;
+
+               return 0;
+           }
+           if (tptr->count == THROTTLE_HITLIMIT)
+               tptr->count = THROTTLE_SENTWARN;
+           
+           return -1;
+       }
+       else {                    /* expired */
+           tptr->last = now;
+           tptr->count = 1;
+
+           if (remote && !tptr->connected
+               && (tptr->connected =
+                   (char *) MyMalloc (strlen (remote) + 1)))
+               strcpy (tptr->connected, remote);
+
+           return 0;
+       }
+    }
+    else {                       /* no record for the host, make one */
+       tptr = (struct aThrottle *) MyMalloc (sizeof (struct aThrottle));
+       tptr->ip = cptr->ip;
+       tptr->next = throttles[hval];
+       tptr->last = now;
+       tptr->count = 1;
+       tptr->connected = NULL;
+
+       if (remote && !tptr->connected
+           && (tptr->connected = (char *) MyMalloc (strlen (remote) + 1)))
+           strcpy (tptr->connected, remote);
+
+       throttles[hval] = tptr;
+       return 0;
+    }
+
+    /* we should never get here, but just in case */
+    return 0;
+}
+#endif /* THROTTLE */
+
+/*
+   ** 'do_nick_name' ensures that the given parameter (nick) is
+   ** really a proper string for a nickname (note, the 'nick'
+   ** may be modified in the process...)
+   **
+   **   RETURNS the length of the final NICKNAME (0, if
+   **   nickname is illegal)
+   **
+   **  Nickname characters are in range
+   **   'A'..'}', '_', '-', '0'..'9'
+   **  anything outside the above set will terminate nickname.
+   **  In addition, the first character cannot be '-'
+   **  or a Digit.
+   **
+   **  Note:
+   **   '~'-character should be allowed, but
+   **   a change should be global, some confusion would
+   **   result if only few servers allowed it...
+ */
+
+int do_nick_name (nick)
+     char *nick;
+{
+    char *ch;
+
+    if (*nick == '-' || isdigit (*nick))       /* first character in [0..9-] */
+       return 0;
+
+    for (ch = nick; *ch && (ch - nick) < NICKLEN; ch++)
+       if (!isvalid (*ch) || isspace (*ch))
+           break;
+
+    *ch = '\0';
+
+    return (ch - nick);
+}
+
+
+/*
+   ** canonize
+   **
+   ** reduce a string of duplicate list entries to contain only the unique
+   ** items.  Unavoidably O(n^2).
+ */
+char *canonize (buffer)
+     char *buffer;
+{
+    static char cbuf[BUFSIZ];
+    register char *s, *t, *cp = cbuf;
+    register int l = 0;
+    char *p = NULL, *p2;
+
+    *cp = '\0';
+
+    for (s = strtoken (&p, buffer, ","); s; s = strtoken (&p, NULL, ",")) {
+       if (l) {
+           for (p2 = NULL, t = strtoken (&p2, cbuf, ","); t;
+                t = strtoken (&p2, NULL, ","))
+               if (!mycmp (s, t))
+                   break;
+               else if (p2)
+                   p2[-1] = ',';
+       }
+       else
+           t = NULL;
+       if (!t) {
+           if (l)
+               *(cp - 1) = ',';
+           else
+               l = 1;
+           (void) strcpy (cp, s);
+           if (p)
+               cp += (p - s);
+       }
+       else if (p2)
+           p2[-1] = ',';
+    }
+    return cbuf;
+}
+
+/*
+   ** register_user
+   **   This function is called when both NICK and USER messages
+   **   have been accepted for the client, in whatever order. Only
+   **   after this the USER message is propagated.
+   **
+   **   NICK's must be propagated at once when received, although
+   **   it would be better to delay them too until full info is
+   **   available. Doing it is not so simple though, would have
+   **   to implement the following:
+   **
+   **   1) user telnets in and gives only "NICK foobar" and waits
+   **   2) another user far away logs in normally with the nick
+   **      "foobar" (quite legal, as this server didn't propagate
+   **      it).
+   **   3) now this server gets nick "foobar" from outside, but
+   **      has already the same defined locally. Current server
+   **      would just issue "KILL foobar" to clean out dups. But,
+   **      this is not fair. It should actually request another
+   **      nick from local user or kill him/her...
+ */
+
+static int register_user (cptr, sptr, nick, username)
+     aClient *cptr;
+     aClient *sptr;
+     char *nick, *username;
+{
+    aConfItem *aconf;
+    char *parv[3], *tmpstr, *encr;
+    char stripuser[USERLEN + 1], *u1 = stripuser, *u2, olduser[USERLEN + 1],
+       userbad[USERLEN * 2 + 1], *ubad = userbad;
+    short oldstatus = sptr->status;
+    anUser *user = sptr->user;
+    aClient *nsptr;
+    int i;
+#ifdef CRYPT_ILINE_PASSWORD
+    char salt[3];
+    extern char *crypt ();
+#endif /* CRYPT_ILINE_PASSWORD */
+
+    user->last = time (NULL);
+    parv[0] = sptr->name;
+    parv[1] = parv[2] = NULL;
+
+    if (MyConnect (sptr)) {
+       /* Set HTC here */
+       sptr->lasthtc = time (NULL);
+       sptr->htccount = 0;
+       sptr->htcignore = 0;
+#ifdef THROTTLE
+       if (check_clones (sptr, get_client_host (sptr)) == -1) {
+           int ret = exit_client (cptr, sptr, sptr,
+                                  "Your host/ip has been throttled");
+           static char hostip[128];
+           strcpy (hostip, inetntoa ((char *) &sptr->ip));
+           if (!ZLineExists (hostip)) {
+               add_temp_conf (CONF_ZAP, hostip,
+                              "Too_many_connection_attempts_from_your_IP_address",
+                              NULL, 0, 0, KLINE_TEMP);
+               AddEvent (RemoveZLine, hostip, CHECK_CLONE_DELAY);
+           }
+           return ret;
+       }
+#endif /* THROTTLE */
+
+       if ((i = check_client (sptr))) {
+           sendto_umode (UMODE_OPER | UMODE_CLIENT,
+                         "*** Notice -- %s from %s.",
+                         i == -3 ? "Too many connections" :
+                         "Unauthorized connection", get_client_host (sptr));
+           ircstp->is_ref++;
+#ifdef THROTTLE
+           if (i == -3)
+               remove_clone_check (sptr);
+#endif
+           return exit_client (cptr, sptr, &me, i == -3 ?
+                               "This server is full.  Please try "
+                               random_serv :
+                               "You are not authorized to connect to this server");
+       }
+       if (IsUnixSocket (sptr))
+           strncpyzt (user->host, me.sockhost, sizeof (user->host));
+       else if (sptr->hostp) {
+           /* No control-chars or ip-like dns replies... I cheat :)
+              -- OnyxDragon */
+           for (tmpstr = sptr->sockhost; *tmpstr > ' ' && *tmpstr < 127;
+                tmpstr++);
+           if (*tmpstr || !*user->host || isdigit (*(tmpstr - 1)))
+               strncpyzt (sptr->sockhost, (char *) inetntoa ((char *) &sptr->ip), sizeof (sptr->sockhost));    /* Fix the sockhost for debug jic */
+           strncpyzt (user->host, sptr->sockhost, sizeof (sptr->sockhost));
+       }
+       else                      /* Failsafe point, don't let the user define their
+                                    own hostname via the USER command --Cabal95 */
+           strncpyzt (user->host, sptr->sockhost, HOSTLEN + 1);
+
+       aconf = sptr->confs->value.aconf;
+       /*
+        * I do not consider *, ~ or ! 'hostile' in usernames,
+        * as it is easy to differentiate them (Use \*, \? and \\)
+        * with the possible?
+        * exception of !. With mIRC etc. ident is easy to fake
+        * to contain @ though, so if that is found use non-ident
+        * username. -Donwulff
+        *
+        * I do, We only allow a-z A-Z 0-9 _ - and . now so the
+        * !strchr(sptr->username, '@') check is out of date. -Cabal95
+        */
+       strncpyzt (user->username, username, USERLEN + 1);
+       /*
+        * Limit usernames to just 0-9 a-z A-Z _ - and .
+        * It strips the "bad" chars out, and if nothing is left
+        * changes the username to the first 8 characters of their
+        * nickname. After the MOTD is displayed it sends numeric
+        * 455 to the user telling them what(if anything) happened.
+        * -Cabal95
+        */
+       for (u2 = user->username; *u2; u2++) {
+           if (isallowed (*u2))
+               *u1++ = *u2;
+           else if (*u2 < 32) {
+               /*
+                * Make sure they can read what control
+                * characters were in their username.
+                */
+               *ubad++ = '^';
+               *ubad++ = *u2 + '@';
+           }
+           else
+               *ubad++ = *u2;
+       }
+       *u1 = '\0';
+       *ubad = '\0';
+       if (strlen (stripuser) != strlen (user->username)) {
+           if (stripuser[0] == '\0') {
+               strncpy (stripuser, cptr->name, 8);
+               stripuser[8] = '\0';
+           }
+           strcpy (olduser, user->username);
+           strncpy (user->username, stripuser, USERLEN);
+           user->username[USERLEN] = '\0';
+       }
+       else
+           u1 = NULL;
+
+       if (!BadPtr (aconf->passwd)) {
+#ifdef CRYPT_ILINE_PASSWORD
+           /* use first two chars of the password they send in as salt */
+           /* passwd may be NULL. Head it off at the pass... */
+           salt[0] = '\0';
+           if (sptr->passwd && aconf->passwd && aconf->passwd[0] &&
+               aconf->passwd[1]) {
+               salt[0] = aconf->passwd[0];
+               salt[1] = aconf->passwd[1];
+               salt[2] = '\0';
+               encr = crypt (sptr->passwd, salt);
+           }
+           else
+               encr = "";
+#else
+           encr = sptr->passwd;
+#endif /* CRYPT_ILINE_PASSWORD */
+
+           if (!StrEq (encr, aconf->passwd)) {
+               ircstp->is_ref++;
+               sendto_one (sptr, err_str (ERR_PASSWDMISMATCH), me.name,
+                           parv[0]);
+#ifdef THROTTLE
+               remove_clone_check (sptr);
+#endif
+               return exit_client (cptr, sptr, &me, "Bad Password");
+           }
+           /* .. Else password check was successful, clear the pass
+            * so it doesn't get sent to NickServ.
+            * - Wizzu
+            */
+           else
+               sptr->passwd[0] = '\0';
+       }
+
+       /*
+        * following block for the benefit of time-dependent K:-lines
+        */
+       if (find_kill (sptr)) {
+           ircstp->is_ref++;
+           return exit_client (cptr, sptr, &me, "K-lined");
+       }
+
+#ifdef                DISALLOW_MIXED_CASE
+/* check for mixed case usernames, meaning probably hacked   Jon2 3-94
+ */
+#ifdef        IGNORE_CASE_FIRST_CHAR
+       tmpstr = &username[1];
+#else
+       tmpstr = username;
+#endif /* IGNORE_CASE_FIRST_CHAR */
+       while (*tmpstr && !(lower && upper || special)) {
+           c = *tmpstr;
+           tmpstr++;
+           if (islower (c)) {
+               lower++;
+               continue;         /* bypass rest of tests */
+           }
+           if (isupper (c)) {
+               upper++;
+               continue;
+           }
+           if (c == '-' || c == '_' || c == '.' || isdigit (c))
+               continue;
+           special++;
+       }
+       if (lower && upper || special) {
+           sendto_ops ("Invalid username: %s",
+                       get_client_name (sptr, FALSE));
+           ircstp->is_ref++;
+           return exit_client (cptr, sptr, sptr, "Invalid username");
+       }
+#endif /* DISALLOW_MIXED_CASE */
+
+       if (oldstatus == STAT_MASTER && MyConnect (sptr))
+           (void) m_oper (&me, sptr, 1, parv);
+    }
+    else
+       strncpyzt (user->username, username, USERLEN + 1);
+    SetClient (sptr);
+
+    calc_mask(sptr) ;
+
+    if (MyConnect (sptr)) {
+       sendto_one (sptr, rpl_str (RPL_WELCOME), me.name, nick, nick,
+                   user->username, user->host);
+       /* This is a duplicate of the NOTICE but see below... */
+       sendto_one (sptr, rpl_str (RPL_YOURHOST), me.name, nick,
+                   me.name, version);
+       sendto_one (sptr, rpl_str (RPL_CREATED), me.name, nick, creation);
+       sendto_one (sptr, rpl_str (RPL_MYINFO), me.name, parv[0],
+                   me.name, version);
+       sendto_one (sptr, rpl_str (RPL_PROTOCTL), me.name, parv[0],
+                   PROTOCTL_SUPPORTED);
+       (void) m_lusers (sptr, sptr, 1, parv);
+       update_load ();
+       (void) m_motd (sptr, sptr, 1, parv);
+       sendto_one (sptr, rpl_str (RPL_POLICY), me.name, parv[0], parv[0]);
+       /*
+        * Now send a numeric to the user telling them what, if
+        * anything, happened.
+        */
+       if (u1)
+           sendto_one (sptr, err_str (ERR_HOSTILENAME), me.name,
+                       sptr->name, olduser, userbad, stripuser);
+       nextping = time (NULL);
+        sendto_umode (UMODE_OPER | UMODE_CLIENT,
+                      "*** Notice -- Client connecting on port %d: %s (%s@%s) [%s] [%s/%s]",
+                      sptr->acpt->port, nick, user->username, user->host, inetntoa ((char *) &sptr->ip), sptr->sup_host, sptr->sup_server);
+    }
+    else if (IsServer (cptr)) {
+       aClient *acptr;
+
+       if (!(acptr = find_server (user->server, NULL))) {
+           sendto_ops ("Bad USER [%s] :%s USER %s %s : No such server",
+                       cptr->name, nick, user->username, user->server);
+           sendto_one (cptr, ":%s KILL %s :%s (No such server: %s)",
+                       me.name, sptr->name, me.name, user->server);
+           sptr->flags |= FLAGS_KILLED;
+           return exit_client (sptr, sptr, &me,
+                               "USER without prefix(2.8) or wrong prefix");
+       }
+       else if (acptr->from != sptr->from) {
+           sendto_ops ("Bad User [%s] :%s USER %s %s, != %s[%s]",
+                       cptr->name, nick, user->username, user->server,
+                       acptr->name, acptr->from->name);
+           sendto_one (cptr, ":%s KILL %s :%s (%s != %s[%s])",
+                       me.name, sptr->name, me.name, user->server,
+                       acptr->from->name, acptr->from->sockhost);
+           sptr->flags |= FLAGS_KILLED;
+           return exit_client (sptr, sptr, &me,
+                               "USER server wrong direction");
+       }
+       else
+           sptr->flags |= (acptr->flags & FLAGS_TS8);
+       /* *FINALL* this gets in ircd... -- Barubary */
+       if (find_conf_host (cptr->confs, sptr->name, CONF_UWORLD)
+           || (sptr->user && find_conf_host (cptr->confs,
+                                             sptr->user->server,
+                                             CONF_UWORLD)))
+           sptr->flags |= FLAGS_ULINE;
+    }
+
+    sptr->umodes |= UMODE_HIDE;
+    calc_mask(sptr) ;
+
+    hash_check_notify (sptr, RPL_LOGON);       /* Uglier hack */
+
+    if (!MyClient (sptr)) {
+       calc_mask(sptr) ;
+       sptr->umodes &= ~UMODE_HIDE;
+
+    }
+
+    sendto_serv_butone (cptr, "NICK %s %d %d %s %s %s %lu :%s", nick,
+                       sptr->hopcount + 1, sptr->lastnick, user->username,
+                       user->host, user->server, sptr->user->servicestamp,
+                       sptr->info);
+
+    /* Send password from sptr->passwd to NickServ for identification,
+     * if passwd given and if NickServ is online.
+     * - by taz, modified by Wizzu
+     */
+    if (MyConnect (sptr)) {
+       send_umode_out (cptr, sptr, 0);
+       if (sptr->passwd[0] && (nsptr = find_person (NickServ, NULL)))
+           sendto_one (nsptr, ":%s PRIVMSG %s@%s :IDENTIFY %s",
+                       sptr->name, NickServ, SERVICES_NAME, sptr->passwd);
+    }
+#ifdef USE_SERVICES
+    check_services_butone (SERVICE_WANT_NICK, sptr,
+                          "NICK %s %d %d %s %s %s %lu :%s", nick,
+                          sptr->hopcount, sptr->lastnick, user->username,
+                          user->host, user->server, sptr->user->servicestamp,
+                          sptr->info);
+    check_services_butone (SERVICE_WANT_USER, sptr, ":%s USER %s %s %s :%s",
+                          nick, user->username, user->host, user->server,
+                          sptr->info);
+#endif
+    if (MyConnect (sptr) && !BadPtr (sptr->passwd))
+       bzero (sptr->passwd, sizeof (sptr->passwd));
+
+    return 0;
+}
+
+/*
+   ** m_svsnick
+   **   parv[0] = sender
+   **   parv[1] = old nickname
+   **   parv[2] = new nickname
+   **   parv[3] = timestamp
+ */
+int m_svsnick (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    char nick[NICKLEN + 2];
+
+    if (!IsULine (cptr, sptr) || parc < 4 || (strlen (parv[2]) > NICKLEN))
+       return -1;                /* This looks like an error anyway -Studded */
+
+    /* Check for silly nicks here -GZ */
+
+    strncpyzt (nick, parv[2], NICKLEN + 1);
+    if (do_nick_name (nick) == 0)
+       sendto_one (sptr, err_str (ERR_ERRONEUSNICKNAME),
+                   me.name, parv[0], parv[1], "Illegal characters");
+
+    if (!hunt_server (cptr, sptr, ":%s SVSNICK %s %s :%s", 1, parc, parv) !=
+       HUNTED_ISME) {
+       if ((acptr = find_person (parv[1], NULL))) {
+           if (find_client (parv[2], NULL))    /* Collision */
+               return exit_client (cptr, acptr, sptr,
+                                   "Nickname collision due to Services enforced "
+                                   "nickname change, your nick was overruled");
+           acptr->umodes &= ~UMODE_IDENTIFY;
+           acptr->lastnick = atoi (parv[3]);
+           sendto_common_channels (acptr, ":%s NICK :%s", parv[1], parv[2]);
+           if (IsPerson (acptr))
+               add_history (acptr);
+           sendto_serv_butone (NULL, ":%s NICK %s :%i", parv[1], parv[2],
+                               atoi (parv[3]));
+           if (acptr->name[0]) {
+               (void) del_from_client_hash_table (acptr->name, acptr);
+               if (IsPerson (acptr))
+                   hash_check_notify (acptr, RPL_LOGOFF);
+           }
+           (void) strcpy (acptr->name, parv[2]);
+           (void) add_to_client_hash_table (parv[2], acptr);
+           if (IsPerson (acptr))
+               hash_check_notify (acptr, RPL_LOGON);
+       }
+    }
+    return 0;
+}
+
+/*
+   ** m_nick
+   **   parv[0] = sender prefix
+   **   parv[1] = nickname
+   **  if from new client  -taz
+   **   parv[2] = nick password
+   **  if from server:
+   **      parv[2] = hopcount
+   **      parv[3] = timestamp
+   **      parv[4] = username
+   **      parv[5] = hostname
+   **      parv[6] = servername
+   **      parv[7] = servicestamp
+   **   parv[8] = info
+ */
+int m_nick (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aConfItem *aconf;
+    aSqlineItem *asqline;
+    aClient *acptr, *serv = NULL;
+    char nick[NICKLEN + 2], *s;
+    Link *lp;
+    time_t lastnick = (time_t) 0;
+    int differ = 1;
+
+#ifdef NOSPOOF
+    u_int32_t md5data[16];
+    static u_int32_t md5hash[4];
+#endif
+
+    /*
+     * If the user didn't specify a nickname, complain
+     */
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+       return 0;
+    }
+    if (MyConnect (sptr) && (s = (char *) index (parv[1], '~')))
+       *s = '\0';
+
+    if (MyConnect (sptr) && IsJinxed (sptr))
+       return 0;                 /* Jinxed Users Get Nothing */
+
+    strncpyzt (nick, parv[1], NICKLEN + 1);
+    /*
+     * if do_nick_name() returns a null name OR if the server sent a nick
+     * name and do_nick_name() changed it in some way (due to rules of nick
+     * creation) then reject it. If from a server and we reject it,
+     * and KILL it. -avalon 4/4/92
+     */
+    if (do_nick_name (nick) == 0
+       || (IsServer (cptr) && strcmp (nick, parv[1]))) {
+       sendto_one (sptr, err_str (ERR_ERRONEUSNICKNAME), me.name, parv[0],
+                   parv[1], "Illegal characters");
+
+       if (IsServer (cptr)) {
+           ircstp->is_kill++;
+           sendto_ops ("Bad Nick: %s From: %s %s",
+                       parv[1], parv[0], get_client_name (cptr, FALSE));
+           sendto_one (cptr, ":%s KILL %s :%s (%s <- %s[%s])",
+                       me.name, parv[1], me.name, parv[1], nick, cptr->name);
+           if (sptr != cptr) {   /* bad nick change */
+               sendto_serv_butone (cptr,
+                                   ":%s KILL %s :%s (%s <- %s!%s@%s)",
+                                   me.name, parv[0], me.name,
+                                   get_client_name (cptr, FALSE),
+                                   parv[0],
+                                   sptr->user ? sptr->username : "",
+                                   sptr->user ? sptr->user->server : cptr->
+                                   name);
+               sptr->flags |= FLAGS_KILLED;
+               return exit_client (cptr, sptr, &me, "BadNick");
+           }
+       }
+       return 0;
+    }
+    /*
+       ** Protocol 4 doesn't send the server as prefix, so it is possible
+       ** the server doesn't exist (a lagged net.burst), in which case
+       ** we simply need to ignore the NICK. Also when we got that server
+       ** name (again) but from another direction. --Run
+     */
+    /* 
+       ** We should really only deal with this for msgs from servers.
+       ** -- Aeto
+     */
+    if (IsServer (cptr) &&
+       (parc > 7 && (!(serv = find_server (parv[6], NULL)) ||
+                     serv->from != cptr->from)))
+       return 0;
+
+    /*
+       ** Check against nick name collisions.
+       **
+       ** Put this 'if' here so that the nesting goes nicely on the screen :)
+       ** We check against server name list before determining if the nickname
+       ** is present in the nicklist (due to the way the below for loop is
+       ** constructed). -avalon
+     */
+    if ((acptr = find_server (nick, NULL))) {
+       if (MyConnect (sptr)) {
+           sendto_one (sptr, err_str (ERR_NICKNAMEINUSE), me.name,
+                       BadPtr (parv[0]) ? "*" : parv[0], nick);
+           return 0;             /* NICK message ignored */
+       }
+    }
+    /*
+       ** Check for Services nicknames, i.e NickServ
+       ** So you cannot change now to thses nicknames even
+       ** if you want too :-)
+     */
+    if (!IsServer (cptr) && (!strcmp (nick, NickServ) ||
+                            !strcmp (nick, ChanServ) ||
+                            !strcmp (nick, OperServ) ||
+                            !strcmp (nick, WebServ) ||
+                            !strcmp (nick, MemoServ))) {
+       sendto_one (sptr, err_str (ERR_ERRONEUSNICKNAME), me.name,
+                   BadPtr (parv[0]) ? "*" : parv[0], nick,
+                   "Reserved Due To Services Abuse.");
+       return 0;                 /* nick ignored */
+    }
+    /*
+       ** Check for a Q-lined nickname. If we find it, and it's our
+       ** client, just reject it. -Lefler
+       ** Allow opers to use Q-lined nicknames. -Russell
+       ** Quick fix to eliminate the duplicate warning if a local
+       ** user tries to get a Q:lined nick... -nikb@dal.net
+     */
+    if ((aconf = find_conf_name (nick, CONF_QUARANTINED_NICK))
+       || (asqline = find_sqline_match (nick))) {
+       char *qrsn = (aconf) ? aconf->passwd : asqline->reason;
+       char *qtyp = (aconf) ? "Q:Lined" : "SQLined";
+
+       if (!IsServer (cptr) && !IsOper (cptr)) {       /* local client and not operator */
+           sendto_one (sptr, err_str (ERR_ERRONEUSNICKNAME), me.name,
+                       BadPtr (parv[0]) ? "*" : parv[0], nick,
+                       BadPtr (qrsn) ? "reason unspecified" : qrsn);
+           sendto_realops ("Forbidding %s nick %s from %s.", qtyp, nick,
+                           get_client_name (cptr, FALSE));
+           return 0;             /* NICK command ignored */
+       }
+       /* either an oper, or a server sent NICK -- warn about it but 
+          don't ignore it, unless it is from an U lined server. */
+       if (!IsULine (cptr, sptr))
+           sendto_realops ("%s nick %s from %s on %s.", qtyp, nick,
+                           (*sptr->name != 0
+                            && !IsServer (sptr)) ? sptr->
+                           name : "<unregistered>",
+                           (sptr->user ==
+                            NULL) ? ((IsServer (sptr)) ? parv[6] : me.
+                                     name) : sptr->user->server);
+    }
+    /*
+       ** acptr already has result from previous find_server()
+     */
+    if (acptr) {
+       /*
+          ** We have a nickname trying to use the same name as
+          ** a server. Send out a nick collision KILL to remove
+          ** the nickname. As long as only a KILL is sent out,
+          ** there is no danger of the server being disconnected.
+          ** Ultimate way to jupiter a nick ? >;-). -avalon
+        */
+       sendto_ops ("Nick collision on %s(%s <- %s)",
+                   sptr->name, acptr->from->name, get_client_name (cptr,
+                                                                   FALSE));
+       ircstp->is_kill++;
+       sendto_one (cptr, ":%s KILL %s :%s (%s <- %s)",
+                   me.name, sptr->name, me.name, acptr->from->name,
+                   /* NOTE: Cannot use get_client_name
+                      ** twice here, it returns static
+                      ** string pointer--the other info
+                      ** would be lost
+                    */
+                   get_client_name (cptr, FALSE));
+       sptr->flags |= FLAGS_KILLED;
+       return exit_client (cptr, sptr, &me, "Nick/Server collision");
+    }
+    if (MyClient (cptr) && !IsOper (cptr))
+       cptr->since += 3;         /* Nick-flood prot. -Donwulff */
+
+    if (!(acptr = find_client (nick, NULL)))
+       goto nickkilldone;        /* No collisions, all clear... */
+    /*
+       ** If the older one is "non-person", the new entry is just
+       ** allowed to overwrite it. Just silently drop non-person,
+       ** and proceed with the nick. This should take care of the
+       ** "dormant nick" way of generating collisions...
+     */
+    /* Moved before Lost User Field to fix some bugs... -- Barubary */
+    if (IsUnknown (acptr) && MyConnect (acptr)) {
+       /* This may help - copying code below */
+       if (acptr == cptr)
+           return 0;
+       acptr->flags |= FLAGS_KILLED;
+       exit_client (NULL, acptr, &me, "Overridden");
+       goto nickkilldone;
+    }
+    /* A sanity check in the user field... */
+    if (acptr->user == NULL) {
+       /* This is a Bad Thing */
+       sendto_ops ("Lost user field for %s in change from %s",
+                   acptr->name, get_client_name (cptr, FALSE));
+       ircstp->is_kill++;
+       sendto_one (acptr, ":%s KILL %s :%s (Lost user field!)",
+                   me.name, acptr->name, me.name);
+       acptr->flags |= FLAGS_KILLED;
+       /* Here's the previous versions' desynch.  If the old one is
+          messed up, trash the old one and accept the new one.
+          Remember - at this point there is a new nick coming in!
+          Handle appropriately. -- Barubary */
+       exit_client (NULL, acptr, &me, "Lost user field");
+       goto nickkilldone;
+    }
+    /*
+       ** If acptr == sptr, then we have a client doing a nick
+       ** change between *equivalent* nicknames as far as server
+       ** is concerned (user is changing the case of his/her
+       ** nickname or somesuch)
+     */
+    {
+       if (acptr == sptr) {
+           if (strcmp (acptr->name, nick) != 0)
+               /*
+                  ** Allows change of case in his/her nick
+                */
+               goto nickkilldone;      /* -- go and process change */
+           else
+               /*
+                  ** This is just ':old NICK old' type thing.
+                  ** Just forget the whole thing here. There is
+                  ** no point forwarding it to anywhere,
+                  ** especially since servers prior to this
+                  ** version would treat it as nick collision.
+                */
+               return 0;         /* NICK Message ignored */
+       }
+    }
+    /*
+       ** Note: From this point forward it can be assumed that
+       ** acptr != sptr (point to different client structures).
+     */
+    /*
+       ** Decide, we really have a nick collision and deal with it
+     */
+    if (!IsServer (cptr)) {
+       /*
+          ** NICK is coming from local client connection. Just
+          ** send error reply and ignore the command.
+        */
+       sendto_one (sptr, err_str (ERR_NICKNAMEINUSE),
+                   /* parv[0] is empty when connecting */
+                   me.name, BadPtr (parv[0]) ? "*" : parv[0], nick);
+       return 0;                 /* NICK message ignored */
+    }
+    /*
+       ** NICK was coming from a server connection.
+       ** This means we have a race condition (two users signing on
+       ** at the same time), or two net fragments reconnecting with
+       ** the same nick.
+       ** The latter can happen because two different users connected
+       ** or because one and the same user switched server during a
+       ** net break.
+       ** If we have the old protocol (no TimeStamp and no user@host)
+       ** or if the TimeStamps are equal, we kill both (or only 'new'
+       ** if it was a "NICK new"). Otherwise we kill the youngest
+       ** when user@host differ, or the oldest when they are the same.
+       ** --Run
+       ** 
+     */
+    if (IsServer (sptr)) {
+       /*
+          ** A new NICK being introduced by a neighbouring
+          ** server (e.g. message type "NICK new" received)
+        */
+       if (parc > 3) {
+           lastnick = atoi (parv[3]);
+           if (parc > 5)
+               differ = (mycmp (acptr->user->username, parv[4]) ||
+                         mycmp (acptr->user->host, parv[5]));
+       }
+       sendto_failops ("Nick collision on %s (%s %d <- %s %d)",
+                       acptr->name, acptr->from->name, acptr->lastnick,
+                       get_client_name (cptr, FALSE), lastnick);
+    }
+    else {
+       /*
+          ** A NICK change has collided (e.g. message type ":old NICK new").
+        */
+       if (parc > 2)
+           lastnick = atoi (parv[2]);
+       differ = (mycmp (acptr->user->username, sptr->user->username) ||
+                 mycmp (acptr->user->host, sptr->user->host));
+       sendto_locfailops
+           ("Nick change collision from %s to %s (%s %d <- %s %d)",
+            sptr->name, acptr->name, acptr->from->name, acptr->lastnick,
+            get_client_name (cptr, FALSE), lastnick);
+    }
+    /*
+       ** Now remove (kill) the nick on our side if it is the youngest.
+       ** If no timestamp was received, we ignore the incoming nick
+       ** (and expect a KILL for our legit nick soon ):
+       ** When the timestamps are equal we kill both nicks. --Run
+       ** acptr->from != cptr should *always* be true (?).
+     */
+    if (acptr->from != cptr) {
+       if (!lastnick || (differ && lastnick >= acptr->lastnick) ||
+           (!differ && lastnick <= acptr->lastnick)) {
+           if (!IsServer (sptr)) {
+               ircstp->is_kill++;
+               sendto_serv_butone (cptr,       /* Kill old from outgoing servers */
+                                   ":%s KILL %s :%s (%s <- %s)",
+                                   me.name, sptr->name, me.name,
+                                   acptr->from->name, get_client_name (cptr,
+                                                                       FALSE));
+               sptr->flags |= FLAGS_KILLED;
+               (void) exit_client (NULL, sptr, &me,
+                                   "Nick collision (you're a ghost)");
+           }
+           if (lastnick && lastnick != acptr->lastnick)
+               return 0;         /* Ignore the NICK */
+       }
+       sendto_one (acptr, err_str (ERR_NICKCOLLISION),
+                   me.name, acptr->name, nick);
+    }
+    ircstp->is_kill++;
+    sendto_serv_butone (cptr,    /* Kill our old from outgoing servers */
+                       ":%s KILL %s :%s (%s <- %s)",
+                       me.name, acptr->name, me.name,
+                       acptr->from->name, get_client_name (cptr, FALSE));
+    acptr->flags |= FLAGS_KILLED;
+    (void) exit_client (NULL, acptr, &me,
+                       "Nick collision (older nick overruled)");
+
+    if (lastnick == acptr->lastnick)
+       return 0;
+
+  nickkilldone:
+    if (IsServer (sptr)) {
+       /* A server introducing a new client, change source */
+
+       sptr = make_client (cptr, serv);
+       add_client_to_list (sptr);
+
+       if (parc > 2)
+           sptr->hopcount = atoi (parv[2]);
+       if (parc > 3)
+           sptr->lastnick = atoi (parv[3]);
+       else                      /* Little bit better, as long as not all upgraded */
+           sptr->lastnick = time (NULL);
+    }
+    else if (sptr->name[0] && IsPerson (sptr)) {
+       /*
+          ** If the client belongs to me, then check to see
+          ** if client is currently on any channels where it
+          ** is currently banned.  If so, do not allow the nick
+          ** change to occur.
+          ** Also set 'lastnick' to current time, if changed.
+        */
+       if (MyClient (sptr))
+           for (lp = cptr->user->channel; lp; lp = lp->next)
+               if (is_banned (cptr, lp->value.chptr) &&
+                   !is_banexception (cptr, lp->value.chptr)) {
+                   sendto_one (cptr,
+                               err_str (ERR_BANNICKCHANGE),
+                               me.name, parv[0], lp->value.chptr->chname);
+                   return 0;
+               }
+       /*
+        * Client just changing his/her nick. If he/she is
+        * on a channel, send note of change to all clients
+        * on that channel. Propagate notice to other servers.
+        */
+       if (mycmp (parv[0], nick) ||
+           /* Next line can be removed when all upgraded  --Run */
+           (!MyClient (sptr) && parc > 2 && atoi (parv[2]) < sptr->lastnick))
+           sptr->lastnick = ((MyClient (sptr) || parc < 3) ?
+                             time (NULL) : atoi (parv[2]));
+       sptr->umodes &= ~UMODE_IDENTIFY;
+       add_history (sptr);
+       sendto_common_channels (sptr, ":%s NICK :%s", parv[0], nick);
+       sendto_serv_butone (cptr, ":%s NICK %s :%d",
+                           parv[0], nick, sptr->lastnick);
+    }
+    else if (!sptr->name[0]) {
+#ifdef NOSPOOF
+       /*
+        * Client setting NICK the first time.
+        *
+        * Generate a random string for them to pong with.
+        *
+        * The first two are server specific.  The intent is to randomize
+        * things well.
+        *
+        * We use lots of junk here, but only "low cost" things.
+        */
+       md5data[0] = NOSPOOF_SEED01;
+       md5data[1] = NOSPOOF_SEED02;
+       md5data[2] = time (NULL);
+       md5data[3] = me.sendM;
+       md5data[4] = me.receiveM;
+       md5data[5] = 0;
+       md5data[6] = getpid ();
+       md5data[7] = sptr->ip.s_addr;
+       md5data[8] = sptr->fd;
+       md5data[9] = 0;
+       md5data[10] = 0;
+       md5data[11] = 0;
+       md5data[12] = md5hash[0]; /* previous runs... */
+       md5data[13] = md5hash[1];
+       md5data[14] = md5hash[2];
+       md5data[15] = md5hash[3];
+
+       /*
+        * initialize the md5 buffer to known values
+        */
+       MD5Init (md5hash);
+
+       /*
+        * transform the above information into gibberish
+        */
+       MD5Transform (md5hash, md5data);
+
+       /*
+        * Never release any internal state of our generator.  Instead,
+        * use two parts of the returned hash and xor them to hide
+        * both values.
+        */
+       sptr->nospoof = (md5hash[0] ^ md5hash[1]);
+
+       /*
+        * If on the odd chance it comes out zero, make it something
+        * non-zero.
+        */
+       if (sptr->nospoof == 0)
+           sptr->nospoof = 0xdeadbeef;
+       sendto_one (sptr, ":%s NOTICE %s :*** If you are having problems"
+                   " connecting due to ping timeouts, please"
+                   " type /notice %X nospoof now.",
+                   me.name, nick, sptr->nospoof, sptr->nospoof);
+       sendto_one (sptr, "PING :%X", sptr->nospoof);
+#endif /* NOSPOOF */
+
+#ifdef CONTACT_EMAIL
+       sendto_one (sptr, ":%s NOTICE %s :*** If you need assistance with a"
+                   " connection problem, please email " CONTACT_EMAIL
+                   " with the name and version of the client you are"
+                   " using, and the server you tried to connect to: %s",
+                   me.name, nick, me.name);
+#endif /* CONTACT_EMAIL */
+#ifdef CONTACT_URL
+       sendto_one (sptr, ":%s NOTICE %s :*** If you need assistance with"
+                   " connecting to this server, %s, please refer to: "
+                   CONTACT_URL, me.name, nick, me.name);
+#endif /* CONTACT_URL */
+
+       /* Copy password to the passwd field if it's given after NICK
+        * - originally by taz, modified by Wizzu
+        */
+       if ((parc > 2) && (strlen (parv[2]) < sizeof (sptr->passwd)))
+           (void) strcpy (sptr->passwd, parv[2]);
+
+       /* This had to be copied here to avoid problems.. */
+       (void) strcpy (sptr->name, nick);
+       if (sptr->user && IsNotSpoof (sptr)) {
+           /*
+              ** USER already received, now we have NICK.
+              ** *NOTE* For servers "NICK" *must* precede the
+              ** user message (giving USER before NICK is possible
+              ** only for local client connection!). register_user
+              ** may reject the client and call exit_client for it
+              ** --must test this and exit m_nick too!!!
+            */
+           sptr->lastnick = time (NULL);       /* Always local client */
+           if (register_user (cptr, sptr, nick,
+                              sptr->user->username) == FLUSH_BUFFER)
+               return FLUSH_BUFFER;
+       }
+    }
+    /*
+     *  Finally set new nick name.
+     */
+    if (sptr->name[0]) {
+       (void) del_from_client_hash_table (sptr->name, sptr);
+       if (IsPerson (sptr))
+           hash_check_notify (sptr, RPL_LOGOFF);
+    }
+    (void) strcpy (sptr->name, nick);
+    (void) add_to_client_hash_table (nick, sptr);
+    if (IsServer (cptr) && parc > 7) {
+       parv[3] = nick;
+       return m_user (cptr, sptr, parc - 3, &parv[3]);
+    }
+    else if (IsPerson (sptr))
+       hash_check_notify (sptr, RPL_LOGON);
+
+    return 0;
+}
+
+/*
+   ** m_message (used in m_private() and m_notice())
+   ** the general function to deliver MSG's between users/channels
+   **
+   **   parv[0] = sender prefix
+   **   parv[1] = receiver list
+   **   parv[2] = message text
+   **
+   ** massive cleanup
+   ** rev argv 6/91
+   **
+ */
+
+static int m_message (cptr, sptr, parc, parv, notice)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+     int notice;
+{
+    aClient *acptr;
+    char *s;
+    aChannel *chptr;
+    char *nick, *server, *p, *cmd;
+    int cansend = 0;
+
+/*
+ * Reasons why someone can't send to a channel
+ */
+    static char *err_cantsend[] = {
+       "This channel is moderated, you need voice (+v)",
+       "This channel does not allow external messages",
+       "This channel does not allow mIRC/ANSI colors",
+       "You have been banned from this channel",
+       NULL
+    };
+
+/* This is related to the test for unregistered users in 
+ * parse.c. Unless NOSPOOF is defined, there is no need to
+ * test this here since m_notice and m_message are disallowed.
+ *  -Studded
+ */
+
+#ifdef NOSPOOF
+    if (notice) {
+       if (check_registered (sptr))
+           return 0;
+    }
+    else if (check_registered_user (sptr))
+       return 0;
+#endif
+
+    sptr->flags &= ~FLAGS_TS8;
+
+    cmd = notice ? MSG_NOTICE : MSG_PRIVATE;
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NORECIPIENT), me.name, parv[0], cmd);
+       return -1;
+    }
+    if (parc < 3 || *parv[2] == '\0') {
+       sendto_one (sptr, err_str (ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if (MyConnect (sptr) && IsJinxed (sptr))
+       return 0;
+
+    if (MyConnect (sptr))
+       parv[1] = canonize (parv[1]);
+    for (p = NULL, nick = strtoken (&p, parv[1], ","); nick;
+        nick = strtoken (&p, NULL, ",")) {
+
+       /*
+          ** nickname addressed?
+        */
+       if ((acptr = find_person (nick, NULL))) {
+           if (MyClient (sptr)
+               && check_for_target_limit (sptr, acptr, acptr->name))
+               continue;
+
+            if (AcceptsRegisteredOnly(acptr)) {
+                if (!IsIdentified(sptr)) {
+                   sendto_one (sptr, err_str (ERR_NEEDREGGEDNICKTOMSG), 
+                            me.name, parv[0], acptr->name);
+                    continue;
+                }
+            }
+            
+           if (!is_silenced (sptr, acptr)) {
+               if (MyClient (sptr) && !notice && parv[2][0] == '\1')
+                   if ((strlen (parv[2]) > 5)
+                       && strncmp (&parv[2][1], "DCC ", 4) == 0) {
+                       if (!IsIdentified(sptr) || !IsIdentified(acptr)) {
+                           sendto_one (sptr, rpl_str (ERR_NEEDIDTODCC), me.name, sptr->name, acptr->name);
+                           continue;
+                       }
+                       if ((strlen (parv[2]) > 9)
+                       && strncmp (&parv[2][1], "DCC SEND", 8) == 0) {
+                       char *fn;
+                       char filename[256];
+                       int pos = 0;
+                       fn = &parv[2][10];
+                       while (fn[pos] != ' ') {
+                           filename[pos] = fn[pos];
+                           pos++;
+                       }
+                       filename[pos++] = '\0';
+                       if (find_dccblock (fn)) {
+                           sendto_one (sptr,
+                                       ":%s NOTICE %s :DCC Transfer of \2%s\2 is not permitted, due to many viruses going around we have disabled the ability to send certain files for your protection. Join #help for more information.",
+                                       me.name, sptr->name, filename);
+                           sendto_one (acptr,
+                                       ":%s NOTICE %s :[\2WARNING\2] The DCC send file you were about to receive (\2%s\2) has been blocked. Due to viruses, we have disabled the sending of certain files. For more information, Please join #help",
+                                       me.name, acptr->name, filename);
+                           continue;
+                       }
+                   }
+               }
+               if (!notice && MyConnect (sptr) && acptr->user
+                   && acptr->user->away)
+                   sendto_one (sptr, rpl_str (RPL_AWAY), me.name, parv[0],
+                               acptr->name, acptr->user->away);
+               sendto_prefix_one (acptr, sptr, ":%s %s %s :%s", parv[0], cmd,
+                                  nick, parv[2]);
+           }
+           continue;
+       }
+
+       if (nick[0] == '@') {
+
+
+           /*   
+            * If its a message for all Channel OPs call the function
+            * sendto_channelops_butone() to handle it.  -Cabal95
+            */
+           if (nick[1] == '#') {
+               if ((chptr = find_channel (nick + 1, NullChn))) {
+
+                   cansend = can_send (sptr, chptr, parv[2]);
+                   if (cansend == 0 || IsULine (cptr, sptr))
+                       sendto_channelops_butone (cptr, sptr, chptr,
+                                                 ":%s %s %s :%s", parv[0],
+                                                 cmd, nick, parv[2]);
+                   else if (!notice)
+                       sendto_one (sptr, err_str (ERR_CANNOTSENDTOCHAN),
+                                   me.name, parv[0], nick + 1,
+                                   err_cantsend[cansend - 1]);
+               }
+               else
+                   sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name,
+                               parv[0], nick + 1);
+           }
+           /*   
+            * If its a message for all Channel ops and voices call the function
+            * sendto_channelvoice_butone() to handle it.  -DuffJ
+            */
+           else if (nick[1] == '+' && nick[2] == '#') {
+               if ((chptr = find_channel (nick + 2, NullChn))) {
+                   cansend = can_send (sptr, chptr, parv[2]);
+                   if (cansend == 0 || IsULine (cptr, sptr))
+                       sendto_channelvoice_butone (cptr, sptr, chptr,
+                                                   ":%s %s %s :%s", parv[0],
+                                                   cmd, nick, parv[2]);
+                   else if (!notice)
+                       sendto_one (sptr, err_str (ERR_CANNOTSENDTOCHAN),
+                                   me.name, parv[0], nick + 2,
+                                   err_cantsend[cansend - 1]);
+               }
+               else
+                   sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name,
+                               parv[0], nick + 2);
+           }
+           continue;
+       }
+       /*
+          ** channel msg?
+          ** Now allows U-lined users to send to channel no problemo
+          ** -- Barubary
+        */
+       if ((chptr = find_channel (nick, NullChn))) {
+           cansend = can_send (sptr, chptr, parv[2]);
+           if (cansend == 0) {
+
+               if (MyClient (sptr)
+                   && check_for_target_limit (sptr, chptr, chptr->chname))
+                   continue;
+               sendto_channel_butone (cptr, sptr, chptr,
+                                      ":%s %s %s :%s", parv[0], cmd, nick,
+                                      parv[2]);
+           }
+           else if (!notice)
+               sendto_one (sptr, err_str (ERR_CANNOTSENDTOCHAN),
+                           me.name, parv[0], nick,
+                           err_cantsend[cansend - 1]);
+           continue;
+       }
+       /*
+          ** the following two cases allow masks in NOTICEs
+          ** (for OPERs only)
+          **
+          ** Armin, 8Jun90 (gruner@informatik.tu-muenchen.de)
+        */
+       if ((*nick == '$' || *nick == '#') && IsAnOper (sptr)) {
+           if (!(s = (char *) rindex (nick, '.'))) {
+               sendto_one (sptr, err_str (ERR_NOTOPLEVEL), me.name, parv[0],
+                           nick);
+               continue;
+           }
+           while (*++s)
+               if (*s == '.' || *s == '*' || *s == '?')
+                   break;
+           if (*s == '*' || *s == '?') {
+               sendto_one (sptr, err_str (ERR_WILDTOPLEVEL), me.name,
+                           parv[0], nick);
+               continue;
+           }
+           sendto_match_butone (IsServer (cptr) ? cptr : NULL,
+                                sptr, nick + 1,
+                                (*nick == '#') ? MATCH_HOST :
+                                MATCH_SERVER,
+                                ":%s %s %s :%s", parv[0], cmd, nick,
+                                parv[2]);
+           continue;
+       }
+       /*
+          ** user[%host]@server addressed?
+        */
+       if ((server = (char *) index (nick, '@')) &&
+           (acptr = find_server (server + 1, NULL))) {
+           /*
+              ** Not destined for a user on me :-(
+            */
+           if (!IsMe (acptr)) {
+               sendto_one (acptr, ":%s %s %s :%s", parv[0], cmd, nick,
+                           parv[2]);
+               continue;
+           }
+           /*
+              ** Find the nick@server using hash.
+            */
+           acptr = find_nickserv (nick, (aClient *) NULL);
+           if (acptr) {
+               sendto_prefix_one (acptr, sptr,
+                                  ":%s %s %s :%s",
+                                  parv[0], cmd, acptr->name, parv[2]);
+               continue;
+           }
+       }
+       if (server && strncasecmp (server + 1, SERVICES_NAME,
+                                  strlen (SERVICES_NAME)) == 0)
+           sendto_one (sptr, err_str (ERR_SERVICESDOWN), me.name, parv[0],
+                       nick);
+       else
+           sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name, parv[0],
+                       nick);
+
+    }
+    return 0;
+}
+static int user_modes[] = { UMODE_OPER, 'o',
+    UMODE_LOCOP, 'O',
+    UMODE_INVISIBLE, 'i',
+    UMODE_FAILOP, 'g',
+    UMODE_SERVNOTICE, 's',
+    UMODE_KILLS, 'k',
+    UMODE_SADMIN, 'a',
+    UMODE_SROOT, 'n',
+    UMODE_NETADMIN, 'N',
+    UMODE_ADMIN, 'A',
+    UMODE_CLIENT, 'c',
+    UMODE_FLOOD, 'f',
+    UMODE_WEBTV, 'w',
+    UMODE_HIDE, 'x',
+    UMODE_WHOIS, 'y',
+#ifdef SEEUSERSTATS
+    UMODE_STATS, 't',
+#endif
+    UMODE_IDENTIFY, 'I',
+    UMODE_REGMSGONLY, 'R',
+    0, 0
+};
+/*
+   ** m_private
+   **   parv[0] = sender prefix
+   **   parv[1] = receiver list
+   **   parv[2] = message text
+ */
+
+int m_private (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    return m_message (cptr, sptr, parc, parv, 0);
+}
+
+/*
+ * Built in services aliasing for ChanServ, Memoserv, NickServ and
+ * OperServ. This not only is an alias, but is also a security measure,
+ * because PRIVMSG's arent sent to 'ChanServ' they are now sent to
+ * 'ChanServ@services.starchat.net' so nobody can snoop /cs commands :) -taz
+ */
+
+int m_chanserv (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    if (IsJinxed (sptr))
+       return 0;                 /* Jinxed Users Get Nothing */
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_person (ChanServ, NULL)))
+       sendto_one (acptr, ":%s PRIVMSG %s@%s :%s", parv[0],
+                   ChanServ, SERVICES_NAME, parv[1]);
+    else
+       sendto_one (sptr, err_str (ERR_SERVICESDOWN), me.name, parv[0],
+                   ChanServ);
+
+    return 0;
+}
+
+int m_memoserv (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+
+    if (IsJinxed (sptr))
+       return 0;                 /* Jinxed Users Get Nothing */
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_person (MemoServ, NULL)))
+       sendto_one (acptr, ":%s PRIVMSG %s@%s :%s", parv[0],
+                   MemoServ, SERVICES_NAME, parv[1]);
+    else
+       sendto_one (sptr, err_str (ERR_SERVICESDOWN), me.name, parv[0],
+                   MemoServ);
+
+    return 0;
+}
+
+int m_nickserv (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+
+    if (IsJinxed (sptr))
+       return 0;                 /* Jinxed Users Get Nothing */
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_person (NickServ, NULL)))
+       sendto_one (acptr, ":%s PRIVMSG %s@%s :%s", parv[0],
+                   NickServ, SERVICES_NAME, parv[1]);
+    else
+       sendto_one (sptr, err_str (ERR_SERVICESDOWN), me.name, parv[0],
+                   NickServ);
+
+    return 0;
+
+}
+
+
+int m_operserv (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+
+    if (check_registered_user (sptr))
+       return 0;
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if ((acptr = find_person (OperServ, NULL)))
+       sendto_one (acptr, ":%s PRIVMSG %s@%s :%s", parv[0],
+                   OperServ, SERVICES_NAME, parv[1]);
+    else
+       sendto_one (sptr, err_str (ERR_SERVICESDOWN), me.name, parv[0],
+                   OperServ);
+
+    return 0;
+}
+
+
+
+/* 
+ * Automatic NickServ direction for the identify command
+ * -taz
+ */
+int m_identify (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NOTEXTTOSEND), me.name, parv[0]);
+       return -1;
+    }
+    if (*parv[1]) {
+        if ((acptr = find_person (NickServ, NULL)))
+            sendto_one (acptr, ":%s PRIVMSG %s@%s :IDENTIFY %s", parv[0],
+                       NickServ, SERVICES_NAME, parv[1]);
+        else
+            sendto_one (sptr, err_str (ERR_SERVICESDOWN), me.name,
+                        parv[0], NickServ);
+    }
+    return 0;
+}
+
+/*
+   ** m_notice
+   **   parv[0] = sender prefix
+   **   parv[1] = receiver list
+   **   parv[2] = notice text
+ */
+
+int m_notice (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    if ((cptr->name[0]) && !IsNotSpoof (cptr)) {
+       if (BadPtr (parv[1]))
+           return 0;
+#ifdef NOSPOOF
+       if (strtoul (parv[1], NULL, 16) != cptr->nospoof)
+           goto temp;
+       sptr->nospoof = 0;
+#endif
+       if (sptr->user && sptr->name[0])
+           return register_user (cptr, sptr, sptr->name,
+                                 sptr->user->username);
+       return 0;
+    }
+#ifdef NOSPOOF
+  temp:
+#endif
+    return m_message (cptr, sptr, parc, parv, 1);
+    return 0;
+}
+
+static void do_who (sptr, acptr, repchan)
+     aClient *sptr, *acptr;
+     aChannel *repchan;
+{
+    char status[5];
+    int i = 0;
+
+    if (acptr->user->away)
+       status[i++] = 'G';
+    else
+       status[i++] = 'H';
+    if (IsAnOper (acptr))
+       status[i++] = '*';
+    else if (IsInvisible (acptr) && sptr != acptr && IsAnOper (sptr))
+       status[i++] = '%';
+    if (repchan && is_chan_op (acptr, repchan))
+       status[i++] = '@';
+    else if (repchan && has_voice (acptr, repchan))
+       status[i++] = '+';
+    status[i] = '\0';
+    if (IsULine (cptr, sptr) || IsAnOper (sptr) || acptr == sptr) {
+       sendto_one (sptr, rpl_str (RPL_WHOREPLY), me.name, sptr->name,
+                   (repchan) ? (repchan->chname) : "*",
+                   acptr->user->username, acptr->user->host,
+                   acptr->user->server, acptr->name, status, acptr->hopcount,
+                   acptr->info);
+    }
+    else {
+       sendto_one (sptr, rpl_str (RPL_WHOREPLY), me.name, sptr->name,
+                   (repchan) ? (repchan->chname) : "*",
+                   acptr->user->username, MaskHost (acptr),
+                   acptr->user->server, acptr->name, status, acptr->hopcount,
+                   acptr->info);
+    }
+}
+
+/*
+   ** m_who
+   **   parv[0] = sender prefix
+   **   parv[1] = nickname mask list
+   **   parv[2] = additional selection flag, only 'o' for now.
+ */
+int m_who (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    char *mask = parc > 1 ? parv[1] : NULL;
+    Link *lp;
+    aChannel *chptr;
+    aChannel *mychannel;
+    char *channame = NULL, *s;
+    int oper = parc > 2 ? (*parv[2] == 'o') : 0;       /* Show OPERS only */
+    int member;
+
+    /* New fx4 HTC code */
+
+    if (MyClient (sptr) && !IsServer (sptr)) {
+
+       if (cptr->htcignore) {
+           if ((time (NULL) - cptr->lasthtc) > (HTCTIME * 3)) {
+               cptr->htcignore = 0;
+               cptr->htccount = 0;
+               cptr->lasthtc = time (NULL);
+           }
+           else {
+               cptr->lasthtc = time (NULL);
+               sendto_one (sptr, rpl_str (ERR_HTCTOOFAST), me.name,
+                           sptr->name, (HTCTIME * 3));
+               return 0;
+           }
+       }
+
+       if (((time (NULL) - cptr->lasthtc) < HTCTIME) && !IsAnOper (cptr))
+           cptr->htccount++;
+       else
+           cptr->htccount = 0;
+
+       if (cptr->htccount > HTCTRIGGER) {
+           cptr->htcignore = 1;
+           cptr->lasthtc = time (NULL);
+           sendto_locfailops
+               ("Warning! %s!%s@%s is exceeding HTC trigger value.",
+                cptr->name, cptr->user->username, cptr->user->host);
+           sendto_one (sptr, rpl_str (ERR_HTCTOOFAST), me.name, sptr->name,
+                       (HTCTIME * 3));
+           return 0;
+       }
+
+       cptr->lasthtc = time (NULL);
+    }
+
+    cptr->flags2 |= FLAGS2_HTC;          /* To prevent a dead socket */
+
+    if (!BadPtr (mask)) {
+       if ((s = (char *) index (mask, ','))) {
+           parv[1] = ++s;
+           (void) m_who (cptr, sptr, parc, parv);
+       }
+       clean_channelname (mask);
+    }
+    mychannel = NullChn;
+    if (sptr->user)
+       if ((lp = sptr->user->channel))
+           mychannel = lp->value.chptr;
+
+    /* Allow use of m_who without registering */
+
+    /*
+       **  Following code is some ugly hacking to preserve the
+       **  functions of the old implementation. (Also, people
+       **  will complain when they try to use masks like "12tes*"
+       **  and get people on channel 12 ;) --msa
+     */
+    if (!mask || *mask == '\0')
+       mask = NULL;
+    else if (mask[1] == '\0' && mask[0] == '*') {
+       mask = NULL;
+       if (mychannel)
+           channame = mychannel->chname;
+    }
+    else if (mask[1] == '\0' && mask[0] == '0')        /* "WHO 0" for irc.el */
+       mask = NULL;
+    else
+       channame = mask;
+    (void) collapse (mask);
+
+    if (IsChannelName (channame)) {
+       /*
+        * List all users on a given channel
+        */
+       chptr = find_channel (channame, NULL);
+       if (chptr) {
+           member = IsMember (sptr, chptr);
+           if (member || !SecretChannel (chptr) || IsAnOper (sptr))
+               for (lp = chptr->members; lp; lp = lp->next) {
+                   if (oper && !IsAnOper (lp->value.cptr))
+                       continue;
+                   if (lp->value.cptr != sptr && (lp->flags & CHFL_ZOMBIE))
+                       continue;
+                   if (SecretChannel (chptr) && !IsSAdmin (sptr) && !member)
+                       continue;
+                   if (lp->value.cptr != sptr && IsInvisible (lp->value.cptr)
+                       && !member && !IsAnOper (sptr))
+                       continue;
+                   do_who (sptr, lp->value.cptr, chptr);
+               }
+       }
+    }
+    else
+       for (acptr = client; acptr; acptr = acptr->next) {
+           aChannel *ch2ptr = NULL;
+           int showperson, isinvis;
+
+           if (!IsPerson (acptr))
+               continue;
+           if (oper && !IsAnOper (acptr))
+               continue;
+           showperson = 0;
+           /*
+            * Show user if they are on the same channel, or not
+            * invisible and on a non secret channel (if any).
+            * Do this before brute force match on all relevant fields
+            * since these are less cpu intensive (I hope :-) and should
+            * provide better/more shortcuts - avalon
+            */
+           isinvis = acptr != sptr && IsInvisible (acptr)
+               && !IsAnOper (sptr);
+           for (lp = acptr->user->channel; lp; lp = lp->next) {
+               chptr = lp->value.chptr;
+               member = IsMember (sptr, chptr);
+               if (isinvis && !member)
+                   continue;
+               if (IsAnOper (sptr))
+                   showperson = 1;
+               if (member || (!isinvis && ShowChannel (sptr, chptr))) {
+                   ch2ptr = chptr;
+                   showperson = 1;
+                   break;
+               }
+           }
+           if (!acptr->user->channel && !isinvis)
+               showperson = 1;
+           /*
+              ** This is brute force solution, not efficient...? ;( 
+              ** Show entry, if no mask or any of the fields match
+              ** the mask. --msa
+              ** 
+              ** WHO is now patched against anti +x scripts -GZ
+            */
+           if (showperson &&
+               (!mask ||
+                match (mask, acptr->name) == 0 ||
+                match (mask, acptr->user->username) == 0 ||
+                (IsAnOper (sptr) && match (mask, acptr->user->host) == 0) ||
+                (IsHidden (acptr) && !IsAnOper (acptr)
+                 && match (mask, MaskHost (acptr)) == 0)
+                || (!IsHidden (acptr)
+                    && match (mask, acptr->user->host) == 0)
+                || match (mask, acptr->user->server) == 0
+                || match (mask, acptr->info) == 0))
+               do_who (sptr, acptr, ch2ptr);
+       }
+    sendto_one (sptr, rpl_str (RPL_ENDOFWHO), me.name, parv[0],
+               BadPtr (mask) ? "*" : mask);
+
+    /* HTC end */
+
+    cptr->flags2 &= ~FLAGS2_HTC;
+
+    return 0;
+}
+
+char *get_mode_str (aClient * acptr)
+{
+    int flag;
+    int *s;
+    char *m;
+
+    m = buf;
+    *m++ = '+';
+    for (s = user_modes; (flag = *s) && (m - buf < BUFSIZE - 4); s += 2)
+       if ((acptr->umodes & flag))
+           *m++ = (char) (*(s + 1));
+    *m = '\0';
+    return buf;
+}
+
+/*
+   ** m_whois
+   **   parv[0] = sender prefix
+   **   parv[1] = nickname masklist
+ */
+int m_whois (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char rpl_oper[] = "an IRC Operator";
+    char rpl_sadmin[] = "an IRC Operator - Services Operator";
+    char rpl_admin[] = "an IRC Operator - Server Administrator";
+
+    static anUser UnknownUser = {
+       NULL,      /* nextu */
+       NULL,      /* channel */
+       NULL,      /* invited */
+       NULL,      /* silence */
+       NULL,      /* away */
+       0,         /* last */
+       0,         /* servicestamp */
+       1,         /* refcount */
+       0,         /* joined */
+       "<Unknown>",    /* username */
+       "<Unknown>",    /* host */
+       "<Unknown>"     /* server */
+    };
+    Link *lp;
+    anUser *user;
+    aClient *acptr, *a2cptr;
+    aChannel *chptr;
+    char *nick, *tmp, *name;
+    char *p = NULL;
+    int found, len, mlen;
+
+    if (IsJinxed (sptr))
+        return 0; /* Jinxed users get nothing */
+    
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+       return 0;
+    }
+    if (parc > 2) {
+       if (hunt_server (cptr, sptr, ":%s WHOIS %s :%s", 1, parc, parv) !=
+           HUNTED_ISME)
+           return 0;
+       parv[1] = parv[2];
+    }
+    if (parv[1][0] == '*' && !IsAnOper (sptr)) {       /* Die evil spammers };) -GZ */
+       sendto_one (sptr, err_str (ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+       return 0;
+    }
+    for (tmp = parv[1]; (nick = strtoken (&p, tmp, ",")); tmp = NULL) {
+       int invis, showperson, member, wilds;
+
+       found = 0;
+       (void) collapse (nick);
+       wilds = (index (nick, '?') || index (nick, '*'));
+       if (wilds && IsServer (cptr))
+           continue;
+       for (acptr = client; (acptr = next_client (acptr, nick));
+            acptr = acptr->next) {
+           if (IsServer (acptr))
+               continue;
+
+           /*
+            * I'm always last :-) and acptr->next == NULL!!
+            */
+           if (IsMe (acptr))
+               break;
+
+           /* WHOIS on an unregistered nick? I think not. -GZ */
+
+           if (!IsRegistered (acptr)) {
+               continue;
+           }
+           /*
+            * 'Rules' established for sending a WHOIS reply:
+            *
+            * - only allow a remote client to get replies for
+            *   local clients if wildcards are being used;
+            *
+            * - if wildcards are being used dont send a reply if
+            *   the querier isnt any common channels and the
+            *   client in question is invisible and wildcards are
+            *   in use (allow exact matches only);
+            *
+            * - only send replies about common or public channels
+            *   the target user(s) are on;
+            */
+           if (!MyConnect (sptr) && !MyConnect (acptr) && wilds)
+               continue;
+
+           user = acptr->user ? acptr->user : &UnknownUser;
+           name = (!*acptr->name) ? "?" : acptr->name;
+
+           invis = acptr != sptr && IsInvisible (acptr);
+           member = (user->channel) ? 1 : 0;
+           showperson = (wilds && !invis && !member) || !wilds;
+           for (lp = user->channel; lp; lp = lp->next) {
+               chptr = lp->value.chptr;
+               member = IsMember (sptr, chptr);
+               if (invis && !member)
+                   continue;
+               if (member || (!invis && PubChannel (chptr))) {
+                   showperson = 1;
+                   break;
+               }
+           }
+           if (!showperson)
+               continue;
+
+           a2cptr = find_server (user->server, NULL);
+
+           if (IsULine (cptr, sptr) || IsAnOper (sptr) || acptr == sptr) {
+               sendto_one (sptr, rpl_str (RPL_WHOISUSER), me.name,
+                           parv[0], name, user->username, user->host,
+                           acptr->info);
+           }
+           else {
+               sendto_one (sptr, rpl_str (RPL_WHOISUSER), me.name,
+                           parv[0], name,
+                           user->username, return_host(sptr, acptr), acptr->info);
+           }
+           if (IsRegistered (acptr) && (IsAnOper (sptr) || acptr == sptr)) {
+               /* send the target user's modes */
+               sendto_one (sptr, rpl_str (RPL_WHOISMODES),
+                           me.name, parv[0],
+                           name, (IsHidden(acptr)) ? acptr->user->mask : acptr->user->host,
+                           get_mode_str (acptr));
+           }
+           if (IsRegistered (acptr) && IsAnOper (sptr) && MyConnect(acptr)) {
+               /* send the target's user host and servername used to connect */
+               sendto_one (sptr, rpl_str (RPL_WHOISCONNECTION),
+                           me.name, parv[0], name, acptr->sup_host, acptr->sup_server);
+           }
+           found = 1;
+           mlen = strlen (me.name) + strlen (parv[0]) + 6 + strlen (name);
+           for (len = 0, *buf = '\0', lp = user->channel; lp; lp = lp->next) {
+               chptr = lp->value.chptr;
+               if (ShowChannel (sptr, chptr)) {
+                   if (len + strlen (chptr->chname)
+                       > (size_t) BUFSIZE - 4 - mlen) {
+                       sendto_one (sptr,
+                                   ":%s %d %s %s :%s",
+                                   me.name, RPL_WHOISCHANNELS, parv[0], name,
+                                   buf);
+                       *buf = '\0';
+                       len = 0;
+                   }
+                   if (is_chan_op (acptr, chptr))
+                       *(buf + len++) = '@';
+                   else if (has_voice (acptr, chptr))
+                       *(buf + len++) = '+';
+
+                   /* no more zombies, we'll use ! to indicate a 
+                      secret channel that SAdmins can see */
+
+                   if (IsSAdmin (sptr) && !PubChannel (chptr))
+                       *(buf + len++) = '!';
+                   if (len)
+                       *(buf + len) = '\0';
+                   (void) strcpy (buf + len, chptr->chname);
+                   len += strlen (chptr->chname);
+                   (void) strcat (buf + len, " ");
+                   len++;
+               }
+           }
+           if (buf[0] != '\0')
+               sendto_one (sptr, rpl_str (RPL_WHOISCHANNELS),
+                           me.name, parv[0], name, buf);
+
+            /* Server they are on */
+           sendto_one (sptr, rpl_str (RPL_WHOISSERVER),
+                       me.name, parv[0], name, user->server,
+                       a2cptr ? a2cptr->info : "*Not On This Net*");
+
+           if (user->away)
+               sendto_one (sptr, rpl_str (RPL_AWAY), me.name,
+                           parv[0], name, user->away);
+
+           /* The following includes admin/Sadmin
+            * status in the WHOISOPERATOR reply.
+            * -DuffJ
+            */
+           if (IsAdmin (acptr))
+               sendto_one (sptr, rpl_str (RPL_WHOISOPERATOR),
+                           me.name, parv[0], name, rpl_admin);
+           else if (IsSAdmin (acptr))
+               sendto_one (sptr, rpl_str (RPL_WHOISOPERATOR),
+                           me.name, parv[0], name, rpl_sadmin);
+           else if (IsAnOper (acptr))
+               sendto_one (sptr, rpl_str (RPL_WHOISOPERATOR),
+                           me.name, parv[0], name, rpl_oper);
+           if (acptr->user && MyConnect (acptr))
+               sendto_one (sptr, rpl_str (RPL_WHOISIDLE),
+                           me.name, parv[0], name,
+                           time (NULL) - user->last, acptr->firsttime);
+           if (IsWmode (acptr) && !IsSRoot (sptr))
+               sendto_one (acptr, ":%s NOTICE %s :WHOIS from %s!%s@%s",
+                           me.name, acptr->name, sptr->name,
+                           sptr->user->username, sptr->user->host);
+
+       }
+       if (!found)
+           sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name, parv[0],
+                       nick);
+       if (p)
+           p[-1] = ',';
+    }
+
+    sendto_one (sptr, rpl_str (RPL_ENDOFWHOIS), me.name, parv[0], parv[1]);
+
+    return 0;
+}
+
+/*
+   ** m_user
+   **   parv[0] = sender prefix
+   **   parv[1] = username (login name, account)
+   **   parv[2] = client host name (used only from other servers)
+   **   parv[3] = server host name (used only from other servers)
+   **   parv[4] = users real name info
+ */
+int m_user (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+#define        UFLAGS  (UMODE_INVISIBLE|UMODE_SERVNOTICE)
+    char *username, *host, *server, *realname;
+    u_int32_t sstamp = 0;
+    anUser *user;
+
+    if (IsServer (cptr) && !IsUnknown (sptr))
+       return 0;
+
+    if (parc > 2 && (username = (char *) index (parv[1], '@')))
+       *username = '\0';
+    if (parc < 5 || *parv[1] == '\0' || *parv[2] == '\0' ||
+       *parv[3] == '\0' || *parv[4] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "USER");
+       if (IsServer (cptr))
+           sendto_ops ("bad USER param count for %s from %s",
+                       parv[0], get_client_name (cptr, FALSE));
+       else
+           return 0;
+    }
+    /* Copy parameters into better documenting variables */
+
+    username = (parc < 2 || BadPtr (parv[1])) ? "<bad-boy>" : parv[1];
+    host = (parc < 3 || BadPtr (parv[2])) ? "<nohost>" : parv[2];
+    server = (parc < 4 || BadPtr (parv[3])) ? "<noserver>" : parv[3];
+
+    if (parc == 6 && IsServer (cptr)) {
+       if (isdigit (*parv[4]))
+           sstamp = atol (parv[4]);
+       realname = (BadPtr (parv[5])) ? "<bad-realname>" : parv[5];
+    }
+    else
+       realname = (BadPtr (parv[4])) ? "<bad-realname>" : parv[4];
+
+    user = make_user (sptr);
+
+    if (!MyConnect (sptr)) {
+       if (sptr->srvptr == NULL)
+           sendto_ops ("WARNING, User %s introduced as being "
+                       "on non-existant server %s.", sptr->name, server);
+       strncpyzt (user->server, server, sizeof (user->server));
+       strncpyzt (user->host, host, sizeof (user->host));
+       //strncpyzt(user->hidden, hidden, sizeof(user->hidden));
+       goto user_finish; }
+    else {
+        strncpyzt(sptr->sup_server, server, sizeof(sptr->sup_server));
+        strncpyzt(sptr->sup_host, host, sizeof(sptr->sup_host));
+    }
+    if (!IsUnknown (sptr)) {
+       sendto_one (sptr, err_str (ERR_ALREADYREGISTRED), me.name, parv[0]);
+       return 0;
+    }
+#ifndef        NO_DEFAULT_INVISIBLE
+    sptr->umodes |= UMODE_INVISIBLE;
+#endif
+    sptr->umodes |= (UFLAGS & atoi (host));
+    strncpyzt (user->host, host, sizeof (user->host));
+    strncpyzt (user->server, me.name, sizeof (user->server));
+
+  user_finish:
+    user->servicestamp = sstamp;
+
+
+    strncpyzt (sptr->info, realname, sizeof (sptr->info));
+    if (sptr->name[0] && (IsServer (cptr) ? 1 : IsNotSpoof (sptr)))
+       /* NICK and no-spoof already received, now we have USER... */
+       return register_user (cptr, sptr, sptr->name, username);
+    else
+       strncpyzt (sptr->user->username, username, USERLEN + 1);
+    return 0;
+}
+
+/*
+   ** m_quit
+   **   parv[0] = sender prefix
+   **   parv[1] = comment
+ */
+int m_quit (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *ocomment = (parc > 1 && parv[1]) ? parv[1] : parv[0];
+    static char comment[QUITLEN];
+    memset (comment, '\0', sizeof (comment));
+    
+    if (!IsServer(cptr)) {
+       strcpy(comment, "Quit: ");
+        if (!IsJinxed(cptr))
+            strncat(comment, ocomment, QUITLEN - strlen(comment));
+        return exit_client(cptr, sptr, sptr, comment);
+    }
+    else
+        return exit_client(cptr, sptr, sptr, ocomment);
+       
+}
+
+/*
+   ** m_kill
+   **   parv[0] = sender prefix
+   **   parv[1] = kill victim(s) - comma separated list
+   **   parv[2] = kill path
+ */
+int m_kill (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    static anUser UnknownUser = {
+       NULL,      /* nextu */
+       NULL,      /* channel */
+       NULL,      /* invited */
+       NULL,      /* silence */
+       NULL,      /* away */
+       0,         /* last */
+       0,         /* servicestamp */
+       1,         /* refcount */
+       0,         /* joined */
+       "<Unknown>",    /* username */
+       "<Unknown>",    /* host */
+       "<Unknown>"     /* server */
+    };
+    aClient *acptr;
+    anUser *auser;
+    char inpath[HOSTLEN * 2 + USERLEN + 5];
+    char *oinpath = get_client_name (cptr, FALSE);
+    char *user, *path, *killer, *nick, *p, *s;
+    int chasing = 0, kcount = 0;
+
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "KILL");
+       return 0;
+    }
+    user = parv[1];
+    path = parv[2];              /* Either defined or NULL (parc >= 2!!) */
+
+    strcpy (inpath, oinpath);
+
+    if (IsServer (cptr) && (s = (char *) index (inpath, '.')) != NULL)
+       *s = '\0';                /* Truncate at first "." */
+
+    if (!IsPrivileged (cptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if (IsAnOper (cptr)) {
+       if (BadPtr (path)) {
+           sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                       me.name, parv[0], "KILL");
+           return 0;
+       }
+       if (strlen (path) > (size_t) TOPICLEN)
+           path[TOPICLEN] = '\0';
+    }
+    if (MyClient (sptr))
+       user = canonize (user);
+
+    for (p = NULL, nick = strtoken (&p, user, ","); nick;
+        nick = strtoken (&p, NULL, ",")) {
+
+       chasing = 0;
+
+       if (!(acptr = find_client (nick, NULL))) {
+           /*
+              ** If the user has recently changed nick, we automaticly
+              ** rewrite the KILL for this new nickname--this keeps
+              ** servers in synch when nick change and kill collide
+            */
+           if (!(acptr = get_history (nick, (long) KILLCHASETIMELIMIT))) {
+               sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name, parv[0],
+                           nick);
+               continue;
+           }
+           sendto_one (sptr, ":%s NOTICE %s :KILL changed from %s to %s",
+                       me.name, parv[0], nick, acptr->name);
+           chasing = 1;
+       }
+       if ((!MyConnect (acptr) && MyClient (cptr) && !OPCanGKill (cptr)) ||
+           (MyConnect (acptr) && MyClient (cptr) && !OPCanLKill (cptr))) {
+           sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+           continue;
+       }
+       if (IsServer (acptr) || IsMe (acptr)) {
+           sendto_one (sptr, err_str (ERR_CANTKILLSERVER), me.name, parv[0]);
+           continue;
+       }
+       /* From here on, the kill is probably going to be successful. */
+
+       kcount++;
+
+       if (!IsServer (sptr) && (kcount > MAXKILLS)) {
+           sendto_one (sptr,
+                       ":%s NOTICE %s :Too many targets, kill list was truncated. Maximum is %d.",
+                       me.name, parv[0], MAXKILLS);
+           break;
+       }
+       if (!IsServer (cptr)) {
+           /*
+              ** The kill originates from this server, initialize path.
+              ** (In which case the 'path' may contain user suplied
+              ** explanation ...or some nasty comment, sigh... >;-)
+              **
+              **   ...!operhost!oper
+              **   ...!operhost!oper (comment)
+            */
+           if (IsUnixSocket (cptr))    /* Don't use get_client_name syntax */
+               strcpy (inpath, me.sockhost);
+           else
+               strcpy (inpath, cptr->name);
+           if (kcount < 2) {     /* Only check the path the first time 
+                                    around, or it gets appended to itself. */
+               if (!BadPtr (path)) {
+                   (void) sprintf (buf, "%s%s (%s)",
+                                   cptr->name, IsOper (sptr) ? "" : "(L)",
+                                   path);
+                   path = buf;
+               }
+               else
+                   path = cptr->name;
+           }
+       }
+       else if (BadPtr (path))
+           path = "*no-path*";   /* Bogus server sending??? */
+       /*
+          ** Notify all *local* opers about the KILL (this includes the one
+          ** originating the kill, if from this server--the special numeric
+          ** reply message is not generated anymore).
+          **
+          ** Note: "acptr->name" is used instead of "user" because we may
+          **    have changed the target because of the nickname change.
+        */
+
+       auser = acptr->user ? acptr->user : &UnknownUser;
+
+       if (index (parv[0], '.'))
+           sendto_umode (UMODE_KILLS | UMODE_OPER,
+                         "*** Notice -- Received KILL message for %s!%s@%s from %s Path: %s!%s",
+                         acptr->name, auser->username, auser->host, parv[0],
+                         inpath, path);
+       else
+           sendto_ops
+               ("Received KILL message for %s!%s@%s from %s Path: %s!%s",
+                acptr->name, auser->username, auser->host, parv[0], inpath,
+                path);
+#if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
+       if (IsOper (sptr))
+           syslog (LOG_DEBUG, "KILL From %s For %s Path %s!%s",
+                   parv[0], acptr->name, inpath, path);
+#endif
+       /*
+          ** And pass on the message to other servers. Note, that if KILL
+          ** was changed, the message has to be sent to all links, also
+          ** back.
+          ** Suicide kills are NOT passed on --SRB
+        */
+       if (!MyConnect (acptr) || !MyConnect (sptr) || !IsAnOper (sptr)) {
+           sendto_serv_butone (cptr, ":%s KILL %s :%s!%s",
+                               parv[0], acptr->name, inpath, path);
+           if (chasing && IsServer (cptr))
+               sendto_one (cptr, ":%s KILL %s :%s!%s",
+                           me.name, acptr->name, inpath, path);
+           acptr->flags |= FLAGS_KILLED;
+       }
+#ifdef USE_SERVICES
+       check_services_butone (SERVICE_WANT_KILL, sptr, ":%s KILL %s :%s!%s",
+                              parv[0], acptr->name, inpath, path);
+#endif
+
+       /*
+          ** Tell the victim she/he has been zapped, but *only* if
+          ** the victim is on current server--no sense in sending the
+          ** notification chasing the above kill, it won't get far
+          ** anyway (as this user don't exist there any more either)
+        */
+
+       if (MyConnect (acptr)) {
+           if (IsAnOper (acptr))
+               sendto_prefix_one (acptr, sptr, ":%s KILL %s :%s!%s",
+                                  parv[0], acptr->name, inpath, path);
+
+           else {
+               sendto_prefix_one (acptr, sptr, ":%s KILL %s :%s",
+                                  parv[0], acptr->name, path);
+           }
+       }
+       /*
+          ** Set FLAGS_KILLED. This prevents exit_one_client from sending
+          ** the unnecessary QUIT for this. (This flag should never be
+          ** set in any other place)
+        */
+       if (MyConnect (acptr) && MyConnect (sptr) && IsAnOper (sptr))
+           (void) sprintf (buf2, "Local kill by %s (%s)", sptr->name,
+                           BadPtr (parv[2]) ? sptr->name : parv[2]);
+       else {
+           if ((killer = index (path, ' '))) {
+               while (*killer && *killer != '!')
+                   killer--;
+               if (!*killer)
+                   killer = path;
+               else
+                   killer++;
+           }
+           else
+               killer = path;
+           (void) sprintf (buf2, "Killed (%s)", killer);
+       }
+       if (exit_client (cptr, acptr, sptr, path) == FLUSH_BUFFER)
+           return FLUSH_BUFFER;
+    }
+    return 0;
+}
+
+/* m_mkill() - kills all users with the host provided or with the same host
+ *      as a nickname they type in
+ */
+int m_mkill (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char buff[512], *lookhost;
+    aClient *acptr;
+    int x = 0, doflush = 0;
+
+    if (check_registered_user (sptr))
+       return 0;
+    if (parc < 3 || *parv[2] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "MKILL");
+       return 0;
+    }
+    if (!IsSAdmin (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOPRIVILEGES), me.name, parv[0]);
+       return 0;
+    }
+    if (!index (parv[1], '.')) {
+       if ((acptr = find_client (parv[1], NULL)))
+           lookhost = acptr->user->host;
+       else {
+           sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name, parv[0],
+                       parv[1]);
+           return 0;
+       }
+    }
+    else
+       lookhost = parv[1];
+    *buff = '\0';
+    for (acptr = client; acptr; acptr = acptr->next) {
+       if (!acptr->user || (!MyConnect (acptr) && !IsOper (sptr))
+           || IsAnOper (acptr))
+           continue;
+       if (!mycmp (acptr->user->host, lookhost)) {
+           x++;
+           if (x > MAXKILLS || strlen (acptr->name) + strlen (buff) > 510) {
+               parv[1] = buff;
+               if (m_kill (cptr, sptr, parc, parv) == FLUSH_BUFFER)
+                   doflush = 1;
+               *buff = '\0';
+               x = 1;
+           }
+           if (*buff)
+               (void) strncat (buff, ",", 512);
+           buff[511] = '\0';
+           (void) strncat (buff, acptr->name, 512);
+           buff[511] = '\0';
+       }
+    }
+    if (*buff == '\0')
+       return 0;                 /* nobody to kill */
+    parv[1] = buff;
+    if (m_kill (cptr, sptr, parc, parv) == FLUSH_BUFFER)
+       doflush = 1;
+    return doflush ? FLUSH_BUFFER : 0;
+}
+
+/***********************************************************************
+ * m_away() - Added 14 Dec 1988 by jto. 
+ *            Not currently really working, I don't like this
+ *            call at all...
+ *
+ *            ...trying to make it work. I don't like it either,
+ *           but perhaps it's worth the load it causes to net.
+ *           This requires flooding of the whole net like NICK,
+ *           USER, MODE, etc messages...  --msa
+ ***********************************************************************/
+
+/*
+   ** m_away
+   **   parv[0] = sender prefix
+   **   parv[1] = away message
+ */
+int m_away (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *away, *awy2 = parv[1];
+
+    if (check_registered_user (sptr))
+       return 0;
+
+    away = sptr->user->away;
+
+    if (parc < 2 || !*awy2) {
+       /* Marking as not away */
+
+       if (away) {
+           MyFree (away);
+           sptr->user->away = NULL;
+       }
+       sendto_serv_butone (cptr, ":%s AWAY", parv[0]);
+       if (MyConnect (sptr))
+           sendto_one (sptr, rpl_str (RPL_UNAWAY), me.name, parv[0]);
+#ifdef  USE_SERVICES
+       check_services_butonee (SERVICE_WANT_AWAY, ":%s AWAY", parv[0]);
+#endif
+       return 0;
+    }
+
+    /* Marking as away */
+
+    if (strlen (awy2) > (size_t) TOPICLEN)
+       awy2[TOPICLEN] = '\0';
+    sendto_serv_butone (cptr, ":%s AWAY :%s", parv[0], awy2);
+#ifdef  USE_SERVICES
+    check_services_butonee (SERVICE_WANT_AWAY, ":%s AWAY :%s",
+                           parv[0], parv[1]);
+#endif
+
+    if (away)
+       away = (char *) MyRealloc (away, strlen (awy2) + 1);
+    else
+       away = (char *) MyMalloc (strlen (awy2) + 1);
+
+    sptr->user->away = away;
+    (void) strcpy (away, awy2);
+    if (MyConnect (sptr))
+       sendto_one (sptr, rpl_str (RPL_NOWAWAY), me.name, parv[0]);
+    return 0;
+}
+
+/*
+   ** m_ping
+   **   parv[0] = sender prefix
+   **   parv[1] = origin
+   **   parv[2] = destination
+ */
+int m_ping (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    char *origin, *destination;
+
+/* Not needed re change to parse.c
+   if (check_registered(sptr))
+   return 0;
+ */
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NOORIGIN), me.name, parv[0]);
+       return 0;
+    }
+    origin = parv[1];
+    destination = parv[2];       /* Will get NULL or pointer (parc >= 2!!) */
+
+    acptr = find_client (origin, NULL);
+    if (!acptr)
+       acptr = find_server (origin, NULL);
+    if (acptr && acptr != sptr)
+       origin = cptr->name;
+    if (!BadPtr (destination) && mycmp (destination, me.name) != 0) {
+       if ((acptr = find_server (destination, NULL)))
+           sendto_one (acptr, ":%s PING %s :%s", parv[0], origin,
+                       destination);
+       else {
+           sendto_one (sptr, err_str (ERR_NOSUCHSERVER),
+                       me.name, parv[0], destination);
+           return 0;
+       }
+    }
+    else
+       sendto_one (sptr, ":%s PONG %s :%s", me.name,
+                   (destination) ? destination : me.name, origin);
+    return 0;
+}
+
+#ifdef NOSPOOF
+/*
+   ** m_nospoof - allows clients to respond to no spoofing patch
+   **   parv[0] = prefix
+   **   parv[1] = code
+ */
+int m_nospoof (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    unsigned long result;
+
+    if (IsNotSpoof (cptr))
+       return 0;
+    if (IsRegistered (cptr))
+       return 0;
+    if (!*sptr->name)
+       return 0;
+    if (BadPtr (parv[1]))
+       goto temp;
+    result = strtoul (parv[1], NULL, 16);
+    /* Accept code in second parameter (ircserv) */
+    if (result != sptr->nospoof) {
+       if (BadPtr (parv[2]))
+           goto temp;
+       result = strtoul (parv[2], NULL, 16);
+       if (result != sptr->nospoof)
+           goto temp;
+    }
+    sptr->nospoof = 0;
+    if (sptr->user && sptr->name[0])
+       return register_user (cptr, sptr, sptr->name, sptr->user->username);
+    return 0;
+  temp:
+    /* Homer compatibility */
+    sendto_one (cptr, ":%X!nospoof@%s PRIVMSG %s :%cVERSION%c",
+               cptr->nospoof, me.name, cptr->name, (char) 1, (char) 1);
+    return 0;
+}
+#endif /* NOSPOOF */
+
+/*
+   ** m_pong
+   **   parv[0] = sender prefix
+   **   parv[1] = origin
+   **   parv[2] = destination
+ */
+int m_pong (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aClient *acptr;
+    char *origin, *destination;
+
+#ifdef NOSPOOF
+    if (!IsRegistered (cptr))
+       return m_nospoof (cptr, sptr, parc, parv);
+#endif
+
+    if (parc < 2 || *parv[1] == '\0') {
+       sendto_one (sptr, err_str (ERR_NOORIGIN), me.name, parv[0]);
+       return 0;
+    }
+    origin = parv[1];
+    destination = parv[2];
+    cptr->flags &= ~FLAGS_PINGSENT;
+    sptr->flags &= ~FLAGS_PINGSENT;
+
+    if (!BadPtr (destination) && mycmp (destination, me.name) != 0) {
+       if ((acptr = find_client (destination, NULL)) ||
+           (acptr = find_server (destination, NULL))) {
+           if (!IsServer (cptr) && !IsServer (acptr)) {
+               sendto_one (sptr, err_str (ERR_NOSUCHSERVER),
+                           me.name, parv[0], destination);
+               return 0;
+           }
+           else
+               sendto_one (acptr, ":%s PONG %s %s", parv[0], origin,
+                           destination);
+       }
+       else {
+           sendto_one (sptr, err_str (ERR_NOSUCHSERVER),
+                       me.name, parv[0], destination);
+           return 0;
+       }
+    }
+#ifdef DEBUGMODE
+    else
+       Debug ((DEBUG_NOTICE, "PONG: %s %s", origin,
+               destination ? destination : "*"));
+#endif
+    return 0;
+}
+
+/*
+   ** m_oper
+   **   parv[0] = sender prefix
+   **   parv[1] = oper name
+   **   parv[2] = oper password
+ */
+int m_oper (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aConfItem *aconf;
+    char *name, *password, *encr;
+#ifdef CRYPT_OPER_PASSWORD
+    char salt[3];
+    extern char *crypt ();
+#endif /* CRYPT_OPER_PASSWORD */
+
+    if (check_registered_user (sptr))
+       return 0;
+
+    if (SVSNOOP == 1 && MyClient (sptr)) {
+       sendto_one (sptr, err_str (ERR_NOOPERHOST), me.name, parv[0]);
+       return 0;
+    }
+
+    name = parc > 1 ? parv[1] : NULL;
+    password = parc > 2 ? parv[2] : NULL;
+
+    if (!IsServer (cptr) && (BadPtr (name) || BadPtr (password))) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "OPER");
+       return 0;
+    }
+
+    /* if message arrived from server, trust it, and set to oper */
+
+    if ((IsServer (cptr) || IsMe (cptr)) && !IsOper (sptr)) {
+       sptr->umodes |= UMODE_OPER;
+       calc_mask(sptr) ;
+       sendto_serv_butone (cptr, ":%s MODE %s :+o", parv[0], parv[0]);
+       if (IsMe (cptr))
+           sendto_one (sptr, rpl_str (RPL_YOUREOPER), me.name, parv[0]);
+       (void) m_opermotd (sptr, sptr, 1, parv);
+#ifdef USE_SERVICES
+       check_services_butone (SERVICE_WANT_OPER, sptr,
+                              ":%s MODE %s :+o", parv[0], parv[0]);
+#endif
+       return 0;
+    }
+    else if (IsOper (sptr)) {
+       if (MyConnect (sptr))
+           sendto_one (sptr, rpl_str (RPL_YOUREOPER), me.name, parv[0]);
+       calc_mask(sptr) ;
+       (void) m_opermotd (sptr, sptr, 1, parv);
+       return 0;
+    }
+    if (!(aconf = find_conf_exact (name, sptr->user->username, sptr->sockhost,
+                                  CONF_OPS)) &&
+       !(aconf = find_conf_exact (name, sptr->user->username,
+                                  inetntoa ((char *) &cptr->ip),
+                                  CONF_OPS))) {
+       sendto_one (sptr, err_str (ERR_NOOPERHOST), me.name, parv[0]);
+       sendto_realops ("Failed OPER attempt by %s (%s@%s)",
+                       parv[0], sptr->user->username, sptr->sockhost);
+       sptr->since += 7;
+       return 0;
+    }
+#ifdef CRYPT_OPER_PASSWORD
+    /* use first two chars of the password they send in as salt */
+
+    /* passwd may be NULL. Head it off at the pass... */
+    salt[0] = '\0';
+    if (password && aconf->passwd && aconf->passwd[0] && aconf->passwd[1]) {
+       salt[0] = aconf->passwd[0];
+       salt[1] = aconf->passwd[1];
+       salt[2] = '\0';
+       encr = crypt (password, salt);
+    }
+    else
+       encr = "";
+#else
+    encr = password;
+#endif /* CRYPT_OPER_PASSWORD */
+
+    if ((aconf->status & CONF_OPS) &&
+       StrEq (encr, aconf->passwd) && !attach_conf (sptr, aconf)) {
+       int old = (sptr->umodes & ALL_UMODES);
+       char *s;
+
+       s = index (aconf->host, '@');
+       *s++ = '\0';
+       if (!(aconf->port & OFLAG_ISGLOBAL))
+           SetLocOp (sptr);
+       else
+           SetOper (sptr);
+       sptr->oflag = aconf->port;
+       *--s = '@';
+       sendto_realops ("%s (%s@%s) is now an IRC Operator (%c)", parv[0],
+                       sptr->user->username,
+                       sptr->user->host, IsOper (sptr) ? 'O' : 'o');
+       sptr->umodes |= (UMODE_SERVNOTICE | UMODE_FAILOP | UMODE_FLOOD);
+       send_umode_out (cptr, sptr, old);
+       sendto_one (sptr, rpl_str (RPL_YOUREOPER), me.name, parv[0]);
+       calc_mask(sptr) ;
+       (void) m_opermotd (sptr, sptr, 1, parv);
+
+#if !defined(CRYPT_OPER_PASSWORD) && (defined(FNAME_OPERLOG) ||\
+    (defined(USE_SYSLOG) && defined(SYSLOG_OPER)))
+       encr = "";
+#endif
+#if defined(USE_SYSLOG) && defined(SYSLOG_OPER)
+       syslog (LOG_INFO, "OPER (%s) (%s) by (%s!%s@%s)",
+               name, encr, parv[0], sptr->user->username, sptr->sockhost);
+#endif
+#ifdef FNAME_OPERLOG
+       {
+           int logfile;
+
+           /*
+            * This conditional makes the logfile active only after
+            * it's been created - thus logging can be turned off by
+            * removing the file.
+            *
+            * stop NFS hangs...most systems should be able to open a
+            * file in 3 seconds. -avalon (curtesy of wumpus)
+            */
+           if (IsPerson (sptr) &&
+               (logfile = open (FNAME_OPERLOG, O_WRONLY | O_APPEND)) != -1) {
+               (void) sprintf (buf, "%s OPER (%s) (%s) by (%s!%s@%s)\n",
+                               myctime (time (NULL)), name, encr,
+                               parv[0], sptr->user->username,
+                               sptr->sockhost);
+               (void) write (logfile, buf, strlen (buf));
+               (void) close (logfile);
+           }
+           /* Modification by pjg */
+       }
+#endif
+#ifdef USE_SERVICES
+       check_services_butone (SERVICE_WANT_OPER, sptr,
+                              ":%s MODE %s :+o", parv[0], parv[0]);
+#endif
+    }
+    else {
+       (void) detach_conf (sptr, aconf);
+       sendto_one (sptr, err_str (ERR_PASSWDMISMATCH), me.name, parv[0]);
+#ifdef  FAILOPER_WARN
+       sendto_one (sptr, ":%s NOTICE :Your attempt has been logged.",
+                   me.name);
+#endif
+       sendto_realops
+           ("Failed OPER attempt by %s (%s@%s) using UID %s [NOPASSWORD]",
+            parv[0], sptr->user->username, sptr->sockhost, name);
+       sendto_serv_butone (&me,
+                           ":%s GLOBOPS :Failed OPER attempt by %s (%s@%s) using UID %s [---]",
+                           me.name, parv[0], sptr->user->username,
+                           sptr->sockhost, name);
+       sptr->since += 7;
+#ifdef FNAME_OPERLOG
+       {
+           int logfile;
+
+           /*
+            * This conditional makes the logfile active only after
+            * it's been created - thus logging can be turned off by
+            * removing the file.
+            *
+            * stop NFS hangs...most systems should be able to open a
+            * file in 3 seconds. -avalon (curtesy of wumpus)
+            */
+           if (IsPerson (sptr) &&
+               (logfile = open (FNAME_OPERLOG, O_WRONLY | O_APPEND)) != -1) {
+               (void) sprintf (buf,
+                               "%s FAILED OPER (%s) (%s) by (%s!%s@%s)\n PASSWORD %s",
+                               myctime (time (NULL)), name, encr, parv[0],
+                               sptr->user->username, sptr->sockhost,
+                               password);
+               (void) write (logfile, buf, strlen (buf));
+               (void) close (logfile);
+           }
+           /* Modification by pjg */
+       }
+#endif
+    }
+    return 0;
+}
+
+/***************************************************************************
+ * m_pass() - Added Sat, 4 March 1989
+ ***************************************************************************/
+
+/*
+   ** m_pass
+   **   parv[0] = sender prefix
+   **   parv[1] = password
+ */
+int m_pass (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char *password = parc > 1 ? parv[1] : NULL;
+
+    if (BadPtr (password)) {
+       sendto_one (cptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "PASS");
+       return 0;
+    }
+    if (!MyConnect (sptr) || (!IsUnknown (cptr) && !IsHandshake (cptr))) {
+       sendto_one (cptr, err_str (ERR_ALREADYREGISTRED), me.name, parv[0]);
+       return 0;
+    }
+
+    strncpyzt (cptr->passwd, password, sizeof (cptr->passwd));
+    return 0;
+}
+
+/*
+ * m_userhost added by Darren Reed 13/8/91 to aid clients and reduce
+ * the need for complicated requests like WHOIS. It returns user/host
+ * information only (no spurious AWAY labels or channels).
+ */
+int m_userhost (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int catsize;
+    char *p = NULL;
+    aClient *acptr;
+    char *s;
+    char *curpos;
+    int resid;
+
+/* Not needed re change to parse.c
+   if (check_registered(sptr))
+   return 0;
+ */
+    if (parc > 2)
+       (void) m_userhost (cptr, sptr, parc - 1, parv + 1);
+
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS),
+                   me.name, parv[0], "USERHOST");
+       return 0;
+    }
+    /*
+     * use curpos to keep track of where we are in the output buffer,
+     * and use resid to keep track of the remaining space in the
+     * buffer
+     */
+    curpos = buf;
+    curpos += sprintf (curpos, rpl_str (RPL_USERHOST), me.name, parv[0]);
+    resid = sizeof (buf) - (curpos - buf) - 1; /* remaining space */
+
+    /*
+     * for each user found, print an entry if it fits.
+     */
+    for (s = strtoken (&p, parv[1], " "); s;
+        s = strtoken (&p, (char *) NULL, " "))
+       if ((acptr = find_person (s, NULL))) {
+           if (sptr != acptr && !IsAnOper (sptr)) {
+               catsize = strlen (acptr->name)
+                   + (IsAnOper (acptr) ? 1 : 0)
+                   + 3 + strlen (acptr->user->username)
+                   + strlen (MaskHost (acptr)) + 1;
+
+               if (catsize <= resid) {
+                   curpos += sprintf (curpos, "%s%s=%c%s@%s ",
+                                      acptr->name,
+                                      IsAnOper (acptr) ? "*" : "",
+                                      (acptr->user->away) ? '-' : '+',
+                                      acptr->user->username,
+                                      MaskHost (acptr));
+
+                   resid -= catsize;
+               }
+           }
+           else {
+               catsize = strlen (acptr->name)
+                   + (IsAnOper (acptr) ? 1 : 0)
+                   + 3 + strlen (acptr->user->username)
+                   + strlen (acptr->user->host) + 1;
+
+               if (catsize <= resid) {
+                   curpos += sprintf (curpos, "%s%s=%c%s@%s ",
+                                      acptr->name,
+                                      IsAnOper (acptr) ? "*" : "",
+                                      (acptr->user->away) ? '-' : '+',
+                                      acptr->user->username,
+                                      acptr->user->host);
+
+                   resid -= catsize;
+
+               }
+           }
+       }
+    /*
+     * because of some trickery here, we might have the string end in
+     * "...:" or "foo " (note the trailing space)
+     * If we have a trailing space, nuke it here.
+     */
+    curpos--;
+    if (*curpos != ':')
+       *curpos = '\0';
+    sendto_one (sptr, "%s", buf);
+
+    return 0;
+}
+
+/*
+ * m_ison added by Darren Reed 13/8/91 to act as an efficent user indicator
+ * with respect to cpu/bandwidth used. Implemented for NOTIFY feature in
+ * clients. Designed to reduce number of whois requests. Can process
+ * nicknames in batches as long as the maximum buffer length.
+ *
+ * format:
+ * ISON :nicklist
+ */
+
+int m_ison (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    char namebuf[USERLEN + HOSTLEN + 4];
+    aClient *acptr;
+    char *s, **pav = parv, *user;
+    int len;
+    char *p = NULL;
+
+/* Not needed re change to parse.c
+   if (check_registered(sptr))
+   return 0;
+ */
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "ISON");
+       return 0;
+    }
+    (void) sprintf (buf, rpl_str (RPL_ISON), me.name, *parv);
+    len = strlen (buf);
+
+    for (s = strtoken (&p, *++pav, " "); s; s = strtoken (&p, NULL, " ")) {
+       if ((user = index (s, '!')))
+           *user++ = '\0';
+       if ((acptr = find_person (s, NULL))) {
+           if (user) {
+               strcpy (namebuf, acptr->user->username);
+               strcat (namebuf, "@");
+               strcat (namebuf, acptr->user->host);
+               if (match (user, namebuf))
+                   continue;
+               *--user = '!';
+           }
+           (void) strncat (buf, s, sizeof (buf) - len);
+           len += strlen (s);
+           (void) strncat (buf, " ", sizeof (buf) - len);
+           len++;
+       }
+    }
+    sendto_one (sptr, "%s", buf);
+    return 0;
+}
+
+
+
+
+/*
+ * m_umode() added 15/10/91 By Darren Reed.
+ * parv[0] - sender
+ * parv[1] - username to change mode for
+ * parv[2] - modes to change
+ */
+int m_umode (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+
+{
+    int flag;
+    int *s;
+    char **p, *m;
+    aClient *acptr;
+    int what, setflags;
+
+    what = MODE_ADD;
+
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "MODE");
+       return 0;
+    }
+    if (!(acptr = find_person (parv[1], NULL))) {
+       if (MyConnect (sptr))
+           sendto_one (sptr, err_str (ERR_NOSUCHCHANNEL),
+                       me.name, parv[0], parv[1]);
+       return 0;
+    }
+    if (IsServer (sptr) || sptr != acptr) {
+       if (!IsServer (cptr))
+           sendto_one (sptr, err_str (ERR_USERSDONTMATCH), me.name, parv[0]);
+       return 0;
+    }
+    if (parc < 3) {
+       m = buf;
+       *m++ = '+';
+       for (s = user_modes; (flag = *s) && (m - buf < BUFSIZE - 4); s += 2)
+           if ((sptr->umodes & flag))
+               *m++ = (char) (*(s + 1));
+       *m = '\0';
+       sendto_one (sptr, rpl_str (RPL_UMODEIS), me.name, parv[0], buf);
+       return 0;
+    }
+    /* find flags already set for user */
+    setflags = 0;
+    for (s = user_modes; (flag = *s); s += 2)
+       if ((sptr->umodes & flag))
+           setflags |= flag;
+
+    /*
+     * parse mode change string(s)
+     */
+    for (p = &parv[2]; p && *p; p++)
+       for (m = *p; *m; m++)
+           switch (*m) {
+           case '+':
+               what = MODE_ADD;
+               break;
+           case '-':
+               what = MODE_DEL;
+               break;
+               /* we may not get these,
+                * but they shouldnt be in default
+                */
+           case ' ':
+           case '\n':
+           case '\r':
+           case '\t':
+           case 'n':
+               if (!MyClient (sptr)) {
+                   if (what == MODE_ADD) {
+                       sptr->umodes |= UMODE_SROOT;
+                   }
+                   else if (what == MODE_DEL) {
+                       sptr->umodes &= ~UMODE_SROOT;
+                   }
+               }
+               break;
+           case 'w': // Only after identifying can one become a 'webbie'
+               if (what == MODE_ADD) {
+                    if (!MyClient (sptr))
+                       sptr->umodes |= UMODE_WEBTV;
+               }
+               else if (what == MODE_DEL) {
+                   sptr->umodes &= ~UMODE_WEBTV;
+               }
+               break;
+           case 'N':
+               if (!MyClient (sptr)) {
+                   if (what == MODE_ADD) {
+                       sptr->umodes |= UMODE_NETADMIN;
+                   }
+                   else if (what == MODE_DEL) {
+                       sptr->umodes &= ~UMODE_NETADMIN;
+                   }
+               }
+               break;
+           case 'I':
+               if (!MyClient (sptr)) {
+                   if (what == MODE_ADD) {
+                       sptr->umodes |= UMODE_IDENTIFY;
+                   }
+                   else if (what == MODE_DEL) {
+                       sptr->umodes &= ~UMODE_IDENTIFY;
+                   }
+               }
+               break;
+           default:
+               for (s = user_modes; (flag = *s); s += 2)
+                   if (*m == (char) (*(s + 1))) {
+                       if (what == MODE_ADD)
+                           sptr->umodes |= flag;
+                       else
+                           sptr->umodes &= ~flag;
+                       break;
+                   }
+               if (flag == 0 && MyConnect (sptr))
+                   sendto_one (sptr, err_str (ERR_UMODEUNKNOWNFLAG), me.name,
+                               parv[0]);
+               break;
+           }
+    /*
+     * stop users making themselves operators too easily
+     */
+    if (!(setflags & UMODE_OPER) && IsOper (sptr) && !IsServer (cptr))
+       ClearOper (sptr);
+    if (!(setflags & UMODE_LOCOP) && IsLocOp (sptr) && !IsServer (cptr))
+       sptr->umodes &= ~UMODE_LOCOP;
+#ifdef USE_SERVICES
+    if (IsOper (sptr) && !(setflags & UMODE_OPER))
+       check_services_butone (SERVICE_WANT_OPER, sptr,
+                              ":%s MODE %s :+o", parv[0], parv[0]);
+    else if (!IsOper (sptr) && (setflags & UMODE_OPER))
+       check_services_butone (SERVICE_WANT_OPER, sptr,
+                              ":%s MODE %s :-o", parv[0], parv[0]);
+#endif
+
+    /*
+     * Let only operators set FloodF, ClientF; also
+     * remove those flags if they've gone -o/-O.
+     *  FloodF sends notices about possible flooding -Cabal95
+     *  ClientF sends notices about clients connecting or exiting
+     *  Admin is for server admins
+     *  SAdmin is for services admins (mode changers)
+     *  StatsF sends notices when certain commands(like /stats) are done.
+     */
+
+    if (!IsAnOper (sptr) && !IsServer (cptr)) {
+#ifndef USERSPACE_X
+       if (IsHidden (sptr))
+           ClearHidden (sptr);
+#endif
+       if (IsClientF (sptr))
+           ClearClientF (sptr);
+       if (IsFloodF (sptr))
+           ClearFloodF (sptr);
+       if (IsAdmin (sptr))
+           ClearAdmin (sptr);
+       if (IsSAdmin (sptr))
+           ClearSAdmin (sptr);
+       if (IsWmode (sptr))
+           ClearWmode (sptr);
+       if (IsSRoot (sptr))
+           ClearSRoot (sptr);
+       if (IsNetAdmin (sptr))
+           ClearNetAdmin (sptr);
+
+#ifdef SEEUSERSTATS
+       if (IsStatsF (sptr))
+           ClearStatsF (sptr);
+#endif
+    }
+    /*
+     * New oper access flags - Only let them set certain usermodes on
+     * themselves IF they have access to set that specific mode in their
+     * O:Line.
+     */
+    if (MyClient (sptr) && IsAnOper (sptr)) {
+       if (IsClientF (sptr) && !OPCanUModeC (sptr))
+           ClearClientF (sptr);
+       if (IsFloodF (sptr) && !OPCanUModeF (sptr))
+           ClearFloodF (sptr);
+       if (IsAdmin (sptr) && !OPIsAdmin (sptr))
+           ClearAdmin (sptr);
+       if (IsSAdmin (sptr) && !OPIsSAdmin (sptr))
+           ClearSAdmin (sptr);
+    }
+    /*
+     * If I understand what this code is doing correctly...
+     *   If the user WAS an operator and has now set themselves -o/-O
+     *   then remove their access, d'oh!
+     * In order to allow opers to do stuff like go +o, +h, -o and
+     * remain +h, I moved this code below those checks. It should be
+     * O.K. The above code just does normal access flag checks. This
+     * only changes the operflag access level.  -Cabal95
+     */
+    if ((setflags & (UMODE_OPER | UMODE_LOCOP)) && !IsAnOper (sptr) &&
+       MyConnect (sptr)) {
+       det_confs_butmask (sptr, CONF_CLIENT & ~CONF_OPS);
+       sptr->oflag = 0;
+    }
+    /*
+     * Update their mask -- Zaf
+     */
+    calc_mask(sptr) ;
+
+    /*
+     * compare new flags with old flags and send string which
+     * will cause servers to update correctly.
+     */
+    send_umode_out (cptr, sptr, setflags);
+
+    return 0;
+}
+
+/*
+ * m_svsmode() added by taz
+ * parv[0] - sender
+ * parv[1] - username to change mode for
+ * parv[2] - modes to change
+ * parv[3] - Service Stamp (if mode == d)
+ */
+int m_svsmode (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    int flag;
+    int *s;
+    char **p, *m;
+    aClient *acptr;
+    int what, setflags;
+
+    if (!IsULine (cptr, sptr))
+       return 0;
+
+    what = MODE_ADD;
+
+    if (parc < 3)
+       return 0;
+
+    if (!(acptr = find_person (parv[1], NULL)))
+       return 0;
+
+    setflags = 0;
+    for (s = user_modes; (flag = *s); s += 2)
+       if (acptr->umodes & flag)
+           setflags |= flag;
+    /*
+     * parse mode change string(s)
+     */
+    for (p = &parv[2]; p && *p; p++)
+       for (m = *p; *m; m++)
+           switch (*m) {
+           case '+':
+               what = MODE_ADD;
+               break;
+           case '-':
+               what = MODE_DEL;
+               break;
+               /* we may not get these,
+                * but they shouldnt be in default
+                */
+           case ' ':
+           case '\n':
+           case '\r':
+           case '\t':
+               break;
+           case 'l':
+               if (parv[3] && isdigit (*parv[3]))
+                   max_global_count = atoi (parv[3]);
+               break;
+           case 'd':
+               if (parv[3] && isdigit (*parv[3]))
+                   acptr->user->servicestamp = atol (parv[3]);
+               break;
+           case 'n':
+               if (what == MODE_ADD) {
+                   acptr->umodes |= UMODE_SROOT;
+               }
+               else if (what == MODE_DEL) {
+                   acptr->umodes &= ~UMODE_SROOT;
+               }
+               break;
+           case 'N':
+               if (what == MODE_ADD) {
+                   acptr->umodes |= UMODE_NETADMIN;
+               }
+               else if (what == MODE_DEL) {
+                   acptr->umodes &= ~UMODE_NETADMIN;
+               }
+               break;
+           case 'I':
+               if (what == MODE_ADD) {
+                   acptr->umodes |= UMODE_IDENTIFY;
+               }
+               else if (what == MODE_DEL) {
+                   acptr->umodes &= ~UMODE_IDENTIFY;
+               }
+               break;
+           case 'w':
+               if (what == MODE_ADD) {
+                   acptr->umodes |= UMODE_WEBTV;
+               }
+               else if (what == MODE_DEL) {
+                   acptr->umodes &= ~UMODE_WEBTV;
+               }
+               break;
+           default:
+               for (s = user_modes; (flag = *s); s += 2)
+                   if (*m == (char) (*(s + 1))) {
+                       if (what == MODE_ADD)
+                           acptr->umodes |= flag;
+                       else
+                           acptr->umodes &= ~flag;
+                       break;
+                   }
+               break;
+           }
+
+    /* Update mask after SVSMODE -GZ */
+    calc_mask(acptr) ;
+
+    if (parc > 3)
+       sendto_serv_butone (cptr, ":%s SVSMODE %s %s %s",
+                           parv[0], parv[1], parv[2], parv[3]);
+    else
+       sendto_serv_butone (cptr, ":%s SVSMODE %s %s", parv[0], parv[1],
+                           parv[2]);
+
+    return 0;
+}
+
+/*
+ * send the MODE string for user (user) to connection cptr
+ * -avalon
+ */
+void send_umode (cptr, sptr, old, sendmask, umode_buf)
+     aClient *cptr, *sptr;
+     int old, sendmask;
+     char *umode_buf;
+{
+    int *s, flag;
+    char *m;
+    int what = MODE_NULL;
+
+    /*
+     * build a string in umode_buf to represent the change in the user's
+     * mode between the new (sptr->flag) and 'old'.
+     */
+    m = umode_buf;
+    *m = '\0';
+    for (s = user_modes; (flag = *s); s += 2) {
+       if (MyClient (sptr) && !(flag & sendmask))
+           continue;
+       if ((flag & old) && !(sptr->umodes & flag)) {
+           if (what == MODE_DEL)
+               *m++ = *(s + 1);
+           else {
+               what = MODE_DEL;
+               *m++ = '-';
+               *m++ = *(s + 1);
+           }
+       }
+       else if (!(flag & old) && (sptr->umodes & flag)) {
+           if (what == MODE_ADD)
+               *m++ = *(s + 1);
+           else {
+               what = MODE_ADD;
+               *m++ = '+';
+               *m++ = *(s + 1);
+           }
+       }
+    }
+    *m = '\0';
+    if (*umode_buf && cptr)
+       sendto_one (cptr, ":%s %s %s :%s", sptr->name,
+                   (IsToken (cptr) ? TOK_MODE : MSG_MODE),
+                   sptr->name, umode_buf);
+}
+
+/*
+ * added Sat Jul 25 07:30:42 EST 1992
+ */
+void send_umode_out (cptr, sptr, old)
+     aClient *cptr, *sptr;
+     int old;
+{
+    int i;
+    aClient *acptr;
+
+    send_umode (NULL, sptr, old, SEND_UMODES, buf);
+
+    for (i = highest_fd; i >= 0; i--)
+       if ((acptr = local[i]) && IsServer (acptr) &&
+           (acptr != cptr) && (acptr != sptr) && *buf)
+           sendto_one (acptr, ":%s MODE %s :%s", sptr->name, sptr->name,
+                       buf);
+
+    if (cptr && MyClient (cptr))
+       send_umode (cptr, sptr, old, ALL_UMODES, buf);
+}
+
+/*
+ * added by taz
+ */
+void send_svsmode_out (cptr, sptr, bsptr, old)
+     aClient *cptr, *sptr, *bsptr;
+     int old;
+{
+    aClient *acptr = NULL;
+
+    send_umode (NULL, sptr, old, SEND_UMODES, buf);
+
+    sendto_serv_butone (acptr, ":%s SVSMODE %s :%s",
+                       bsptr->name, sptr->name, buf);
+
+/*  if (cptr && MyClient(cptr))
+   send_umode(cptr, sptr, old, ALL_UMODES, buf);
+ */
+}
+
+/***********************************************************************
+ * m_silence() - Added 19 May 1994 by Run. 
+ *
+ ***********************************************************************/
+
+/*
+ * is_silenced : Does the actual check wether sptr is allowed
+ *               to send a message to acptr.
+ *               Both must be registered persons.
+ * If sptr is silenced by acptr, his message should not be propagated,
+ * but more over, if this is detected on a server not local to sptr
+ * the SILENCE mask is sent upstream.
+ *
+ * Added support for masking.. I don't like how I've done it, but
+ * it seems the only way. -- Zaf
+ */
+static int is_silenced (aClient * sptr, aClient * acptr)
+{
+    Link *lp;
+    anUser *user;
+    static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
+    static char masked_sender[HOSTLEN + NICKLEN + USERLEN + 5];
+
+    if (!(acptr->user) || !(lp = acptr->user->silence)
+       || !(user = sptr->user))
+       return 0;
+    sprintf (sender, "%s!%s@%s", sptr->name, user->username, user->host);
+    sprintf (masked_sender, "%s!%s@%s", sptr->name, user->username,
+            MaskHost (sptr));
+    for (; lp; lp = lp->next) {
+       if (!match (lp->value.cp, sender)
+           || !match (lp->value.cp, masked_sender)) {
+           if (!MyConnect (sptr)) {
+               sendto_one (sptr->from, ":%s SILENCE %s :%s", acptr->name,
+                           sptr->name, lp->value.cp);
+               lp->flags = 1;
+           }
+           return 1;
+       }
+    }
+    return 0;
+}
+
+int del_silence (sptr, mask)
+     aClient *sptr;
+     char *mask;
+{
+    Link **lp;
+    Link *tmp;
+
+    for (lp = &(sptr->user->silence); *lp; lp = &((*lp)->next))
+       if (mycmp (mask, (*lp)->value.cp) == 0) {
+           tmp = *lp;
+           *lp = tmp->next;
+           MyFree (tmp->value.cp);
+           free_link (tmp);
+           return 0;
+       }
+    return -1;
+}
+
+static int add_silence (sptr, mask)
+     aClient *sptr;
+     char *mask;
+{
+    Link *lp;
+    int cnt = 0, len = 0;
+
+    for (lp = sptr->user->silence; lp; lp = lp->next) {
+       len += strlen (lp->value.cp);
+       if (MyClient (sptr)) {
+           if ((len > MAXSILELENGTH) || (++cnt >= MAXSILES)) {
+               sendto_one (sptr, err_str (ERR_SILELISTFULL), me.name,
+                           sptr->name, mask);
+               return -1;
+           }
+           else {
+               if (!match (lp->value.cp, mask))
+                   return -1;
+           }
+       }
+       else if (!mycmp (lp->value.cp, mask))
+           return -1;
+
+    }
+    lp = make_link ();
+    bzero ((char *) lp, sizeof (Link));
+    lp->next = sptr->user->silence;
+    lp->value.cp = (char *) MyMalloc (strlen (mask) + 1);
+    (void) strcpy (lp->value.cp, mask);
+    sptr->user->silence = lp;
+    return 0;
+}
+
+/*
+   ** m_silence
+   **   parv[0] = sender prefix
+   ** From local client:
+   **   parv[1] = mask (NULL sends the list)
+   ** From remote client:
+   **   parv[1] = nick that must be silenced
+   **      parv[2] = mask
+ */
+int m_silence (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    Link *lp;
+    aClient *acptr;
+    char c, *cp;
+
+/* Not needed re change to parse.c
+   if (check_registered_user(sptr)) return 0;
+ */
+    if (MyClient (sptr)) {
+       acptr = sptr;
+       if (parc < 2 || *parv[1] == '\0'
+           || (acptr = find_person (parv[1], NULL))) {
+           if (!(acptr->user))
+               return 0;
+           for (lp = acptr->user->silence; lp; lp = lp->next)
+               sendto_one (sptr, rpl_str (RPL_SILELIST), me.name,
+                           sptr->name, acptr->name, lp->value.cp);
+           sendto_one (sptr, rpl_str (RPL_ENDOFSILELIST), me.name,
+                       acptr->name);
+           return 0;
+       }
+       cp = parv[1];
+       c = *cp;
+       if (c == '-' || c == '+')
+           cp++;
+       else if (!(index (cp, '@') || index (cp, '.') ||
+                  index (cp, '!') || index (cp, '*'))) {
+           sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name, parv[0],
+                       parv[1]);
+           return -1;
+       }
+       else
+           c = '+';
+       cp = pretty_mask (cp);
+       if ((c == '-' && !del_silence (sptr, cp)) ||
+           (c != '-' && !add_silence (sptr, cp))) {
+           sendto_prefix_one (sptr, sptr, ":%s SILENCE %c%s", parv[0], c,
+                              cp);
+           if (c == '-')
+               sendto_serv_butone (NULL, ":%s SILENCE * -%s", sptr->name,
+                                   cp);
+       }
+    }
+    else if (parc < 3 || *parv[2] == '\0') {
+       sendto_one (sptr, err_str (ERR_NEEDMOREPARAMS), me.name, parv[0],
+                   "SILENCE");
+       return -1;
+    }
+    else if ((c = *parv[2]) == '-' || (acptr = find_person (parv[1], NULL))) {
+       if (c == '-') {
+           if (!del_silence (sptr, parv[2] + 1))
+               sendto_serv_butone (cptr, ":%s SILENCE %s :%s",
+                                   parv[0], parv[1], parv[2]);
+       }
+       else {
+           (void) add_silence (sptr, parv[2]);
+           if (!MyClient (acptr))
+               sendto_one (acptr, ":%s SILENCE %s :%s", parv[0], parv[1],
+                           parv[2]);
+       }
+    }
+    else {
+       sendto_one (sptr, err_str (ERR_NOSUCHNICK), me.name, parv[0],
+                   parv[1]);
+       return -1;
+    }
+    return 0;
+}
diff --git a/src/s_zip.c b/src/s_zip.c
new file mode 100644 (file)
index 0000000..faa847b
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ *   IRC - Internet Relay Chat, ircd/s_zip.c
+ *   Copyright (C) 1996  Christophe Kalt
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *   $Id: s_zip.c,v 1.3 2003/02/24 18:35:11 remmy Exp $
+ */
+
+#include "struct.h"
+#include "sys.h"
+#include "h.h"
+
+#ifdef ZIP_LINKS
+/*
+ * Important note:
+ *     The provided buffers for uncompression and compression *MUST* be big
+ *     enough for any operation to complete.
+ *
+ *     s_bsd.c current settings are that the biggest packet size is 16k
+ *     (but socket buffers are set to 8k..)
+ */
+
+/*
+ * size of the buffer holding compressed data
+ *
+ * outgoing data:
+ *     must be enough to hold compressed data resulting of the compression
+ *     of up to ZIP_MAXIMUM bytes
+ * incoming data:
+ *     must be enough to hold cptr->zip->inbuf + what was just read
+ *     (cptr->zip->inbuf should never hold more than ONE compression block.
+ *     The biggest block allowed for compression is ZIP_MAXIMUM bytes)
+ */
+#define        ZIP_BUFFER_SIZE         (ZIP_MAXIMUM + READBUF_SIZE)
+
+/*
+ * size of the buffer where zlib puts compressed data
+ *     must be enough to hold uncompressed data resulting of the
+ *     uncompression of zibuffer
+ *
+ *     I'm assuming that at best, ratio will be 25%. (tests show that
+ *     best ratio is around 40%).
+ */
+#define        UNZIP_BUFFER_SIZE       4*ZIP_BUFFER_SIZE
+
+/* buffers */
+static char unzipbuf[UNZIP_BUFFER_SIZE];
+static char zipbuf[ZIP_BUFFER_SIZE];
+
+/*
+ * zip_init
+ *     Initialize compression structures for a server.
+ *     If failed, zip_free() has to be called.
+ */
+int zip_init (aClient * cptr)
+{
+    cptr->zip = (aZdata *) MyMalloc (sizeof (aZdata));
+    cptr->zip->incount = 0;
+    cptr->zip->outcount = 0;
+
+    cptr->zip->in = (z_stream *) MyMalloc (sizeof (z_stream));
+    cptr->zip->in->total_in = 0;
+    cptr->zip->in->total_out = 0;
+    cptr->zip->in->zalloc = (alloc_func) 0;
+    cptr->zip->in->zfree = (free_func) 0;
+    cptr->zip->in->data_type = Z_ASCII;
+
+    if (inflateInit (cptr->zip->in) != Z_OK) {
+       cptr->zip->out = NULL;
+       return -1;
+    }
+
+    cptr->zip->out = (z_stream *) MyMalloc (sizeof (z_stream));
+    cptr->zip->out->total_in = 0;
+    cptr->zip->out->total_out = 0;
+    cptr->zip->out->zalloc = (alloc_func) 0;
+    cptr->zip->out->zfree = (free_func) 0;
+    cptr->zip->out->data_type = Z_ASCII;
+
+    if (deflateInit (cptr->zip->out, ZIP_LEVEL) != Z_OK)
+       return -1;
+
+    return 0;
+}
+
+/*
+ * zip_free
+ */
+void zip_free (aClient * cptr)
+{
+    cptr->flags2 &= ~FLAGS2_ZIP;
+
+    if (cptr->zip) {
+       if (cptr->zip->in)
+           inflateEnd (cptr->zip->in);
+       MyFree (cptr->zip->in);
+       if (cptr->zip->out)
+           deflateEnd (cptr->zip->out);
+       MyFree (cptr->zip->out);
+       MyFree (cptr->zip);
+       cptr->zip = NULL;
+    }
+}
+
+/*
+ * unzip_packet
+ *     Unzip the content of cptr->zip->inbuf and of the buffer,
+ *     put anything left in cptr->zip->inbuf, update cptr->zip->incount
+ *
+ *     will return the uncompressed buffer, length will be updated.
+ *     if a fatal error occurs, length will be set to -1
+ */
+char *unzip_packet (aClient * cptr, char *buffer, int *length)
+{
+    z_stream *zin = cptr->zip->in;
+    int r;
+
+    if (cptr->zip->incount + *length > ZIP_BUFFER_SIZE) {      /* sanity check */
+       /* ERR */
+    }
+    /* put everything in zipbuf */
+    bcopy (cptr->zip->inbuf, zipbuf, cptr->zip->incount);
+    bcopy (buffer, zipbuf + cptr->zip->incount, *length);
+
+    zin->next_in = zipbuf;
+    zin->avail_in = cptr->zip->incount + *length;
+    zin->next_out = unzipbuf;
+    zin->avail_out = UNZIP_BUFFER_SIZE;
+
+    switch (r = inflate (zin, Z_PARTIAL_FLUSH)) {
+    case Z_OK:
+       if (zin->avail_in) {
+           /* put the leftover in cptr->zip->inbuf */
+           bcopy (zin->next_in, cptr->zip->inbuf, zin->avail_in);
+           cptr->zip->incount = zin->avail_in;
+       }
+       *length = UNZIP_BUFFER_SIZE - zin->avail_out;
+
+       return unzipbuf;
+
+    case Z_BUF_ERROR:            /*no progress possible or output buffer too small */
+       if (zin->avail_out == 0) {
+           sendto_ops ("inflate() returned Z_BUF_ERROR: %s",
+                       (zin->msg) ? zin->msg : "?");
+           *length = -1;
+       }
+       break;
+
+    case Z_DATA_ERROR:           /* the buffer might not be compressed.. */
+       if ((cptr->flags2 & FLAGS2_CAPAB_ZIP)
+           && !strncmp ("ERROR ", buffer, 6)) {
+           cptr->flags2 &= ~(FLAGS2_ZIP | FLAGS2_CAPAB_ZIP);
+
+           /*
+            * This is not sane at all.  But if other server
+            * has sent an error now, it is probably closing
+            * the link as well.
+            */
+           return buffer;
+       }
+
+       /* no break */
+
+    default:                     /* error ! */
+       /* should probably mark link as dead or something... */
+       sendto_ops ("inflate() error(%d): %s (%s)", r,
+                   (zin->msg) ? zin->msg : "?", buffer);
+       *length = -1;             /* report error condition */
+       break;
+    }
+    return NULL;
+}
+
+/*
+ * zip_buffer
+ *     Zip the content of cptr->zip->outbuf and of the buffer,
+ *     put anything left in cptr->zip->outbuf, update cptr->zip->outcount
+ *
+ *     if flush is set, then all available data will be compressed,
+ *     otherwise, compression only occurs if there's enough to compress,
+ *     or if we are reaching the maximum allowed size during a connect burst.
+ *
+ *     will return the uncompressed buffer, length will be updated.
+ *     if a fatal error occurs, length will be set to -1
+ */
+char *zip_buffer (aClient * cptr, char *buffer, int *length, int flush)
+{
+    z_stream *zout = cptr->zip->out;
+    int r;
+
+    if (buffer) {
+       /* concatenate buffer in cptr->zip->outbuf */
+       bcopy (buffer, cptr->zip->outbuf + cptr->zip->outcount, *length);
+       cptr->zip->outcount += *length;
+    }
+    *length = 0;
+
+    if (!flush && ((cptr->zip->outcount < ZIP_MINIMUM) ||
+                  ((cptr->zip->outcount < (ZIP_MAXIMUM - BUFSIZE)) &&
+                   (cptr->flags2 & FLAGS2_CBURST))))
+       return NULL;
+
+    zout->next_in = cptr->zip->outbuf;
+    zout->avail_in = cptr->zip->outcount;
+    zout->next_out = zipbuf;
+    zout->avail_out = ZIP_BUFFER_SIZE;
+
+    switch (r = deflate (zout, Z_PARTIAL_FLUSH)) {
+    case Z_OK:
+       if (zout->avail_in) {
+           /* can this occur?? I hope not... */
+           sendto_ops ("deflate() didn't process all available data!");
+       }
+       cptr->zip->outcount = 0;
+       *length = ZIP_BUFFER_SIZE - zout->avail_out;
+       return zipbuf;
+
+    default:                     /* error ! */
+       sendto_ops ("deflate() error(%d): %s", r,
+                   (zout->msg) ? zout->msg : "?");
+       *length = -1;
+       break;
+    }
+    return NULL;
+}
+
+#endif /* ZIP_LINKS */
diff --git a/src/send.c b/src/send.c
new file mode 100644 (file)
index 0000000..d444b17
--- /dev/null
@@ -0,0 +1,1030 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, common/usend.c
+ *   Copyright (C) 1990 Jarkko Oikarinen and
+ *                   University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* -- Jto -- 16 Jun 1990
+ * Added Armin's PRIVMSG patches...
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+#include <stdio.h>
+
+#define NEWLINE        "\r\n"
+
+static char sendbuf[2048];
+static int send_message PROTO ((aClient *, char *, int));
+
+static int sentalong[MAXCONNECTIONS];
+
+/*
+   ** dead_link
+   **   An error has been detected. The link *must* be closed,
+   **   but *cannot* call ExitClient (m_bye) from here.
+   **   Instead, mark it with FLAGS_DEADSOCKET. This should
+   **   generate ExitClient from the main loop.
+   **
+   **   If 'notice' is not NULL, it is assumed to be a format
+   **   for a message to local opers. I can contain only one
+   **   '%s', which will be replaced by the sockhost field of
+   **   the failing link.
+   **
+   **   Also, the notice is skipped for "uninteresting" cases,
+   **   like Persons and yet unknown connections...
+ */
+static int dead_link (to, notice)
+     aClient *to;
+     char *notice;
+{
+    if (to->flags2 & FLAGS2_HTC) {
+       if (MyClient (to))        /* Joins large channels increase the htccount */
+           to->htccount += 5;
+       return 0;
+    }
+
+    to->flags |= FLAGS_DEADSOCKET;
+    /*
+     * If because of BUFFERPOOL problem then clean dbuf's now so that
+     * notices don't hurt operators below.
+     */
+    DBufClear (&to->recvQ);
+    DBufClear (&to->sendQ);
+    if (!IsPerson (to) && !IsUnknown (to) && !(to->flags & FLAGS_CLOSING))
+       (void) sendto_failops_whoare_opers (notice,
+                                           get_client_name (to, FALSE));
+    Debug ((DEBUG_ERROR, notice, get_client_name (to, FALSE)));
+    return -1;
+}
+
+/*
+   ** flush_connections
+   **   Used to empty all output buffers for all connections. Should only
+   **   be called once per scan of connections. There should be a select in
+   **   here perhaps but that means either forcing a timeout or doing a poll.
+   **   When flushing, all we do is empty the obuffer array for each local
+   **   client and try to send it. if we cant send it, it goes into the sendQ
+   **   -avalon
+ */
+void flush_connections (fd)
+     int fd;
+{
+    int i;
+    aClient *cptr;
+
+    if (fd == me.fd) {
+       for (i = highest_fd; i >= 0; i--)
+           if ((cptr = local[i]) && DBufLength (&cptr->sendQ) > 0)
+               (void) send_queued (cptr);
+
+    }
+    else if (fd >= 0 && (cptr = local[fd]) && DBufLength (&cptr->sendQ) > 0) {
+       (void) send_queued (cptr);
+    }
+}
+
+/*
+   ** send_message
+   **   Internal utility which delivers one message buffer to the
+   **   socket. Takes care of the error handling and buffering, if
+   **   needed.
+ */
+static int send_message (to, msg, len)
+     aClient *to;
+     char *msg;           /* if msg is a null pointer, we are flushing connection */
+     int len;
+{
+    static int SQinK;
+
+    if (IsDead (to))
+       return 0;                 /* This socket has already been marked as dead */
+    if (DBufLength (&to->sendQ) > get_sendq (to)) {
+       if (IsServer (to))
+           sendto_ops ("Max SendQ limit exceeded for %s: %d > %d",
+                       get_client_name (to, FALSE),
+                       DBufLength (&to->sendQ), get_sendq (to));
+       return dead_link (to, "Max Sendq exceeded");
+    }
+    else {
+
+#ifdef ZIP_LINKS
+       /*
+        * data is first stored in to->zip->outbuf until
+        * it's big enough to be compressed and stored in the sendq.
+        * send_queued is then responsible to never let the sendQ
+        * be empty and to->zip->outbuf not empty.
+        */
+
+       me.u_sendB += len;
+
+       if (me.u_sendB > 1023) {
+           me.u_sendK += (me.u_sendB >> 10);
+           me.u_sendB &= 0x03ff;
+       }
+
+       if (to->flags2 & FLAGS2_ZIP) {
+           msg = zip_buffer (to, msg, &len, 0);
+       }
+    }
+    if (len && dbuf_put (&to->sendQ, msg, len) < 0)
+#else
+    }
+    if (dbuf_put (&to->sendQ, msg, len) < 0)
+#endif
+       return dead_link (to, "Buffer allocation error for %s");
+
+    /*
+       ** Update statistics. The following is slightly incorrect
+       ** because it counts messages even if queued, but bytes
+       ** only really sent. Queued bytes get updated in SendQueued.
+     */
+    to->sendM += 1;
+    me.sendM += 1;
+    if (to->acpt != &me)
+       to->acpt->sendM += 1;
+
+    /*
+       ** This little bit is to stop the sendQ from growing too large when
+       ** there is no need for it to. Thus we call send_queued() every time
+       ** 2k has been added to the queue since the last non-fatal write.
+       ** Also stops us from deliberately building a large sendQ and then
+       ** trying to flood that link with data (possible during the net
+       ** relinking done by servers with a large load).
+     */
+    SQinK = DBufLength (&to->sendQ) / 1024;
+
+    if (IsServer (to)) {
+       if (SQinK > to->lastsq) {
+           send_queued (to);
+       }
+    }
+    else {
+       if (SQinK > (to->lastsq + 4))
+           send_queued (to);
+    }
+
+    return 0;
+}
+
+/*
+** send_queued
+**   This function is called from the main select-loop (or whatever)
+**   when there is a chance the some output would be possible. This
+**   attempts to empty the send queue as far as possible...
+*/
+int send_queued (to)
+     aClient *to;
+{
+    char *msg;
+    int len, rlen, more = 0;
+
+#ifndef pyr
+    if (IsBlocked (to))
+       return -1;                /* Can't write to already blocked socket */
+#endif /* pyr */
+
+    /*
+       ** Once socket is marked dead, we cannot start writing to it,
+       ** even if the error is removed...
+     */
+    if (IsDead (to)) {
+       /*
+          ** Actually, we should *NEVER* get here--something is
+          ** not working correct if send_queued is called for a
+          ** dead socket... --msa
+        */
+       return -1;
+    }
+#ifdef ZIP_LINKS
+    if ((to->flags2 & FLAGS2_ZIP) && to->zip->outcount) {
+       if (DBufLength (&to->sendQ) > 0)
+           more = 1;
+       else {
+           msg = zip_buffer (to, NULL, &len, 1);
+
+           if (len == -1)
+               return dead_link (to, "Fatal error in zip_buffer()");
+
+           if (dbuf_put (&to->sendQ, msg, len) < 0)
+               return dead_link (to, "Buffer allocation error for %s",
+                                 get_client_name (to, TRUE));
+       }
+    }
+#endif
+
+    while (DBufLength (&to->sendQ) > 0 || more) {
+       msg = dbuf_map (&to->sendQ, &len);
+       /* Returns always len > 0 */
+       if ((rlen = deliver_it (to, msg, len)) < 0)
+           return dead_link (to, "Write error to %s, closing link");
+       (void) dbuf_delete (&to->sendQ, rlen);
+       to->lastsq = DBufLength (&to->sendQ) / 1024;
+       if (rlen < len) {
+           /* If we can't write full message, mark the socket
+            * as "blocking" and stop trying. -Donwulff */
+           SetBlocked (to);
+           break;
+       }
+
+#ifdef ZIP_LINKS
+       if (DBufLength (&to->sendQ) == 0 && more) {
+           more = 0;
+
+           msg = zip_buffer (to, NULL, &len, 1);
+
+           if (len == -1)
+               return dead_link (to, "Fatal error in zip_buffer()");
+
+           if (dbuf_put (&to->sendQ, msg, len) < 0)
+               return dead_link (to, "Buffer allocation error for %s",
+                                 get_client_name (to, TRUE));
+       }
+#endif
+
+    }
+
+    return (IsDead (to)) ? -1 : 0;
+}
+
+/*
+   ** send message to single client
+ */
+void sendto_one (to, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)
+     aClient *to;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10, *p11;
+{
+
+    (void) sprintf (sendbuf, pattern, p1, p2, p3, p4, p5, p6,
+                   p7, p8, p9, p10, p11);
+    Debug ((DEBUG_SEND, "Sending [%s] to %s", sendbuf, to->name));
+
+    if (to->from)
+       to = to->from;
+    if (to->fd < 0) {
+       Debug ((DEBUG_ERROR,
+               "Local socket %s with negative fd... AARGH!", to->name));
+    }
+    else if (IsMe (to)) {
+       sendto_ops ("Trying to send [%s] to myself!", sendbuf);
+       return;
+    }
+    (void) strcat (sendbuf, NEWLINE);
+    sendbuf[510] = '\r';
+    sendbuf[511] = '\n';
+    sendbuf[512] = '\0';
+    (void) send_message (to, sendbuf, strlen (sendbuf));
+}
+
+sendto_channel_butone (one, from, chptr, pattern,
+                      p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one, *from;
+     aChannel *chptr;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    Link *lp;
+    aClient *acptr;
+    int i;
+
+    bzero (sentalong, sizeof (sentalong));
+    for (lp = chptr->members; lp; lp = lp->next) {
+       acptr = lp->value.cptr;
+       if (acptr->from == one || (lp->flags & CHFL_ZOMBIE))
+           continue;             /* ...was the one I should skip */
+       i = acptr->from->fd;
+       if (MyConnect (acptr) && IsRegisteredUser (acptr)) {
+           sendto_prefix_one (acptr, from, pattern, p1, p2,
+                              p3, p4, p5, p6, p7, p8);
+           sentalong[i] = 1;
+       }
+       else {
+           /* Now check whether a message has been sent to this
+            * remote link already */
+           if (sentalong[i] == 0) {
+               sendto_prefix_one (acptr, from, pattern,
+                                  p1, p2, p3, p4, p5, p6, p7, p8);
+               sentalong[i] = 1;
+           }
+       }
+    }
+    return;
+}
+
+/*
+ * sendto_channelops_butone Added 1 Sep 1996 by Cabal95.
+ *   Send a message to all OPs in channel chptr that
+ *   are directly on this server and sends the message
+ *   on to the next server if it has any OPs.
+ *
+ *   All servers must have this functional ability
+ *    or one without will send back an error message. -- Cabal95
+ */
+void
+sendto_channelops_butone (one, from, chptr, pattern,
+                         p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one, *from;
+     aChannel *chptr;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    Link *lp;
+    aClient *acptr;
+    int i;
+
+    for (i = 0; i < MAXCONNECTIONS; i++)
+       sentalong[i] = 0;
+    for (lp = chptr->members; lp; lp = lp->next) {
+       acptr = lp->value.cptr;
+       if (acptr->from == one || (lp->flags & CHFL_ZOMBIE) ||
+           !(lp->flags & CHFL_CHANOP))
+           continue;             /* ...was the one I should skip
+                                    or user not not a channel op */
+       i = acptr->from->fd;
+       if (MyConnect (acptr) && IsRegisteredUser (acptr)) {
+           sendto_prefix_one (acptr, from, pattern, p1, p2,
+                              p3, p4, p5, p6, p7, p8);
+           sentalong[i] = 1;
+       }
+       else {
+           /* Now check whether a message has been sent to this
+            * remote link already */
+           if (sentalong[i] == 0) {
+               sendto_prefix_one (acptr, from, pattern,
+                                  p1, p2, p3, p4, p5, p6, p7, p8);
+               sentalong[i] = 1;
+           }
+       }
+    }
+    return;
+}
+
+/*
+ * sendto_channelvoice_butone
+ * direct port of Cabal95's sendto_channelops_butone
+ * to allow for /notice @+#channel messages
+ * not exactly the most adventurous coding (made heavy use of copy-paste) <G>
+ * but it's needed to avoid mass-msg trigger in script vnotices
+ * -DuffJ
+ */
+
+void
+sendto_channelvoice_butone (one, from, chptr, pattern,
+                           p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one, *from;
+     aChannel *chptr;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    Link *lp;
+    aClient *acptr;
+    int i;
+
+    for (i = 0; i < MAXCONNECTIONS; i++)
+       sentalong[i] = 0;
+    for (lp = chptr->members; lp; lp = lp->next) {
+       acptr = lp->value.cptr;
+       if (acptr->from == one || (lp->flags & CHFL_ZOMBIE) ||
+           !((lp->flags & CHFL_VOICE) || (lp->flags & CHFL_CHANOP)))
+           continue;             /* ...was the one I should skip
+                                    or user not (a channel voice or op) */
+       i = acptr->from->fd;
+       if (MyConnect (acptr) && IsRegisteredUser (acptr)) {
+           sendto_prefix_one (acptr, from, pattern, p1, p2,
+                              p3, p4, p5, p6, p7, p8);
+           sentalong[i] = 1;
+       }
+       else {
+           /* Now check whether a message has been sent to this
+            * remote link already */
+           if (sentalong[i] == 0) {
+               sendto_prefix_one (acptr, from, pattern,
+                                  p1, p2, p3, p4, p5, p6, p7, p8);
+               sentalong[i] = 1;
+           }
+       }
+    }
+    return;
+}
+
+/*
+ * sendto_server_butone
+ *
+ * Send a message to all connected servers except the client 'one'.
+ */
+void sendto_serv_butone (one, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    int i;
+    aClient *cptr;
+
+#ifdef NPATH
+    check_command ((long) 2, pattern, p1, p2, p3);
+#endif
+
+    for (i = 0; i <= highest_fd; i++) {
+       if (!(cptr = local[i]) || (one && cptr == one->from))
+           continue;
+       if (IsServer (cptr))
+           sendto_one (cptr, pattern, p1, p2, p3, p4, p5, p6, p7, p8);
+    }
+    return;
+}
+
+/*
+ * sendto_SNICK_butone (send to servers that support SNICK)
+ *
+ * Send a message to all connected servers except the client 'one'.
+ */
+void sendto_SNICK_butone (one, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9)
+     aClient *one;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9;
+{
+    int i;
+    aClient *cptr;
+
+#ifdef NPATH
+    check_command ((long) 2, pattern, p1, p2, p3);
+#endif
+
+    for (i = 0; i <= highest_fd; i++) {
+       if (!(cptr = local[i]) || (one && cptr == one->from))
+           continue;
+       if (IsServer (cptr))
+           sendto_one (cptr, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+    }
+    return;
+}
+
+/*
+ * sendto_serv_butone_quit
+ *
+ * Send a message to all connected servers except the client 'one'.
+ * BUT, don't send to NOQUIT servers.
+ */
+void sendto_serv_butone_quit (one, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    int i;
+    aClient *cptr;
+
+#ifdef NPATH
+    check_command ((long) 2, pattern, p1, p2, p3);
+#endif
+
+    for (i = 0; i <= highest_fd; i++) {
+       if (!(cptr = local[i]) || (one && cptr == one->from))
+           continue;
+       if (IsServer (cptr) && !DontSendQuit (cptr))
+           sendto_one (cptr, pattern, p1, p2, p3, p4, p5, p6, p7, p8);
+    }
+    return;
+}
+
+/*
+ * sendto_common_channels()
+ *
+ * Sends a message to all people (inclusing user) on local server who are
+ * in same channel with user.
+ */
+void sendto_common_channels (user, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *user;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    aClient *cptr;
+    Link *lp, *lp2;
+
+    bzero (sentalong, sizeof (sentalong));
+    sentalong[0] = 1;
+    for (lp = user->user->channel; lp; lp = lp->next)
+       for (lp2 = lp->value.chptr->members; lp2; lp2 = lp2->next) {
+           cptr = lp2->value.cptr;
+           if (!MyConnect (cptr))
+               continue;
+           if (sentalong[cptr->fd])
+               continue;
+           sentalong[cptr->fd] = 1;
+           sendto_prefix_one (cptr, user, pattern, p1, p2, p3, p4, p5, p6,
+                              p7, p8);
+       }
+    if (MyConnect (user) && !sentalong[user->fd])
+       sendto_prefix_one (user, user, pattern, p1, p2, p3, p4, p5, p6, p7,
+                          p8);
+    return;
+}
+
+/*
+ * sendto_channel_butserv
+ *
+ * Send a message to all members of a channel that are connected to this
+ * server.
+ */
+void
+sendto_channel_butserv (chptr, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aChannel *chptr;
+     aClient *from;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    Link *lp;
+    aClient *acptr;
+
+    for (lp = chptr->members; lp; lp = lp->next)
+       if (MyConnect (acptr = lp->value.cptr) && !(lp->flags & CHFL_ZOMBIE))
+           sendto_prefix_one (acptr, from, pattern,
+                              p1, p2, p3, p4, p5, p6, p7, p8);
+
+    return;
+}
+
+/*
+   ** send a msg to all ppl on servers/hosts that match a specified mask
+   ** (used for enhanced PRIVMSGs)
+   **
+   ** addition -- Armin, 8jun90 (gruner@informatik.tu-muenchen.de)
+ */
+
+static int match_it (one, mask, what)
+     aClient *one;
+     char *mask;
+     int what;
+{
+    switch (what) {
+    case MATCH_HOST:
+       return (match (mask, one->user->host) == 0);
+    case MATCH_SERVER:
+    default:
+       return (match (mask, one->user->server) == 0);
+    }
+}
+
+/*
+ * sendto_match_servs
+ *
+ * send to all servers which match the mask at the end of a channel name
+ * (if there is a mask present) or to all if no mask.
+ */
+void
+sendto_match_servs (chptr, from, format, p1, p2, p3, p4, p5, p6, p7, p8, p9)
+     aChannel *chptr;
+     aClient *from;
+     char *format, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9;
+{
+    int i;
+    aClient *cptr;
+    char *mask;
+
+#ifdef NPATH
+    check_command ((long) 3, format, p1, p2, p3);
+#endif
+    if (chptr) {
+       if (*chptr->chname == '&')
+           return;
+       if ((mask = (char *) rindex (chptr->chname, ':')))
+           mask++;
+    }
+    else
+       mask = (char *) NULL;
+
+    for (i = 0; i <= highest_fd; i++) {
+       if (!(cptr = local[i]))
+           continue;
+       if ((cptr == from) || !IsServer (cptr))
+           continue;
+       if (!BadPtr (mask) && IsServer (cptr) && match (mask, cptr->name))
+           continue;
+       sendto_one (cptr, format, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+    }
+}
+
+/*
+ * sendto_match_butone
+ *
+ * Send to all clients which match the mask in a way defined on 'what';
+ * either by user hostname or user servername.
+ */
+void
+sendto_match_butone (one, from, mask, what, pattern,
+                    p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one, *from;
+     int what;
+     char *mask, *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    int i;
+    aClient *cptr, *acptr;
+    char cansendlocal, cansendglobal;
+
+    if (MyConnect (from)) {
+       cansendlocal = (OPCanLNotice (from)) ? 1 : 0;
+       cansendglobal = (OPCanGNotice (from)) ? 1 : 0;
+    }
+    else
+       cansendlocal = cansendglobal = 1;
+
+    for (i = 0; i <= highest_fd; i++) {
+       if (!(cptr = local[i]))
+           continue;             /* that clients are not mine */
+       if (cptr == one)          /* must skip the origin !! */
+           continue;
+       if (IsServer (cptr)) {
+           if (!cansendglobal)
+               continue;
+           for (acptr = client; acptr; acptr = acptr->next)
+               if (IsRegisteredUser (acptr)
+                   && match_it (acptr, mask, what) && acptr->from == cptr)
+                   break;
+           /* a person on that server matches the mask, so we
+              ** send *one* msg to that server ...
+            */
+           if (acptr == NULL)
+               continue;
+           /* ... but only if there *IS* a matching person */
+       }
+       /* my client, does he match ? */
+       else if (!cansendlocal || (!(IsRegisteredUser (cptr) &&
+                                    match_it (cptr, mask, what))))
+           continue;
+       sendto_prefix_one (cptr, from, pattern, p1, p2, p3, p4, p5, p6, p7,
+                          p8);
+    }
+    return;
+}
+
+/*
+ * sendto_all_butone.
+ *
+ * Send a message to all connections except 'one'. The basic wall type
+ * message generator.
+ */
+void sendto_all_butone (one, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one, *from;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    int i;
+    aClient *cptr;
+
+    for (i = 0; i <= highest_fd; i++)
+       if ((cptr = local[i]) && !IsMe (cptr) && one != cptr)
+           sendto_prefix_one (cptr, from, pattern, p1, p2, p3, p4, p5, p6,
+                              p7, p8);
+
+    return;
+}
+
+/*
+ * sendto_ops
+ *
+ *  Send to *local* ops only.
+ */
+void sendto_ops (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    aClient *cptr;
+    int i;
+    char nbuf[1024];
+
+    for (i = 0; i <= highest_fd; i++)
+       if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
+           IsAnOper (cptr) && SendServNotice (cptr)) {
+           (void) sprintf (nbuf, ":%s NOTICE %s :*** Notice -- ",
+                           me.name, cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#ifdef USE_SERVICES
+       else if (cptr && IsService (cptr) &&
+                (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
+           (void) sprintf (nbuf, "NOTICE %s :*** Notice -- ", cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#endif /* USE_SERVICES */
+    return;
+}
+
+/*
+ * sendto_failops
+ *
+ *      Send to *local* mode +g ops only.
+ */
+void sendto_failops (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    aClient *cptr;
+    int i;
+    char nbuf[1024];
+
+    for (i = 0; i <= highest_fd; i++)
+       if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
+           SendFailops (cptr)) {
+           (void) sprintf (nbuf, ":%s NOTICE %s :*** Global -- ",
+                           me.name, cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#ifdef  USE_SERVICES
+       else if (cptr && IsService (cptr) &&
+                (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
+           (void) sprintf (nbuf, "NOTICE %s :*** Notice -- ", cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#endif
+    return;
+}
+
+/*
+ * sendto_umode
+ *
+ *  Send to specified umode
+ */
+void sendto_umode (umodes, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     int umodes;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    aClient *cptr;
+    int i;
+    char nbuf[1024];
+
+    for (i = 0; i <= highest_fd; i++)
+       if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
+           (cptr->umodes & umodes) == umodes) {
+           (void) sprintf (nbuf, ":%s NOTICE %s :", me.name, cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+    return;
+}
+
+/*
+ * sendto_failops_whoare_opers
+ *
+ *      Send to *local* mode +g ops only who are also +o.
+ */
+void sendto_failops_whoare_opers (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    aClient *cptr;
+    int i;
+    char nbuf[1024];
+
+    for (i = 0; i <= highest_fd; i++)
+       if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
+           SendFailops (cptr) && IsAnOper (cptr)) {
+           (void) sprintf (nbuf, ":%s NOTICE %s :*** Global -- ",
+                           me.name, cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#ifdef  USE_SERVICES
+       else if (cptr && IsService (cptr) &&
+                (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
+           (void) sprintf (nbuf, "NOTICE %s :*** Notice -- ", cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#endif
+    return;
+}
+
+/*
+ * sendto_locfailops
+ *
+ *      Send to *local* mode +g ops only who are also +o.
+ */
+void sendto_locfailops (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    aClient *cptr;
+    int i;
+    char nbuf[1024];
+
+    for (i = 0; i <= highest_fd; i++)
+       if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
+           SendFailops (cptr) && IsAnOper (cptr)) {
+           (void) sprintf (nbuf, ":%s NOTICE %s :*** LocOps -- ",
+                           me.name, cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#ifdef  USE_SERVICES
+       else if (cptr && IsService (cptr) &&
+                (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
+           (void) sprintf (nbuf, "NOTICE %s :*** Notice -- ", cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#endif
+    return;
+}
+
+/*
+ * sendto_opers
+ *
+ *  Send to *local* ops only. (all +O or +o people)
+ */
+void sendto_opers (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    aClient *cptr;
+    int i;
+    char nbuf[1024];
+
+    for (i = 0; i <= highest_fd; i++)
+       if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
+           IsAnOper (cptr)) {
+           (void) sprintf (nbuf, ":%s NOTICE %s :*** Oper -- ",
+                           me.name, cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#ifdef USE_SERVICES
+       else if (cptr && IsService (cptr) &&
+                (cptr->service->wanted & SERVICE_WANT_SERVNOTE)) {
+           (void) sprintf (nbuf, "NOTICE %s :*** GLOBAL OPER Notice -- ",
+                           cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+#endif
+    return;
+}
+
+/* ** sendto_ops_butone
+   **   Send message to all operators.
+   ** one - client not to send message to
+   ** from- client which message is from *NEVER* NULL!!
+ */
+void sendto_ops_butone (one, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one, *from;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    int i;
+    aClient *cptr;
+
+    for (i = 0; i <= highest_fd; i++)
+       sentalong[i] = 0;
+    for (cptr = client; cptr; cptr = cptr->next) {
+       i = cptr->from->fd;       /* find connection oper is on */
+       if (sentalong[i])         /* sent message along it already ? */
+           continue;
+       if (cptr->from == one)
+           continue;             /* ...was the one I should skip */
+       sentalong[i] = 1;
+       sendto_prefix_one (cptr->from, from, pattern,
+                          p1, p2, p3, p4, p5, p6, p7, p8);
+    }
+    return;
+}
+
+/*
+   ** sendto_ops_butone
+   **   Send message to all operators regardless of whether they are +w or 
+   **   not.. 
+   ** one - client not to send message to
+   ** from- client which message is from *NEVER* NULL!!
+ */
+void sendto_opers_butone (one, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *one, *from;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    int i;
+    aClient *cptr;
+
+    for (i = 0; i <= highest_fd; i++)
+       sentalong[i] = 0;
+    for (cptr = client; cptr; cptr = cptr->next) {
+       if (!IsAnOper (cptr))
+           continue;
+       i = cptr->from->fd;       /* find connection oper is on */
+       if (sentalong[i])         /* sent message along it already ? */
+           continue;
+       if (cptr->from == one)
+           continue;             /* ...was the one I should skip */
+       sentalong[i] = 1;
+       sendto_prefix_one (cptr->from, from, pattern,
+                          p1, p2, p3, p4, p5, p6, p7, p8);
+    }
+    return;
+}
+
+/*
+   ** sendto_ops_butme
+   **   Send message to all operators except local ones
+   ** from- client which message is from *NEVER* NULL!!
+ */
+void sendto_ops_butme (from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient **from;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    int i;
+    aClient *cptr;
+
+    for (i = 0; i <= highest_fd; i++)
+       sentalong[i] = 0;
+    for (cptr = client; cptr; cptr = cptr->next) {
+       i = cptr->from->fd;       /* find connection oper is on */
+       if (sentalong[i])         /* sent message along it already ? */
+           continue;
+       if (!strcmp (cptr->user->server, me.name))      /* a locop */
+           continue;
+       sentalong[i] = 1;
+       sendto_prefix_one (cptr->from, from, pattern,
+                          p1, p2, p3, p4, p5, p6, p7, p8);
+    }
+    return;
+}
+
+/*
+ * sendto_prefix_one()
+ *
+ * to - destination client
+ * from - client which message is from
+ *
+ * NOTE: NEITHER OF THESE SHOULD *EVER* BE NULL!!
+ * -avalon
+ */
+void sendto_prefix_one (to, from, pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     aClient *to;
+     aClient *from;
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
+    anUser *user;
+    char *par;
+    int flag = 0;
+
+    par = p1;
+    
+    if (to && from && MyClient (to) && IsPerson (from) &&
+       !mycmp (par, from->name)) {
+       user = from->user;
+       (void) strcpy (sender, from->name);
+
+       if (user) {
+           if (*user->username) {
+               (void) strcat (sender, "!");
+               (void) strcat (sender, user->username);
+           }
+           if (*user->host && !MyConnect (from)) {
+               (void) strcat (sender, "@");
+               if ((IsAnOper (to) && !IsAnOper (from)) || (to == from))
+                   (void) strcat (sender, user->host);
+               else
+                   (void) strcat (sender, MaskHost (from));
+
+               flag = 1;
+           }
+       }
+       /*
+          ** flag is used instead of index(sender, '@') for speed and
+          ** also since username/nick may have had a '@' in them. -avalon
+        */
+       if (!flag && MyConnect (from) && *user->host) {
+           (void) strcat (sender, "@");
+           if ((IsAnOper (to) && !IsAnOper (from)) || (to == from))
+               (void) strcat (sender, user->host);
+           else
+               (void) strcat (sender, MaskHost (from));
+       }
+       par = sender;
+    }
+    sendto_one (to, pattern, par, p2, p3, p4, p5, p6, p7, p8);
+}
+
+/*
+ * sendto_realops
+ *
+ *  Send to *local* ops only but NOT +s nonopers.
+ */
+void sendto_realops (pattern, p1, p2, p3, p4, p5, p6, p7, p8)
+     char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8;
+{
+    aClient *cptr;
+    int i;
+    char nbuf[1024];
+
+    for (i = 0; i <= highest_fd; i++)
+       if ((cptr = local[i]) && !IsServer (cptr) && !IsMe (cptr) &&
+           IsOper (cptr)) {
+           (void) sprintf (nbuf, ":%s NOTICE %s :*** Notice -- ",
+                           me.name, cptr->name);
+           (void) strncat (nbuf, pattern, sizeof (nbuf) - strlen (nbuf));
+           sendto_one (cptr, nbuf, p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+    return;
+}
diff --git a/src/strtoul.c b/src/strtoul.c
new file mode 100644 (file)
index 0000000..1ce5082
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1990 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *  This product includes software developed by the University of
+ *  California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+/*
+ * Convert a string to an unsigned long integer.
+ *
+ * Ignores `locale' stuff.  Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+unsigned long strtoul (nptr, endptr, base)
+     char *nptr;
+     char **endptr;
+     int base;
+{
+    char *s;
+    unsigned long acc, cutoff;
+    int c;
+    int neg, any, cutlim;
+
+    /*
+     * See strtol for comments as to the logic used.
+     */
+    s = nptr;
+    do {
+       c = (unsigned char) *s++;
+    }
+    while (isspace (c));
+    if (c == '-') {
+       neg = 1;
+       c = *s++;
+    }
+    else {
+       neg = 0;
+       if (c == '+')
+           c = *s++;
+    }
+    if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) {
+       c = s[1];
+       s += 2;
+       base = 16;
+    }
+    if (base == 0)
+       base = c == '0' ? 8 : 10;
+
+    cutoff = ULONG_MAX / (unsigned long) base;
+    cutlim = ULONG_MAX % (unsigned long) base;
+    for (acc = 0, any = 0;; c = (unsigned char) *s++) {
+       if (isdigit (c))
+           c -= '0';
+       else if (isalpha (c))
+           c -= isupper (c) ? 'A' - 10 : 'a' - 10;
+       else
+           break;
+       if (c >= base)
+           break;
+       if (any < 0)
+           continue;
+       if (acc > cutoff || acc == cutoff && c > cutlim) {
+           any = -1;
+           acc = ULONG_MAX;
+           errno = ERANGE;
+       }
+       else {
+           any = 1;
+           acc *= (unsigned long) base;
+           acc += c;
+       }
+    }
+    if (neg && any > 0)
+       acc = -acc;
+    if (endptr != 0)
+       *endptr = (char *) (any ? s - 1 : nptr);
+    return (acc);
+}
diff --git a/src/support.c b/src/support.c
new file mode 100644 (file)
index 0000000..d2e6620
--- /dev/null
@@ -0,0 +1,440 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, common/support.c
+ *   Copyright (C) 1990, 1991 Armin Gruner
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "config.h"
+#ifdef DYNIXPTX
+#include <sys/timers.h>
+#include <stddef.h>
+#endif
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "h.h"
+
+extern int errno;  /* ...seems that errno.h doesn't define this everywhere */
+
+extern void outofmemory ();
+
+#ifndef HAVE_STRTOKEN
+/*
+   **   strtoken.c --   walk through a string of tokens, using a set
+   **           of separators
+   **           argv 9/90
+   **
+ */
+
+char *strtoken (save, str, fs)
+     char **save;
+     char *str, *fs;
+{
+    char *pos = *save; /* keep last position across calls */
+    char *tmp;
+
+    if (str)
+       pos = str;                /* new string scan */
+
+    while (pos && *pos && index (fs, *pos) != NULL)
+       pos++;                    /* skip leading separators */
+
+    if (!pos || !*pos)
+       return (pos = *save = NULL);    /* string contains only sep's */
+
+    tmp = pos;                   /* now, keep position of the token */
+
+    while (*pos && index (fs, *pos) == NULL)
+       pos++;                    /* skip content of the token */
+
+    if (*pos)
+       *pos++ = '\0';            /* remove first sep after the token */
+    else
+       pos = NULL;               /* end of string */
+
+    *save = pos;
+    return (tmp);
+}
+#endif /* HAVE_STRTOKEN */
+
+#ifdef NEED_STRTOK
+/*
+   ** NOT encouraged to use!
+ */
+
+char *strtok (str, fs)
+     char *str, *fs;
+{
+    static char *pos;
+
+    return strtoken (&pos, str, fs);
+}
+
+#endif /* NEED_STRTOK */
+
+#ifdef NEED_STRERROR
+/*
+   **   strerror - return an appropriate system error string to a given errno
+   **
+   **          argv 11/90
+ */
+
+char *strerror (err_no)
+     int err_no;
+{
+    extern char *sys_errlist[];        /* Sigh... hopefully on all systems */
+    extern int sys_nerr;
+
+    static char buff[40];
+    char *errp;
+
+    errp = (err_no > sys_nerr ? (char *) NULL : sys_errlist[err_no]);
+
+    if (errp == (char *) NULL) {
+       errp = buff;
+       (void) sprintf (errp, "Unknown Error %d", err_no);
+    }
+    return errp;
+}
+
+#endif /* NEED_STRERROR */
+
+/*
+   **   inetntoa  --    changed name to remove collision possibility and
+   **           so behaviour is gaurunteed to take a pointer arg.
+   **           -avalon 23/11/92
+   **   inet_ntoa --    returned the dotted notation of a given
+   **           internet number (some ULTRIX don't have this)
+   **           argv 11/90).
+   **   inet_ntoa --    its broken on some Ultrix/Dynix too. -avalon
+ */
+
+char *inetntoa (in)
+     char *in;
+{
+    static char buf[16];
+    u_char *s = (u_char *) in;
+    int a, b, c, d;
+
+    a = (int) *s++;
+    b = (int) *s++;
+    c = (int) *s++;
+    d = (int) *s++;
+    (void) sprintf (buf, "%d.%d.%d.%d", a, b, c, d);
+
+    return buf;
+}
+
+#ifndef HAVE_INET_NETOF
+/*
+   **   inet_netof --   return the net portion of an internet number
+   **           argv 11/90
+ */
+
+int inet_netof (in)
+     struct in_addr in;
+{
+    int addr = in.s_net;
+
+    if (addr & 0x80 == 0)
+       return ((int) in.s_net);
+
+    if (addr & 0x40 == 0)
+       return ((int) in.s_net * 256 + in.s_host);
+
+    return ((int) in.s_net * 256 + in.s_host * 256 + in.s_lh);
+}
+
+#endif /* HAVE_INET_NETOF */
+
+
+#ifdef DEBUGMODE
+void dumpcore (msg, p1, p2, p3, p4, p5, p6, p7, p8, p9)
+     char *msg, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9;
+{
+    static time_t lastd = 0;
+    static int dumps = 0;
+    char corename[12];
+    time_t now;
+    int p;
+
+    now = time (NULL);
+
+    if (!lastd)
+       lastd = now;
+    else if (now - lastd < 60 && dumps > 2)
+       (void) s_die ();
+    if (now - lastd > 60) {
+       lastd = now;
+       dumps = 1;
+    }
+    else
+       dumps++;
+    p = getpid ();
+    if (fork () > 0) {
+       kill (p, 3);
+       kill (p, 9);
+    }
+    write_pidfile ();
+    (void) sprintf (corename, "core.%d", p);
+    (void) rename ("core", corename);
+    Debug ((DEBUG_FATAL, "Dumped core : core.%d", p));
+    sendto_ops ("Dumped core : core.%d", p);
+    Debug ((DEBUG_FATAL, msg, p1, p2, p3, p4, p5, p6, p7, p8, p9));
+    sendto_ops (msg, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+    (void) s_die ();
+}
+
+static char *marray[20000];
+static int mindex = 0;
+
+#define        SZ_EX   (sizeof(char *) + sizeof(size_t) + 4)
+#define        SZ_CHST (sizeof(char *) + sizeof(size_t))
+#define        SZ_CH   (sizeof(char *))
+#define        SZ_ST   (sizeof(size_t))
+
+char *MyMalloc (x)
+     size_t x;
+{
+    register int i;
+    register char **s;
+    char *ret;
+
+    ret = (char *) malloc (x + (size_t) SZ_EX);
+
+    if (!ret) {
+       outofmemory ();
+    }
+    bzero (ret, (int) x + SZ_EX);
+    bcopy ((char *) &ret, ret, SZ_CH);
+    bcopy ((char *) &x, ret + SZ_CH, SZ_ST);
+    bcopy ("VAVA", ret + SZ_CHST + (int) x, 4);
+    Debug ((DEBUG_MALLOC, "MyMalloc(%ld) = %#x", x, ret + 8));
+    for (i = 0, s = marray; *s && i < mindex; i++, s++);
+    if (i < 20000) {
+       *s = ret;
+       if (i == mindex)
+           mindex++;
+    }
+    return ret + SZ_CHST;
+}
+
+char *MyRealloc (x, y)
+     char *x;
+     size_t y;
+{
+    register int l;
+    register char **s;
+    char *ret, *cp;
+    size_t i;
+    int k;
+
+    x -= SZ_CHST;
+    bcopy (x, (char *) &cp, SZ_CH);
+    bcopy (x + SZ_CH, (char *) &i, SZ_ST);
+    bcopy (x + (int) i + SZ_CHST, (char *) &k, 4);
+    if (bcmp ((char *) &k, "VAVA", 4) || (x != cp))
+       dumpcore ("MyRealloc %#x %d %d %#x %#x", x, y, i, cp, k);
+    ret = (char *) realloc (x, y + (size_t) SZ_EX);
+
+    if (!ret) {
+       outofmemory ();
+    }
+    bcopy ((char *) &ret, ret, SZ_CH);
+    bcopy ((char *) &y, ret + SZ_CH, SZ_ST);
+    bcopy ("VAVA", ret + SZ_CHST + (int) y, 4);
+    Debug ((DEBUG_NOTICE, "MyRealloc(%#x,%ld) = %#x", x, y, ret + SZ_CHST));
+    for (l = 0, s = marray; *s != x && l < mindex; l++, s++);
+    if (l < mindex)
+       *s = NULL;
+    else if (l == mindex)
+       Debug ((DEBUG_MALLOC, "%#x !found", x));
+    for (l = 0, s = marray; *s && l < mindex; l++, s++);
+    if (l < 20000) {
+       *s = ret;
+       if (l == mindex)
+           mindex++;
+    }
+    return ret + SZ_CHST;
+}
+
+void MyFree (x)
+     char *x;
+{
+    size_t i;
+    char *j;
+    u_char k[4];
+    register int l;
+    register char **s;
+
+    if (!x)
+       return;
+    x -= SZ_CHST;
+
+    bcopy (x, (char *) &j, SZ_CH);
+    bcopy (x + SZ_CH, (char *) &i, SZ_ST);
+    bcopy (x + SZ_CHST + (int) i, (char *) k, 4);
+
+    if (bcmp ((char *) k, "VAVA", 4) || (j != x))
+       dumpcore ("MyFree %#x %ld %#x %#x", x, i, j,
+                 (k[3] << 24) | (k[2] << 16) | (k[1] << 8) | k[0]);
+
+#undef free
+    (void) free (x);
+#define        free(x) MyFree(x)
+    Debug ((DEBUG_MALLOC, "MyFree(%#x)", x + SZ_CHST));
+
+    for (l = 0, s = marray; *s != x && l < mindex; l++, s++);
+    if (l < mindex)
+       *s = NULL;
+    else if (l == mindex)
+       Debug ((DEBUG_MALLOC, "%#x !found", x));
+}
+
+#else /* DEBUGMODE */
+char *MyMalloc (x)
+     size_t x;
+{
+    char *ret = (char *) malloc (x);
+
+    if (!ret) {
+       outofmemory ();
+    }
+    return ret;
+}
+
+char *MyRealloc (x, y)
+     char *x;
+     size_t y;
+{
+    char *ret = (char *) realloc (x, y);
+
+    if (!ret) {
+       outofmemory ();
+    }
+    return ret;
+}
+#endif /* DEBUGMODE */
+
+
+/*
+   ** read a string terminated by \r or \n in from a fd
+   **
+   ** Created: Sat Dec 12 06:29:58 EST 1992 by avalon
+   ** Returns:
+   **   0 - EOF
+   **   -1 - error on read
+   **     >0 - number of bytes returned (<=num)
+   ** After opening a fd, it is necessary to init dgets() by calling it as
+   **   dgets(x,y,0);
+   ** to mark the buffer as being empty.
+ */
+int dgets (fd, buf, num)
+     int fd, num;
+     char *buf;
+{
+    static char dgbuf[8192];
+    static char *head = dgbuf, *tail = dgbuf;
+    register char *s, *t;
+    register int n, nr;
+
+    /*
+       ** Sanity checks.
+     */
+    if (head == tail)
+       *head = '\0';
+    if (!num) {
+       head = tail = dgbuf;
+       *head = '\0';
+       return 0;
+    }
+    if (num > sizeof (dgbuf) - 1)
+       num = sizeof (dgbuf) - 1;
+  dgetsagain:
+    if (head > dgbuf) {
+       for (nr = tail - head, s = head, t = dgbuf; nr > 0; nr--)
+           *t++ = *s++;
+       tail = t;
+       head = dgbuf;
+    }
+    /*
+       ** check input buffer for EOL and if present return string.
+     */
+    if (head < tail &&
+       ((s = index (head, '\n')) || (s = index (head, '\r'))) && s < tail) {
+       n = MIN (s - head + 1, num);    /* at least 1 byte */
+      dgetsreturnbuf:
+       bcopy (head, buf, n);
+       head += n;
+       if (head == tail)
+           head = tail = dgbuf;
+       return n;
+    }
+    if (tail - head >= num) {    /* dgets buf is big enough */
+       n = num;
+       goto dgetsreturnbuf;
+    }
+    n = sizeof (dgbuf) - (tail - dgbuf) - 1;
+    nr = read (fd, tail, n);
+    if (nr == -1) {
+       head = tail = dgbuf;
+       return -1;
+    }
+    if (!nr) {
+       if (head < tail) {
+           n = MIN (tail - head, num);
+           goto dgetsreturnbuf;
+       }
+       head = tail = dgbuf;
+       return 0;
+    }
+    tail += nr;
+    *tail = '\0';
+    for (t = head; (s = index (t, '\n'));) {
+       if ((s > head) && (s > dgbuf)) {
+           t = s - 1;
+           for (nr = 0; *t == '\\'; nr++)
+               t--;
+           if (nr & 1) {
+               t = s + 1;
+               s--;
+               nr = tail - t;
+               while (nr--)
+                   *s++ = *t++;
+               tail -= 2;
+               *tail = '\0';
+           }
+           else
+               s++;
+       }
+       else
+           s++;
+       t = s;
+    }
+    *tail = '\0';
+    goto dgetsagain;
+}
+
+void strip_trailing_spaces (char *input)
+{
+    int x;
+
+    x = strlen (input) - 1;
+    while (input[x] == ' ') {
+       input[x] = '\0';
+       x--;
+    }
+}
diff --git a/src/userload.c b/src/userload.c
new file mode 100644 (file)
index 0000000..af77cf5
--- /dev/null
@@ -0,0 +1,375 @@
+/****************************************************************************
+ *  Userload module by Michael L. VanLoon (mlv) <michaelv@iastate.edu>
+ *  Written 2/93.  Originally grafted into irc2.7.2g 4/93.
+ *
+ *   IRC - Internet Relay Chat, ircd/userload.c
+ *   Copyright (C) 1990 University of Oulu, Computing Center
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ ****************************************************************************/
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "userload.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include <string.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include "h.h"
+
+
+struct current_load_struct current_load_data;
+struct load_entry *load_list_head = NULL, *load_list_tail =
+    NULL, *load_free_head = NULL, *load_free_tail = NULL;
+
+#ifdef DEBUGMODE
+clock_t clock_last = 0;
+#endif
+
+void update_load ()
+{
+    static struct timeval now, last;
+    register struct load_entry *cur_load_entry;
+
+    /* This seems to get polluted on startup by an exit_client()
+     * before any connections have been recorded.
+     */
+    if (current_load_data.local_count > MAXCONNECTIONS ||
+       current_load_data.client_count > MAXCONNECTIONS ||
+       current_load_data.conn_count > MAXCONNECTIONS)
+       bzero (&current_load_data, sizeof (struct current_load_struct));
+
+    memcpy (&last, &now, sizeof (struct timeval));
+
+    if (gettimeofday (&now, NULL) != 0)
+       return;                   /* error getting time of day--can't calculate time diff */
+
+    if (load_free_tail == NULL) {
+       if ((cur_load_entry =
+            (struct load_entry *) MyMalloc (sizeof (struct load_entry))) ==
+           NULL)
+           return;
+       /* printf("malloc pointer: %x\n", cur_load_entry); */
+    }
+    else {
+       cur_load_entry = load_free_tail;
+       load_free_tail = cur_load_entry->prev;
+       if (load_free_tail == NULL)
+           load_free_head = NULL;
+       /* printf("free pointer: %x\n", cur_load_entry); */
+    }
+    if (load_list_tail != NULL) {
+
+       cur_load_entry->time_incr =
+           ((now.tv_sec * 1000 + now.tv_usec / 1000 + 5) -
+            (last.tv_sec * 1000 + last.tv_usec / 1000)) / 10;
+
+       cur_load_entry->local_count = current_load_data.local_count;
+       cur_load_entry->client_count = current_load_data.client_count;
+       cur_load_entry->conn_count = current_load_data.conn_count;
+#ifdef DEBUGMODE
+       cur_load_entry->cpu_usage = (clock () - clock_last);
+       clock_last = clock ();
+#endif
+    }
+    else {
+       load_list_head = cur_load_entry;
+       bzero (cur_load_entry, sizeof (struct load_entry));
+       cur_load_entry->time_incr = 1;
+    }
+    cur_load_entry->prev = load_list_tail;
+    load_list_tail = cur_load_entry;
+}
+
+
+void calc_load (sptr, parv)
+     aClient *sptr;
+     char *parv;   /* we only get passed the original parv[0] */
+{
+    register struct load_entry *cur_load_entry;
+    struct load_entry *last = NULL;
+#ifdef DEBUGMODE
+    u_long secs = 0, adj_secs, total[4], adj[4];       /*[local,client,conn,cpu] */
+    int i;
+    u_int times[5][4]; /* [min,hour,day,Yest,YYest][local,client,conn,cpu] */
+    char what[4][HOSTLEN + 1];
+
+    bzero (total, 4 * sizeof (u_long));
+#else
+    u_long secs = 0, adj_secs, total[3], adj[3];       /*[local,client,conn] */
+    int i, times[5][3];        /* [min,hour,day,Yest,YYest][local,client,conn] */
+    char what[3][HOSTLEN + 1];
+
+    bzero (total, 3 * sizeof (u_long));
+#endif
+
+    current_load_data.entries = 0;
+
+    update_load ();              /* we want stats accurate as of *now* */
+
+    for (cur_load_entry = load_list_tail; (secs < 6000) &&
+        (cur_load_entry != NULL); cur_load_entry = cur_load_entry->prev) {
+       u_long time_incr = cur_load_entry->time_incr;
+       total[0] += time_incr * cur_load_entry->local_count;
+       total[1] += time_incr * cur_load_entry->client_count;
+       total[2] += time_incr * cur_load_entry->conn_count;
+#ifdef DEBUGMODE
+       total[3] += cur_load_entry->cpu_usage;
+#endif
+       last = cur_load_entry;
+       secs += cur_load_entry->time_incr;
+       current_load_data.entries++;
+    }
+    if ((secs > 6000) && (last != NULL)) {
+       adj_secs = secs - 6000;
+       adj[0] = adj_secs * last->local_count;
+       adj[1] = adj_secs * last->client_count;
+       adj[2] = adj_secs * last->conn_count;
+#ifdef DEBUGMODE
+       times[0][3] =
+           total[3] -
+           (last->cpu_usage * (double) adj_secs / last->time_incr);
+    }
+    else {
+       adj_secs = adj[0] = adj[1] = adj[2] = adj[3] = 0;
+       times[0][3] = total[3];
+    }
+#else
+    }
+    else
+       adj_secs = adj[0] = adj[1] = adj[2] = 0;
+#endif
+    for (i = 0; i < 3; i++) {
+       times[0][i] =
+           ((total[i] - adj[i]) * 1000 / (secs - adj_secs) + 5) / 10;
+    }
+
+    secs = (secs + 5) / 10;
+    for (i = 0; i < 3; i++)
+       total[i] = (total[i] + 5) / 10;
+
+    for (; (secs < 36000) && (cur_load_entry != NULL); secs +=
+        (cur_load_entry->time_incr + 5) / 10, cur_load_entry =
+        cur_load_entry->prev, current_load_data.entries++) {
+       u_long time_incr = (cur_load_entry->time_incr + 5) / 10;
+       total[0] += time_incr * cur_load_entry->local_count;
+       total[1] += time_incr * cur_load_entry->client_count;
+       total[2] += time_incr * cur_load_entry->conn_count;
+#ifdef DEBUGMODE
+       total[3] += cur_load_entry->cpu_usage;
+#endif
+       last = cur_load_entry;
+    }
+    if ((secs > 36000) && (last != NULL)) {
+       adj_secs = secs - 36000;
+       adj[0] = adj_secs * last->local_count;
+       adj[1] = adj_secs * last->client_count;
+       adj[2] = adj_secs * last->conn_count;
+#ifdef DEBUGMODE
+       times[1][3] =
+           total[3] -
+           (last->cpu_usage * (double) adj_secs / last->time_incr);
+    }
+    else {
+       adj_secs = adj[0] = adj[1] = adj[2] = adj[3] = 0;
+       times[1][3] = total[3];
+    }
+#else
+    }
+    else
+       adj_secs = adj[0] = adj[1] = adj[2] = 0;
+#endif
+    for (i = 0; i < 3; i++) {
+       times[1][i] =
+           ((total[i] - adj[i]) * 100 / (secs - adj_secs) + 5) / 10;
+    }
+
+    secs = (secs + 5) / 10;
+    for (i = 0; i < 3; i++)
+       total[i] = (total[i] + 5) / 10;
+
+    for (; (secs < 86400) && (cur_load_entry != NULL); secs +=
+        (cur_load_entry->time_incr + 50) / 100, cur_load_entry =
+        cur_load_entry->prev, current_load_data.entries++) {
+       u_long time_incr = (cur_load_entry->time_incr + 50) / 100;
+       total[0] += time_incr * cur_load_entry->local_count;
+       total[1] += time_incr * cur_load_entry->client_count;
+       total[2] += time_incr * cur_load_entry->conn_count;
+#ifdef DEBUGMODE
+       total[3] += cur_load_entry->cpu_usage;
+#endif
+       last = cur_load_entry;
+    }
+    if ((secs > 86400) && (last != NULL)) {
+       adj_secs = secs - 86400;
+       adj[0] = adj_secs * last->local_count;
+       adj[1] = adj_secs * last->client_count;
+       adj[2] = adj_secs * last->conn_count;
+#ifdef DEBUGMODE
+       times[2][3] =
+           total[3] -
+           (last->cpu_usage * (double) adj_secs / last->time_incr);
+    }
+    else {
+       adj_secs = adj[0] = adj[1] = adj[2] = adj[3] = 0;
+       times[2][3] = total[3];
+    }
+#else
+    }
+    else
+       adj_secs = adj[0] = adj[1] = adj[2] = 0;
+#endif
+    for (i = 0; i < 3; i++) {
+       times[2][i] = ((total[i] - adj[i]) * 10 / (secs - adj_secs) + 5) / 10;
+    }
+
+#ifdef DEBUGMODE
+    bzero (total, 4 * sizeof (u_long));
+#else
+    bzero (total, 3 * sizeof (u_long));
+#endif
+
+    for (secs = 1; (secs < 86400) && (cur_load_entry != NULL); secs +=
+        (cur_load_entry->time_incr + 50) / 100, cur_load_entry =
+        cur_load_entry->prev, current_load_data.entries++) {
+       u_long time_incr = (cur_load_entry->time_incr + 50) / 100;
+       total[0] += time_incr * cur_load_entry->local_count;
+       total[1] += time_incr * cur_load_entry->client_count;
+       total[2] += time_incr * cur_load_entry->conn_count;
+#ifdef DEBUGMODE
+       total[3] += cur_load_entry->cpu_usage;
+#endif
+       last = cur_load_entry;
+    }
+    if ((secs > 86400) && (last != NULL)) {
+       adj_secs = secs - 86400;
+       adj[0] = adj_secs * last->local_count;
+       adj[1] = adj_secs * last->client_count;
+       adj[2] = adj_secs * last->conn_count;
+#ifdef DEBUGMODE
+       times[3][3] =
+           total[3] -
+           (last->cpu_usage * (double) adj_secs / last->time_incr);
+    }
+    else {
+       adj_secs = adj[0] = adj[1] = adj[2] = adj[3] = 0;
+       times[3][3] = total[3];
+    }
+#else
+    }
+    else
+       adj_secs = adj[0] = adj[1] = adj[2] = 0;
+#endif
+    for (i = 0; i < 3; i++) {
+       times[3][i] = ((total[i] - adj[i]) * 10 / (secs - adj_secs) + 5) / 10;
+    }
+
+#ifdef DEBUGMODE
+    bzero (total, 4 * sizeof (u_long));
+#else
+    bzero (total, 3 * sizeof (u_long));
+#endif
+
+    for (secs = 1; (secs < 86400) && (cur_load_entry != NULL); secs +=
+        (cur_load_entry->time_incr + 50) / 100, cur_load_entry =
+        cur_load_entry->prev, current_load_data.entries++) {
+       u_long time_incr = (cur_load_entry->time_incr + 50) / 100;
+       total[0] += time_incr * cur_load_entry->local_count;
+       total[1] += time_incr * cur_load_entry->client_count;
+       total[2] += time_incr * cur_load_entry->conn_count;
+#ifdef DEBUGMODE
+       total[3] += cur_load_entry->cpu_usage;
+#endif
+       last = cur_load_entry;
+    }
+    if ((secs > 86400) && (last != NULL)) {
+       adj_secs = secs - 86400;
+       adj[0] = adj_secs * last->local_count;
+       adj[1] = adj_secs * last->client_count;
+       adj[2] = adj_secs * last->conn_count;
+#ifdef DEBUGMODE
+       times[4][3] =
+           total[3] -
+           (last->cpu_usage * (double) adj_secs / last->time_incr);
+    }
+    else {
+       adj_secs = adj[0] = adj[1] = adj[2] = adj[3] = 0;
+       times[4][3] = total[3];
+    }
+#else
+    }
+    else
+       adj_secs = adj[0] = adj[1] = adj[2] = 0;
+#endif
+    for (i = 0; i < 3; i++) {
+       times[4][i] = ((total[i] - adj[i]) * 10 / (secs - adj_secs) + 5) / 10;
+    }
+
+    if ((cur_load_entry != NULL) && (cur_load_entry->prev != NULL) && (secs > 86400)) {        /* have nodes to free -- more than 3 days old */
+       struct load_entry *cur_free_entry = load_free_head;
+
+       load_free_head = load_list_head;
+       load_list_head = cur_load_entry;
+       if (cur_free_entry != NULL)
+           cur_free_entry->prev = cur_load_entry->prev;
+       else
+           load_free_tail = cur_load_entry->prev;
+
+       /* printf("freeing: %x  (head: %x,  tail: %x)\n", cur_load_entry->prev,
+          load_free_head, load_free_tail); */
+
+       cur_load_entry->prev = NULL;
+    }
+    strcpy (what[0], DOMAINNAME);
+    strcat (what[0], " clients");
+    strcpy (what[1], "total clients");
+    strcpy (what[2], "total connections");
+#ifdef DEBUGMODE
+    strcpy (what[3], "CPU usage");
+#endif
+    sendto_one (sptr,
+               ":%s NOTICE %s :Minute   Hour  Day  Yest.  YYest.  Userload for:",
+               me.name, parv);
+    for (i = 0; i < 3; i++)
+       sendto_one (sptr,
+                   ":%s NOTICE %s :%3d.%02d  %3d.%01d  %3d   %3d     %3d   %s",
+                   me.name, parv, times[0][i] / 100, times[0][i] % 100,
+                   times[1][i] / 10, times[1][i] % 10, times[2][i],
+                   times[3][i], times[4][i], what[i]);
+
+#ifdef DEBUGMODE
+    sendto_one (sptr,
+               ":%s NOTICE %s :%6.2f%% %5.1f%% %3d%%  %3d%%    %3d%%  %s",
+               me.name, parv,
+               (double) ((double) times[0][3] / (0.6 * CLOCKS_PER_SEC)),
+               (double) ((double) times[1][3] / (36 * CLOCKS_PER_SEC)),
+               (int) ((double) times[2][3] / (864 * CLOCKS_PER_SEC)),
+               (int) ((double) times[3][3] / (864 * CLOCKS_PER_SEC)),
+               (int) ((double) times[4][3] / (864 * CLOCKS_PER_SEC)),
+               what[3]);
+#endif
+}
+
+
+void initload ()
+{
+    bzero (&current_load_data, sizeof (struct current_load_struct));
+    update_load ();              /* Initialize the load list */
+}
diff --git a/src/version.c.SH b/src/version.c.SH
new file mode 100755 (executable)
index 0000000..03bec23
--- /dev/null
@@ -0,0 +1,82 @@
+case $CONFIG in
+'')     if test -r ../config.sh
+        then 
+           . ../config.sh ;
+        else 
+           spitshell=cat
+           package=IRC
+        fi
+        ;;
+esac
+
+echo "Extracting $package/ircd/version.c..."
+
+if test -r version.c
+then
+   generation=`sed -n 's/^char \*generation = \"\(.*\)\";/\1/p' < version.c`
+   if test ! "$generation" ; then generation=0; fi
+else
+   generation=0
+fi
+
+generation=`expr $generation + 1`
+
+creation=`date | \
+awk '{if (NF == 6) \
+         { print $1 " "  $2 " " $3 " "  $6 " at " $4 " " $5 } \
+else \
+         { print $1 " "  $2 " " $3 " " $7 " at " $4 " " $5 " " $6 }}'`
+
+$spitshell >version.c <<!SUB!THIS!
+/*
+ *   IRC - Internet Relay Chat, ircd/version.c
+ *   Copyright (C) 1990 Chelsea Ashley Dyerman
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This file is generated by version.c.SH. Any changes made will go away.
+ */
+
+#include "struct.h"
+#include "patchlevel.h"
+
+char *generation = "$generation";
+char *creation = "$creation";
+char *version = IRCD_VERSION PATCH1 PATCH2 PATCH3 PATCH4\
+               PATCH5 PATCH6 PATCH7 PATCH8 PATCH9;
+
+char *infotext[] =
+    {
+       "$package -- Serenity IRCD " IRCD_VERSION,
+       "Based on the original code written by Jarkko Oikarinen",
+       "Copyright 1988, 1989, 1990, 1991 University of Oulu, Computing Center",
+       "-",
+       "This program is free software; you can redistribute it and/or",
+       "modify it under the terms of the GNU General Public License as",
+       "published by the Free Software Foundation; either version 1, or",
+       "(at your option) any later version.",
+       "-",
+        "This version is based on StarIRCD 5.27",
+        "-",
+        "Current coders and designers:",
+        "Remmy - remmy@serenity-irc.net",
+       "-",
+       IRCD_VERSION PATCH1 PATCH2 PATCH3 PATCH4 PATCH5 PATCH6 PATCH7
+               PATCH8 PATCH9,
+       0,
+    };
+!SUB!THIS!
diff --git a/src/whowas.c b/src/whowas.c
new file mode 100644 (file)
index 0000000..86cc49e
--- /dev/null
@@ -0,0 +1,276 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, ircd/whowas.c
+ *   Copyright (C) 1990 Markku Savela
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 1, or (at your option)
+ *   any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * --- avalon --- 6th April 1992
+ * rewritten to scrap linked lists and use a table of structures which
+ * is referenced like a circular loop. Should be faster and more efficient.
+ */
+
+#include "struct.h"
+#include "common.h"
+#include "sys.h"
+#include "numeric.h"
+#include "whowas.h"
+#include "h.h"
+
+
+static aName was[NICKNAMEHISTORYLENGTH];
+static int ww_index = 0;
+
+void add_history (cptr)
+     aClient *cptr;
+{
+    aName ntmp;
+    aName *np = &ntmp, *np2;
+    Link *lp;
+    char ipaddress[30];
+    char *ip;
+
+    memset (ipaddress, '\0', 30);
+
+    ip = ipaddress;
+
+    if (MyClient (cptr))
+       ip = inetntoa ((char *) &cptr->ip);
+    else {
+       strncpyzt (ip, "<n/a>", 6);
+       ip = ipaddress;
+    }
+    strncpyzt (np->ww_ip, ip, 30);
+
+    strncpyzt (np->ww_nick, cptr->name, NICKLEN + 1);
+    strncpyzt (np->ww_info, cptr->info, REALLEN + 1);
+    strncpyzt (np->ww_mask, cptr->user->mask, HOSTLEN + 1);
+    np->ww_user = cptr->user;
+    np->ww_logout = time (NULL);
+    np->ww_online = (cptr->from != NULL) ? cptr : NULL;
+    np->ww_user->refcnt++;
+
+    np2 = &was[ww_index];
+    if (np2->ww_user)
+       free_user (np2->ww_user, np2->ww_online);
+    /*
+     * New whowas handling, we keep a list of what whowas entries
+     * are "used" by a client, in its cptr structure.  This means
+     * that when we overwrite a whowas entry, we have to remove the
+     * relative pointer in the client. -Cabal95
+     */
+    if (np2->ww_online) {
+       Link *last = NULL;
+
+       for (lp = np2->ww_online->history; lp; last = lp, lp = lp->next)
+           if (lp->value.whowas == np2)
+               break;
+
+       if (lp) {                 /* Sanity check, never trust anything */
+           if (last)
+               last->next = lp->next;
+           else
+               np2->ww_online->history = lp->next;
+
+           free_link (lp);
+       }
+    }
+    bcopy ((char *) &ntmp, (char *) np2, sizeof (aName));
+
+    /*
+     * Add this whowas entry into the clients history list
+     */
+    lp = make_link ();
+    lp->value.whowas = np2;
+    lp->next = cptr->history;
+    cptr->history = lp;
+
+    ww_index++;
+    if (ww_index >= NICKNAMEHISTORYLENGTH)
+       ww_index = 0;
+    return;
+}
+
+/*
+   ** get_history
+   **      Return the current client that was using the given
+   **      nickname within the timelimit. Returns NULL, if no
+   **      one found...
+ */
+aClient *get_history (nick, timelimit)
+     char *nick;
+     time_t timelimit;
+{
+    aName *wp, *wp2;
+    int i = 0;
+
+    if (ww_index == 0)
+       wp = wp2 = &was[NICKNAMEHISTORYLENGTH - 1];
+    else
+       wp = wp2 = &was[ww_index - 1];
+    timelimit = time (NULL) - timelimit;
+
+    do {
+       if (!mycmp (nick, wp->ww_nick) && wp->ww_logout >= timelimit)
+           break;
+       if (wp == was) {
+           i = 1;
+           wp = &was[NICKNAMEHISTORYLENGTH - 1];
+       }
+       else
+           wp--;
+    }
+    while (wp != wp2);
+
+    if (wp != wp2 || !i)
+       return (wp->ww_online);
+    return (NULL);
+}
+
+void off_history (cptr)
+     aClient *cptr;
+{
+    Link *lp;
+    Link *next;
+
+
+    for (lp = cptr->history; lp; lp = next) {
+       next = lp->next;
+       lp->value.whowas->ww_online = NULL;
+       free_link (lp);
+    }
+
+    cptr->history = NULL;
+
+    return;
+}
+
+void initwhowas ()
+{
+    int i;
+
+    for (i = 0; i < NICKNAMEHISTORYLENGTH; i++)
+       bzero ((char *) &was[i], sizeof (aName));
+    return;
+}
+
+
+/*
+   ** m_whowas
+   **   parv[0] = sender prefix
+   **   parv[1] = nickname queried
+ */
+int m_whowas (cptr, sptr, parc, parv)
+     aClient *cptr, *sptr;
+     int parc;
+     char *parv[];
+{
+    aName *wp, *wp2 = NULL;
+    int j = 0;
+    anUser *up = NULL;
+    int max = -1;
+    char *p, *nick, *s;
+
+    if (parc < 2) {
+       sendto_one (sptr, err_str (ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+       return 0;
+    }
+    if (parc > 2)
+       max = atoi (parv[2]);
+
+
+    if (parc > 3)
+       if (hunt_server (cptr, sptr, ":%s WHOWAS %s %s :%s", 3, parc, parv))
+           return 0;
+
+    for (s = parv[1]; (nick = strtoken (&p, s, ",")); s = NULL) {
+       wp = wp2 = &was[ww_index - 1];
+
+       do {
+           if (wp < was)
+               wp = &was[NICKNAMEHISTORYLENGTH - 1];
+           if (mycmp (nick, wp->ww_nick) == 0) {
+               up = wp->ww_user;
+               if (IsOper (sptr)) {
+                   sendto_one (sptr, rpl_str (RPL_WHOWASUSER),
+                               me.name, parv[0], wp->ww_nick,
+                               up->username, up->host, wp->ww_info);
+               }
+               else {
+                   sendto_one (sptr, rpl_str (RPL_WHOWASUSER),
+                               me.name, parv[0], wp->ww_nick,
+                               up->username, wp->ww_mask, wp->ww_info);
+               }
+               if (IsOper (sptr)) {
+                   sendto_one (sptr, rpl_str (RPL_WHOWASIP),
+                               me.name, parv[0], wp->ww_nick, wp->ww_ip);
+               }
+                sendto_one (sptr, rpl_str (RPL_WHOISSERVER),
+                            me.name, parv[0], wp->ww_nick,
+                            up->server, myctime (wp->ww_logout));
+               if (up->away)
+                   sendto_one (sptr, rpl_str (RPL_AWAY),
+                               me.name, parv[0], wp->ww_nick, up->away);
+               j++;
+           }
+           if (max > 0 && j >= max)
+               break;
+           wp--;
+       }
+       while (wp != wp2);
+
+       if (up == NULL)
+           sendto_one (sptr, err_str (ERR_WASNOSUCHNICK), me.name, parv[0],
+                       nick);
+       up = NULL;
+
+       if (p)
+           p[-1] = ',';
+    }
+    sendto_one (sptr, rpl_str (RPL_ENDOFWHOWAS), me.name, parv[0], parv[1]);
+    return 0;
+}
+
+
+void count_whowas_memory (wwu, wwa, wwam)
+     int *wwu, *wwa;
+     u_long *wwam;
+{
+    anUser *tmp;
+    int i, j;
+    int u = 0, a = 0;
+    u_long am = 0;
+
+    for (i = 0; i < NICKNAMEHISTORYLENGTH; i++)
+       if ((tmp = was[i].ww_user))
+           if (!was[i].ww_online) {
+               for (j = 0; j < i; j++)
+                   if (was[j].ww_user == tmp)
+                       break;
+               if (j < i)
+                   continue;
+               u++;
+               if (tmp->away) {
+                   a++;
+                   am += (strlen (tmp->away) + 1);
+               }
+           }
+    *wwu = u;
+    *wwa = a;
+    *wwam = am;
+
+    return;
+}