This commit was generated by cvs2svn to compensate for changes in r2,
authorBleep <tomh@inxpress.net>
Tue, 16 Nov 1999 05:13:14 +0000 (05:13 +0000)
committerBleep <tomh@inxpress.net>
Tue, 16 Nov 1999 05:13:14 +0000 (05:13 +0000)
which included commits to RCS files with non-trunk default branches.

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@3 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

134 files changed:
.cvsignore [new file with mode: 0644]
.indent.pro [new file with mode: 0644]
.patches [new file with mode: 0644]
BUGS [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
INSTALL [new file with mode: 0644]
LICENSE [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
config/.cvsignore [new file with mode: 0644]
config/Configure.in [new file with mode: 0644]
config/Makefile.in [new file with mode: 0644]
config/README [new file with mode: 0644]
config/acconfig.h [new file with mode: 0644]
config/acinclude.m4 [new file with mode: 0644]
config/aclocal.m4 [new file with mode: 0644]
config/config-sh.in [new file with mode: 0644]
config/configure [new file with mode: 0644]
config/configure.in [new file with mode: 0644]
config/gen.doc.Makefile [new file with mode: 0644]
config/gen.ircd.Makefile [new file with mode: 0644]
config/install-sh [new file with mode: 0644]
config/parse.none [new file with mode: 0644]
config/setup.h.in [new file with mode: 0644]
config/setup.h.top [new file with mode: 0644]
config/stamp-h.in [new file with mode: 0644]
configure [new file with mode: 0755]
doc/.cvsignore [new file with mode: 0644]
doc/Authors [new file with mode: 0644]
doc/Configure.help [new file with mode: 0644]
doc/Makefile.in [new file with mode: 0644]
doc/example.conf [new file with mode: 0644]
doc/history/2.4.notes [new file with mode: 0644]
doc/history/2.7-New [new file with mode: 0644]
doc/history/Manual [new file with mode: 0644]
doc/history/README-2.6 [new file with mode: 0644]
doc/history/README.patches [new file with mode: 0644]
doc/history/history.pre24 [new file with mode: 0644]
doc/history/overview.u2.9 [new file with mode: 0644]
doc/irc.1 [new file with mode: 0644]
doc/ircd.8 [new file with mode: 0644]
doc/readme.crules [new file with mode: 0644]
doc/readme.indent [new file with mode: 0644]
doc/readme.who [new file with mode: 0644]
doc/readme.www [new file with mode: 0644]
include/IPcheck.h [new file with mode: 0644]
include/bsd.h [new file with mode: 0644]
include/channel.h [new file with mode: 0644]
include/class.h [new file with mode: 0644]
include/common.h [new file with mode: 0644]
include/crule.h [new file with mode: 0644]
include/dbuf.h [new file with mode: 0644]
include/fileio.h [new file with mode: 0644]
include/h.h [new file with mode: 0644]
include/hash.h [new file with mode: 0644]
include/ircd.h [new file with mode: 0644]
include/list.h [new file with mode: 0644]
include/map.h [new file with mode: 0644]
include/match.h [new file with mode: 0644]
include/msg.h [new file with mode: 0644]
include/numeric.h [new file with mode: 0644]
include/numnicks.h [new file with mode: 0644]
include/opercmds.h [new file with mode: 0644]
include/packet.h [new file with mode: 0644]
include/parse.h [new file with mode: 0644]
include/patchlevel.h [new file with mode: 0644]
include/querycmds.h [new file with mode: 0644]
include/random.h [new file with mode: 0644]
include/res.h [new file with mode: 0644]
include/runmalloc.h [new file with mode: 0644]
include/s_auth.h [new file with mode: 0644]
include/s_bsd.h [new file with mode: 0644]
include/s_conf.h [new file with mode: 0644]
include/s_debug.h [new file with mode: 0644]
include/s_err.h [new file with mode: 0644]
include/s_misc.h [new file with mode: 0644]
include/s_numeric.h [new file with mode: 0644]
include/s_ping.h [new file with mode: 0644]
include/s_serv.h [new file with mode: 0644]
include/s_user.h [new file with mode: 0644]
include/send.h [new file with mode: 0644]
include/sprintf_irc.h [new file with mode: 0644]
include/struct.h [new file with mode: 0644]
include/support.h [new file with mode: 0644]
include/sys.h [new file with mode: 0644]
include/userload.h [new file with mode: 0644]
include/version.h [new file with mode: 0644]
include/whocmds.h [new file with mode: 0644]
include/whowas.h [new file with mode: 0644]
ircd/.cvsignore [new file with mode: 0644]
ircd/IPcheck.c [new file with mode: 0644]
ircd/Makefile.in [new file with mode: 0644]
ircd/bsd.c [new file with mode: 0644]
ircd/channel.c [new file with mode: 0644]
ircd/chkconf.c [new file with mode: 0644]
ircd/class.c [new file with mode: 0644]
ircd/common.c [new file with mode: 0644]
ircd/crule.c [new file with mode: 0644]
ircd/crypt/Makefile [new file with mode: 0644]
ircd/crypt/README [new file with mode: 0644]
ircd/crypt/crypter [new file with mode: 0644]
ircd/crypt/mkpasswd.c [new file with mode: 0644]
ircd/crypt/sums [new file with mode: 0644]
ircd/dbuf.c [new file with mode: 0644]
ircd/fileio.c [new file with mode: 0644]
ircd/hash.c [new file with mode: 0644]
ircd/ircd.c [new file with mode: 0644]
ircd/list.c [new file with mode: 0644]
ircd/map.c [new file with mode: 0644]
ircd/match.c [new file with mode: 0644]
ircd/numnicks.c [new file with mode: 0644]
ircd/opercmds.c [new file with mode: 0644]
ircd/packet.c [new file with mode: 0644]
ircd/parse.c [new file with mode: 0644]
ircd/querycmds.c [new file with mode: 0644]
ircd/random.c [new file with mode: 0644]
ircd/res.c [new file with mode: 0644]
ircd/runmalloc.c [new file with mode: 0644]
ircd/s_auth.c [new file with mode: 0644]
ircd/s_bsd.c [new file with mode: 0644]
ircd/s_conf.c [new file with mode: 0644]
ircd/s_debug.c [new file with mode: 0644]
ircd/s_err.c [new file with mode: 0644]
ircd/s_misc.c [new file with mode: 0644]
ircd/s_numeric.c [new file with mode: 0644]
ircd/s_ping.c [new file with mode: 0644]
ircd/s_serv.c [new file with mode: 0644]
ircd/s_user.c [new file with mode: 0644]
ircd/send.c [new file with mode: 0644]
ircd/sprintf_irc.c [new file with mode: 0644]
ircd/support.c [new file with mode: 0644]
ircd/userload.c [new file with mode: 0644]
ircd/version.c.SH [new file with mode: 0644]
ircd/whocmds.c [new file with mode: 0644]
ircd/whowas.c [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..e4125b6
--- /dev/null
@@ -0,0 +1,2 @@
+makefile
+Makefile
diff --git a/.indent.pro b/.indent.pro
new file mode 100644 (file)
index 0000000..84ea6ad
--- /dev/null
@@ -0,0 +1,50 @@
+--leave-preprocessor-space
+--dont-break-procedure-type
+--no-space-after-function-call-names
+--brace-indent0
+--indent-level2
+--dont-line-up-parentheses
+--continuation-indentation4
+--case-indentation2
+--no-space-after-casts
+--blank-lines-after-procedures
+--no-blank-lines-after-declarations
+--braces-on-struct-decl-line
+--paren-indentation0
+--case-brace-indentation0
+--line-length80
+--declaration-indentation4
+-T size_t
+-T aClass
+-T aClient
+-T aServer
+-T anUser
+-T aChannel
+-T Mode
+-T aConfItem
+-T aMessage
+-T aMessageTree
+-T aGline
+-T aListingArgs
+-T snomask_t
+-T n_short
+-T n_long
+-T n_time
+-T u_char
+-T u_short
+-T u_long
+-T u_int
+-T dbuf
+-T dbufbuf
+-T aHashEntry
+-T Link
+-T Dlink
+-T VOIDSIG
+-T aHostent
+-T ResRQ
+-T aCache
+-T CacheTable
+-T cainfo
+-T reinfo
+-T RETSIGTYPE
+-T OPT_TYPE
diff --git a/.patches b/.patches
new file mode 100644 (file)
index 0000000..5682e37
--- /dev/null
+++ b/.patches
@@ -0,0 +1 @@
+ircu2.10.07+
diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..80df2d9
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,7 @@
+#
+# $Id: BUGS,v 1.1.1.1 1999-11-16 05:13:14 codercom Exp $
+#
+# Undernet ircu BUGS File
+# Please put information about known bugs here, if the bug has been resolved
+# please comment the entry with "(fixed)"
+#
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..d295e4b
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,11 @@
+#
+# ChangeLog for Undernet ircu Servers
+#
+# $Id: ChangeLog,v 1.1.1.1 1999-11-16 05:13:14 codercom Exp $
+#
+# Please insert new entries on the top of the list, a one or two line comment
+# is sufficient. Please include your name on the entries we know who to blame.
+# Please keep lines < 80 chars.
+#-------------------------------------------------------------------------------
+* ChangeLog (file): added ChangeLog, and BUGS files, import to ircu2.10 --Bleep
+
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..2336cb8
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,104 @@
+                           INSTALL file        by Run <carlo@runaway.xs4all.nl>
+
+
+This is the UnderNet IRC daemon.
+
+The installation of the IRC daemon (ircd) exists of the following steps:
+
+1) Untar the package.
+2) cd into the base directory.
+3 )`./configure'
+4) `make config'
+5) `make'
+6) `make install'
+
+1) Untar the package
+====================
+
+The name of the package is something like `ircu2.x.y.z.tgz', where
+"x.y.z" is the current release (at the time of writing we have
+ircu2.10.00.beta3.tgz).
+
+You need `gzip', the GNU unzip command, to uncompress this package.
+You can download this from every GNU ftp site for almost any Operating system.
+
+If you have GNU tar, type:
+
+tar xzf ircu2.x.y.z.tgz
+
+where "ircu2.x.y.z.tgz" is the name of the package.
+
+If your tar doesn't support the 'z' flag, you can type alternatively:
+
+gzip -dc ircu2.x.y.z.tgz | tar xf -
+
+Both methods result in a directory "ircu2.x.y.z" in your current directory.
+
+2) cd into the base directory
+=============================
+
+Make this directory your current directory by typing:
+
+cd ircu2.x.y.z
+
+where "ircu2.x.y.z" is the name of the unpacked directory.
+
+3) `./configure'
+==============
+
+This will generate 'config/setup.h', your Operating System dependend
+configuration.
+
+4) `make config'
+================
+
+This will (re)generate the include/config.h file.  You can run this
+as often as you like and it will use your last values as defaults.
+At every question you can type a '?' (followed by a return) to get
+extensive help, or a 'c' to continue using your old values by default
+(quickly finishing the script).
+
+5) `make'
+=========
+
+Type:
+
+make
+
+in the base directory.  It should compile without errors or warnings.
+Please mail any problem to the maintainer, but only AFTER you made sure
+you did everything the right way.  If you want your Operating System
+to be supported in future releases, you best make a patch that
+actually fixes the problem.
+
+6) `make install'
+=================
+
+This should install the ircd and the man page. Please recheck the
+permissions of the binary.
+You need to create some of the logfiles that you have chosen by hand
+(for instance with 'touch') before the ircd starts writing to them.
+Of course, you need a syntactically correct ircd.conf in DPATH.  See the
+docs for some info on this.  Also create an ircd.motd with the text of
+your MOTD.  And finally create a remote.motd with three lines of text
+as the remote MOTD.  Again, all of these files should be readable by the
+ircd, and the logfiles should be writeable.
+
+
+In case of problems
+===================
+
+If you have problems configuring the server you might consider installing
+GNU make in your PATH.  In some cases a brain-dead /bin/sh is causing the
+problem, in which case I suggest to install 'bash' and use that (as sh -> bash).
+Finally, any other compile problem should be solved when you install gcc.
+
+If you have problems with starting the ircd, run 'make config' again
+and define DEBUGMODE.  Recompile the ircd, and run it by hand as:
+
+ircd -t -x9
+
+This will write debug output to your screen, probably showing why it
+doesn't start.
+
+Don't use a server with DEBUGMODE defined on a production net.
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.in b/Makefile.in
new file mode 100644 (file)
index 0000000..8a0bad9
--- /dev/null
@@ -0,0 +1,124 @@
+# Makefile for the Undernet IRC Daemon.
+# Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+
+# 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., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+srcdir=@srcdir@
+VPATH=@srcdir@
+
+SHELL=@SHPROG@
+RM=@RMPROG@
+AWK=@AWK@
+@SET_MAKE@
+#### End of system configuration section. ####
+
+all: build
+
+.PHONY: server build depend install config update diff patch export
+# Some versions of make give a warning when this is empty:
+.SUFFIXES: .dummy
+
+build:
+       @if [ ! -f config/config.h ]; then \
+               echo "Run 'make config' to configure the server"; \
+       else \
+         for i in config ircd; do \
+                 echo "Building $$i..."; \
+                 cd $$i; ${MAKE} build; cd ..; \
+         done; \
+       fi
+
+root-clean:
+       @for i in '*.orig' '.*.orig' '\#*' '*~' '.*~' '*.bak' '.*.bak' core; do\
+               echo "Removing $$i"; \
+               REMOVE_FILES="`find . -name "$$i" -print`"; \
+               test -n "$$REMOVE_FILES" && ${RM} -f $$REMOVE_FILES; \
+       done || true
+
+clean: root-clean
+       @for i in ircd config; do \
+               echo "Cleaning $$i..."; \
+               cd $$i; ${MAKE} clean; cd ..;\
+       done
+
+root-distclean: root-clean
+       @for i in '*.rej'; do \
+               echo "Removing $$i"; \
+               REMOVE_FILES="`find . -name "$$i" -print`"; \
+               test -n "$$REMOVE_FILES" && ${RM} -f $$REMOVE_FILES; \
+       done || true
+       ${RM} -f Makefile
+
+distclean: root-distclean
+       @for i in doc ircd config; do \
+               echo "Dist-cleaning $$i..."; \
+               cd $$i; ${MAKE} distclean; cd ..;\
+       done
+
+maintainer-clean: root-distclean
+       @for i in doc ircd config; do \
+               echo "maintainer-cleaning $$i..."; \
+               cd $$i; ${MAKE} maintainer-clean; cd ..;\
+       done
+
+depend:
+       @for i in ircd; do \
+               echo "Making dependencies in $$i..."; \
+               cd $$i; ${MAKE} depend; cd ..; \
+       done
+
+install:
+       @if [ -f ircd/ircd ]; then \
+               for i in ircd doc; do \
+                       echo "Installing $$i..."; \
+                       cd $$i; ${MAKE} install; cd ..; \
+               done \
+       else \
+               echo "First run 'make'"; \
+       fi
+
+uninstall:
+       @for i in doc ircd; do \
+               echo "Uninstalling $$i..."; \
+               cd $$i; ${MAKE} uninstall; cd ..; \
+       done
+
+config: FORCE
+       @cd config; ${MAKE} config
+       @echo
+       @echo "The Undernet IRC daemon is now hopefully configured for your setup."
+       @echo "Next run 'make' to build the server."
+       @echo
+
+# Coders: You need GNU make for this to work.
+Makefile: config/config.status Makefile.in
+       @echo "recreating Makefile"
+       @cd config; \
+       CONFIG_FILES=../Makefile CONFIG_HEADERS= ./config.status > /dev/null
+
+config/config.status:
+       @cd config; ${MAKE} config.status
+
+# Some versions of 'make' do not support the .PHONY target :
+FORCE:
+
+# Indent all headers and source files:
+indent:
+       @test "`indent --version`" = "GNU indent 2.1.0" || \
+         (echo "You need GNU indent 2.1.0; See doc/readme.indent" && exit -1);
+       VERSION_CONTROL=none indent include/*.h ircd/*.c
diff --git a/config/.cvsignore b/config/.cvsignore
new file mode 100644 (file)
index 0000000..8f1df63
--- /dev/null
@@ -0,0 +1,11 @@
+Makefile
+config.cache
+config.log
+config.status
+setup.h
+stamp-h
+config-sh
+Configure
+config.h
+.config
+.tmpconfig.h
diff --git a/config/Configure.in b/config/Configure.in
new file mode 100644 (file)
index 0000000..3217707
--- /dev/null
@@ -0,0 +1,549 @@
+# @configure_input@
+#
+# This script is used to configure the Undernet Daemon.
+# It was copied from the linux kernel distribution and only slightly
+# changed.
+#
+# 170897 (carlo@runaway.xs4all.nl) -
+# Changed the default configuration file to be defconfig.
+# Changed the location of autoconf.h to be 'include/'.
+# Changed the final text printed at the end.
+# Changed replacement to only match 'is not set' (and not 'is not').
+# Added function string() and define_string().
+# Added function define_macro()
+#
+# 180897 (carlo@runaway.xs4all.nl) -
+# Changed bool() so it can take a default argument.
+# Introduced hard link ../.config, to inherit config files over new
+# distributions.
+# Got rid of 'is not set' and using '=n' instead.
+# Changed configuration help file to be doc/Configure.help.
+# Changed DEFAULT into USE_DEFAULT, which is asked for in config.in.
+#
+# 210897 (carlo@runaway.xs4all.nl) -
+# Changed the location of autoconf.h and .config to be 'configure/'.
+# Fixed the script to work with a general 'sh' and not only with bash.
+#      (removing 'function', using `` instead of $(), using \\$ inside
+#       that instead of \$, using 'eval set -- $choices' rather then
+#       'set -- $choices', introduction of function 'expr' instead of
+#       using external executable 'expr', using set -fh rather then
+#       set -f -h, using "" around reg.exp rather then '' and removing
+#       unnecessary backslashes from it).
+# Removed tristate and hex functions.
+# Making the hardlink to ../.config only when configure/.config doesn't exist.
+#
+# 280897 (carlo@runaway.xs4all.nl) -
+# Added CONFIG_NEW: Ask if one wants to use this configuration as main
+#      configuration.
+# Changed /bin/rm into $RM, so now this environment variable must be set!
+#
+# 290897 (carlo@runaway.xs4all.nl) -
+# Added support for the solaris Born shell.
+# Changed the directory configure/ to config/ in order not to collide with
+# GNU autoconf in the future.
+#
+# 020997 (carlo@runaway.xs4all.nl) -
+# The future is here... renamed 'config.in' to 'config-sh', reserving
+# 'file.in' for input files of autoconfs' 'configure' script.
+# 'autoconf.h' has been renamed to 'config.h', because it would be confusing
+# otherwise with autoconf.
+# Changed Configure to be run from within config/.
+#
+# 050997 (carlo@runaway.xs4all.nl) -
+# Don't use 'def={$old:-$3}' but instead the equivalent ': ${def=$3}' because
+# some sh don't understand the first expression.
+# Pressing a 'c' at any question continues in 'batch' mode.
+#
+# 220199 (carlo@runaway.xs4all.nl) -
+# Put MACRO name at front, making it easier to find it when scanning
+# through the defaults to change just one.
+# Make pressing 'c' stop at every paragraph, and pressing 'C' finish the
+# whole script.
+#
+# -----------------------------------------------------------------------------
+#
+# This script is used to configure the linux kernel.
+#
+# It was inspired by the challenge in the original Configure script
+# to ``do something better'', combined with the actual need to ``do
+# something better'' because the old configure script wasn't flexible
+# enough.
+#
+# Please send comments / questions / bug fixes to raymondc@microsoft.com.
+#
+#              ***** IMPORTANT COMPATIBILITY NOTE ****
+# If configuration changes are made which might adversely effect 
+# Menuconfig or xconfig, please notify the respective authors so that 
+# those utilities can be updated in parallel.
+#
+# Menuconfig:  <roadcapw@cfw.com>
+# xconfig:     <apenwarr@foxnet.net>  <eric@aib.com>
+#              ****************************************
+#
+# Each line in the config file is a command.
+#
+# 050793 - use IFS='@' to get around a bug in a pre-version of bash-1.13
+# with an empty IFS.
+#
+# 030995 (storner@osiris.ping.dk) - added support for tri-state answers,
+# for selecting modules to compile.
+#
+# 180995 Bernhard Kaindl (bkaindl@ping.at) - added dummy functions for
+# use with a config.in modified for make menuconfig.
+#
+# 301195 (boldt@math.ucsb.edu) - added help text support
+#
+# 281295 Paul Gortmaker - make tri_state functions collapse to boolean
+# if module support is not enabled.
+#
+# 010296 Aaron Ucko (ucko@vax1.rockhurst.edu) - fix int and hex to accept
+# arbitrary ranges
+#
+# 150296 Dick Streefland (dicks@tasking.nl) - report new configuration
+# items and ask for a value even when doing a "make oldconfig"
+#
+# 200396 Tom Dyas (tdyas@eden.rutgers.edu) - when the module option is
+# chosen for an item, define the macro <option_name>_MODULE
+#
+# 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular 
+# expressions for GNU expr since version 1.15 and up use \? and \+.
+
+#### Start of system configuration section. ####
+
+SHELL=@SHPROG@
+RM=@RMPROG@
+LN_S="@LN_S@"
+unet_cv_sys_set_h=@unet_cv_sys_set_h@
+
+#### End of system configuration section. ####
+
+# Disable filename globbing
+set -f
+
+if test $unet_cv_sys_set_h = yes; then
+       set -h
+fi
+
+# Figure out how to do 'echo' without newline:
+c=''
+n=''
+if [ "`eval echo -n 'a'`" = "-n a" ] ; then
+       c='\c'
+else
+       n='-n'
+fi
+
+# Check if ${VAR:-def} works
+DUMMY_VAR=""
+DUMMY_VAR2="some value"
+DUMMY_VAR=${DUMMY_VAR:-test}
+DUMMY_VAR2=${DUMMY_VAR2:-test}
+if [ "$DUMMY_VAR" != "test" -o "$DUMMY_VAR2" != "some value" ]; then
+  echo "You /bin/sh doesn't understand '\${VAR:-default}'"
+  exit 1
+fi
+
+#
+# Dummy functions for use with a config.in modified for menuconf
+#
+mainmenu_option () {
+       :
+}
+mainmenu_name () {
+       :
+}
+endmenu () {
+       :
+}
+
+#
+# help prints the corresponding help text from Configure.help to stdout
+#
+#       help variable
+#
+help () {
+  if [ -f ../doc/Configure.help ]
+  then
+    #first escape regexp special characters in the argument:
+    var=`echo "$1"|sed 's/[][\/.^$*]/\\&/g'`
+    #now pick out the right help text:
+    text=`sed -n "/^$var[      ]*\\$/,\\${
+        /^$var[        ]*\\$/b
+        /^#.*/b
+        /^[    ]*\\$/q
+        p
+    }" ../doc/Configure.help`
+    if [ -z "$text" ]
+    then
+      echo; echo "  Sorry, no help available for this option yet.";echo
+    else
+      (echo; echo "$text"; echo) | ${PAGER:-more}
+    fi
+  else
+    echo;
+    echo "  Can't access the file doc/Configure.help which"
+    echo "  should contain the help texts."
+    echo
+  fi
+}
+
+
+#
+# readln reads a line into $ans.
+#
+#      readln macro prompt default oldval
+#
+readln () {
+  echo $1 | awk '{ printf("%-20.20s: ", $1); }'
+  if [ "$USE_DEFAULT" != "n" -a -n "$4" ]; then
+    echo $2
+    ans=$3
+  else
+    echo $n "$2$c"
+    [ -z "$4" ] && echo $n "(NEW) $c"
+    IFS='@' read ans </dev/tty || exit 1
+    [ -z "$ans" ] && ans=$3
+    if [ "$ans" = "c" -o "$ans" = "C" ]; then
+      USE_DEFAULT=$ans
+      ans=$3
+    fi
+  fi
+}
+
+#
+# comment does some pretty-printing
+#
+#      comment 'xxx'
+# 
+comment () {
+  echo ""
+  echo "*-----------------------------------------------------------------------------";
+  echo "* $1" ;
+  echo "*"
+  (echo "" ; echo "#"; echo "# $1" ; echo "#") >>$CONFIG
+  (echo "" ; echo "/*"; echo " * $1" ; echo " */") >>$CONFIG_H
+  if [ "$USE_DEFAULT" = "c" ]; then
+    USE_DEFAULT=n
+  fi
+}
+
+#
+# define_bool sets the value of a boolean argument
+#
+#      define_bool define value
+#
+define_bool () {
+  case "$2" in
+    "y")
+      echo "$1=y" >>$CONFIG
+      echo "#define $1" >>$CONFIG_H
+      ;;
+
+    "n")
+      echo "$1=n" >>$CONFIG
+      echo "#undef  $1" >>$CONFIG_H
+      ;;
+  esac
+  eval "$1=$2"
+}
+
+#
+# expr determines if string matches a regular expression
+#
+#      expr string reg_exp
+#
+expr () {
+  MATCH=`echo $1 | egrep -e $2`
+  if [ -z "$MATCH" ]; then
+    return 1
+  else
+    return 0
+  fi
+}
+
+#
+# bool processes a boolean argument
+#
+#      bool question define
+#
+bool () {
+  old=`eval echo "\\${$2}"`
+  if [ -z "$old" -a -n "$3" ]; then
+    def=$3
+  else
+    def=${old:-n}
+  fi
+  case "$def" in
+    "y")
+      defprompt="Y/n/?"
+      def="y"
+      ;;
+
+    "n")
+      defprompt="N/y/?"
+      ;;
+  esac
+  while :; do
+    readln $2 "$1 [$defprompt] " "$def" "$old"
+    case "$ans" in
+      [yY] | [yY]es )
+        define_bool "$2" "y"
+        break
+       ;;
+
+      [nN] | [nN]o )
+        define_bool "$2" "n"
+       break
+       ;;
+
+      * )
+        help "$2"
+        ;;
+    esac
+  done
+}
+
+#
+# define_int sets the value of a integer argument
+#
+#      define_int define value
+#
+define_int () {
+  echo "$1=$2" >>$CONFIG
+  echo "#define $1 ($2)" >>$CONFIG_H
+  eval "$1=$2"
+}
+
+#
+# int processes an integer argument
+#
+#      int question define default
+#
+int () {
+  old=`eval echo "\\${$2}"`
+  def=${old:-$3}
+  while :; do
+    readln $2 "$1 [$def] " "$def" "$old"
+    if expr "$ans" "^0$|^(-[1-9]|[1-9])[0-9]*$"; then
+      define_int "$2" "$ans"
+      break
+    else
+      help "$2"
+    fi
+  done
+}
+
+#
+# choice processes a choice list (1-out-of-n)
+#
+#      choice question choice-list default
+#
+# The choice list has a syntax of:
+#      NAME WHITESPACE VALUE { WHITESPACE NAME WHITESPACE VALUE }
+# The user may enter any unique prefix of one of the NAMEs and
+# choice will define VALUE as if it were a boolean option.
+# VALUE must be in all uppercase.  Normally, VALUE is of the
+# form CONFIG_<something>.  Thus, if the user selects <something>,
+# the CPP symbol CONFIG_<something> will be defined and the
+# shell variable CONFIG_<something> will be set to "y".
+#
+choice () {
+  question="$1"
+  choices="$2"
+  old=
+  def=$3
+
+  # determine default answer:
+  names=""
+  eval set -- $choices
+  firstvar=$2
+  while [ -n "$2" ]; do
+    if [ -n "$names" ]; then
+      names="$names, $1"
+    else
+      names="$1"
+    fi
+    if [ "`eval echo "\\${$2}"`" = "y" ]; then
+      old=$1
+      def=$1
+    fi
+    shift; shift
+  done
+
+  val=""
+  while [ -z "$val" ]; do
+    ambg=n
+    readln $names "$question [$def] " "$def" "$old"
+    ans=`echo $ans | tr a-z A-Z`
+    eval set -- $choices
+    while [ -n "$1" ]; do
+      name=`echo $1 | tr a-z A-Z`
+      case "$name" in
+        "$ans"* )
+         if [ "$name" = "$ans" ]; then
+           val="$2"
+           break       # stop on exact match
+         fi
+         if [ -n "$val" ]; then
+           echo;
+           echo \
+               "  Sorry, \"$ans\" is ambiguous; please enter a longer string."
+           echo
+           val=""
+           ambg=y
+           break
+         else
+           val="$2"
+         fi
+         ;;
+      esac
+      shift; shift
+    done
+    if [ "$val" = "" -a "$ambg" = "n" ]; then
+      help "$firstvar"
+    fi
+  done
+  eval set -- $choices
+  while [ -n "$2" ]; do
+    if [ "$2" = "$val" ]; then
+      echo "  defined $val"
+      define_bool "$2" "y"
+    else
+      define_bool "$2" "n"
+    fi
+    shift; shift
+  done
+}
+
+#
+# define_macro sets the value of a macro argument
+#
+#      define_macro define value
+#
+define_macro () {
+  if [ -n "$2" ]; then
+    echo "$1=\"$2\"" >>$CONFIG
+    echo "#define $1 $2" >>$CONFIG_H
+  fi
+  eval "$1=\"$2\""
+}
+
+#
+# define_string sets the value of a string argument
+#
+#      define_string define value
+#
+define_string () {
+  if [ -n "$2" ]; then
+    echo "$1=\"$2\"" >>$CONFIG
+    echo "#define $1 \"$2\"" >>$CONFIG_H
+  fi
+  eval "$1=\"$2\""
+}
+
+#
+# string processes a string argument
+#
+#      string question define default
+#
+string () {
+  old=`eval echo "\\${$2}"`
+  def=${old:-$3}
+  while :; do
+    readln $2 "$1 [$def] " "$def" "$old"
+    if expr "$ans" "^[^\"].*[^\"]$"; then
+      define_string "$2" "$ans"
+      break
+    else
+      help "$2"
+    fi
+  done
+}
+
+CONFIG=.tmpconfig
+CONFIG_H=.tmpconfig.h
+trap "$RM -f $CONFIG $CONFIG_H ; exit 1" 1 2 15
+
+#
+# Make sure we start out with a clean slate.
+#
+echo "#" > $CONFIG
+echo "# Configuration default generated for:" > $CONFIG
+echo '# `pwd`' > $CONFIG
+echo "#" > $CONFIG
+echo "# Automatically generated by 'make config': don't edit" >> $CONFIG
+echo "#" >> $CONFIG
+
+echo "/*" > $CONFIG_H
+echo " * Automatically generated C config: don't edit" >> $CONFIG_H
+echo " */" >> $CONFIG_H
+echo "#define AUTOCONF_INCLUDED" >> $CONFIG_H
+
+CONFIG_IN=./config-sh
+if [ "$1" != "" ] ; then
+  CONFIG_IN=$1
+fi
+
+DEFAULTS=none
+if [ -r .config ]; then
+  DEFAULTS=./.config
+else
+  if [ -r ../../.config ]; then
+    echo "*"
+    echo "* WARNING: Inheriting .config from previous version!"
+    DEFAULTS=../../.config
+  fi
+fi
+
+if [ -r $DEFAULTS ]; then
+  echo "*"
+  echo "* Using defaults found in" $DEFAULTS
+  echo "* If you type a 'C' the script will finish using all defaults."
+  echo "* If you type a 'c' the script will skip to the next paragraph."
+  echo "*"
+  . $DEFAULTS
+else
+  echo "*"
+  echo "* No defaults found"
+  echo "*"
+fi
+
+. $CONFIG_IN
+
+mv $CONFIG_H config.h
+$RM -f .config.old
+if [ -f .config ]; then
+  # Keep the inode of .config (hardlink to ../../.config) intact:
+  cp .config .config.old
+  cat $CONFIG > .config
+  $RM -f $CONFIG
+fi
+if test ! -f .config -o ! -r ../../.config; then
+  if [ "$CONFIG_BATCH" = "y" ]; then
+    CONFIG_NEW=n
+  else
+    CONFIG_NEW=y
+    USE_DEFAULT=n
+    echo "*"
+    echo "*"
+    echo "*"
+    bool 'Use .config of THIS source tree as your upgrade default' CONFIG_NEW
+  fi
+  if [ ! -f .config ]; then
+    mv $CONFIG .config
+  fi
+  if [ "$CONFIG_NEW" = "y" ]; then
+    CONFIG_CURDIR=`pwd`
+    cd ../..
+    $RM -f .config .config.cache
+    ln $CONFIG_CURDIR/.config .config
+    ln $CONFIG_CURDIR/config.cache .config.cache
+    echo
+    echo "NOTE: Linking ../.config to THIS source tree's configuration !"
+    echo "(The configuration of this source tree will be used as default when you upgrade)"
+  fi
+fi
+
+echo
+
+exit 0
diff --git a/config/Makefile.in b/config/Makefile.in
new file mode 100644 (file)
index 0000000..3804b73
--- /dev/null
@@ -0,0 +1,104 @@
+# config/Makefile for the Undernet IRC Daemon.
+# Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+
+# 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., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+SHELL=@SHPROG@
+RM=@RMPROG@
+TOUCH=touch
+@SET_MAKE@
+#### End of system configuration section. ####
+
+# Some versions of make give a warning when this is empty:
+.SUFFIXES: .dummy
+
+# These files need to be up to date when we are building the ircd
+build: config.h setup.h
+
+clean:
+       ${RM} -f .tmpconfig.h .tmpconfig
+
+distclean: clean
+       ${RM} -f .config setup.h stamp-h config.h .config.old \
+               config.cache config.log config.status Makefile config-sh \
+               Configure
+
+maintainer-clean: distclean
+       ${RM} -f aclocal.m4 setup.h.in stamp-h.in configure
+       @echo Generating aclocal.m4...
+       aclocal
+       @echo Generating setup.h.in...
+       autoheader
+       @echo timestamp > stamp-h.in
+       @echo Generating configure...
+       autoconf
+
+config: configure setup.h.in config.status Configure FORCE
+       @CONFIG_BATCH=n ${SHELL} ./Configure || exit 1
+       @# Allow the use of non-GNU make with 'make config':
+       @cd ../ircd; ${MAKE} Makefile
+       @cd ../doc; ${MAKE} Makefile
+
+config.h: Configure config-sh
+       @echo Generating config.h...
+       @CONFIG_BATCH=y ${SHELL} ./Configure || exit 1
+       @cd ../ircd; ${MAKE} Makefile
+
+FORCE:
+
+#==============================================================================
+# Rules to automatically remake Configuration :
+
+Makefile: config.status Makefile.in
+       @echo "recreating config/Makefile"
+       @CONFIG_FILES=Makefile CONFIG_HEADERS= ./config.status > /dev/null
+
+aclocal.m4: acinclude.m4
+       @echo Generating aclocal.m4...
+       @echo aclocal
+       @aclocal || ( echo "NOT recreating 'aclocal.m4', you don't have 'aclocal'"; touch aclocal.m4 )
+
+configure: configure.in aclocal.m4
+       @echo Generating configure...
+       @echo autoconf
+       @autoconf || ( echo "NOT recreating 'configure', you don't have 'autoconf'"; touch configure )
+
+setup.h.in: stamp-h.in
+stamp-h.in: configure.in acconfig.h setup.h.top aclocal.m4 #setup.h.bot
+       @echo Generating setup.h.in...
+       @echo autoheader
+       @autoheader || ( echo "NOT recreating 'setup.h.in', you don't have 'autoheader'"; touch setup.h.in )
+       @echo timestamp > stamp-h.in
+
+setup.h: stamp-h
+stamp-h: stamp-h.in config.status
+       @echo Generating setup.h...
+       @CONFIG_FILES= CONFIG_HEADERS=setup.h ./config.status
+
+config.status: configure
+       @echo Generating config.status...
+       @CONFIG_FILES= CONFIG_HEADERS= ./config.status --recheck || \
+               (./configure && ${TOUCH} ../doc/stamp-m ../ircd/stamp-m)
+
+config-sh: config-sh.in config.status
+       @echo Generating config-sh...
+       @CONFIG_FILES=config-sh CONFIG_HEADERS= ./config.status
+
+Configure: Configure.in config.status
+       @echo Generating Configure...
+       @CONFIG_FILES=Configure CONFIG_HEADERS= ./config.status
diff --git a/config/README b/config/README
new file mode 100644 (file)
index 0000000..7baeded
--- /dev/null
@@ -0,0 +1,94 @@
+This directory contains NOTHING for the admin to configure.
+
+If you want to configure the IRC server, go to the top level directory and
+type: 'make config'.
+
+-------------------------------------------------------------------------------
+
+If you are a coder and are adding a feature to the server that you need
+automatic or manual configuration for, then this is where you need to
+change things.
+
+If you touch any of the following files, then you need to have 'autoconf'
+(and 'autoheader') installed - you might also need GNU m4 and perl then:
+
+configure.in
+acconfig.h
+setup.h.top
+
+Never touch setup.h.in and stamp-h.in.
+Don't run 'make maintainer-clean' when you don't have autoconf etc. installed.
+
+If you touch any of the following files, then you need to have GNU make
+in order to have the Makefiles correctly rebuild automatically:
+
+parse.none
+gen.ircd.Makefile
+../ircd/Makefile.in
+../doc/Makefile.in
+../Makefile.in
+
+Note that after running 'make config' special added code in config/Makefile
+will remake the Makefiles as needed to allow the admins to use a non-GNU make.
+But coders should use GNU make.
+
+Here is a sheme of which files are needed to generate which files:
+
+                                        /> (config.log)
+                                       /-> (config.cache)
+configure.in  --(autoconf)--> configure --> config.status
+             \
+acconfig.h    --(autoheader)--> setup.h.in --(config.status)--> setup.h
+setup.h.top   /                (stamp-h.in)                    (stamp-h)
+
+config-sh.in  --(config.status)--> config-sh --(Configure)--> config.h
+                                  (.config) -/           \-> (.config)
+                                                          \> (.config.old)
+Makefile.in --(config.status)--> Makefile
+
+The exectuable scripts (configure, config.status and Configure) should only be
+called from Makefile (automatically, when needed, by means of makefile rules).
+
+Further more, config.status is also used to (re)generate doc/Makefile and
+ircd/Makefile (from respectively doc/Makefile.in and ircd/Makefile.in).
+However, ircd/Makefile is not functional then, first also gen.ircd.Makefile
+must be called.  All of this is done automatically by the Makefile rules
+inside doc/Makefile and ircd/Makefile respectively.  Finally, gen.ircd.Makefile
+also calls parse.none.  So:
+
+../ircd/Makefile.in  --\
+gen.ircd.Makefile    ----(config.status)--> ../ircd/Makefile
+.config              --/
+parse.none           -/
+
+../doc/Makefile.in   --\
+gen.doc.Makefile     ----(config.status)--> ../doc/Makefile
+.config              --/
+parse.none           -/
+
+../Makefile.in   ----(config.status)--> ../Makefile
+
+'Configure' uses the file ../doc/Configure.help for the help texts.
+
+-------------------------------------------------------------------------------
+
+Adding an 'autoconf' check.
+
+1) Install GNU m4, autoconf & autoheader.
+2) Add your check to configure.in, if needed also add an entry to acconfig.h
+3) Add the #ifdefs to the source code.
+
+Adding a manual configuration.
+
+1) Edit config-sh.in and add the question.
+2) Edit ../doc/Configure.help and add the help text !
+3) If necessary, add something to parse.none to allow the use of the
+   value 'none'.
+4) If necessary edit gen.ircd.Makefile and ../ircd/Makefile to get your
+   variable in ../ircd/Makefile.  (It will already be in config.h).
+5) Add the #ifdefs to the source code.
+
+In both cases, all needed files will be automatically (re) generated when
+you run 'make' from the top level directory (you need GNU make though).
+
+Carlo Wood
diff --git a/config/acconfig.h b/config/acconfig.h
new file mode 100644 (file)
index 0000000..af2787a
--- /dev/null
@@ -0,0 +1,32 @@
+/* Define if you have the resolv library (-lresolv) */
+#undef HAVE_LIB_RESOLV
+
+/* Define if you have the setrlimit function */
+#undef HAVE_SETRLIMIT
+
+/* Define one of these, depending on wether you have
+   POSIX, BSD or SYSV non-blocking stuff */
+#undef NBLOCK_POSIX
+#undef NBLOCK_BSD
+#undef NBLOCK_SYSV
+
+/* Define on of these, depending on wether you have
+   POSIX, BSD or SYSV signal handling */
+#undef POSIX_SIGNALS
+#undef BSD_RELIABLE_SIGNALS
+#undef SYSV_UNRELIABLE_SIGNALS
+
+/* Define these to be unsigned integral internal types,
+ * of respecitvely 2 and 4 bytes in size, when not already
+ * defined in <sys/types.h>, <stdlib.h> or <stddef.h> */
+#undef u_int16_t
+#undef u_int32_t
+
+/* Define this to the printf format for size_t */
+#undef SIZE_T_FMT
+
+/* Define this to the printf format for time_t */
+#undef TIME_T_FMT
+
+/* Define this to the printf signed format for time_t */
+#undef STIME_T_FMT
diff --git a/config/acinclude.m4 b/config/acinclude.m4
new file mode 100644 (file)
index 0000000..2119861
--- /dev/null
@@ -0,0 +1,290 @@
+dnl
+dnl Macro: unet_PIPE_CFLAGS
+dnl
+dnl   If the compiler understands -pipe, add it to CFLAGS if not already
+dnl   there.
+dnl
+AC_DEFUN(unet_PIPE_CFLAGS,
+[AC_MSG_CHECKING([if the compiler understands -pipe])
+unet_cv_pipe_flags="$ac_cv_prog_gcc"
+if test "$ac_cv_prog_gcc" = no; then
+  OLDCFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -pipe"
+  AC_TRY_COMPILE(,,unet_cv_pipe_flags=yes,)
+  CFLAGS="$OLDCFLAGS"
+fi
+AC_MSG_RESULT($unet_cv_pipe_flags)
+if test "$unet_cv_pipe_flags" = yes ; then
+  x=`echo $CFLAGS | grep 'pipe' 2>/dev/null`
+  if test "$x" = "" ; then
+    CFLAGS="$CFLAGS -pipe"
+  fi
+fi
+])
+
+dnl
+dnl Macro: unet_CHECK_LIB_RESOLV
+dnl
+dnl   Check for res_mkquery in -lresolv and add that to LIBS when needed.
+dnl
+AC_DEFUN(unet_CHECK_LIB_RESOLV,
+[AC_CACHE_CHECK([for res_mkquery in -lresolv], unet_cv_lib_resolv,
+[AC_TRY_LINK([struct rrec;
+extern int res_mkquery(int, const char *, int, int, const char *,
+    int, struct rrec *, unsigned char *, int);],
+[int op;
+const char *dname;
+int class, type;
+const char *data;
+int datalen;
+struct rrec *newrr;
+unsigned char *buf;
+int buflen;
+res_mkquery(op,dname,class,type,data,datalen,newrr,buf,buflen)],
+unet_cv_lib_resolv=no, [OLD_LIBS="$LIBS"
+LIBS="$LIBS -lresolv"
+AC_TRY_LINK([extern char *_res;], [*_res=0],
+unet_cv_lib_resolv=yes, unet_cv_lib_resolv=no)
+LIBS="$OLD_LIBS"])])
+if test $unet_cv_lib_resolv = yes; then
+  AC_DEFINE(HAVE_LIB_RESOLV)
+  LIBS="$LIBS -lresolv"
+fi])
+
+dnl
+dnl Macro: unet_NONBLOCKING
+dnl
+dnl   Check whether we have posix, bsd or sysv non-blocking sockets and
+dnl   define respectively NBLOCK_POSIX, NBLOCK_BSD or NBLOCK_SYSV.
+dnl
+AC_DEFUN(unet_NONBLOCKING,
+[dnl Do we have posix, bsd or sysv non-blocking stuff ?
+AC_CACHE_CHECK([for posix non-blocking], unet_cv_sys_nonblocking_posix,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+  char b[12];
+  struct sockaddr x;
+  size_t l = sizeof(x);
+  int f = socket(AF_INET, SOCK_DGRAM, 0);
+  if (f >= 0 && !(fcntl(f, F_SETFL, O_NONBLOCK)))
+  {
+    signal(SIGALRM, alarmed);
+    alarm(2);
+    recvfrom(f, b, 12, 0, &x, &l);
+    alarm(0);
+    exit(0);
+  }
+  exit(1);
+}], unet_cv_sys_nonblocking_posix=yes, unet_cv_sys_nonblocking_posix=no)])
+if test $unet_cv_sys_nonblocking_posix = yes; then
+  AC_DEFINE(NBLOCK_POSIX)
+else
+AC_CACHE_CHECK([for bsd non-blocking], unet_cv_sys_nonblocking_bsd,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+  char b[12];
+  struct sockaddr x;
+  size_t l = sizeof(x);
+  int f = socket(AF_INET, SOCK_DGRAM, 0);
+  if (f >= 0 && !(fcntl(f, F_SETFL, O_NDELAY)))
+  {
+    signal(SIGALRM, alarmed);
+    alarm(2);
+    recvfrom(f, b, 12, 0, &x, &l);
+    alarm(0);
+    exit(0);
+  }
+  exit(1);
+}], unet_cv_sys_nonblocking_bsd=yes, unet_cv_sys_nonblocking_bsd=no)])
+if test $unet_cv_sys_nonblocking_bsd = yes; then
+  AC_DEFINE(NBLOCK_BSD)
+else
+  AC_DEFINE(NBLOCK_SYSV)
+fi
+fi])
+
+dnl
+dnl Macro: unet_SIGNALS
+dnl
+dnl   Check if we have posix signals, reliable bsd signals or
+dnl   unreliable sysv signals and define respectively POSIX_SIGNALS,
+dnl   BSD_RELIABLE_SIGNALS or SYSV_UNRELIABLE_SIGNALS.
+dnl
+AC_DEFUN(unet_SIGNALS,
+[dnl Do we have posix signals, reliable bsd signals or unreliable sysv signals ?
+AC_CACHE_CHECK([for posix signals], unet_cv_sys_signal_posix,
+[AC_TRY_COMPILE([#include <signal.h>],
+[sigaction(SIGTERM, (struct sigaction *)0L, (struct sigaction *)0L)],
+unet_cv_sys_signal_posix=yes, unet_cv_sys_signal_posix=no)])
+if test $unet_cv_sys_signal_posix = yes; then
+  AC_DEFINE(POSIX_SIGNALS)
+else
+AC_CACHE_CHECK([for bsd reliable signals], unet_cv_sys_signal_bsd,
+[AC_TRY_RUN([#include <signal.h>
+int calls = 0;
+$ac_cv_type_signal handler()
+{
+  if (calls) return;
+  calls++;
+  kill(getpid(), SIGTERM);
+  sleep(1);
+}
+int main(void)
+{
+  signal(SIGTERM, handler);
+  kill(getpid(), SIGTERM);
+  exit (0);
+}], unet_cv_sys_signal_bsd=yes, unet_cv_sys_signal_bsd=no)])
+if test $unet_cv_sys_signal_bsd = yes; then
+  AC_DEFINE(BSD_RELIABLE_SIGNALS)
+else
+  AC_DEFINE(SYSV_UNRELIABLE_SIGNALS)
+fi
+fi])
+
+dnl
+dnl Macro: unet_DEFINE_SIZE_T_FMT
+dnl
+dnl Define SIZE_T_FMT to be "%u" or "%lu", whichever seems more appropriate.
+dnl
+AC_DEFUN(unet_DEFINE_SIZE_T_FMT,
+[dnl Make educated guess :/, if size_t is a long or not
+AC_CHECK_SIZEOF(size_t)dnl
+AC_MSG_CHECKING(printf format of size_t)
+if test "$ac_cv_sizeof_size_t" = 4 ; then
+  AC_MSG_RESULT("%u")
+  AC_DEFINE(SIZE_T_FMT, "%u")
+else
+  AC_MSG_RESULT("%lu")
+  AC_DEFINE(SIZE_T_FMT, "%lu")
+fi])
+
+dnl
+dnl Macro: unet_DEFINE_TIME_T_FMT
+dnl
+dnl Try to figure out if time_t is an int or not, and if so define
+dnl TIME_T_FMT to be "%u", otherwise define it to be "%lu".
+dnl Likewise define STIME_T_FMT for the signed format.
+dnl
+AC_DEFUN(unet_DEFINE_TIME_T_FMT,
+[dnl Make educated guess :/, if time_t is a long or not
+AC_MSG_CHECKING(size of time_t)
+AC_CACHE_VAL(unet_cv_sizeof_time_t,
+[AC_TRY_RUN([#include <stdio.h>
+#include <sys/types.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(time_t));
+  exit(0);
+}], unet_cv_sizeof_time_t=`cat conftestval`,
+unet_cv_sizeof_time_t=0, unet_cv_sizeof_time_t=0)])
+if test "$unet_cv_sizeof_time_t" = 0 ; then
+  AC_MSG_RESULT(unknown)
+  AC_DEFINE(TIME_T_FMT, "%lu")
+  AC_DEFINE(STIME_T_FMT, "%ld")
+else
+  AC_MSG_RESULT([$unet_cv_sizeof_time_t])
+  AC_MSG_CHECKING(printf format of time_t)
+  if test "$unet_cv_sizeof_time_t" = "$ac_cv_sizeof_long" ; then
+    AC_MSG_RESULT("%lu")
+    AC_DEFINE(TIME_T_FMT, "%lu")
+    AC_DEFINE(STIME_T_FMT, "%ld")
+  else
+    AC_MSG_RESULT("%u")
+    AC_DEFINE(TIME_T_FMT, "%u")
+    AC_DEFINE(STIME_T_FMT, "%d")
+  fi
+fi])
+
+dnl
+dnl Macro: unet_CHECK_TYPE_SIZES
+dnl
+dnl Check the size of several types and define a valid int16_t and int32_t.
+dnl
+AC_DEFUN(unet_CHECK_TYPE_SIZES,
+[dnl Check type sizes
+AC_CHECK_SIZEOF(short)
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+if test "$ac_cv_sizeof_int" = 2 ; then
+  AC_CHECK_TYPE(int16_t, int)
+  AC_CHECK_TYPE(u_int16_t, unsigned int)
+elif test "$ac_cv_sizeof_short" = 2 ; then
+  AC_CHECK_TYPE(int16_t, short)
+  AC_CHECK_TYPE(u_int16_t, unsigned short)
+else
+  AC_MSG_ERROR([Cannot find a type with size of 16 bits])
+fi
+if test "$ac_cv_sizeof_int" = 4 ; then
+  AC_CHECK_TYPE(int32_t, int)
+  AC_CHECK_TYPE(u_int32_t, unsigned int)
+elif test "$ac_cv_sizeof_short" = 4 ; then
+  AC_CHECK_TYPE(int32_t, short)
+  AC_CHECK_TYPE(u_int32_t, unsigned short)
+elif test "$ac_cv_sizeof_long" = 4 ; then
+  AC_CHECK_TYPE(int32_t, long)
+  AC_CHECK_TYPE(u_int32_t, unsigned long)
+else
+  AC_MSG_ERROR([Cannot find a type with size of 32 bits])
+fi])
+
+dnl
+dnl Macro: unet_FUNC_POLL_SYSCALL
+dnl
+dnl Try to figure out if we have a system call poll (not if it is emulated).
+dnl Manical laughter...
+dnl
+AC_DEFUN(unet_FUNC_POLL_SYSCALL,
+[AC_CHECK_HEADERS(poll.h)dnl
+if test -z "$unet_cv_func_poll_syscall" ; then
+  AC_MSG_CHECKING([if poll is a system call (please wait)])
+else
+  AC_MSG_CHECKING([if poll is a system call])
+fi
+AC_CACHE_VAL(unet_cv_func_poll_syscall,
+[unet_cv_func_poll_syscall=no
+dnl No need to go through the trouble when we don't have poll.h:
+changequote(, )dnl
+if test "$ac_cv_header_poll_h" = yes; then
+  unet_dirs=`find /usr/include/sys -type f -name '*.h' -exec egrep '^#include <[^/]*/.*>' {} \; | sed -e 's/^.*<//' -e 's%/.*$%%' | sort | uniq`
+  for i in $unet_dirs ; do
+    if test "$unet_cv_func_poll_syscall" = no ; then
+      unet_files=`ls /usr/include/$i/*.h 2> /dev/null`
+      if test -n "$unet_files" ; then
+       for j in $unet_files ; do
+         if test "$unet_cv_func_poll_syscall" = no ; then
+           unet_line=`egrep '^#define[[:space:]]+[[:alnum:]_]*[Pp][Oo][Ll][Ll]' $j`
+           if test -n "$unet_line" ; then
+             unet_sig=`echo "$unet_line" | sed -e 's/poll/fork/g' -e 's/POLL/FORK/g' -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g'`
+             unet_set=`for k in "$unet_sig" ; do echo $k; done | sed -e 's% %|%g'`
+             unet_match=`sed -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g' $j | egrep "$unet_set"`
+             if test -n "$unet_match" ; then
+               unet_cv_func_poll_syscall=yes
+             fi
+           fi
+         fi
+       done
+      fi
+    fi
+  done
+fi
+changequote([, ])dnl
+])
+AC_MSG_RESULT([$unet_cv_func_poll_syscall])
+])
diff --git a/config/aclocal.m4 b/config/aclocal.m4
new file mode 100644 (file)
index 0000000..8cd8680
--- /dev/null
@@ -0,0 +1,391 @@
+dnl aclocal.m4 generated automatically by aclocal 1.4a
+
+dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+dnl PARTICULAR PURPOSE.
+
+dnl
+dnl Macro: unet_PIPE_CFLAGS
+dnl
+dnl   If the compiler understands -pipe, add it to CFLAGS if not already
+dnl   there.
+dnl
+AC_DEFUN(unet_PIPE_CFLAGS,
+[AC_MSG_CHECKING([if the compiler understands -pipe])
+unet_cv_pipe_flags="$ac_cv_prog_gcc"
+if test "$ac_cv_prog_gcc" = no; then
+  OLDCFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -pipe"
+  AC_TRY_COMPILE(,,unet_cv_pipe_flags=yes,)
+  CFLAGS="$OLDCFLAGS"
+fi
+AC_MSG_RESULT($unet_cv_pipe_flags)
+if test "$unet_cv_pipe_flags" = yes ; then
+  x=`echo $CFLAGS | grep 'pipe' 2>/dev/null`
+  if test "$x" = "" ; then
+    CFLAGS="$CFLAGS -pipe"
+  fi
+fi
+])
+
+dnl
+dnl Macro: unet_CHECK_LIB_RESOLV
+dnl
+dnl   Check for res_mkquery in -lresolv and add that to LIBS when needed.
+dnl
+AC_DEFUN(unet_CHECK_LIB_RESOLV,
+[AC_CACHE_CHECK([for res_mkquery in -lresolv], unet_cv_lib_resolv,
+[AC_TRY_LINK([struct rrec;
+extern int res_mkquery(int, const char *, int, int, const char *,
+    int, struct rrec *, unsigned char *, int);],
+[int op;
+const char *dname;
+int class, type;
+const char *data;
+int datalen;
+struct rrec *newrr;
+unsigned char *buf;
+int buflen;
+res_mkquery(op,dname,class,type,data,datalen,newrr,buf,buflen)],
+unet_cv_lib_resolv=no, [OLD_LIBS="$LIBS"
+LIBS="$LIBS -lresolv"
+AC_TRY_LINK([extern char *_res;], [*_res=0],
+unet_cv_lib_resolv=yes, unet_cv_lib_resolv=no)
+LIBS="$OLD_LIBS"])])
+if test $unet_cv_lib_resolv = yes; then
+  AC_DEFINE(HAVE_LIB_RESOLV)
+  LIBS="$LIBS -lresolv"
+fi])
+
+dnl
+dnl Macro: unet_NONBLOCKING
+dnl
+dnl   Check whether we have posix, bsd or sysv non-blocking sockets and
+dnl   define respectively NBLOCK_POSIX, NBLOCK_BSD or NBLOCK_SYSV.
+dnl
+AC_DEFUN(unet_NONBLOCKING,
+[dnl Do we have posix, bsd or sysv non-blocking stuff ?
+AC_CACHE_CHECK([for posix non-blocking], unet_cv_sys_nonblocking_posix,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+  char b[12];
+  struct sockaddr x;
+  size_t l = sizeof(x);
+  int f = socket(AF_INET, SOCK_DGRAM, 0);
+  if (f >= 0 && !(fcntl(f, F_SETFL, O_NONBLOCK)))
+  {
+    signal(SIGALRM, alarmed);
+    alarm(2);
+    recvfrom(f, b, 12, 0, &x, &l);
+    alarm(0);
+    exit(0);
+  }
+  exit(1);
+}], unet_cv_sys_nonblocking_posix=yes, unet_cv_sys_nonblocking_posix=no)])
+if test $unet_cv_sys_nonblocking_posix = yes; then
+  AC_DEFINE(NBLOCK_POSIX)
+else
+AC_CACHE_CHECK([for bsd non-blocking], unet_cv_sys_nonblocking_bsd,
+[AC_TRY_RUN([#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+  char b[12];
+  struct sockaddr x;
+  size_t l = sizeof(x);
+  int f = socket(AF_INET, SOCK_DGRAM, 0);
+  if (f >= 0 && !(fcntl(f, F_SETFL, O_NDELAY)))
+  {
+    signal(SIGALRM, alarmed);
+    alarm(2);
+    recvfrom(f, b, 12, 0, &x, &l);
+    alarm(0);
+    exit(0);
+  }
+  exit(1);
+}], unet_cv_sys_nonblocking_bsd=yes, unet_cv_sys_nonblocking_bsd=no)])
+if test $unet_cv_sys_nonblocking_bsd = yes; then
+  AC_DEFINE(NBLOCK_BSD)
+else
+  AC_DEFINE(NBLOCK_SYSV)
+fi
+fi])
+
+dnl
+dnl Macro: unet_SIGNALS
+dnl
+dnl   Check if we have posix signals, reliable bsd signals or
+dnl   unreliable sysv signals and define respectively POSIX_SIGNALS,
+dnl   BSD_RELIABLE_SIGNALS or SYSV_UNRELIABLE_SIGNALS.
+dnl
+AC_DEFUN(unet_SIGNALS,
+[dnl Do we have posix signals, reliable bsd signals or unreliable sysv signals ?
+AC_CACHE_CHECK([for posix signals], unet_cv_sys_signal_posix,
+[AC_TRY_COMPILE([#include <signal.h>],
+[sigaction(SIGTERM, (struct sigaction *)0L, (struct sigaction *)0L)],
+unet_cv_sys_signal_posix=yes, unet_cv_sys_signal_posix=no)])
+if test $unet_cv_sys_signal_posix = yes; then
+  AC_DEFINE(POSIX_SIGNALS)
+else
+AC_CACHE_CHECK([for bsd reliable signals], unet_cv_sys_signal_bsd,
+[AC_TRY_RUN([#include <signal.h>
+int calls = 0;
+$ac_cv_type_signal handler()
+{
+  if (calls) return;
+  calls++;
+  kill(getpid(), SIGTERM);
+  sleep(1);
+}
+int main(void)
+{
+  signal(SIGTERM, handler);
+  kill(getpid(), SIGTERM);
+  exit (0);
+}], unet_cv_sys_signal_bsd=yes, unet_cv_sys_signal_bsd=no)])
+if test $unet_cv_sys_signal_bsd = yes; then
+  AC_DEFINE(BSD_RELIABLE_SIGNALS)
+else
+  AC_DEFINE(SYSV_UNRELIABLE_SIGNALS)
+fi
+fi])
+
+dnl
+dnl Macro: unet_DEFINE_SIZE_T_FMT
+dnl
+dnl Define SIZE_T_FMT to be "%u" or "%lu", whichever seems more appropriate.
+dnl
+AC_DEFUN(unet_DEFINE_SIZE_T_FMT,
+[dnl Make educated guess :/, if size_t is a long or not
+AC_CHECK_SIZEOF(size_t)dnl
+AC_MSG_CHECKING(printf format of size_t)
+if test "$ac_cv_sizeof_size_t" = 4 ; then
+  AC_MSG_RESULT("%u")
+  AC_DEFINE(SIZE_T_FMT, "%u")
+else
+  AC_MSG_RESULT("%lu")
+  AC_DEFINE(SIZE_T_FMT, "%lu")
+fi])
+
+dnl
+dnl Macro: unet_DEFINE_TIME_T_FMT
+dnl
+dnl Try to figure out if time_t is an int or not, and if so define
+dnl TIME_T_FMT to be "%u", otherwise define it to be "%lu".
+dnl Likewise define STIME_T_FMT for the signed format.
+dnl
+AC_DEFUN(unet_DEFINE_TIME_T_FMT,
+[dnl Make educated guess :/, if time_t is a long or not
+AC_MSG_CHECKING(size of time_t)
+AC_CACHE_VAL(unet_cv_sizeof_time_t,
+[AC_TRY_RUN([#include <stdio.h>
+#include <sys/types.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(time_t));
+  exit(0);
+}], unet_cv_sizeof_time_t=`cat conftestval`,
+unet_cv_sizeof_time_t=0, unet_cv_sizeof_time_t=0)])
+if test "$unet_cv_sizeof_time_t" = 0 ; then
+  AC_MSG_RESULT(unknown)
+  AC_DEFINE(TIME_T_FMT, "%lu")
+  AC_DEFINE(STIME_T_FMT, "%ld")
+else
+  AC_MSG_RESULT([$unet_cv_sizeof_time_t])
+  AC_MSG_CHECKING(printf format of time_t)
+  if test "$unet_cv_sizeof_time_t" = "$ac_cv_sizeof_long" ; then
+    AC_MSG_RESULT("%lu")
+    AC_DEFINE(TIME_T_FMT, "%lu")
+    AC_DEFINE(STIME_T_FMT, "%ld")
+  else
+    AC_MSG_RESULT("%u")
+    AC_DEFINE(TIME_T_FMT, "%u")
+    AC_DEFINE(STIME_T_FMT, "%d")
+  fi
+fi])
+
+dnl
+dnl Macro: unet_CHECK_TYPE_SIZES
+dnl
+dnl Check the size of several types and define a valid int16_t and int32_t.
+dnl
+AC_DEFUN(unet_CHECK_TYPE_SIZES,
+[dnl Check type sizes
+AC_CHECK_SIZEOF(short)
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+if test "$ac_cv_sizeof_int" = 2 ; then
+  AC_CHECK_TYPE(int16_t, int)
+  AC_CHECK_TYPE(u_int16_t, unsigned int)
+elif test "$ac_cv_sizeof_short" = 2 ; then
+  AC_CHECK_TYPE(int16_t, short)
+  AC_CHECK_TYPE(u_int16_t, unsigned short)
+else
+  AC_MSG_ERROR([Cannot find a type with size of 16 bits])
+fi
+if test "$ac_cv_sizeof_int" = 4 ; then
+  AC_CHECK_TYPE(int32_t, int)
+  AC_CHECK_TYPE(u_int32_t, unsigned int)
+elif test "$ac_cv_sizeof_short" = 4 ; then
+  AC_CHECK_TYPE(int32_t, short)
+  AC_CHECK_TYPE(u_int32_t, unsigned short)
+elif test "$ac_cv_sizeof_long" = 4 ; then
+  AC_CHECK_TYPE(int32_t, long)
+  AC_CHECK_TYPE(u_int32_t, unsigned long)
+else
+  AC_MSG_ERROR([Cannot find a type with size of 32 bits])
+fi])
+
+dnl
+dnl Macro: unet_FUNC_POLL_SYSCALL
+dnl
+dnl Try to figure out if we have a system call poll (not if it is emulated).
+dnl Manical laughter...
+dnl
+AC_DEFUN(unet_FUNC_POLL_SYSCALL,
+[AC_CHECK_HEADERS(poll.h)dnl
+if test -z "$unet_cv_func_poll_syscall" ; then
+  AC_MSG_CHECKING([if poll is a system call (please wait)])
+else
+  AC_MSG_CHECKING([if poll is a system call])
+fi
+AC_CACHE_VAL(unet_cv_func_poll_syscall,
+[unet_cv_func_poll_syscall=no
+dnl No need to go through the trouble when we don't have poll.h:
+changequote(, )dnl
+if test "$ac_cv_header_poll_h" = yes; then
+  unet_dirs=`find /usr/include/sys -type f -name '*.h' -exec egrep '^#include <[^/]*/.*>' {} \; | sed -e 's/^.*<//' -e 's%/.*$%%' | sort | uniq`
+  for i in $unet_dirs ; do
+    if test "$unet_cv_func_poll_syscall" = no ; then
+      unet_files=`ls /usr/include/$i/*.h 2> /dev/null`
+      if test -n "$unet_files" ; then
+       for j in $unet_files ; do
+         if test "$unet_cv_func_poll_syscall" = no ; then
+           unet_line=`egrep '^#define[[:space:]]+[[:alnum:]_]*[Pp][Oo][Ll][Ll]' $j`
+           if test -n "$unet_line" ; then
+             unet_sig=`echo "$unet_line" | sed -e 's/poll/fork/g' -e 's/POLL/FORK/g' -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g'`
+             unet_set=`for k in "$unet_sig" ; do echo $k; done | sed -e 's% %|%g'`
+             unet_match=`sed -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g' $j | egrep "$unet_set"`
+             if test -n "$unet_match" ; then
+               unet_cv_func_poll_syscall=yes
+             fi
+           fi
+         fi
+       done
+      fi
+    fi
+  done
+fi
+changequote([, ])dnl
+])
+AC_MSG_RESULT([$unet_cv_func_poll_syscall])
+])
+
+
+# serial 1
+
+# @defmac AC_PROG_CC_STDC
+# @maindex PROG_CC_STDC
+# @ovindex CC
+# If the C compiler in not in ANSI C mode by default, try to add an option
+# to output variable @code{CC} to make it so.  This macro tries various
+# options that select ANSI C on some system or another.  It considers the
+# compiler to be in ANSI C mode if it handles function prototypes correctly.
+#
+# If you use this macro, you should check after calling it whether the C
+# compiler has been set to accept ANSI C; if not, the shell variable
+# @code{am_cv_prog_cc_stdc} is set to @samp{no}.  If you wrote your source
+# code in ANSI C, you can make an un-ANSIfied copy of it by using the
+# program @code{ansi2knr}, which comes with Ghostscript.
+# @end defmac
+
+AC_DEFUN(AM_PROG_CC_STDC,
+[AC_REQUIRE([AC_PROG_CC])
+AC_BEFORE([$0], [AC_C_INLINE])
+AC_BEFORE([$0], [AC_C_CONST])
+dnl Force this before AC_PROG_CPP.  Some cpp's, eg on HPUX, require
+dnl a magic option to avoid problems with ANSI preprocessor commands
+dnl like #elif.
+dnl FIXME: can't do this because then AC_AIX won't work due to a
+dnl circular dependency.
+dnl AC_BEFORE([$0], [AC_PROG_CPP])
+AC_MSG_CHECKING(for ${CC-cc} option to accept ANSI C)
+AC_CACHE_VAL(am_cv_prog_cc_stdc,
+[am_cv_prog_cc_stdc=no
+ac_save_CC="$CC"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX                  -qlanglvl=ansi
+# Ultrix and OSF/1     -std1
+# HP-UX                        -Aa -D_HPUX_SOURCE
+# SVR4                 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  AC_TRY_COMPILE(
+[#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+], [
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+],
+[am_cv_prog_cc_stdc="$ac_arg"; break])
+done
+CC="$ac_save_CC"
+])
+if test -z "$am_cv_prog_cc_stdc"; then
+  AC_MSG_RESULT([none needed])
+else
+  AC_MSG_RESULT($am_cv_prog_cc_stdc)
+fi
+case "x$am_cv_prog_cc_stdc" in
+  x|xno) ;;
+  *) CC="$CC $am_cv_prog_cc_stdc" ;;
+esac
+])
+
diff --git a/config/config-sh.in b/config/config-sh.in
new file mode 100644 (file)
index 0000000..dee9749
--- /dev/null
@@ -0,0 +1,382 @@
+# @configure_input@
+#
+# By Carlo Wood (carlo@runaway.xs4all.nl)
+#
+# For a description of the syntax of this configuration file,
+# see the config/Configure script.
+#
+# TO CONFIGURE THE SERVER, TYPE: 'make config' in the top level directory !
+#
+
+#### Start of system configuration section. ####
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+mandir=@mandir@
+CC_DEFAULT="@CC@"
+CFLAGS_DEFAULT="@CFLAGS@"
+CPPFLAGS_DEFAULT="@CPPFLAGS@"
+LDFLAGS_DEFAULT="@LDFLAGS@"
+LIBS="@LIBS@"
+ac_cv_header_poll_h=@ac_cv_header_poll_h@
+unet_cv_func_poll_syscall=@unet_cv_func_poll_syscall@
+ac_cv_header_syslog_h=@ac_cv_header_syslog_h@
+
+#### End of system configuration section. ####
+
+if [ -z "$CONFIG_BATCH" ]; then
+  echo "**************************************************************************"
+  echo "Error: Please run 'make config' that resides in the top level directory!"
+  echo "**************************************************************************"
+  exit -1
+fi
+
+mainmenu_name "Undernet IRC Daemon Configuration"
+mainmenu_option next_comment
+  if [ "$CONFIG_BATCH" = "n" ]; then
+    echo "* Welcome to the Undernet IRC Daemon Configuration script."
+    echo "* You can restart this configuration at any time by typing 'make config'."
+    echo "* If you need help with your decision on any question, type a '?'"
+    echo "*"
+    USE_DEFAULT=n
+  else
+    USE_DEFAULT=C
+  fi
+  CHANGE_CONFIG=n
+  if [ "$DEFAULTS" != "none" ]; then
+    bool 'Do you want to change your previous configuration' CHANGE_CONFIG
+    if [ "$CHANGE_CONFIG" = "n" ]; then
+      echo "Ok, I will only prompt you for NEW parameters."
+      USE_DEFAULT=C
+    else
+      USE_DEFAULT=n
+    fi
+  else
+    USE_DEFAULT=n
+  fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Compile stuff'
+  if [ "$prefix" = "NONE" ]; then
+    prefix=/usr/local
+  fi
+  if [ "$exec_prefix" = "NONE" ]; then
+    eval exec_prefix="$prefix"
+  fi
+  string 'Which compiler do you want to use' CC "$CC_DEFAULT"
+  echo "* For the following four questions, specify 'none' when you want it to be empty."
+  if [ -z "$cflags_O3_remark" -a -n "$CFLAGS" ]; then
+    CFLAGS_DEFAULT=`echo "$CFLAGS" | sed -e 's%-O2%-O3%'`
+    if [ "$CFLAGS_DEFAULT" != "$CFLAGS" ]; then
+      echo "You are highly advised to use -O3 instead of -O2 if you're short in cpu cycles!"
+      echo "Please read documentation (press '?'):"
+      CFLAGS=
+    fi
+  fi
+  eval string "'What flags should I pass to $CC [none]'" CFLAGS "'$CFLAGS_DEFAULT'"
+  define_macro cflags_O3_remark done
+  string 'Do you need extra include directories [none]' EXTRA_INCLUDEDIRS none
+  if [ -z "$LDFLAGS_DEFAULT" ]; then
+    LDFLAGS_DEFAULT=none
+  else
+    eval LDFLAGS_DEFAULT="$LDFLAGS_DEFAULT"
+  fi
+  string 'Which linker flags do you need [none]' LDFLAGS "$LDFLAGS_DEFAULT"
+  if [ -z "$LIBS" ]; then
+    LIBS=none
+  fi
+  string 'Which extra libraries do you need [none]' IRCDLIBS "$LIBS"
+  eval bindir="$bindir"
+  string 'In which directory should I install the ircd binary' BINDIR $bindir
+  if [ ! -d "$BINDIR" ]; then
+    echo "$BINDIR : No such directory"
+  fi
+  string 'What should the name of the installed symbolic link to the exectuable be' SYMLINK ircd
+  string 'Which permissions do you want the binary to have' IRCDMODE 711
+  string 'Which owner do you want the binary to have' IRCDOWN "`id | sed -e 's/.*uid=[0-9]*(//' -e 's/).*//' 2> /dev/null`"
+  string 'Which group do you want the binary to have' IRCDGRP "`id | sed -e 's/.*gid=[0-9]*(//' -e 's/).*//' 2> /dev/null`"
+  eval mandir=$mandir
+  string 'Where should I install the man page' MANDIR $mandir
+  if [ "$CFLAGS" = "none" ]; then
+    CFLAGS=""
+  fi
+  if [ "$EXTRA_INCLUDEDIRS" = "none" ]; then
+    EXTRA_INCLUDEDIRS=""
+  fi
+  if [ "$LDFLAGS" = "none" ]; then
+    LDFLAGS=""
+  fi
+  if [ "$IRCDLIBS" = "none" ]; then
+    IRCDLIBS=""
+  fi
+  EXTRA_CPPFLAGS=""
+  if [ -n "$EXTRA_INCLUDEDIRS" ]; then
+    for i in $EXTRA_INCLUDEDIRS; do
+      if [ -z "$EXTRA_CPPFLAGS" ]; then
+       EXTRA_CPPFLAGS=-I$i
+      else
+       EXTRA_CPPFLAGS="$EXTRA_CPPFLAGS -I$i"
+      fi
+    done
+  fi
+  if [ -z "$EXTRA_CPPFLAGS" ]; then
+    CPPFLAGS=-I../include
+  else
+    CPPFLAGS="-I../include $EXTRA_CPPFLAGS"
+  fi
+  echo "EXTRA_CPPFLAGS=\"$EXTRA_CPPFLAGS\"" >>$CONFIG
+  echo "CPPFLAGS=\"$CPPFLAGS\"" >>$CONFIG
+  bool 'Use inlining for a few crucial functions' FORCEINLINE y
+endmenu
+
+if [ "$ac_cv_header_poll_h" = "yes" ]; then
+  if [ "$unet_cv_func_poll_syscall" = "yes" ]; then
+    define_bool USE_POLL y
+  else
+    mainmenu_option next_comment
+    comment 'Operating System specific defines.'
+      bool 'You have poll(), but do you want to use it' USE_POLL n
+    endmenu
+  fi
+fi
+
+mainmenu_option next_comment
+comment 'Host specific defines'
+  if [ -f /etc/resolv.conf ]; then
+    DOMAINNAME_DEFAULT="`awk '/^domain/ { print $2; exit }' /etc/resolv.conf`"
+  fi
+  string 'What is the domain name of your network' DOMAINNAME $DOMAINNAME_DEFAULT
+  if [ -z "$DOMAINNAME" ]; then
+    DOMAINNAME=none
+  fi
+  string 'Please give a random seed of eight characters' RANDOM_SEED 12345678
+  bool 'Does your host have a reliable clock' RELIABLE_CLOCK
+endmenu
+
+mainmenu_option next_comment
+comment 'General defines'
+  bool 'Change root ('/') after start of daemon' CHROOTDIR
+  bool 'Do you want the daemon set its own uid/gid' CONFIG_SETUGID
+  if [ "$CONFIG_SETUGID" = "y" ]; then
+    int '   UID of irc daemon' IRC_UID
+    int '   GID of irc daemon' IRC_GID
+  else
+    define_int IRC_UID $IRC_UID
+    define_int IRC_GID $IRC_GID
+    bool 'Allow to specify configuration file on command line' CMDLINE_CONFIG
+    if [ "$CMDLINE_CONFIG" = "y" ]; then
+      echo "   SECURITY: Then don't install the daemon SUID or SGID !"
+    fi
+  fi
+  bool 'Set up a Unix domain socket to connect clients/servers' UNIXPORT
+  bool 'Do you need virtual hosting' VIRTUAL_HOST
+  PREV_HUB=$HUB
+  bool 'Will you connect to more then one server at a time' HUB
+  if [ "$PREV_HUB" != "$HUB" ]; then
+    BUFFERPOOL=
+  fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Debugging (do not define this on production servers)'
+  bool 'Do you want to enable debugging output' DEBUGMODE
+  bool 'Do you want memory- allocation and/or leak checking' DEBUGMALLOC
+  if [ "$DEBUGMALLOC" = "y" ]; then
+    bool 'Do you want to have boundary checking' MEMMAGICNUMS
+    bool 'Do you want memory leak testing (stats M)' MEMLEAKSTATS y
+    if [ "$MEMLEAKSTATS" = "y" ]; then
+      if [ "$MEMMAGICNUMS" = "y" ]; then
+       echo "You will have extra info on allocated sizes too (MEMSIZESTATS)"
+       define_bool MEMSIZESTATS $MEMSIZESTATS
+      else
+        bool 'Do you want extra info on allocated sizes' MEMSIZESTATS y
+      fi
+      bool 'Do you want support for a time interval with /stats M' MEMTIMESTATS y
+    fi
+  else
+    define_bool MEMMAGICNUMS $MEMMAGICNUMS
+    define_bool MEMLEAKSTATS $MEMLEAKSTATS
+    define_bool MEMSIZESTATS $MEMSIZESTATS
+    define_bool MEMTIMESTATS $MEMTIMESTATS
+  fi
+  bool 'Are you testing on a host without DNS' NODNS
+endmenu
+
+mainmenu_option next_comment
+comment 'Paths and files'
+  eval DPATH_DEFAULT="${prefix}/lib/ircd"
+  string 'Directory where all ircd stuff resides' DPATH $DPATH_DEFAULT
+  define_string SPATH "$BINDIR/ircd"
+  echo "The following filenames are either full paths or files within DPATH"
+  string 'Server configuration file' CPATH 'ircd.conf'
+  string 'Server MOTD file' MPATH 'ircd.motd'
+  string 'Server remote MOTD file (3 lines max)' RPATH 'remote.motd'
+  if [ "$DEBUGMODE" = "y" ]; then
+    string 'Debug file if DEBUGMODE' LPATH '/tmp/ircd.log'
+  else
+    define_string LPATH "$LPATH"
+  fi
+  string 'File for server pid' PPATH 'ircd.pid'
+endmenu
+
+mainmenu_option next_comment
+comment 'Logging (filenames are either full paths or files within DPATH)'
+  bool 'Do you want to log the use of /WHO x% (recommended)' CONFIG_LOG_WHOX y
+    if [ "$CONFIG_LOG_WHOX" = "y" ]; then
+      string '   Give the path and(or) filename of this log file' WPATH 'whox.log'
+    fi
+  bool 'Do you want to log G-lines to a separate file' CONFIG_LOG_GLINES
+    if [ "$CONFIG_LOG_GLINES" = "y" ]; then
+      string '   Give the path and(or) filename of this log file' GPATH 'gline.log'
+    fi
+  bool 'Do you want to log connecting users to a separate file' CONFIG_LOG_USERS
+    if [ "$CONFIG_LOG_USERS" = "y" ]; then
+      string '   Give the path and(or) filename of this log file' FNAME_USERLOG $DPATH/users
+    fi
+  bool 'Do you want to log Opers to a separate file' CONFIG_LOG_OPERS
+    if [ "$CONFIG_LOG_OPERS" = "y" ]; then
+      string '   Give the path and(or) filename of this log file' FNAME_OPERLOG $DPATH/opers
+    fi
+  if [ "$ac_cv_header_syslog_h" = "yes" ]; then
+    bool 'Do you want to use syslog' USE_SYSLOG
+  else
+    USE_SYSLOG=n
+  fi
+    if [ "$USE_SYSLOG" = "y" ]; then
+      bool '   Log all operator kills to syslog' SYSLOG_KILL
+      bool '   Log all remote squits for all servers to syslog' SYSLOG_SQUIT
+      bool '   Log remote connect messages for other all servs' SYSLOG_CONNECT
+      bool '   Log all users who successfully become an Oper' SYSLOG_OPER
+      bool '   Send userlog stuff to syslog' SYSLOG_USERS
+      if [ "$SYSLOG_KILL" = "n" -a "$SYSLOG_SQUIT" = "n" -a \
+           "$SYSLOG_CONNECT" = "n" -a "$SYSLOG_OPER" = "n" -a \
+          "$SYSLOG_USERS" = "n" ]; then
+       define_macro LOG_FACILITY $LOG_FACILITY
+        define_bool USE_SYSLOG n
+      else
+        choice '   Log facility' \
+               "daemon         CONFIG_DAEMON   \
+               user            CONFIG_USER     \
+               local0-7        CONFIG_LOCAL" daemon
+       if [ "$CONFIG_DAEMON" = "y" ]; then
+         define_macro LOG_FACILITY LOG_DAEMON
+       else
+         if [ "$CONFIG_USER" = "y" ]; then
+           define_macro LOG_FACILITY LOG_USER
+         else
+           int '      Which local facility (0-7)' INT_LOCAL
+           define_macro LOG_FACILITY LOG_LOCAL$INT_LOCAL
+         fi
+       fi
+       echo "    Using log facility $LOG_FACILITY"
+      fi
+    else
+      define_bool SYSLOG_KILL $SYSLOG_KILL
+      define_bool SYSLOG_SQUIT $SYSLOG_SQUIT
+      define_bool SYSLOG_CONNECT $SYSLOG_CONNECT
+      define_bool SYSLOG_OPER $SYSLOG_OPER
+      define_bool SYSLOG_USERS $SYSLOG_USERS
+      define_macro LOG_FACILITY $LOG_FACILITY
+    fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Configuration'
+  bool 'Use crypted passwords for N: lines' CRYPT_LINK_PASSWORD y
+  bool 'Use crypted passwords for operators' CRYPT_OPER_PASSWORD y
+  DUMMY=`echo "$BUFFERPOOL" | sed -e 's/[0-9]//g'`
+  if [ "$DUMMY" != "" ]; then
+    BUFFERPOOL=
+  fi
+  if [ "$HUB" = "y" ]; then
+    int 'Max size of the total of of all sendqs (bytes)' BUFFERPOOL 27000000
+  else
+    int 'Max size of the total of of all sendqs (bytes)' BUFFERPOOL 9000000
+  fi
+  int 'Max receive queue for clients (bytes)' CLIENT_FLOOD 1024
+  int 'Maximum number of network connections (23 - (FD_SETSIZE-4))' MAXCONNECTIONS 252
+  int 'Default client listen port' PORTNUM 6667
+  int 'Nickname history length' NICKNAMEHISTORYLENGTH 800
+  bool 'Allow Opers to see (dis)connects of local clients' ALLOW_SNO_CONNEXIT
+  if [ "$ALLOW_SNO_CONNEXIT" = "y" ]; then
+    bool 'Show IP address in client connection notices' SNO_CONNEXIT_IP
+  fi
+  bool 'Do you want to use R: lines in your configuration file' R_LINES
+  if [ "$R_LINES" = "y" ]; then
+    bool 'Process R: lines every rehash' R_LINES_REHASH y
+    bool 'Process R: lines always' R_LINES_OFTEN
+  else
+    define_bool R_LINES_REHASH $R_LINES_REHASH
+    define_bool R_LINES_OFTEN $R_LINES_OFTEN
+  fi
+  bool 'Do you want support for the old I:*:ONE:*:: construct (read help text!)' USEONE n
+  bool 'Send a short message instead of the MOTD to connecting clients' NODEFAULTMOTD y
+endmenu
+
+mainmenu_option next_comment
+comment 'Oper commands'
+  bool 'Allow (local) Opers to see all local invisible users' SHOW_INVISIBLE_USERS y
+  if [ "$SHOW_INVISIBLE_USERS" = "y" ]; then
+    bool 'Allow Opers to see all invisible users' SHOW_ALL_INVISIBLE_USERS y
+  fi
+  bool 'Allow global Opers (O:) to see inside secret channels' OPERS_SEE_IN_SECRET_CHANNELS y
+  if [ "$OPERS_SEE_IN_SECRET_CHANNELS" = "y" ]; then
+    bool 'Allow local Opers (o:) to see inside secret channels' LOCOP_SEE_IN_SECRET_CHANNELS n
+  fi
+  bool 'Do not truncate obnoxiously long /who output for opers' UNLIMIT_OPER_QUERY
+  bool 'Allow Opers to use the KILL command' OPER_KILL y
+  bool 'Allow Opers to use the REHASH command' OPER_REHASH y
+  bool 'Allow Opers to use the RESTART command' OPER_RESTART y
+  bool 'Allow Opers to use the DIE command' OPER_DIE y
+  bool 'Allow Opers to add local G-lines' OPER_LGLINE y
+  bool 'Allow Opers to connect from a remote site' OPER_REMOTE y
+  bool 'Allow local opers to use the REHASH command' LOCOP_REHASH y
+  bool 'Allow local opers to use the RESTART command' LOCOP_RESTART
+  bool 'Allow local opers to use the DIE command' LOCOP_DIE
+  bool 'Allow local opers to add local G-lines' LOCOP_LGLINE y
+endmenu
+
+mainmenu_option next_comment
+comment 'Server characteristics'
+  bool 'Do you want to have a default LIST parameter' CONFIG_LIST y
+  if [ "$CONFIG_LIST" = "y" ]; then
+    string 'Give default LIST parameter' DEFAULT_LIST 'T<10'
+    define_string DEFAULT_LIST_PARAM "$DEFAULT_LIST"
+  else
+    define_string DEFAULT_LIST "$DEFAULT_LIST"
+    define_bool DEFAULT_LIST_PARAM n
+  fi
+  bool 'K: line comments treated as a file by default' COMMENT_IS_FILE y
+  bool 'Only nullify idle-time on PRIVMSG' IDLE_FROM_MSG y
+endmenu
+
+mainmenu_option next_comment
+comment 'Mandatory defines (you should leave these untouched)'
+  int 'Max auto connects per class (1!)' MAXIMUM_LINKS 1
+  echo '* Never define this on a production server:'
+  bool 'Enable message logging' MSGLOG_ENABLED
+  if [ "$MSGLOG_ENABLED" = "y" ]; then
+    int 'Message log size' MSGLOG_SIZE 128
+  fi
+  if [ "$OPER_KILL" = "y" ]; then
+    bool 'Only allow KILLs of local clients' LOCAL_KILL_ONLY
+  else
+    define_bool LOCAL_KILL_ONLY $LOCAL_KILL_ONLY
+  fi
+  int 'KILL nick chase time limit (30)' KILLCHASETIMELIMIT 30
+  int 'Max number of channels per user (recommended: 5)' MAXCHANNELSPERUSER 10
+  int 'Max number of silence masks (15!)' MAXSILES 15
+  int 'Expected average banmask length (40!)' AVBANLEN 40
+  eval define_macro MAXSILELENGTH \'\($AVBANLEN * MAXSILES\)\'
+  echo '* These are default values, used for class 0:'
+  int 'Max server idle time (60)' TIMESEC 60
+  int 'Class 0 ping frequency (120)' PINGFREQUENCY 120
+  int 'Class 0 connect frequency (600)' CONNECTFREQUENCY 600
+  int 'Min time before a link is good (300)' HANGONGOODLINK 300
+  int 'Wait before reconnecting to good link (10!)' HANGONRETRYDELAY 10
+  int 'connect(2) timeout (90!)' CONNECTTIMEOUT 90
+  int 'Max send queue (40000)' DEFAULTMAXSENDQLENGTH 40000
+endmenu
+
diff --git a/config/configure b/config/configure
new file mode 100644 (file)
index 0000000..62f9400
--- /dev/null
@@ -0,0 +1,3937 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13 
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  case "$ac_option" in
+  -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) ac_optarg= ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case "$ac_option" in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir="$ac_optarg" ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build="$ac_optarg" ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file="$ac_optarg" ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir="$ac_optarg" ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    eval "enable_${ac_feature}=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+    fi
+    ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix="$ac_optarg" ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he)
+    # Omit some internal or obsolete options to make the list less imposing.
+    # This message is too long to be a string in the A/UX 3.1 sh.
+    cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+  --cache-file=FILE       cache test results in FILE
+  --help                  print this message
+  --no-create             do not create output files
+  --quiet, --silent       do not print \`checking...' messages
+  --version               print the version of autoconf that created configure
+Directory and file names:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [same as prefix]
+  --bindir=DIR            user executables in DIR [EPREFIX/bin]
+  --sbindir=DIR           system admin executables in DIR [EPREFIX/sbin]
+  --libexecdir=DIR        program executables in DIR [EPREFIX/libexec]
+  --datadir=DIR           read-only architecture-independent data in DIR
+                          [PREFIX/share]
+  --sysconfdir=DIR        read-only single-machine data in DIR [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data in DIR
+                          [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data in DIR [PREFIX/var]
+  --libdir=DIR            object code libraries in DIR [EPREFIX/lib]
+  --includedir=DIR        C header files in DIR [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc in DIR [/usr/include]
+  --infodir=DIR           info documentation in DIR [PREFIX/info]
+  --mandir=DIR            man documentation in DIR [PREFIX/man]
+  --srcdir=DIR            find the sources in DIR [configure dir or ..]
+  --program-prefix=PREFIX prepend PREFIX to installed program names
+  --program-suffix=SUFFIX append SUFFIX to installed program names
+  --program-transform-name=PROGRAM
+                          run sed PROGRAM on installed program names
+EOF
+    cat << EOF
+Host type:
+  --build=BUILD           configure for building on BUILD [BUILD=HOST]
+  --host=HOST             configure for HOST [guessed]
+  --target=TARGET         configure for TARGET [TARGET=HOST]
+Features and packages:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --x-includes=DIR        X include files are in DIR
+  --x-libraries=DIR       X library files are in DIR
+EOF
+    if test -n "$ac_help"; then
+      echo "--enable and --with options recognized:$ac_help"
+    fi
+    exit 0 ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host="$ac_optarg" ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir="$ac_optarg" ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir="$ac_optarg" ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir="$ac_optarg" ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir="$ac_optarg" ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir="$ac_optarg" ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir="$ac_optarg" ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir="$ac_optarg" ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix="$ac_optarg" ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix="$ac_optarg" ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix="$ac_optarg" ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name="$ac_optarg" ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir="$ac_optarg" ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir="$ac_optarg" ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site="$ac_optarg" ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir="$ac_optarg" ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir="$ac_optarg" ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target="$ac_optarg" ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers)
+    echo "configure generated by autoconf version 2.13"
+    exit 0 ;;
+
+  -with-* | --with-*)
+    ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case "$ac_option" in
+      *=*) ;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_${ac_package}='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+    # Reject names that are not valid shell variable names.
+    if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+      { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+    fi
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    eval "with_${ac_package}=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes="$ac_optarg" ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries="$ac_optarg" ;;
+
+  -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+    ;;
+
+  *)
+    if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+      echo "configure: warning: $ac_option: invalid host type" 1>&2
+    fi
+    if test "x$nonopt" != xNONE; then
+      { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+    fi
+    nonopt="$ac_option"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+  exec 6>/dev/null
+else
+  exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+  case "$ac_arg" in
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c) ;;
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+  *" "*|*"     "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+  ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+  *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+  esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set.  These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}"   = set; then LANG=C;   export LANG;   fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=config-sh.in
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_prog=$0
+  ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+  test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+  else
+    { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+  fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    echo "loading site script $ac_site_file"
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  echo "loading cache $cache_file"
+  . $cache_file
+else
+  echo "creating cache $cache_file"
+  > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+  # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+  if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+    ac_n= ac_c='
+' ac_t='       '
+  else
+    ac_n=-n ac_c= ac_t=
+  fi
+else
+  ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+ac_aux_dir=
+for ac_dir in . $srcdir/.; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { echo "configure: error: can not find install-sh or install.sh in . $srcdir/." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+
+
+
+
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:553: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="gcc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:583: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_prog_rejected=no
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+        ac_prog_rejected=yes
+       continue
+      fi
+      ac_cv_prog_CC="cc"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# -gt 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    set dummy "$ac_dir/$ac_word" "$@"
+    shift
+    ac_cv_prog_CC="$@"
+  fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+  if test -z "$CC"; then
+    case "`uname -s`" in
+    *win32* | *WIN32*)
+      # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:634: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_CC="cl"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+  echo "$ac_t""$CC" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+ ;;
+    esac
+  fi
+  test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:666: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 677 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:682: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  ac_cv_prog_cc_works=yes
+  # If we can't run a trivial program, we are probably using a cross compiler.
+  if (./conftest; exit) 2>/dev/null; then
+    ac_cv_prog_cc_cross=no
+  else
+    ac_cv_prog_cc_cross=yes
+  fi
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+  { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:708: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:713: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.c <<EOF
+#ifdef __GNUC__
+  yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:722: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+  ac_cv_prog_gcc=yes
+else
+  ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:741: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+  ac_cv_prog_cc_g=yes
+else
+  ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:774: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    # This must be in double quotes, not single quotes, because CPP may get
+  # substituted into the Makefile and "${CC-cc}" will confuse make.
+  CPP="${CC-cc} -E"
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp.
+  cat > conftest.$ac_ext <<EOF
+#line 789 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:795: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -E -traditional-cpp"
+  cat > conftest.$ac_ext <<EOF
+#line 806 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:812: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP="${CC-cc} -nologo -E"
+  cat > conftest.$ac_ext <<EOF
+#line 823 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:829: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  :
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+  ac_cv_prog_CPP="$CPP"
+fi
+  CPP="$ac_cv_prog_CPP"
+else
+  ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+echo $ac_n "checking for AIX""... $ac_c" 1>&6
+echo "configure:854: checking for AIX" >&5
+cat > conftest.$ac_ext <<EOF
+#line 856 "configure"
+#include "confdefs.h"
+#ifdef _AIX
+  yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "yes" >/dev/null 2>&1; then
+  rm -rf conftest*
+  echo "$ac_t""yes" 1>&6; cat >> confdefs.h <<\EOF
+#define _ALL_SOURCE 1
+EOF
+
+else
+  rm -rf conftest*
+  echo "$ac_t""no" 1>&6
+fi
+rm -f conftest*
+
+
+echo $ac_n "checking for POSIXized ISC""... $ac_c" 1>&6
+echo "configure:878: checking for POSIXized ISC" >&5
+if test -d /etc/conf/kconfig.d &&
+  grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1
+then
+  echo "$ac_t""yes" 1>&6
+  ISC=yes # If later tests want to check for ISC.
+  cat >> confdefs.h <<\EOF
+#define _POSIX_SOURCE 1
+EOF
+
+  if test "$GCC" = yes; then
+    CC="$CC -posix"
+  else
+    CC="$CC -Xp"
+  fi
+else
+  echo "$ac_t""no" 1>&6
+  ISC=
+fi
+
+ac_safe=`echo "minix/config.h" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for minix/config.h""... $ac_c" 1>&6
+echo "configure:900: checking for minix/config.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 905 "configure"
+#include "confdefs.h"
+#include <minix/config.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:910: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  MINIX=yes
+else
+  echo "$ac_t""no" 1>&6
+MINIX=
+fi
+
+if test "$MINIX" = yes; then
+  cat >> confdefs.h <<\EOF
+#define _POSIX_SOURCE 1
+EOF
+
+  cat >> confdefs.h <<\EOF
+#define _POSIX_1_SOURCE 2
+EOF
+
+  cat >> confdefs.h <<\EOF
+#define _MINIX 1
+EOF
+
+fi
+
+
+
+
+echo $ac_n "checking for ${CC-cc} option to accept ANSI C""... $ac_c" 1>&6
+echo "configure:951: checking for ${CC-cc} option to accept ANSI C" >&5
+if eval "test \"`echo '$''{'am_cv_prog_cc_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  am_cv_prog_cc_stdc=no
+ac_save_CC="$CC"
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX                  -qlanglvl=ansi
+# Ultrix and OSF/1     -std1
+# HP-UX                        -Aa -D_HPUX_SOURCE
+# SVR4                 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  cat > conftest.$ac_ext <<EOF
+#line 967 "configure"
+#include "confdefs.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+
+int main() {
+
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+
+; return 0; }
+EOF
+if { (eval echo configure:1004: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  am_cv_prog_cc_stdc="$ac_arg"; break
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+done
+CC="$ac_save_CC"
+
+fi
+
+if test -z "$am_cv_prog_cc_stdc"; then
+  echo "$ac_t""none needed" 1>&6
+else
+  echo "$ac_t""$am_cv_prog_cc_stdc" 1>&6
+fi
+case "x$am_cv_prog_cc_stdc" in
+  x|xno) ;;
+  *) CC="$CC $am_cv_prog_cc_stdc" ;;
+esac
+
+if test "$CFLAGS" != "" ; then
+  CFLAGS=`echo "$CFLAGS" | sed -e 's/-O2/-O3/'`
+fi
+if test "$CFLAGS" != "" ; then
+  CFLAGS=`echo "$CFLAGS" | sed -e 's/-pipe//g'`
+fi
+
+echo $ac_n "checking for crypt in -lc""... $ac_c" 1>&6
+echo "configure:1035: checking for crypt in -lc" >&5
+ac_lib_var=`echo c'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lc  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1043 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1054: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  true
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for crypt in -ldescrypt""... $ac_c" 1>&6
+echo "configure:1073: checking for crypt in -ldescrypt" >&5
+ac_lib_var=`echo descrypt'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldescrypt  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1081 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1092: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="-ldescrypt $LIBS"
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for crypt in -lcrypt""... $ac_c" 1>&6
+echo "configure:1111: checking for crypt in -lcrypt" >&5
+ac_lib_var=`echo crypt'_'crypt | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lcrypt  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1119 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char crypt();
+
+int main() {
+crypt()
+; return 0; }
+EOF
+if { (eval echo configure:1130: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_lib=HAVE_LIB`echo crypt | sed -e 's/^a-zA-Z0-9_/_/g' \
+    -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+  LIBS="-lcrypt $LIBS"
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+fi
+
+echo $ac_n "checking for gethostbyname in -lc""... $ac_c" 1>&6
+echo "configure:1162: checking for gethostbyname in -lc" >&5
+ac_lib_var=`echo c'_'gethostbyname | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lc  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1170 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char gethostbyname();
+
+int main() {
+gethostbyname()
+; return 0; }
+EOF
+if { (eval echo configure:1181: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  true
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for gethostbyaddr in -lnsl""... $ac_c" 1>&6
+echo "configure:1200: checking for gethostbyaddr in -lnsl" >&5
+ac_lib_var=`echo nsl'_'gethostbyaddr | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lnsl  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1208 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char gethostbyaddr();
+
+int main() {
+gethostbyaddr()
+; return 0; }
+EOF
+if { (eval echo configure:1219: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="-lnsl $LIBS"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for socket in -lc""... $ac_c" 1>&6
+echo "configure:1242: checking for socket in -lc" >&5
+ac_lib_var=`echo c'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lc  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1250 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:1261: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  true
+else
+  echo "$ac_t""no" 1>&6
+echo $ac_n "checking for socket in -lsocket""... $ac_c" 1>&6
+echo "configure:1280: checking for socket in -lsocket" >&5
+ac_lib_var=`echo socket'_'socket | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-lsocket  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1288 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char socket();
+
+int main() {
+socket()
+; return 0; }
+EOF
+if { (eval echo configure:1299: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  LIBS="-lsocket $LIBS"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+echo $ac_n "checking for res_mkquery in -lresolv""... $ac_c" 1>&6
+echo "configure:1322: checking for res_mkquery in -lresolv" >&5
+if eval "test \"`echo '$''{'unet_cv_lib_resolv'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1327 "configure"
+#include "confdefs.h"
+struct rrec;
+extern int res_mkquery(int, const char *, int, int, const char *,
+    int, struct rrec *, unsigned char *, int);
+int main() {
+int op;
+const char *dname;
+int class, type;
+const char *data;
+int datalen;
+struct rrec *newrr;
+unsigned char *buf;
+int buflen;
+res_mkquery(op,dname,class,type,data,datalen,newrr,buf,buflen)
+; return 0; }
+EOF
+if { (eval echo configure:1344: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  unet_cv_lib_resolv=no
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  OLD_LIBS="$LIBS"
+LIBS="$LIBS -lresolv"
+cat > conftest.$ac_ext <<EOF
+#line 1354 "configure"
+#include "confdefs.h"
+extern char *_res;
+int main() {
+*_res=0
+; return 0; }
+EOF
+if { (eval echo configure:1361: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  unet_cv_lib_resolv=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  unet_cv_lib_resolv=no
+fi
+rm -f conftest*
+LIBS="$OLD_LIBS"
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$unet_cv_lib_resolv" 1>&6
+if test $unet_cv_lib_resolv = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_LIB_RESOLV 1
+EOF
+
+  LIBS="$LIBS -lresolv"
+fi
+
+echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
+echo "configure:1386: checking for ANSI C header files" >&5
+if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1391 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1399: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  ac_cv_header_stdc=yes
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1416 "configure"
+#include "confdefs.h"
+#include <string.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "memchr" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat > conftest.$ac_ext <<EOF
+#line 1434 "configure"
+#include "confdefs.h"
+#include <stdlib.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "free" >/dev/null 2>&1; then
+  :
+else
+  rm -rf conftest*
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then
+  :
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1455 "configure"
+#include "confdefs.h"
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+if { (eval echo configure:1466: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  :
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_header_stdc=no
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_header_stdc" 1>&6
+if test $ac_cv_header_stdc = yes; then
+  cat >> confdefs.h <<\EOF
+#define STDC_HEADERS 1
+EOF
+
+fi
+
+echo $ac_n "checking for sys/wait.h that is POSIX.1 compatible""... $ac_c" 1>&6
+echo "configure:1490: checking for sys/wait.h that is POSIX.1 compatible" >&5
+if eval "test \"`echo '$''{'ac_cv_header_sys_wait_h'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1495 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+int main() {
+int s;
+wait (&s);
+s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+; return 0; }
+EOF
+if { (eval echo configure:1511: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_header_sys_wait_h=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_sys_wait_h=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_sys_wait_h" 1>&6
+if test $ac_cv_header_sys_wait_h = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_SYS_WAIT_H 1
+EOF
+
+fi
+
+for ac_hdr in malloc.h sys/malloc.h fcntl.h string.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h memory.h errno.h net/errno.h sys/cdefs.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1535: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1540 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1545: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+echo $ac_n "checking for working const""... $ac_c" 1>&6
+echo "configure:1573: checking for working const" >&5
+if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1578 "configure"
+#include "confdefs.h"
+
+int main() {
+
+/* Ultrix mips cc rejects this.  */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this.  */
+char const *const *ccp;
+char **p;
+/* NEC SVR4.0.2 mips cc rejects this.  */
+struct point {int x, y;};
+static struct point const zero = {0,0};
+/* AIX XL C 1.02.0.0 rejects this.
+   It does not let you subtract one const X* pointer from another in an arm
+   of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+ccp = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this.  */
+  char *t;
+  char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+  *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+  int x[] = {25, 17};
+  const int *foo = &x[0];
+  ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+  typedef const int *iptr;
+  iptr p = 0;
+  ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+     "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+  struct s { int j; const int *ap[3]; };
+  struct s *b; b->j = 5;
+}
+{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+  const int foo = 10;
+}
+
+; return 0; }
+EOF
+if { (eval echo configure:1627: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_const=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_const=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_c_const" 1>&6
+if test $ac_cv_c_const = no; then
+  cat >> confdefs.h <<\EOF
+#define const 
+EOF
+
+fi
+
+echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
+echo "configure:1648: checking whether byte ordering is bigendian" >&5
+if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_cv_c_bigendian=unknown
+# See if sys/param.h defines the BYTE_ORDER macro.
+cat > conftest.$ac_ext <<EOF
+#line 1655 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1666: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat > conftest.$ac_ext <<EOF
+#line 1670 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/param.h>
+int main() {
+
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+; return 0; }
+EOF
+if { (eval echo configure:1681: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_c_bigendian=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_c_bigendian=no
+fi
+rm -f conftest*
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+if test $ac_cv_c_bigendian = unknown; then
+if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1701 "configure"
+#include "confdefs.h"
+main () {
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+if { (eval echo configure:1714: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_c_bigendian=no
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_c_bigendian=yes
+fi
+rm -fr conftest*
+fi
+
+fi
+fi
+
+echo "$ac_t""$ac_cv_c_bigendian" 1>&6
+if test $ac_cv_c_bigendian = yes; then
+  cat >> confdefs.h <<\EOF
+#define WORDS_BIGENDIAN 1
+EOF
+
+fi
+
+echo $ac_n "checking for size_t""... $ac_c" 1>&6
+echo "configure:1738: checking for size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1743 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_size_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_size_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_size_t" 1>&6
+if test $ac_cv_type_size_t = no; then
+  cat >> confdefs.h <<\EOF
+#define size_t unsigned
+EOF
+
+fi
+
+echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
+echo "configure:1771: checking whether time.h and sys/time.h may both be included" >&5
+if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1776 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+int main() {
+struct tm *tp;
+; return 0; }
+EOF
+if { (eval echo configure:1785: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_header_time=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_header_time=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_time" 1>&6
+if test $ac_cv_header_time = yes; then
+  cat >> confdefs.h <<\EOF
+#define TIME_WITH_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking whether struct tm is in sys/time.h or time.h""... $ac_c" 1>&6
+echo "configure:1806: checking whether struct tm is in sys/time.h or time.h" >&5
+if eval "test \"`echo '$''{'ac_cv_struct_tm'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1811 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <time.h>
+int main() {
+struct tm *tp; tp->tm_sec;
+; return 0; }
+EOF
+if { (eval echo configure:1819: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_struct_tm=time.h
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_struct_tm=sys/time.h
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_struct_tm" 1>&6
+if test $ac_cv_struct_tm = sys/time.h; then
+  cat >> confdefs.h <<\EOF
+#define TM_IN_SYS_TIME 1
+EOF
+
+fi
+
+echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6
+echo "configure:1840: checking for uid_t in sys/types.h" >&5
+if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1845 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "uid_t" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_uid_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_type_uid_t" 1>&6
+if test $ac_cv_type_uid_t = no; then
+  cat >> confdefs.h <<\EOF
+#define uid_t int
+EOF
+
+  cat >> confdefs.h <<\EOF
+#define gid_t int
+EOF
+
+fi
+
+echo $ac_n "checking size of short""... $ac_c" 1>&6
+echo "configure:1874: checking size of short" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_short'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1882 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(short));
+  exit(0);
+}
+EOF
+if { (eval echo configure:1893: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_sizeof_short=`cat conftestval`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_sizeof_short=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_short" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_SHORT $ac_cv_sizeof_short
+EOF
+
+
+echo $ac_n "checking size of int""... $ac_c" 1>&6
+echo "configure:1913: checking size of int" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_int'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1921 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(int));
+  exit(0);
+}
+EOF
+if { (eval echo configure:1932: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_sizeof_int=`cat conftestval`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_sizeof_int=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_int" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+EOF
+
+
+echo $ac_n "checking size of long""... $ac_c" 1>&6
+echo "configure:1952: checking size of long" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_long'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1960 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(long));
+  exit(0);
+}
+EOF
+if { (eval echo configure:1971: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_sizeof_long=`cat conftestval`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_sizeof_long=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_long" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+EOF
+
+
+if test "$ac_cv_sizeof_int" = 2 ; then
+  echo $ac_n "checking for int16_t""... $ac_c" 1>&6
+echo "configure:1992: checking for int16_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int16_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1997 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_int16_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_int16_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int16_t" 1>&6
+if test $ac_cv_type_int16_t = no; then
+  cat >> confdefs.h <<\EOF
+#define int16_t int
+EOF
+
+fi
+
+  echo $ac_n "checking for u_int16_t""... $ac_c" 1>&6
+echo "configure:2025: checking for u_int16_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int16_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2030 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])u_int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_u_int16_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_u_int16_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int16_t" 1>&6
+if test $ac_cv_type_u_int16_t = no; then
+  cat >> confdefs.h <<\EOF
+#define u_int16_t unsigned int
+EOF
+
+fi
+
+elif test "$ac_cv_sizeof_short" = 2 ; then
+  echo $ac_n "checking for int16_t""... $ac_c" 1>&6
+echo "configure:2059: checking for int16_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int16_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2064 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_int16_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_int16_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int16_t" 1>&6
+if test $ac_cv_type_int16_t = no; then
+  cat >> confdefs.h <<\EOF
+#define int16_t short
+EOF
+
+fi
+
+  echo $ac_n "checking for u_int16_t""... $ac_c" 1>&6
+echo "configure:2092: checking for u_int16_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int16_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2097 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])u_int16_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_u_int16_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_u_int16_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int16_t" 1>&6
+if test $ac_cv_type_u_int16_t = no; then
+  cat >> confdefs.h <<\EOF
+#define u_int16_t unsigned short
+EOF
+
+fi
+
+else
+  { echo "configure: error: Cannot find a type with size of 16 bits" 1>&2; exit 1; }
+fi
+if test "$ac_cv_sizeof_int" = 4 ; then
+  echo $ac_n "checking for int32_t""... $ac_c" 1>&6
+echo "configure:2129: checking for int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2134 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_int32_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int32_t" 1>&6
+if test $ac_cv_type_int32_t = no; then
+  cat >> confdefs.h <<\EOF
+#define int32_t int
+EOF
+
+fi
+
+  echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
+echo "configure:2162: checking for u_int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2167 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])u_int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_u_int32_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_u_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int32_t" 1>&6
+if test $ac_cv_type_u_int32_t = no; then
+  cat >> confdefs.h <<\EOF
+#define u_int32_t unsigned int
+EOF
+
+fi
+
+elif test "$ac_cv_sizeof_short" = 4 ; then
+  echo $ac_n "checking for int32_t""... $ac_c" 1>&6
+echo "configure:2196: checking for int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2201 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_int32_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int32_t" 1>&6
+if test $ac_cv_type_int32_t = no; then
+  cat >> confdefs.h <<\EOF
+#define int32_t short
+EOF
+
+fi
+
+  echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
+echo "configure:2229: checking for u_int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2234 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])u_int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_u_int32_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_u_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int32_t" 1>&6
+if test $ac_cv_type_u_int32_t = no; then
+  cat >> confdefs.h <<\EOF
+#define u_int32_t unsigned short
+EOF
+
+fi
+
+elif test "$ac_cv_sizeof_long" = 4 ; then
+  echo $ac_n "checking for int32_t""... $ac_c" 1>&6
+echo "configure:2263: checking for int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_int32_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2268 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_int32_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_int32_t" 1>&6
+if test $ac_cv_type_int32_t = no; then
+  cat >> confdefs.h <<\EOF
+#define int32_t long
+EOF
+
+fi
+
+  echo $ac_n "checking for u_int32_t""... $ac_c" 1>&6
+echo "configure:2296: checking for u_int32_t" >&5
+if eval "test \"`echo '$''{'ac_cv_type_u_int32_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2301 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#if STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#endif
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "(^|[^a-zA-Z_0-9])u_int32_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_type_u_int32_t=yes
+else
+  rm -rf conftest*
+  ac_cv_type_u_int32_t=no
+fi
+rm -f conftest*
+
+fi
+echo "$ac_t""$ac_cv_type_u_int32_t" 1>&6
+if test $ac_cv_type_u_int32_t = no; then
+  cat >> confdefs.h <<\EOF
+#define u_int32_t unsigned long
+EOF
+
+fi
+
+else
+  { echo "configure: error: Cannot find a type with size of 32 bits" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking size of size_t""... $ac_c" 1>&6
+echo "configure:2333: checking size of size_t" >&5
+if eval "test \"`echo '$''{'ac_cv_sizeof_size_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2341 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(size_t));
+  exit(0);
+}
+EOF
+if { (eval echo configure:2352: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_sizeof_size_t=`cat conftestval`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_sizeof_size_t=0
+fi
+rm -fr conftest*
+fi
+
+fi
+echo "$ac_t""$ac_cv_sizeof_size_t" 1>&6
+cat >> confdefs.h <<EOF
+#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t
+EOF
+
+echo $ac_n "checking printf format of size_t""... $ac_c" 1>&6
+echo "configure:2371: checking printf format of size_t" >&5
+if test "$ac_cv_sizeof_size_t" = 4 ; then
+  echo "$ac_t"""%u"" 1>&6
+  cat >> confdefs.h <<\EOF
+#define SIZE_T_FMT "%u"
+EOF
+
+else
+  echo "$ac_t"""%lu"" 1>&6
+  cat >> confdefs.h <<\EOF
+#define SIZE_T_FMT "%lu"
+EOF
+
+fi
+echo $ac_n "checking size of time_t""... $ac_c" 1>&6
+echo "configure:2386: checking size of time_t" >&5
+if eval "test \"`echo '$''{'unet_cv_sizeof_time_t'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  unet_cv_sizeof_time_t=0
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2394 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+#include <sys/types.h>
+main()
+{
+  FILE *f=fopen("conftestval", "w");
+  if (!f) exit(1);
+  fprintf(f, "%d\n", sizeof(time_t));
+  exit(0);
+}
+EOF
+if { (eval echo configure:2406: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  unet_cv_sizeof_time_t=`cat conftestval`
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  unet_cv_sizeof_time_t=0
+fi
+rm -fr conftest*
+fi
+
+fi
+
+if test "$unet_cv_sizeof_time_t" = 0 ; then
+  echo "$ac_t""unknown" 1>&6
+  cat >> confdefs.h <<\EOF
+#define TIME_T_FMT "%lu"
+EOF
+
+  cat >> confdefs.h <<\EOF
+#define STIME_T_FMT "%ld"
+EOF
+
+else
+  echo "$ac_t""$unet_cv_sizeof_time_t" 1>&6
+  echo $ac_n "checking printf format of time_t""... $ac_c" 1>&6
+echo "configure:2433: checking printf format of time_t" >&5
+  if test "$unet_cv_sizeof_time_t" = "$ac_cv_sizeof_long" ; then
+    echo "$ac_t"""%lu"" 1>&6
+    cat >> confdefs.h <<\EOF
+#define TIME_T_FMT "%lu"
+EOF
+
+    cat >> confdefs.h <<\EOF
+#define STIME_T_FMT "%ld"
+EOF
+
+  else
+    echo "$ac_t"""%u"" 1>&6
+    cat >> confdefs.h <<\EOF
+#define TIME_T_FMT "%u"
+EOF
+
+    cat >> confdefs.h <<\EOF
+#define STIME_T_FMT "%d"
+EOF
+
+  fi
+fi
+
+if test $ac_cv_prog_gcc = yes; then
+    echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6
+echo "configure:2459: checking whether ${CC-cc} needs -traditional" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    ac_pattern="Autoconf.*'x'"
+  cat > conftest.$ac_ext <<EOF
+#line 2465 "configure"
+#include "confdefs.h"
+#include <sgtty.h>
+Autoconf TIOCGETP
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "$ac_pattern" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_prog_gcc_traditional=yes
+else
+  rm -rf conftest*
+  ac_cv_prog_gcc_traditional=no
+fi
+rm -f conftest*
+
+
+  if test $ac_cv_prog_gcc_traditional = no; then
+    cat > conftest.$ac_ext <<EOF
+#line 2483 "configure"
+#include "confdefs.h"
+#include <termio.h>
+Autoconf TCGETA
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  egrep "$ac_pattern" >/dev/null 2>&1; then
+  rm -rf conftest*
+  ac_cv_prog_gcc_traditional=yes
+fi
+rm -f conftest*
+
+  fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6
+  if test $ac_cv_prog_gcc_traditional = yes; then
+    CC="$CC -traditional"
+  fi
+fi
+
+echo $ac_n "checking for 8-bit clean memcmp""... $ac_c" 1>&6
+echo "configure:2505: checking for 8-bit clean memcmp" >&5
+if eval "test \"`echo '$''{'ac_cv_func_memcmp_clean'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+  ac_cv_func_memcmp_clean=no
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2513 "configure"
+#include "confdefs.h"
+
+main()
+{
+  char c0 = 0x40, c1 = 0x80, c2 = 0x81;
+  exit(memcmp(&c0, &c2, 1) < 0 && memcmp(&c1, &c2, 1) < 0 ? 0 : 1);
+}
+
+EOF
+if { (eval echo configure:2523: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_func_memcmp_clean=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_func_memcmp_clean=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_func_memcmp_clean" 1>&6
+test $ac_cv_func_memcmp_clean = no && LIBOBJS="$LIBOBJS memcmp.${ac_objext}"
+
+echo $ac_n "checking whether setvbuf arguments are reversed""... $ac_c" 1>&6
+echo "configure:2541: checking whether setvbuf arguments are reversed" >&5
+if eval "test \"`echo '$''{'ac_cv_func_setvbuf_reversed'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2549 "configure"
+#include "confdefs.h"
+#include <stdio.h>
+/* If setvbuf has the reversed format, exit 0. */
+main () {
+  /* This call has the arguments reversed.
+     A reversed system may check and see that the address of main
+     is not _IOLBF, _IONBF, or _IOFBF, and return nonzero.  */
+  if (setvbuf(stdout, _IOLBF, (char *) main, BUFSIZ) != 0)
+    exit(1);
+  putc('\r', stdout);
+  exit(0);                     /* Non-reversed systems segv here.  */
+}
+EOF
+if { (eval echo configure:2563: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_func_setvbuf_reversed=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_func_setvbuf_reversed=no
+fi
+rm -fr conftest*
+fi
+
+rm -f core core.* *.core
+fi
+
+echo "$ac_t""$ac_cv_func_setvbuf_reversed" 1>&6
+if test $ac_cv_func_setvbuf_reversed = yes; then
+  cat >> confdefs.h <<\EOF
+#define SETVBUF_REVERSED 1
+EOF
+
+fi
+
+echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
+echo "configure:2587: checking return type of signal handlers" >&5
+if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2592 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+#ifdef __cplusplus
+extern "C" void (*signal (int, void (*)(int)))(int);
+#else
+void (*signal ()) ();
+#endif
+
+int main() {
+int i;
+; return 0; }
+EOF
+if { (eval echo configure:2609: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  ac_cv_type_signal=void
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  ac_cv_type_signal=int
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_type_signal" 1>&6
+cat >> confdefs.h <<EOF
+#define RETSIGTYPE $ac_cv_type_signal
+EOF
+
+
+echo $ac_n "checking for vprintf""... $ac_c" 1>&6
+echo "configure:2628: checking for vprintf" >&5
+if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2633 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char vprintf(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char vprintf();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_vprintf) || defined (__stub___vprintf)
+choke me
+#else
+vprintf();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2656: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_vprintf=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_vprintf=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_VPRINTF 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+if test "$ac_cv_func_vprintf" != yes; then
+echo $ac_n "checking for _doprnt""... $ac_c" 1>&6
+echo "configure:2680: checking for _doprnt" >&5
+if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2685 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char _doprnt(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char _doprnt();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub__doprnt) || defined (__stub____doprnt)
+choke me
+#else
+_doprnt();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2708: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func__doprnt=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func__doprnt=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_DOPRNT 1
+EOF
+
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+fi
+
+for ac_func in strchr memcpy memmove
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2735: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2740 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2763: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in gethostname gettimeofday mkdir strerror strtoken
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2790: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2795 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2818: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in select socket uname
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2845: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2850 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2873: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in setrlimit inet_netof getrusage times res_init
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:2900: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2905 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func(); below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:2928: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+
+for ac_hdr in poll.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:2957: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 2962 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:2967: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+if test -z "$unet_cv_func_poll_syscall" ; then
+  echo $ac_n "checking if poll is a system call (please wait)""... $ac_c" 1>&6
+echo "configure:2994: checking if poll is a system call (please wait)" >&5
+else
+  echo $ac_n "checking if poll is a system call""... $ac_c" 1>&6
+echo "configure:2997: checking if poll is a system call" >&5
+fi
+if eval "test \"`echo '$''{'unet_cv_func_poll_syscall'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  unet_cv_func_poll_syscall=no
+if test "$ac_cv_header_poll_h" = yes; then
+  unet_dirs=`find /usr/include/sys -type f -name '*.h' -exec egrep '^#include <[^/]*/.*>' {} \; | sed -e 's/^.*<//' -e 's%/.*$%%' | sort | uniq`
+  for i in $unet_dirs ; do
+    if test "$unet_cv_func_poll_syscall" = no ; then
+      unet_files=`ls /usr/include/$i/*.h 2> /dev/null`
+      if test -n "$unet_files" ; then
+       for j in $unet_files ; do
+         if test "$unet_cv_func_poll_syscall" = no ; then
+           unet_line=`egrep '^#define[[:space:]]+[[:alnum:]_]*[Pp][Oo][Ll][Ll]' $j`
+           if test -n "$unet_line" ; then
+             unet_sig=`echo "$unet_line" | sed -e 's/poll/fork/g' -e 's/POLL/FORK/g' -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g'`
+             unet_set=`for k in "$unet_sig" ; do echo $k; done | sed -e 's% %|%g'`
+             unet_match=`sed -e 's/[[:space:]]//g' -e 's%/\*.*\*/%%g' -e 's/[0-9]//g' $j | egrep "$unet_set"`
+             if test -n "$unet_match" ; then
+               unet_cv_func_poll_syscall=yes
+             fi
+           fi
+         fi
+       done
+      fi
+    fi
+  done
+fi
+
+fi
+
+echo "$ac_t""$unet_cv_func_poll_syscall" 1>&6
+
+
+echo $ac_n "checking for restartable system calls""... $ac_c" 1>&6
+echo "configure:3033: checking for restartable system calls" >&5
+if eval "test \"`echo '$''{'ac_cv_sys_restartable_syscalls'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3041 "configure"
+#include "confdefs.h"
+/* Exit 0 (true) if wait returns something other than -1,
+   i.e. the pid of the child, which means that wait was restarted
+   after getting the signal.  */
+#include <sys/types.h>
+#include <signal.h>
+ucatch (isig) { }
+main () {
+  int i = fork (), status;
+  if (i == 0) { sleep (3); kill (getppid (), SIGINT); sleep (3); exit (0); }
+  signal (SIGINT, ucatch);
+  status = wait(&i);
+  if (status == -1) wait(&i);
+  exit (status == -1);
+}
+
+EOF
+if { (eval echo configure:3059: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  ac_cv_sys_restartable_syscalls=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  ac_cv_sys_restartable_syscalls=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_sys_restartable_syscalls" 1>&6
+if test $ac_cv_sys_restartable_syscalls = yes; then
+  cat >> confdefs.h <<\EOF
+#define HAVE_RESTARTABLE_SYSCALLS 1
+EOF
+
+fi
+
+
+for ac_prog in mawk gawk nawk awk
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:3087: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AWK'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_prog_AWK="$ac_prog"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+fi
+fi
+AWK="$ac_cv_prog_AWK"
+if test -n "$AWK"; then
+  echo "$ac_t""$AWK" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$AWK" && break
+done
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:3117: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftestmake <<\EOF
+all:
+       @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  SET_MAKE=
+else
+  echo "$ac_t""no" 1>&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:3155: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+    IFS="${IFS=        }"; ac_save_IFS="$IFS"; IFS=":"
+  for ac_dir in $PATH; do
+    # Account for people who put trailing slashes in PATH elements.
+    case "$ac_dir/" in
+    /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+    *)
+      # OSF1 and SCO ODT 3.0 have their own names for install.
+      # Don't use installbsd from OSF since it installs stuff as root
+      # by default.
+      for ac_prog in ginstall scoinst install; do
+        if test -f $ac_dir/$ac_prog; then
+         if test $ac_prog = install &&
+            grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         else
+           ac_cv_path_install="$ac_dir/$ac_prog -c"
+           break 2
+         fi
+       fi
+      done
+      ;;
+    esac
+  done
+  IFS="$ac_save_IFS"
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL="$ac_cv_path_install"
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL="$ac_install_sh"
+  fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo $ac_n "checking whether ln -s works""... $ac_c" 1>&6
+echo "configure:3208: checking whether ln -s works" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LN_S'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  rm -f conftestdata
+if ln -s X conftestdata 2>/dev/null
+then
+  rm -f conftestdata
+  ac_cv_prog_LN_S="ln -s"
+else
+  ac_cv_prog_LN_S=ln
+fi
+fi
+LN_S="$ac_cv_prog_LN_S"
+if test "$ac_cv_prog_LN_S" = "ln -s"; then
+  echo "$ac_t""yes" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+for ac_prog in rm
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:3233: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_RMPROG'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$RMPROG" in
+  /*)
+  ac_cv_path_RMPROG="$RMPROG" # Let the user override the test with a path.
+  ;;
+  ?:/*)                         
+  ac_cv_path_RMPROG="$RMPROG" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do 
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_RMPROG="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+fi
+RMPROG="$ac_cv_path_RMPROG"
+if test -n "$RMPROG"; then
+  echo "$ac_t""$RMPROG" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$RMPROG" && break
+done
+test -n "$RMPROG" || RMPROG="/bin/rm"
+
+for ac_prog in sh
+do
+# Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:3274: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_SHPROG'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  case "$SHPROG" in
+  /*)
+  ac_cv_path_SHPROG="$SHPROG" # Let the user override the test with a path.
+  ;;
+  ?:/*)                         
+  ac_cv_path_SHPROG="$SHPROG" # Let the user override the test with a dos path.
+  ;;
+  *)
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS=":"
+  ac_dummy="$PATH"
+  for ac_dir in $ac_dummy; do 
+    test -z "$ac_dir" && ac_dir=.
+    if test -f $ac_dir/$ac_word; then
+      ac_cv_path_SHPROG="$ac_dir/$ac_word"
+      break
+    fi
+  done
+  IFS="$ac_save_ifs"
+  ;;
+esac
+fi
+SHPROG="$ac_cv_path_SHPROG"
+if test -n "$SHPROG"; then
+  echo "$ac_t""$SHPROG" 1>&6
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+test -n "$SHPROG" && break
+done
+test -n "$SHPROG" || SHPROG="/bin/sh"
+
+
+echo $ac_n "checking for set -h""... $ac_c" 1>&6
+echo "configure:3312: checking for set -h" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_set_h'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  echo "set -h; exit $?" > conftest ;
+$SHPROG ./conftest 2> conftest.out
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  unet_cv_sys_set_h=yes
+else
+  unet_cv_sys_set_h=no
+fi
+$RMPROG -fr conftest*
+fi
+
+echo "$ac_t""$unet_cv_sys_set_h" 1>&6
+
+
+echo $ac_n "checking for posix non-blocking""... $ac_c" 1>&6
+echo "configure:3331: checking for posix non-blocking" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_nonblocking_posix'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3339 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+  char b[12];
+  struct sockaddr x;
+  size_t l = sizeof(x);
+  int f = socket(AF_INET, SOCK_DGRAM, 0);
+  if (f >= 0 && !(fcntl(f, F_SETFL, O_NONBLOCK)))
+  {
+    signal(SIGALRM, alarmed);
+    alarm(2);
+    recvfrom(f, b, 12, 0, &x, &l);
+    alarm(0);
+    exit(0);
+  }
+  exit(1);
+}
+EOF
+if { (eval echo configure:3365: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  unet_cv_sys_nonblocking_posix=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  unet_cv_sys_nonblocking_posix=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$unet_cv_sys_nonblocking_posix" 1>&6
+if test $unet_cv_sys_nonblocking_posix = yes; then
+  cat >> confdefs.h <<\EOF
+#define NBLOCK_POSIX 1
+EOF
+
+else
+echo $ac_n "checking for bsd non-blocking""... $ac_c" 1>&6
+echo "configure:3387: checking for bsd non-blocking" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_nonblocking_bsd'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3395 "configure"
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <signal.h>
+$ac_cv_type_signal alarmed() { exit(1); }
+int main(void)
+{
+  char b[12];
+  struct sockaddr x;
+  size_t l = sizeof(x);
+  int f = socket(AF_INET, SOCK_DGRAM, 0);
+  if (f >= 0 && !(fcntl(f, F_SETFL, O_NDELAY)))
+  {
+    signal(SIGALRM, alarmed);
+    alarm(2);
+    recvfrom(f, b, 12, 0, &x, &l);
+    alarm(0);
+    exit(0);
+  }
+  exit(1);
+}
+EOF
+if { (eval echo configure:3421: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  unet_cv_sys_nonblocking_bsd=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  unet_cv_sys_nonblocking_bsd=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$unet_cv_sys_nonblocking_bsd" 1>&6
+if test $unet_cv_sys_nonblocking_bsd = yes; then
+  cat >> confdefs.h <<\EOF
+#define NBLOCK_BSD 1
+EOF
+
+else
+  cat >> confdefs.h <<\EOF
+#define NBLOCK_SYSV 1
+EOF
+
+fi
+fi
+echo $ac_n "checking for posix signals""... $ac_c" 1>&6
+echo "configure:3449: checking for posix signals" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_signal_posix'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3454 "configure"
+#include "confdefs.h"
+#include <signal.h>
+int main() {
+sigaction(SIGTERM, (struct sigaction *)0L, (struct sigaction *)0L)
+; return 0; }
+EOF
+if { (eval echo configure:3461: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  unet_cv_sys_signal_posix=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  unet_cv_sys_signal_posix=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$unet_cv_sys_signal_posix" 1>&6
+if test $unet_cv_sys_signal_posix = yes; then
+  cat >> confdefs.h <<\EOF
+#define POSIX_SIGNALS 1
+EOF
+
+else
+echo $ac_n "checking for bsd reliable signals""... $ac_c" 1>&6
+echo "configure:3481: checking for bsd reliable signals" >&5
+if eval "test \"`echo '$''{'unet_cv_sys_signal_bsd'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  if test "$cross_compiling" = yes; then
+    { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
+else
+  cat > conftest.$ac_ext <<EOF
+#line 3489 "configure"
+#include "confdefs.h"
+#include <signal.h>
+int calls = 0;
+$ac_cv_type_signal handler()
+{
+  if (calls) return;
+  calls++;
+  kill(getpid(), SIGTERM);
+  sleep(1);
+}
+int main(void)
+{
+  signal(SIGTERM, handler);
+  kill(getpid(), SIGTERM);
+  exit (0);
+}
+EOF
+if { (eval echo configure:3507: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+  unet_cv_sys_signal_bsd=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -fr conftest*
+  unet_cv_sys_signal_bsd=no
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$unet_cv_sys_signal_bsd" 1>&6
+if test $unet_cv_sys_signal_bsd = yes; then
+  cat >> confdefs.h <<\EOF
+#define BSD_RELIABLE_SIGNALS 1
+EOF
+
+else
+  cat >> confdefs.h <<\EOF
+#define SYSV_UNRELIABLE_SIGNALS 1
+EOF
+
+fi
+fi
+
+echo $ac_n "checking if the compiler understands -pipe""... $ac_c" 1>&6
+echo "configure:3536: checking if the compiler understands -pipe" >&5
+unet_cv_pipe_flags="$ac_cv_prog_gcc"
+if test "$ac_cv_prog_gcc" = no; then
+  OLDCFLAGS="$CFLAGS"
+  CFLAGS="$CFLAGS -pipe"
+  cat > conftest.$ac_ext <<EOF
+#line 3542 "configure"
+#include "confdefs.h"
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:3549: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+  rm -rf conftest*
+  unet_cv_pipe_flags=yes
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+fi
+rm -f conftest*
+  CFLAGS="$OLDCFLAGS"
+fi
+echo "$ac_t""$unet_cv_pipe_flags" 1>&6
+if test "$unet_cv_pipe_flags" = yes ; then
+  x=`echo $CFLAGS | grep 'pipe' 2>/dev/null`
+  if test "$x" = "" ; then
+    CFLAGS="$CFLAGS -pipe"
+  fi
+fi
+
+
+
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs.  It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already.  You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+  case `(ac_space=' '; set | grep ac_space) 2>&1` in
+  *ac_space=\ *)
+    # `set' does not quote correctly, so add quotes (double-quote substitution
+    # turns \\\\ into \\, and sed turns \\ into \).
+    sed -n \
+      -e "s/'/'\\\\''/g" \
+      -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+    ;;
+  *)
+    # `set' quotes correctly as required by POSIX, so do not add quotes.
+    sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+    ;;
+  esac >> confcache
+if cmp -s $cache_file confcache; then
+  :
+else
+  if test -w $cache_file; then
+    echo "updating cache $cache_file"
+    cat confcache > $cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[        ]*VPATH[        ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+  case "\$ac_option" in
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+    exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+  -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+    echo "$CONFIG_STATUS generated by autoconf version 2.13"
+    exit 0 ;;
+  -help | --help | --hel | --he | --h)
+    echo "\$ac_cs_usage"; exit 0 ;;
+  *) echo "\$ac_cs_usage"; exit 1 ;;
+  esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "config-sh Configure ../Makefile ../ircd/Makefile ../doc/Makefile Makefile setup.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+s%@LIBOBJS@%$LIBOBJS%g
+s%@AWK@%$AWK%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@LN_S@%$LN_S%g
+s%@RMPROG@%$RMPROG%g
+s%@SHPROG@%$SHPROG%g
+s%@unet_cv_sys_set_h@%$unet_cv_sys_set_h%g
+s%@ac_cv_header_poll_h@%$ac_cv_header_poll_h%g
+s%@ac_cv_header_syslog_h@%$ac_cv_header_syslog_h%g
+s%@unet_cv_func_poll_syscall@%$unet_cv_func_poll_syscall%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+  if test $ac_beg -gt 1; then
+    sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+  else
+    sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+  fi
+  if test ! -s conftest.s$ac_file; then
+    ac_more_lines=false
+    rm -f conftest.s$ac_file
+  else
+    if test -z "$ac_sed_cmds"; then
+      ac_sed_cmds="sed -f conftest.s$ac_file"
+    else
+      ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+    fi
+    ac_file=`expr $ac_file + 1`
+    ac_beg=$ac_end
+    ac_end=`expr $ac_end + $ac_max_sed_cmds`
+  fi
+done
+if test -z "$ac_sed_cmds"; then
+  ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"config-sh Configure ../Makefile ../ircd/Makefile ../doc/Makefile Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+  # Remove last slash and all that follows it.  Not all systems have dirname.
+  ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+  if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+    # The file is in a subdirectory.
+    test ! -d "$ac_dir" && mkdir "$ac_dir"
+    ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+    # A "../" for each directory in $ac_dir_suffix.
+    ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+  else
+    ac_dir_suffix= ac_dots=
+  fi
+
+  case "$ac_given_srcdir" in
+  .)  srcdir=.
+      if test -z "$ac_dots"; then top_srcdir=.
+      else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+  /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+  *) # Relative path.
+    srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+    top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  case "$ac_given_INSTALL" in
+  [/$]*) INSTALL="$ac_given_INSTALL" ;;
+  *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+  esac
+
+  echo creating "$ac_file"
+  rm -f "$ac_file"
+  configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+  case "$ac_file" in
+  *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+  *) ac_comsub= ;;
+  esac
+
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([  ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='\([     ][      ]*\)[^  ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='\([     ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([  ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+  CONFIG_HEADERS="setup.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case "$ac_file" in
+  *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+       ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+  *) ac_file_in="${ac_file}.in" ;;
+  esac
+
+  echo creating $ac_file
+
+  rm -f conftest.frag conftest.in conftest.out
+  ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+  cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h.  And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[   ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+  ac_lines=`grep -c . conftest.vals`
+  # grep -c gives empty output for an empty file on some AIX systems.
+  if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+  # Write a limited-size here document to conftest.frag.
+  echo '  cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+  echo 'CEOF
+  sed -f conftest.frag conftest.in > conftest.out
+  rm -f conftest.in
+  mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+  rm -f conftest.vals
+  mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+  rm -f conftest.frag conftest.h
+  echo "/* $ac_file.  Generated automatically by configure.  */" > conftest.h
+  cat conftest.in >> conftest.h
+  rm -f conftest.in
+  if cmp -s $ac_file conftest.h 2>/dev/null; then
+    echo "$ac_file is unchanged"
+    rm -f conftest.h
+  else
+    # Remove last slash and all that follows it.  Not all systems have dirname.
+      ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+      if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+      # The file is in a subdirectory.
+      test ! -d "$ac_dir" && mkdir "$ac_dir"
+    fi
+    rm -f $ac_file
+    mv conftest.h $ac_file
+  fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+echo timestamp > stamp-h;
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/config/configure.in b/config/configure.in
new file mode 100644 (file)
index 0000000..2459635
--- /dev/null
@@ -0,0 +1,121 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl
+dnl Copyright (c) 1997, by Carlo Wood <carlo@runaway.xs4all.nl>
+
+dnl Make sure we are in the correct directory (someone could have run
+dnl 'configure' with a wrong '--srcdir'); Note that a bug in autoconf
+dnl forces us to have srcdir == dir where configure resides (config):
+AC_INIT(config-sh.in)
+
+dnl 'configure' must be run from within 'config/'.
+AC_CONFIG_AUX_DIR(.)
+
+dnl Define the input and output configuration header file,
+dnl (Generate config/setup.h from config/setup.h.in):
+AC_CONFIG_HEADER(setup.h)
+
+dnl Demand at least version 2.13 of autoconf
+AC_PREREQ(2.13)
+
+dnl This should be done early.
+AC_PROG_CC
+
+dnl UNIX Variants
+dnl Allow the use of BSD functions on AIX.
+AC_AIX
+dnl Allow the use of POSIX functions on several OS.
+AC_ISC_POSIX
+AC_MINIX
+dnl ANSIfy the C compiler whenever possible.
+AM_PROG_CC_STDC
+dnl Use -O3 instead of -O2.
+if test "$CFLAGS" != "" ; then
+  CFLAGS=`echo "$CFLAGS" | sed -e 's/-O2/-O3/'`
+fi
+dnl Remove -pipe during configure
+if test "$CFLAGS" != "" ; then
+  CFLAGS=`echo "$CFLAGS" | sed -e 's/-pipe//g'`
+fi
+
+dnl Checks for libraries.
+AC_CHECK_LIB(c, crypt, [true],
+    AC_CHECK_LIB(descrypt, crypt, LIBS="-ldescrypt $LIBS",
+    AC_CHECK_LIB(crypt, crypt)))
+AC_CHECK_LIB(c, gethostbyname, [true],
+    AC_CHECK_LIB(nsl, gethostbyaddr, LIBS="-lnsl $LIBS"))
+dnl IRIX has -lsocket, but doesn't need it.
+AC_CHECK_LIB(c, socket, [true],
+    AC_CHECK_LIB(socket, socket, LIBS="-lsocket $LIBS"))
+unet_CHECK_LIB_RESOLV
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS(malloc.h sys/malloc.h fcntl.h string.h strings.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h memory.h errno.h net/errno.h sys/cdefs.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_C_BIGENDIAN
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+AC_STRUCT_TM
+AC_TYPE_UID_T
+unet_CHECK_TYPE_SIZES
+
+dnl Define SIZE_T_FMT and TIME_T_FMT to be the printf format for
+dnl respectively size_t and time_t.
+unet_DEFINE_SIZE_T_FMT
+unet_DEFINE_TIME_T_FMT
+
+dnl Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_MEMCMP
+AC_FUNC_SETVBUF_REVERSED
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS(strchr memcpy memmove)
+AC_CHECK_FUNCS(gethostname gettimeofday mkdir strerror strtoken)
+AC_CHECK_FUNCS(select socket uname)
+AC_CHECK_FUNCS(setrlimit inet_netof getrusage times res_init)
+
+dnl Do we have a system call poll?
+unet_FUNC_POLL_SYSCALL
+
+dnl Do we have restarting syscalls ?
+AC_SYS_RESTARTABLE_SYSCALLS
+
+dnl Test for programs
+AC_PROG_AWK
+AC_PROG_MAKE_SET
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PATH_PROGS(RMPROG, rm, /bin/rm)
+AC_PATH_PROGS(SHPROG, sh, /bin/sh)
+
+dnl Test if /bin/sh supports 'set -h'
+AC_CACHE_CHECK([for set -h], unet_cv_sys_set_h,
+[echo "set -h; exit $?" > conftest ;
+$SHPROG ./conftest 2> conftest.out
+ac_err=`grep -v '^ *+' conftest.out`
+if test -z "$ac_err"; then
+  unet_cv_sys_set_h=yes
+else
+  unet_cv_sys_set_h=no
+fi
+$RMPROG -fr conftest*])
+dnl Used in Configure.
+AC_SUBST(unet_cv_sys_set_h)
+
+unet_NONBLOCKING
+unet_SIGNALS
+
+dnl Add -pipe when possible
+unet_PIPE_CFLAGS
+
+dnl Used in config-sh.
+AC_SUBST(ac_cv_header_poll_h)
+AC_SUBST(ac_cv_header_syslog_h)
+AC_SUBST(unet_cv_func_poll_syscall)
+
+dnl Finally really generate all output files:
+AC_OUTPUT(config-sh Configure ../Makefile ../ircd/Makefile ../doc/Makefile Makefile, [echo timestamp > stamp-h;],)
diff --git a/config/gen.doc.Makefile b/config/gen.doc.Makefile
new file mode 100644 (file)
index 0000000..3a0a539
--- /dev/null
@@ -0,0 +1,7 @@
+. ./.config
+. ./parse.none
+mv ../doc/Makefile ../doc/Makefile.tmp
+sed -e "s:^MANDIR=.*:MANDIR=$MANDIR:" \
+    -e "s:^INSTALL *= *\.\..*:INSTALL=../config/install-sh -c:" \
+    ../doc/Makefile.tmp > ../doc/Makefile
+$RM -f ../doc/Makefile.tmp
diff --git a/config/gen.ircd.Makefile b/config/gen.ircd.Makefile
new file mode 100644 (file)
index 0000000..f6db89c
--- /dev/null
@@ -0,0 +1,20 @@
+. ./.config
+. ./parse.none
+mv ../ircd/Makefile ../ircd/Makefile.tmp
+sed -e "s:^CC=.*:CC=$CC:" \
+    -e "s:^CFLAGS=.*:CFLAGS=$CFLAGS:" \
+    -e "s:^CPPFLAGS=.*:CPPFLAGS=$CPPFLAGS:" \
+    -e "s:^LDFLAGS=.*:LDFLAGS=$LDFLAGS:" \
+    -e "s:^IRCDLIBS=.*:IRCDLIBS=$IRCDLIBS:" \
+    -e "s:^IRCDMODE=.*:IRCDMODE=$IRCDMODE:" \
+    -e "s:^IRCDOWN=.*:IRCDOWN=$IRCDOWN:" \
+    -e "s:^IRCDGRP=.*:IRCDGRP=$IRCDGRP:" \
+    -e "s:^BINDIR=.*:BINDIR=$BINDIR:" \
+    -e "s:^SYMLINK=.*:SYMLINK=$SYMLINK:" \
+    -e "s:^INCLUDEFLAGS=.*:INCLUDEFLAGS=$INCLUDEFLAGS:" \
+    -e "s:^DPATH=.*:DPATH=$DPATH:" \
+    -e "s:^MPATH=.*:MPATH=$MPATH:" \
+    -e "s:^RPATH=.*:RPATH=$RPATH:" \
+    -e "s:^INSTALL *= *\.\..*:INSTALL=../config/install-sh -c:" \
+    ../ircd/Makefile.tmp > ../ircd/Makefile
+$RM -f ../ircd/Makefile.tmp
diff --git a/config/install-sh b/config/install-sh
new file mode 100644 (file)
index 0000000..ebc6691
--- /dev/null
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/config/parse.none b/config/parse.none
new file mode 100644 (file)
index 0000000..447f9f5
--- /dev/null
@@ -0,0 +1,9 @@
+if [ "$CFLAGS" = "none" ]; then
+  CFLAGS=""
+fi
+if [ "$LDFLAGS" = "none" ]; then
+  LDFLAGS=""
+fi
+if [ "$IRCDLIBS" = "none" ]; then
+  IRCDLIBS=""
+fi
diff --git a/config/setup.h.in b/config/setup.h.in
new file mode 100644 (file)
index 0000000..84e336e
--- /dev/null
@@ -0,0 +1,219 @@
+/* setup.h.in.  Generated automatically from configure.in by autoheader.  */
+/* Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* Define if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+#undef _ALL_SOURCE
+#endif
+
+/* Define to empty if the keyword does not work.  */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef gid_t
+
+/* Define if you don't have vprintf but do have _doprnt.  */
+#undef HAVE_DOPRNT
+
+/* Define if system calls automatically restart after interruption
+   by a signal.  */
+#undef HAVE_RESTARTABLE_SYSCALLS
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the vprintf function.  */
+#undef HAVE_VPRINTF
+
+/* Define if on MINIX.  */
+#undef _MINIX
+
+/* Define if the system does not provide POSIX.1 features except
+   with this defined.  */
+#undef _POSIX_1_SOURCE
+
+/* Define if you need to in order for stat and other things to work.  */
+#undef _POSIX_SOURCE
+
+/* Define as the return type of signal handlers (int or void).  */
+#undef RETSIGTYPE
+
+/* Define if the setvbuf function takes the buffering type as its second
+   argument and the buffer pointer as the third, as on System V
+   before release 3.  */
+#undef SETVBUF_REVERSED
+
+/* Define to `unsigned' if <sys/types.h> doesn't define.  */
+#undef size_t
+
+/* Define if you have the ANSI C header files.  */
+#undef STDC_HEADERS
+
+/* Define if you can safely include both <sys/time.h> and <time.h>.  */
+#undef TIME_WITH_SYS_TIME
+
+/* Define if your <sys/time.h> declares struct tm.  */
+#undef TM_IN_SYS_TIME
+
+/* Define to `int' if <sys/types.h> doesn't define.  */
+#undef uid_t
+
+/* Define if your processor stores words with the most significant
+   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
+#undef WORDS_BIGENDIAN
+
+/* Define if you have the resolv library (-lresolv) */
+#undef HAVE_LIB_RESOLV
+
+/* Define one of these, depending on wether you have
+   POSIX, BSD or SYSV non-blocking stuff */
+#undef NBLOCK_POSIX
+#undef NBLOCK_BSD
+#undef NBLOCK_SYSV
+
+/* Define on of these, depending on wether you have
+   POSIX, BSD or SYSV signal handling */
+#undef POSIX_SIGNALS
+#undef BSD_RELIABLE_SIGNALS
+#undef SYSV_UNRELIABLE_SIGNALS
+
+/* Define these to be unsigned integral internal types,
+ * of respecitvely 2 and 4 bytes in size, when not already
+ * defined in <sys/types.h>, <stdlib.h> or <stddef.h> */
+#undef u_int16_t
+#undef u_int32_t
+
+/* Define this to the printf format for size_t */
+#undef SIZE_T_FMT
+
+/* Define this to the printf format for time_t */
+#undef TIME_T_FMT
+
+/* Define this to the printf signed format for time_t */
+#undef STIME_T_FMT
+
+/* The number of bytes in a int.  */
+#undef SIZEOF_INT
+
+/* The number of bytes in a long.  */
+#undef SIZEOF_LONG
+
+/* The number of bytes in a short.  */
+#undef SIZEOF_SHORT
+
+/* The number of bytes in a size_t.  */
+#undef SIZEOF_SIZE_T
+
+/* Define if you have the gethostname function.  */
+#undef HAVE_GETHOSTNAME
+
+/* Define if you have the getrusage function.  */
+#undef HAVE_GETRUSAGE
+
+/* Define if you have the gettimeofday function.  */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define if you have the inet_netof function.  */
+#undef HAVE_INET_NETOF
+
+/* Define if you have the memcpy function.  */
+#undef HAVE_MEMCPY
+
+/* Define if you have the memmove function.  */
+#undef HAVE_MEMMOVE
+
+/* Define if you have the mkdir function.  */
+#undef HAVE_MKDIR
+
+/* Define if you have the res_init function.  */
+#undef HAVE_RES_INIT
+
+/* Define if you have the select function.  */
+#undef HAVE_SELECT
+
+/* Define if you have the setrlimit function.  */
+#undef HAVE_SETRLIMIT
+
+/* Define if you have the socket function.  */
+#undef HAVE_SOCKET
+
+/* Define if you have the strchr function.  */
+#undef HAVE_STRCHR
+
+/* Define if you have the strerror function.  */
+#undef HAVE_STRERROR
+
+/* Define if you have the strtoken function.  */
+#undef HAVE_STRTOKEN
+
+/* Define if you have the times function.  */
+#undef HAVE_TIMES
+
+/* Define if you have the uname function.  */
+#undef HAVE_UNAME
+
+/* Define if you have the <errno.h> header file.  */
+#undef HAVE_ERRNO_H
+
+/* Define if you have the <fcntl.h> header file.  */
+#undef HAVE_FCNTL_H
+
+/* Define if you have the <malloc.h> header file.  */
+#undef HAVE_MALLOC_H
+
+/* Define if you have the <memory.h> header file.  */
+#undef HAVE_MEMORY_H
+
+/* Define if you have the <net/errno.h> header file.  */
+#undef HAVE_NET_ERRNO_H
+
+/* Define if you have the <poll.h> header file.  */
+#undef HAVE_POLL_H
+
+/* Define if you have the <string.h> header file.  */
+#undef HAVE_STRING_H
+
+/* Define if you have the <strings.h> header file.  */
+#undef HAVE_STRINGS_H
+
+/* Define if you have the <sys/cdefs.h> header file.  */
+#undef HAVE_SYS_CDEFS_H
+
+/* Define if you have the <sys/file.h> header file.  */
+#undef HAVE_SYS_FILE_H
+
+/* Define if you have the <sys/ioctl.h> header file.  */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define if you have the <sys/malloc.h> header file.  */
+#undef HAVE_SYS_MALLOC_H
+
+/* Define if you have the <sys/time.h> header file.  */
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have the <syslog.h> header file.  */
+#undef HAVE_SYSLOG_H
+
+/* Define if you have the <unistd.h> header file.  */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the crypt library (-lcrypt).  */
+#undef HAVE_LIBCRYPT
diff --git a/config/setup.h.top b/config/setup.h.top
new file mode 100644 (file)
index 0000000..27fa27c
--- /dev/null
@@ -0,0 +1,17 @@
+/* Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
diff --git a/config/stamp-h.in b/config/stamp-h.in
new file mode 100644 (file)
index 0000000..9788f70
--- /dev/null
@@ -0,0 +1 @@
+timestamp
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..9fe9154
--- /dev/null
+++ b/configure
@@ -0,0 +1,9 @@
+#! /bin/sh
+chmod u+x config/install-sh config/configure ircd/crypt/sums ircd/crypt/crypter
+cd config
+if test ! -f config.cache && test -r ../../.config.cache; then
+       cp ../../.config.cache config.cache;
+fi
+./configure
+echo timestamp > ../doc/stamp-m
+echo timestamp > ../ircd/stamp-m
diff --git a/doc/.cvsignore b/doc/.cvsignore
new file mode 100644 (file)
index 0000000..0be7160
--- /dev/null
@@ -0,0 +1,2 @@
+Makefile
+stamp-m
diff --git a/doc/Authors b/doc/Authors
new file mode 100644 (file)
index 0000000..31076f5
--- /dev/null
@@ -0,0 +1,171 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, doc/AUTHORS
+ *   Copyright (C) 1990
+ *
+ * AUTHORS FILE:
+ *   This file attempts to remember all contributors to the IRC
+ *   developement. Names can be only added this file, no name
+ *   should never be removed. This file must be included into all
+ *   distributions of IRC and derived works.
+ *
+ *   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.
+ */
+
+IRC was conceived of and written by Jarkko Oikarinen <jto@tolsun.oulu.fi>.
+IRC was originally written in University of Oulu, Computing Center.
+Jan 1991 - IRC 2.6  jto@tolsun.oulu.fi
+       - Multiple Channels and protocol changes
+
+Contributions were made by a cast of dozens, including the following:
+
+Markku Jarvinen <mta@tut.fi>: Emacs-like editing facility for the client
+
+Kimmo Suominen <kim@kannel.lut.fi>: HP-UX port
+
+Jeff Trim <jtrim@orion.cair.du.edu>: enhancements and advice
+
+Vijay Subramaniam <vijay@lll-winken.llnl.gov>: advice and ruthless publicity
+
+Karl Kleinpaste <karl@cis.ohio-state.edu>: user's manual
+
+Greg Lindahl <gl8f@virginia.edu>: AUTOMATON code, the Wumpus GM automaton,
+myriad bug fixes
+
+Bill Wisner <wisner@hayes.fai.alaska.edu>: numerous bug fixes and code
+enhancements
+
+Tom Davis <conslt16@zeus.unl.edu> and Tim Russell <russell@zeus.unl.edu>:
+VMS modifications
+
+Markku Savela <msa@tel4.tel.vtt.fi>: advice, support, and being the
+incentive to do some of our *own* coding. :)
+
+Tom Hopkins <hoppie@buengf.bu.edu>: bug fixes, quarantine lines,
+consolidation of various patches.
+
+Christopher Davis <ckd@cs.bu.edu>: EFnet/Anet gateway coding,
+many automata ;), documentation fixing.
+
+Helen Rose <hrose@cs.bu.edu>: documentation updating, and fixing.
+
+Tom Hinds <rocker@bucsf.bu.edu>: emacs client updating.
+
+Tim Miller <cerebus@bu-pub.bu.edu>: various server and client-breaking
+features.
+
+Darren Reed <avalon@coombs.anu.edu.au>: various bug fixes and enhancements.
+Introduced nickname and channelname hash tables into the server.
+
+The version 2.2 release was coordinated by Mike Bolotski
+<mikeb@salmon.ee.ubc.ca>.
+
+The version 2.4 release was coordinated by Markku Savela and
+Chelsea Ashley Dyerman
+
+The version 2.5.2 release was coordinated by Christopher Davis, Helen Rose,
+and Tom Hopkins.
+
+The versions 2.6.2, 2.7 and 2.8 releases were coordinated by Darren Reed.
+
+Contributions for the 2.8 release from the following people:
+Matthew Green <phone@coombs.anu.edu.au>
+Chuck Kane <ckane@ece.uiuc.edu>
+Matt Lyle <matt@oc.com>
+Vesa Ruokonen <ruokonen@lut.fi>
+
+Markku Savela <Markku.Savela@vtt.fi> / April 1990
+Fixed various bugs in 2.2PL1 release server (2.2msa.4) and changed
+sockets to use non-blocking mode (2.2msa.9). [I have absolutely
+nothing to do with clients :-]
+
+Chelsea Ashley Dyerman <chelsea@earth.cchem.berkeley.edu> / April 1990
+Rewrote the Makefiles, restructuring of source tree. Added libIrcd.a to
+the Makefile macros, numerous reformatting of server text messages, and
+added mkversion.sh to keep track of compilation statistics. Numerous
+bug fixes and enhancements, and co-coordinator of the 2.4 release.
+
+Jarle Lyngaas (nmijl@alf.uib.no) added Note functions to ircd.
+
+Armin Gruner <gruner@informatik.tu-muenchen.de> / May, June 1990:
+* Patched KILL-line feature for ircd.conf, works now.
+  Enhancement:  Time intervals can be specified in passwd-field.
+  Result: KILL-Line is only active during these intervals
+* Patched PRIVMSG handling, now OPER can specify masks for sending
+  private messages, advantage: msg to all at a specified server or host.
+* Little tests on irc 2.5 alpha, fixed some little typos in client code.
+  Change: common/debug.c has been moved to ircd/s_debug.c, and a
+  irc/c_debug.c has been created, for the benefit that wrong server msg
+  are displayed if client does not recognize them. (strange, if a server
+  sends an 'unknown command', isn't it?)
+
+Tom Hopkins <hoppie@buengf.bu.edu> / September, October 1990:
+* Patched msa's K lines for servers (Q lines).
+* Consolidated several patches, including Stealth's logging patch. 
+* Fixed several minor bugs. 
+* Has done lots of other stuff that I can't seem to remember, but he
+  always works on code, so he has to have done alot more than three
+  lines worth. :) 
+
+Thanks go to those persons not mentioned here who have added their advice,
+opinions, and code to IRC.
+
+Various modifications, bugreports, cleanups and testing by:
+
+Hugo Calendar <hugo@ucscb.ucsc.edu>
+Bo Adler <adler@csvax.cs.caltech.edu>
+Michael Sandrof <ms5n+@andrew.cmu.edu>
+Jon Solomon <jsol@cs.bu.edu>
+Jan Peterson <jlp@hamblin.math.byu.edu>
+Nathan Glasser <nathan@brokaw.lcs.mit.edu>
+Helen Rose <hrose@eff.org>
+Mike Pelletier <stealth@caen.engin.umich.edu>
+Basalat Ali Raja <gwydion@tavi.rice.edu>
+Eric P. Scott <eps@toaster.sfsu.edu>
+Dan Goodwin <fornax@wpi.wpi.edu>
+Noah Friedman <friedman@ai.mit.edu>
+
+
+UNDERNET (1991 - 1999)
+--------
+
+The Undernet versions (TSpre8, u2.9 and u2.10) are based on ircd-2.8.10 and
+contain thousands of hours of work by Carlo Wood <carlo@runaway.xs4all.nl>
+(Run on IRC). The number of protocol enhancements, changes and additions that
+have been added are too many to summarize.  All patches are kept in the patch
+repository at http://www.xs4all.nl/~carlo17/ircd-dev/
+
+Various additions and bugfixes have been contributed by:
+
+Aaron <agifford@sci.dixie.edu>
+CapVideo <majdi@puck.nether.net>
+Chaos <simon@troll.elec.uow.edu.au>
+Cym <cym@acrux.net>
+Derrick <dirk@servtech.com>
+Ensor <dholmes@rahul.net>
+flux <cmlambertus@ucdavis.edu>
+Ghostwolf <foxxe@wolfspirit.org>
+Jamey <woodjr@durrance.Colorado.EDU>
+Jarle <jarlel@II.UIB.NO>
+Kev <klmitch@mit.edu>
+Nemesi <cocito@tin.it>
+Niels <niels@holding.pi.net>
+record <jegelhof@cloud9.net>
+smg <smg@lm.com>
+SeKs <intru@step.polymtl.ca>
+Simon- <sim@peace.netnation.com>
+Starfox <starfox@quicklink.com>
+Trio <trio@b62897.STUDENT.CWRU.Edu>
+WildThang <dvmitche@antietam.nssl.uoknor.edu>
+Xorath <vorac@wheel.dcn.davis.ca.us>
diff --git a/doc/Configure.help b/doc/Configure.help
new file mode 100644 (file)
index 0000000..508e208
--- /dev/null
@@ -0,0 +1,1076 @@
+# Format of this file: description<nl>variable<nl>helptext<nl><nl>.
+# If the question being documented is of type "choice", we list
+# only the first occurring config variable. The help texts
+# must not contain empty lines. No variable should occur twice; if it
+# does, only the first occurrence will be used by Configure. The lines
+# in a help text should be indented two positions. Lines starting with
+# `#' are ignored. Limit your lines to 78 characters.
+#
+# If you add a help text to this file, please try to be as gentle as
+# possible. Don't use unexplained acronyms and generally write for the
+# hypothetical admin who has just downloaded ircu for the first time.
+# Tell them what to do if they're unsure. Technical information
+# should go in a README in the Documentation directory. Mention all
+# the relevant READMEs and HOWTOs in the help text.
+#
+# All this was shamelessly stolen from several different sources. Many
+# thanks to all the contributors. The texts are copyrighted # (c) 1997
+# by Carlo Wood and governed by the GNU Public License.
+#
+
+Do you want to change your previous configuration
+CHANGE_CONFIG
+  You will be presented a series of questions that you have to answer
+  in order to configure the IRC daemon, prior to compilation.
+  If you went through this before, then your choices have been stored
+  in a file '.config'.  If you want to use the same stored configuration
+  now, specify 'n'; this will quickly skip through all questions that
+  you already answered previously, only prompting you for NEW questions.
+  Note that NEW questions only can occur when you just upgraded to a
+  new version.  Note also that if you abort by pressing ^C (control-C)
+  anywhere, then all answers are lost; you must finish it before the
+  answers are stored.
+  Pressing a 'c' or 'C' (followed by a return) on any question will
+  Continue the script in "use_defaults mode", that means that it will
+  take all default values unless it finds a NEW question (like when you
+  specify a 'n' here).  'C' will finish everything, but a 'c' will
+  only finish the current paragraph.
+  If you are unsure, or if you want to change a previously entered
+  configuration, specify 'y'.
+
+Which compiler do you want to use
+CC
+  Here you need to specify the C compiler you want to use.
+  Using 'gcc' is highly recommended, you might even want to install it
+  on your machine first.  Note that you can specify the full path if you
+  are not sure if the compiler is in your PATH (or whether or not the right
+  compiler will be used).  An example is: "/usr/ucb/cc".
+  The package needs an ANSI compiler.  Some compilers need an extra option
+  to compile ANSI C.  In those cases you can add these options also here.
+  For example, on a HPUX-8.x you would use (if you don't have gcc):
+  "cc -Aa -D_HPUX_SOURCE".
+  Note that you should not use quotes.
+
+What flags should I pass to $CC
+CFLAGS
+  These are the compiler flags, used for CC when compiling.
+  If you are not using gcc, it might be possible that your compiler is not
+  supporting -g and -O at the same time.  The -g option is necessary to be
+  able to debug the daemon in the case it contains a bug that makes the
+  ircd core dump.  Unless you use a version that is proven to be VERY stable,
+  it is highly recommended to use this option.  All Undernet production servers
+  are expected to use it in order to help coder-com to track down bugs.
+  The -O3 will optimize the code - it also makes debugging harder.
+  If you have plenty of cpu cycles then you can use -O2 instead of -O3:
+  it will disable inlining which makes it easier to debug or core dump,
+  the daemon will use a few percent more cpu however.
+  If you are not running a production server you should remove the -Ox.
+  Ircd developers can optionally use more options to turn on extra warnings.
+  Developers (which are using gcc of course ;), should use:
+  "-g -Wall -pedantic -DGODMODE"
+  Note that you should not use quotes.
+  Note that the server uses several non-ANSI (though POSIX.1) function calls.
+
+Do you need extra include directories
+EXTRA_INCLUDEDIRS
+  If your compiler needs extra include directories, you can specify them
+  here as a space separated list of directories.  Do not use quotes and do
+  not specify the '-I' prefix.  Usually you don't have to specify any
+  extra include directory, in that case you should specify "none" here.
+  If unsure, try "none" (without quotes) and see if all the '#include'
+  header files are found during compilation.
+
+Which linker flags do you need
+LDFLAGS
+  Here you can specify extra flags that will be passed to the linker.
+  Usually you will not need to pass any flags and you can therefore
+  specify "none" here (without the quotes).
+  SunOS users may want to add "-Bstatic" (but only if you need it).
+  You can also specify any "-L..." flags here if you need those for
+  extra libraries.
+
+Which extra libraries do you need
+IRCDLIBS
+  Some Operating Systems need linking with extra libraries for some of the
+  functions used by the daemon.  In some cases, it is not known which
+  libraries are needed, even when the Operating System is known.  This is
+  for instance the case with SunOS, some need -lresolv, while others don't.
+  If you forget to add a library then this will result in 'undefined variables'
+  during linking.  If you do not know which library to add, it might be
+  helpful to use the unix command `nm', which lists the variables of a
+  library.  For instance, if you get "unknown variable '_res_mkquery'", and you
+  wonder if this is in /usr/lib/libresolv.so, you can do:
+  nm /usr/lib/libresolv.so | grep res_mkquery
+  Do not use the leading '_' in the grep, this underscore is added by the
+  assembler but is not part of the original variable name and does not show
+  up in the output of nm.
+  Most libraries are in /lib or /usr/lib, which are scanned by default. In
+  some cases you will need to tell the linker where to search for a library.
+  You can do this by adding an -L... option to IRCDLIBS.  For instance:
+  "-L/usr/ucblib -lucb" will look for 'libucb.so' in /usr/ucblib too.
+  Here is a list of what you MAYBE need to specify depending on your
+  Operating System:
+  OS                   Specify here
+  NeXT != 2.0          -lsys_s
+  Dynix/ptx            -lsocket -linet -lnsl -lseq
+  Dell SVR4            -lsocket -lnsl -lucb
+  All others           Default provided by autoconf
+  If unsure use the default provided by autoconf.
+
+Where should I install the ircd binary
+BINDIR
+  After compilation (by typing 'make'), you can install the server with
+  the command 'make install'.  This will install the ircd in the directory
+  you specify here.  The package tries to use a meaningful name by naming
+  the binary "ircd.<tag>", where <tag> is the name of the last patch that
+  was applied by the maintainer.  A symbolic link (to be specified next)
+  will be used to point to this binary.  This allows a /RESTART to
+  immediately start the new version, while keeping the old binary.
+  Note that you need to have write permissions in this directory during
+  the install.  Please re-check the permissions/owner and group after
+  installation.
+
+What should the name of the installed symbolic link to the executable be
+SYMLINK
+  'make install' installs the binary with an unique name, however it makes
+  a symbolic link to this newly installed executable which always has the
+  same name, so you can use /RESTART and/or use this name in scripts that
+  automatically restart the ircd after a reboot or crash.
+  Here you can specify the name of that symbolic link.  Note that it may
+  not contain a '/'; it is just the name if the symbolic link and will be
+  installed in BINDIR.
+
+Which permissions do you want the binary to have
+IRCDMODE
+  Here you need to specify the octal file mode of the ircd binary.
+  Recommended is 711 - but you might need setuid or something.
+  Note that using a setuid and starting the daemon as another user
+  does prohibit the daemon from core dumping in case of a crash on some
+  Operating Systems.
+
+Which owner do you want the binary to have
+IRCDOWN
+This will be the owner of the ircd binary after installation.
+
+Which group do you want the binary to have
+IRCDGRP
+This will be the group of the ircd binary after installation.
+
+Where should I install the man page
+MANDIR
+  This is the base directory where the manual page of the ircd is installed.
+  If you are not root on your system, you can change it to your personal
+  manpath directory (which of course should be in your MANPATH environment
+  variable then).
+
+Use inlining for a few crucial functions
+FORCEINLINE
+  This will increases the size of the executable with 7 kb, but it also
+  speeds up execution a bit :).  Your compiler needs to understand the
+  keyword __inline__ (GNU gcc and egcs do).
+  If unsure, try if `y' compiles.  If it doesn't, you can try using a
+  C++ compiler (ie, configure CC to be 'g++' instead 'gcc').
+
+You have poll(), but do you want to use it
+USE_POLL
+  Some Operating Systems implement select() by calling poll(), others
+  implement poll() by calling select().  The best performance will be
+  achieved by calling the lowest (sys)call ourselves of course.
+  The Undernet Daemon allows you to use select() or poll().
+  If you specify 'y' here, the daemon will use poll() directly, otherwise
+  it will use select().  If you don't know what your Operating System
+  uses as syscall, you can compile the server with USE_POLL and detach
+  the running process with 'strace -p <pid>', 'truss -p <pid>' or
+  'trace -p <pid>' depending on your Operating System, these UNIX commands
+  will show you the syscalls and therefore show if you use poll() or select().
+  The advantage of using poll() is that you are not bothered by the limits
+  of select() and fd_set size (ie, the number of clients that connect).
+  The following Operating Systems seem to use poll():
+  Solaris 2.x, SunOS 4.x, AIX, Digital UNIX, and NetBSD.
+  linux-2.2.x use poll(), but only of your glibc was compiled with that
+  kernel (and it won't unless you compile it yourself).
+  The following Operating Systems use select():
+  linux-2.0.x.
+  If unsure, test it (a ./configure check will be added in ircu2.10.06).
+
+What is the domain name of your network
+DOMAINNAME
+  This define allows you to specify what you consider to be 'local'.
+  It is only used for statistics.  When you issue the IRC command /stats w,
+  the server will respond with statistics of how many clients have been
+  connecting to your server in the last minute, hour and day.  It will
+  give these statistics for all connections (including the servers), all
+  clients (from anywhere) and also for clients whose hostname ends on
+  the domain you specify here.  So if you are an ISP and you want to know
+  specifically the client load from your own domain, specify that domain
+  here.  If you are unsure what to do, then it isn't really important what
+  you give here, just don't give an empty string.  A good guess is the last
+  two parts of your own hostname (ie, if your hostname is foo.bar.nowhere.org,
+  specify 'nowhere.org').  Note that the string you give should NOT start
+  with a '.' and you should not use quotes.
+
+Please give a random seed of eight characters
+RANDOM_SEED
+  You should specify exactly eight characters (0-9A-Za-z) here.  Do not use
+  quotes or any other special characters.  This value is used to initialize
+  the random generator of the server which is used to generate PING/PONG
+  cookies in order to stop spoofing IP-numbers (a PING with a random number is
+  sent to this IP-number and if the client doesn't respond with the
+  exact same number, access is denied).  In order to make the random
+  number impossible to guess, it is important that you use your own random
+  seed here.
+
+Does your host have a reliable clock
+RELIABLE_CLOCK
+  You should really ONLY specify 'y' here when your system clock is
+  stable and accurate at all times (within a few seconds).
+  If you are running ntpdate on a regular basis, or an equivalent
+  like xntpd, to keep your system clock synchronized over the network,
+  then you might have an accurate clock.  However, this is not guaranteed,
+  for example, it is known that xntpd gives unstable results on linux
+  in some cases.  Note that an unstable clock is worse then an clock that
+  has a constant offset, because the servers attempt to correct for a
+  constant offset, but do not correct jumps of your system clock !
+  In general you SHOULD be running ntpdate or equivalent AND make sure it
+  works when you run a production server on Undernet.  Otherwise leave
+  your clock alone and specify 'n' here.
+  If unsure specify 'n' !
+
+Change root (/) after start of daemon
+CHROOTDIR
+  If you are a security freak and you want to the daemon to run in
+  its own environment, then you can specify 'y' here.  The daemon will
+  change '/' to 'DPATH' (which you will have to specify later).
+  If this confuses you or if you are uncertain, specify 'n'.
+
+Do you want the daemon set its own uid/gid
+CONFIG_SETUGID
+  If you specify 'y' here, then the daemon will attempt to set its
+  User ID (uid) and Group ID (gid) to the numeric values that you will
+  have to specify next.  This only makes sense if you (have to) start
+  the server as root.  The most secure operation of the server is to
+  not use setuid stuff (here or by means of setting the file mode)
+  and to run the server as a special user only (ie 'irc').  Of course
+  this user must have access to all log and configuration files.
+  Note that using a setuid and starting the daemon as another user
+  does prohibit the daemon from core dumping in case of a crash on some
+  Operating Systems.
+  This option is actually only necessary when you use the Change Root
+  option, because otherwise you can use the file mode to set the uid
+  and gid.  Note that the server refuses to run as root.
+  If unsure, specify 'n'.
+
+UID of irc daemon
+IRC_UID
+  Ok, if you insist on using this option: Here you must specify the
+  numeric value of the uid that you want the server to run as.
+  Note that you need to look in the right /etc/passwd file, which isn't
+  the same file when you used the Change Root option.
+
+GID of irc daemon
+IRC_GID
+  Ok, if you insist on using this option: Here you must specify the
+  numeric value of the gid that you want the server to run as.
+  Note that you need to look in the right /etc/group file, which isn't
+  the same file when you used the Change Root option.
+
+Allow to specify configuration file on command line
+CMDLINE_CONFIG
+  If you specify 'y' here, you will be allowed to specify the ircd.conf
+  path (the ircd daemon configuration file) on the command line when
+  starting the daemon (with the -f <ircd.conf file> option).
+  Note that defining this 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 other users have accounts on the same machine;
+  when 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.
+
+Set up a Unix domain socket to connect clients/servers
+UNIXPORT
+  If there are lots of users having an account on the same machine
+  (which is very unlikely because the server needs all cpu ;), then
+  using a UNIX domain socket to connect these clients to is more
+  efficient then letting them connect via TCP/IP.  A UNIX domain
+  socket is a special device that will be created in your File System.
+  Your client must also support connecting to a UNIX domain socket.
+  The name of the special device must be specified in the "ircd.conf"
+  file by means of an extra 'P: line', see doc/example.conf for the
+  syntax.
+  If you don't have many IRC-ing users on the same host as the server,
+  or when your local IRC client doesn't support UNIX domain sockets,
+  specify 'n' here.  Otherwise specify 'y'.
+
+Do you need virtual hosting
+VIRTUAL_HOST
+  This is only needed when you want to run two or more servers on the
+  same machine and on the same port (but different devices).
+  In general you will only need this if you have at least two ethernet
+  cards in your machine with a different IP-number.
+  If you specify 'y' here, then you can "bind" a server to one of your
+  interfaces.  You should use the command line option '-w' to tell the
+  server to which interface to bind to.  No error is reported if this
+  fails, the server will simply not run.
+  If no '-w' option is given then the server name specified in the
+  'M: line' of the "ircd.conf" file of the server is used, provided it
+  resolves to an IP-number of one of your interfaces.  Note that
+  normally the name does not have to resolve, but when you define this,
+  it MUST resolve or you must use the -w command line option, or the
+  "bind" will fail.
+  If you are unsure, specify 'n'.
+
+Will you connect to more then one server at a time
+HUB
+  All servers of one IRC "network" are connected in a "tree" (no loops).
+  Servers that are only connected to one other server (called the
+  'uplink') are called "leafs", servers that are connected to more then
+  one other server are called HUBs.
+  If you specify 'n' here then your server will prevent itself from acciden-
+  tally connecting to two servers at once, which is good because this is
+  generally bad for servers in "leaf" positions (they are net.wise located
+  too bad to route traffic).  Note that on Undernet all newly linked servers
+  are linked as leafs during their test phase, and should specify 'n' here.
+
+Do you want support for the old I:*:ONE:*:: construct
+USEONE
+  Server versions prior to ircu2.10.05 used to use the string "ONE"
+  as password in an I: line to indicate that only one connection was
+  allowed for any given IP number that matched that I: line.
+  This method only counted the *local* connections though.
+  As of ircu2.10.05 you can specifiy a single(!) digit as password
+  which then will allow that many connections from the same IP number.
+  However, now the IP numbers of ALL clients are counted, also those
+  that are connected to other servers.
+  If you do not use the depricated "ONE" password in your ircd.conf,
+  specify 'n' here.  Note that if you you DO use the "ONE" password
+  and you specify 'n' here, then you should change all occurances of
+  "ONE" to "1" (this is the recommended procedure).
+  If you are lazy and you don't want to change the "ONE" passwords
+  into a "1", then specify 'y' here.
+
+Send a short message instead of the MOTD to connecting clients
+NODEFAULTMOTD
+  Every time a client connects to your server, the full Message Of
+  The Day (as specified in its file MPATH) is sent to the client.
+  Even while many clients allow the user to ignore the message of
+  the day: the server still sends it. Many users never read the
+  message of the day anyway, making it a huge waste of bandwidth.
+  If you specify 'y' here than the server won't send the MOTD by
+  default to the client, but rather tell the client when the MOTD
+  was last changed and how to receive the MOTD by typing /MOTD.
+  If unsure specify 'n'.
+
+Do you want to enable debugging output
+DEBUGMODE
+  Sometimes things just don't work.  This doesn't have to be a crash,
+  but it is also possible that your server just doesn't want to start
+  at all, or disallows clients to connect at all, etc.
+  With all such drastic and REPRODUCIBLE problems, it makes sense to
+  recompile the server with this option set and then running the
+  ircd (irc daemon) with the (extra) command line options: -t -x9
+  This will make the server run in the foreground and write debug output
+  to the terminal; in a lot of cases this can give a clue on what is
+  wrong (although more often it doesn't).
+  Because defining DEBUGMODE uses a LOT of cpu and is never useful
+  unless you are debugging a reproducible test case, you should never
+  specify 'y' here except for the reason just mentioned.
+  You should certainly NEVER specify 'y' for a server that runs on a
+  production net.
+
+Do you want memory- allocation and/or leak checking
+DEBUGMALLOC
+  If you specify 'y' here, then the server will start to do book keeping
+  on the allocated memory blocks.  This uses extra cpu and memory,
+  so normally you do not want this - unless you are debugging.
+  This option uses 8 bytes extra per allocated memory block.
+  The main purpose of this option is to check if a call to free(2) is done
+  with a valid pointer - if the pointer was not previously returned by
+  malloc(2), calloc(2) or realloc(2), the server will core dump in a place
+  that allows the maintainer to get an idea of what went wrong - but only
+  when the server was compiled with the -g flag of course.
+  You also need to specify 'y' here if you want to search for memory leaks.
+  On a production server, specify 'n' - unless you have lots of cpu to
+  spare and you volunteer to search for memory leaks - contact the
+  maintainer in this case.
+  If unsure, specify 'n'.
+
+Do you want to have boundary checking
+MEMMAGICNUMS
+  One of the most nasty bugs are those where buffer overruns are involved.
+  In an attempt to catch those in an early stage, this option will add
+  so called "magic numbers" to the beginning and end of each allocated
+  memory block.  When a block is freed or reallocated, the magic numbers
+  are checked and the server core dumps when they were corrupted.
+  This option uses 12 bytes extra per allocated memory block.
+  It doesn't really use much extra cpu compared to defining DEBUGMALLOC, so
+  you might as well specify 'y' here, just in case.  It only makes sense
+  though if you compiled the server with compiler option '-g'.
+  If unsure, specify 'n'.
+
+Do you want memory leak testing (stats M)
+MEMLEAKSTATS
+  If you specify 'y' here then the server will start to do extra book keeping
+  on the allocated memory blocks, counting the number of currently allocated
+  blocks per source code location (file and line number).  You will be able
+  to retrieve these statistics with the command /stats M.
+  When there is a memory leak, then allocated memory blocks that were allocated
+  under certain conditions are never freed (however the contents of those
+  memory blocks are never used anymore); this would result in a (slow?)
+  increase of the count of allocated memory blocks.  This option allows to
+  find where these blocks were allocated which might give a clue on the memory
+  leak problem.
+  This option uses 4 bytes extra per allocated memory block.
+  If you want to look for memory leaks, specify 'y' - otherwise specify 'n'.
+
+Do you want extra info on allocated sizes
+MEMSIZESTATS
+  If you specify 'y' here then the server will start to do extra book keeping
+  on the sizes of the allocated memory blocks.  /stats M will not only return
+  the number of allocated blocks, but also the total number of allocated
+  bytes involved.  If you defined MEMLEAKSTATS to look for memory leaks, it
+  will give the total number of allocated memory per source code location
+  (file and line number).
+  This option uses 4 bytes extra per allocated memory block, unless you already
+  specified 'y' for MEMMAGICNUMS (boundary checking), because in that case
+  it was already included (and it doesn't matter what you specify here).
+  I think you should specify 'y' here, its more fun to see the sizes :).
+
+Do you want support for a time interval with /stats M
+MEMTIMESTATS
+  If you specify 'y' here then the server will start to do extra book keeping
+  on the allocated memory blocks, storing the time at which the memory block
+  was allocated.  This especially slows down /stats M (but unless you use
+  that command frequently, it shouldn't really matter) and uses again 4 bytes
+  of extra memory per allocated memory block.
+  This option is especially useful if you are looking for memory leaks
+  because it allows you to specify a time window with /stats M for which
+  counted blocks must be returned.  This allows to ignore recently allocated
+  blocks and permanently allocated blocks (since the start of the server).
+
+Are you testing on a host without DNS
+NODNS
+  If you are playing with the server off-line, and no DNS is available, then
+  long delays occur before the server starts up because it tries to resolv
+  the name given on the M:line (which usually isn't given in /etc/hosts) and
+  for each connecting client.
+  If you specify 'y' here, then a call to gethostbyname() will be done only
+  for the real hostname, and the server will not try to resolv clients that
+  connect to `localhost'.
+  Note that other calls to gethostbyname() are still done anyway if you
+  use VIRTUAL_HOST and that the server still tries to resolv clients
+  that connect to the real IP-number of the server.
+
+Directory where all ircd stuff sits
+DPATH
+  DPATH is provided so that the other path names may be provided in just
+  filename form.  It is the Default PATH.  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 the server core dumps.
+  Note that you should not include quotes here.
+  Note also that the command line option "-d <dir>" overrides the DPATH
+  you give here, except for the chroot (if you use that).
+
+Server configuration file
+CPATH
+  This is the IRC daemon Configuration filename, mostly called "ircd.conf".
+  If you just specify the filename, the server will read its configuration
+  file from the Default Path "DPATH", which you specified above.  However,
+  you are also allowed to specify a full path.
+  Note that you should not include quotes here.
+
+Server MOTD file
+MPATH
+  MPATH is the filename, relative to DPATH, or the full path, of the
+  "Message Of The Day" file; mostly called "ircd.motd".  The contents
+  of this file will be sent to every client that connects to the server,
+  after registration.
+  Note that you should not include quotes here.
+
+Server remote MOTD file (3 lines max)
+RPATH
+  RPATH is the filename, relative to DPATH, or the full path, of the
+  "Remote Message Of The Day" file; mostly called "remote.motd".  The
+  contents of this file will be sent to every remote client that issues
+  a /MOTD <your server name>.  Only the first three lines are sent, so
+  you might want to keep it that short ;).
+  Note that you should not include quotes here.
+
+File for server pid
+PPATH
+  PPATH is the filename, relative to DPATH, or the full path, of the
+  "PID file", mostly called "ircd.pid".  It is used for storing the
+  server's PID so a ps(1) isn't necessary.
+  Note that you should not include quotes here.
+
+Do you want to log the use of /WHO x% (recommended)
+CONFIG_LOG_WHOX
+  Specify 'y' here if you want to log the use of /WHO ... x%... by your
+  Opers (see doc/readme.who).  This is highly recommended since it will
+  reduce the abuse of this `spy' function.  Note: You can disable this
+  spy function completely below, in which case you can give 'n' here.
+  If unsure specify 'y'.
+
+Give the path and(or) filename of this log file
+WPATH
+  WPATH is the filename, relative to DPATH, or the full path, of the
+  log file where the use of /WHO ... x% ... by your Opers will be logged
+  (see doc/readme.who), mostly called "whox.log".
+  Note that you should not include quotes here.
+
+Do you want to log G-lines to a separate file
+CONFIG_LOG_GLINES
+  Specify 'y' here if you want to log G-lines (Global access bans)
+  to a local file.
+
+Give the path and(or) filename of this log file
+GPATH
+  GPATH is the filename, relative to DPATH, or the full path, of the
+  log file where the G-lines will be stored, mostly called "gline.log".
+  Note that you should not include quotes here.
+
+Do you want to log connecting users to a separate file
+CONFIG_LOG_USERS
+  Specify 'y' here if you want to log who is connecting to your server.
+  This file can grow VERY fast on a large net work, so you probably
+  want to specify 'n' here.
+
+Give the path and(or) filename of this log file
+FNAME_USERLOG
+  Here you need to specify the name of the log file where the server
+  should write the data about connecting users to. You can also specify
+  a full path.  Note that you should not include quotes here.
+
+Do you want to log Opers to a separate file
+CONFIG_LOG_OPERS
+  Specify 'y' here if you want to log who is successfully becoming an
+  IRC Operator on your server.
+
+Give the path and(or) filename of this log file
+FNAME_OPERLOG
+  Here you need to specify the name of the log file where the server
+  should write the data about Opering users.  You can also specify a
+  full path.  Note that you should not include quotes here.
+
+Do you want to use syslog
+USE_SYSLOG
+  If you are the sys admin of this machine, or if you have permission
+  of the sys admin to do so, you can let the server write data about
+  certain events to the syslog.  You will be prompted for the events
+  that you want to log being one or more of: KILL's, SQUIT's, CONNECT's,
+  OPERing, Connecting Users and finally the log facility.
+  If you are unsure, specify 'n'.  It is probably not a good idea to use
+  this on a large IRC net work.
+
+Log all operator kills to syslog
+SYSLOG_KILL
+  Specify 'y' here if you want all KILLs to be written to syslog.
+  Note that on a large IRC net work this is a LOT of data.
+
+Log all remote squits for all servers to syslog
+SYSLOG_SQUIT
+  Specify 'y' here if you want all SQUITs to be written to syslog.
+  Note that on a large IRC net work this is a LOT of data.
+
+Log remote connect messages for other all servers
+SYSLOG_CONNECT
+  Specify 'y' here if you want all CONNECTs to be written to syslog.
+  Note that on a large IRC net work this is a LOT of data.
+
+Log all users who successfully become an Oper
+SYSLOG_OPER
+  Specify 'y' here if you want all OPERs to be written to syslog.
+  Note that on a large IRC net work this is a LOT of data.
+
+Send userlog stuff to syslog
+SYSLOG_USERS
+  Specify 'y' here if you want all connecting users to be written to syslog.
+  Note that on a large IRC net work this is EXTREMELY MUCH data.
+  You really want to specify 'n' here.
+
+Log facility (daemon, user, local0-7)
+CONFIG_DAEMON
+  Well if you got this far and still need help, then I think you should
+  go back and specify 'n' at the question "Do you want to use syslog".
+
+Which local facility (0-7)
+INT_LOCAL
+  Well if you got this far and still need help, then I think you should
+  go back and specify 'n' at the question "Do you want to use syslog".
+
+Use m4 as a preprocessor on CPATH
+M4_PREPROC
+  If you use m4 macro's in your "ircd.conf" file, then you need to specify 'y',
+  which will enable m4 preprocessing of the "ircd.conf" file.
+  If you are unsure specify 'n'.  Note using m4 macros has often lead to
+  problems (passwords or server names that match a macro name for instance),
+  while the benefits of using m4 are highly doubtful.  Unless you are
+  already a m4 wizard and insist on using it I recommend to specify 'n' here.
+
+Use crypted passwords for N: lines
+CRYPT_LINK_PASSWORD
+  In order to allow other servers to connect to you, you need to specify
+  two configuration lines in the "ircd.conf" configuration file (CPATH).
+  Each of these lines contains a password; the C: line is used for connecting
+  to a remote server and contains the password that is sent to the remote
+  server for authentication, thus this password must be in plain text.
+  The other is the N: line and contains the password that the remote server
+  is sending to you.  For security reasons it is advised to store this
+  password in DES encrypted form.  If you specify 'y' here, you will be
+  allowed to use the DES encrypted password in the password field of the
+  N: line, see doc/example.conf for more details.  Note that you should
+  use *different* passwords in the C: and N: lines respectively for obvious
+  reasons.
+
+Use crypted passwords for operators
+CRYPT_OPER_PASSWORD
+  In order to allow certain users to become IRC OPERators, they must
+  authenticate themselves with a password.  This password is matched
+  against an 'O: line' in the "ircd.conf" configuration file, see
+  doc/example.conf for more details.  If you specify 'y' here, you are
+  allowed to use the DES encrypted form of these passwords in your
+  "ircd.conf" file (even more, your Opers don't have to tell you their
+  real password, they can provide the DES encrypted form themselves).
+  Since it has happened often in the past that the "ircd.conf" file
+  was compromised somehow, you are highly encouraged to specify 'y' here
+  and use the DES encrypted form.  You can find a program 'mkpasswd' in
+  the ircd/crypt directory that will allow you to generate the encrypted
+  form.
+
+Max size of the total of of all sendqs (bytes)
+BUFFERPOOL
+  This specifies the maximum amount of RAM that your server will allocate
+  for buffering sendQ's.  Small leafs can use a value as little as 1000000,
+  while large HUBs need to specify a value as high as 20000000.
+  If you run out of memory, clients and/or servers are dropped with the
+  error "Buffer allocation error".  Then you will have to up this number
+  (and install more RAM if appropriate).
+  If you want a more educated guess for this value then realize that any
+  value is good if you _really_ rather want to drop servers and clients
+  then allocate more memory; this will be the case when there is the
+  danger to run out memory for other allocations.
+  Even if you run the daemon on a dedicated machine, then specifying the 
+  maximum of the RAM you have is a Bad Thing because really running out 
+  of memory is a lot worse then dropping clients in a controlled way:
+  if possible you should have memory left for all the internal structures
+  (channels, clients, banlists, receive buffers) at all times.
+  On average, clients seem to use 150 bytes of sendQ, but at peak moments
+  this can easily increase to 2032 bytes per client (sendQs are allocated
+  in chunks of 2032 bytes).
+  The maximum possible ammount that can be allocated for sendQs is the
+  number of connected clients times whatever you specified as maximum
+  sendQ in your Y: lines in the ircd.conf file.  Likely, that value will
+  be larger then the ammount of RAM you have.
+  The educated guess I talked about earlier would be 'number of clients'
+  times * 2048 bytes + 'size of net.burst' * n, where `n' is 1 for leafs
+  and up till 5 for HUB's.  The 'size of net.burst' is about 125 bytes
+  per online client (on the total network).
+  For large HUBs with 4000 clients on undernet (30,000 users), this results
+  in 27 Mb.  Leafs could use 12 Mb.  Of course you can use less when you
+  have less than 4000 local clients.
+  Don't forget to specify this value in bytes.
+
+Max receive queue for clients (bytes)
+CLIENT_FLOOD
+  Currently, everything that a client sends to a server is read by the server
+  and stored in a buffer (the clients receive queue).  The server will
+  process messages from this queue one by one (running over all clients
+  each time).  When a client sends new messages faster they get processed,
+  and the size of its receive buffer reaches this value, the client is
+  dropped with the error "Excess flood".  A reasonable value is 1024 bytes.
+  The maximum size is 8000 bytes.
+
+Maximum number of network connections (23 - (FD_SETSIZE-4))
+MAXCONNECTIONS
+  This specifies the maximum number of network connections the server
+  will use.  You also need some non-network connects (log files etc), so
+  the maximum value is "FD_SETSIZE-4".  The minimum value is 23.
+  The only benefit of using a small value is that your server uses less
+  memory - but *only* when you really have a small (client) load.
+  Routing server that hardly take clients can use 128 here for instance.
+  Servers that are always full should just specify the maximum amount
+  that still works (which might be less then FD_SETSIZE-4, some OS need
+  kernel hacking to allow more then 1024 fds per process). The only max.
+  value that is guaranteed to work is 252 ;).  Note that if the value of
+  FD_SETSIZE is for instance 1024, then that doesn't mean you can't
+  connect MORE clients - but in this case you certainly need kernel
+  hacking.  Find an experienced admin with the same Operating System and
+  ask him what the maximum is and how to achieve it.
+
+Default client listen port
+PORTNUM
+  This is the default listen port.  You can specify more listen ports
+  in the "ircd.conf" file with 'P: lines'; see doc/example.conf for more
+  details on P: lines.  Note that /stats p currently only shows P: lines,
+  which might be a reason for you to use a less often used value here.
+  Note that there is actually no difference between client and server
+  listen ports, but it is good practice to separate them for statistical
+  purpose (bandwidth usage statistics).
+
+Nickname history length
+NICKNAMEHISTORYLENGTH
+  This value specifies the length of the nick name history list, which
+  is only used for /WHOWAS.  It uses about 300 to 400 bytes per entry.
+  Note that at a net.break so many client disappear that the whole
+  "whowas" list refreshed a few times (unless you make it as big as
+  20,000 of course - but you shouldn't because thats a waste of ram
+  and cpu).  A reasonable value is 'total number of clients' / 25.
+
+Allow Opers to see (dis)connects of local clients
+ALLOW_SNO_CONNEXIT
+  If you specify 'y' here, you will be allowed to see all client connects and
+  disconnects as a server notice.  The historical reason for adding this
+  option was to detect clone bots that connected to your server.  However,
+  on a large IRC network like Undernet, the number of clients that connect
+  are so huge that it is not possible to keep an eye on this and everyone
+  has been filtering these notices out anyway.  Next to that it turned out
+  to use no less then 10% of the total cpu usage last time I measured it
+  (this has been improved after that, but still).
+  Unless you insist on seeing those notices you should specify 'n' here.
+  Note that in the meantime Undernet has a LOT of other (semi- and fully-
+  automated) ways to detect clone bots, which work a LOT better for this
+  purpose.
+
+Show IP address in client connection notices
+SNO_CONNEXIT_IP
+  Usually when showing a client connection, a nick, userid and hostname are
+  displayed.  Selecting 'y' here will also display the numeric IP and connection
+  class of the connecting client. This can be useful for detecting spoofed DNS and
+  virtual hosted clones. This does use extra CPU though and is generally not needed, 
+  however if a connection monitor bot is the only client that looks at these 
+  notices, it is more efficient than sending USERIP for every connection. This
+  option makes the server compatible with Hybrid tcm bots.
+
+Do you want to use R: lines in your configuration file
+R_LINES
+  If you specify 'y' here you will be allowed to use R:lines in the "ircd.conf".
+  This allows more freedom in restricting connections to your server by
+  calling an external program to determine whether to allow the connection.
+  It also uses a lot of overhead however, and can bog things down, so you should
+  consider whether you really need them, and if you can handle the extra load.
+  If unsure, specify 'n'.
+
+Process R: lines every rehash
+R_LINES_REHASH
+  You may not want to have the R: lines checks everywhere since this can
+  cost a lot of time and delays.  If you specify 'y' here, then R: lines are
+  checked whenever the "ircd.conf" file is reloaded (when the REHASH command
+  is used, or a signal SIGHUP is received by the daemon).  This shouldn't be
+  too much of a drain on the system if the R:lines programs are short.
+
+Process R: lines always
+R_LINES_OFTEN
+  If you specify 'y' here then R: lines will be checked as often as K: lines.
+  Note that this is -very- likely to cause a severe drain on your resources.
+  Use at your own risk, specify 'n' unless your really sure.
+
+Allow (local) Opers to see all local invisible users
+SHOW_INVISIBLE_USERS
+  If you specify 'y' here, then your (local) IRC Operators will be able to
+  see all local invisible users (clients connected to your own server).
+  The reason for this is to hunt for clone bots, make sure your Operators do
+  not use this "feature" for spying on individuals and respect the user that
+  wishes to be invisible (mostly meaning that they don't want to be found when
+  on certain channels).
+  Note: If you answer 'n' here, then you will also not be able to see remote
+  invisible users (if you specify 'y' you will also get a configuration
+  question that asks you to specify whether or not you want your Opers to see
+  remote invisible users or not).
+
+Allow Opers to see all invisible users
+SHOW_ALL_INVISIBLE_USERS
+  If you specify 'y' here, then your global IRC Operators (O:) will be able
+  to see ALL invisible users.  The reason for this is to hunt for clone bots,
+  make sure your Operators do not use this "feature" for spying on individuals
+  and respect the user that wishes to be invisible (mostly meaning that they
+  don't want to be found when on certain channels).
+
+Allow global Opers (O:) to see inside secret channels
+OPERS_SEE_IN_SECRET_CHANNELS
+  If you specify 'y' here, then your global IRC Operators (O:) will be able
+  to see who is on a specified, secret channel, without joining themselfs.
+  This can be needed to make a reasonable judgement in the case of a "channel
+  takeover" being reported, while the channel is set invite only.
+  See doc/readme.who for more details.
+
+Allow local Opers (o:) to see inside secret channels
+LOCOP_SEE_IN_SECRET_CHANNELS
+  If you specify 'y' here, then your local IRC Operators (o:) will be able
+  to see who is on a specified, secret channel, without joining themselfs.
+  This can be needed to make a reasonable judgement in the case of a "channel
+  takeover" being reported, while the channel is set invite only.
+  See doc/readme.who for more details.
+  If unsure, specify 'n'.
+
+Don't truncate obnoxiously long /who output for opers
+UNLIMIT_OPER_QUERY
+  A /who command can sometimes return several hundred lines of info. To
+  reduce flooding and sending too much, the output is truncated. By
+  answering 'y' to this, when an IRC Operator uses /who, the output will
+  not be truncated, no matter how much data is returned.
+
+Allow Opers to use the KILL command
+OPER_KILL
+  You can select 'n' if you don't think operators should be able
+  to use the KILL command, and wish to prevent your operators from
+  using it.  This will not, however, prevent operators on other
+  servers from issuing KILLs to your clients.  You probably want to
+  select 'y' for this unless you really really don't think KILL
+  should -ever- be used by an operator.
+
+Allow Opers to use the REHASH command
+OPER_REHASH
+  Allows operators to use the REHASH command to reload the servers
+  configuration file (ircd.conf) if you select 'n', you can still
+  reload the configuration file with a unix command,
+  kill -HUP `cat ircd.pid`.  If unsure, select 'y'.
+
+Allow Opers to use the RESTART command
+OPER_RESTART
+  Allows an operator to use the RESTART command to cause the server
+  to restart, using the ircd executable in SPATH.  If unsure, select 'y'.
+
+Allow Opers to use the DIE command
+OPER_DIE
+  Allows an operator to use the DIE command to shutdown the server
+  online.  If you select 'n' you will need to send the server a kill
+  signal to shutdown the server.  If unsure, select 'y'.
+
+Allow Opers to add local G-lines
+OPER_LGLINE
+  Allows operators to add local G-lines with the GLINE command.  This is
+  like a *local* KILL, except that the user being killed can't immediately
+  reconnect: He will have to wait for the G-line to expire.
+  The reason for adding this is that a KILL is rather useless for removing
+  (or 'warning') abusers (it is still THE command to remove ghosts and
+  a-like, the reason KILL was added in the first place).  However, adding
+  G-lines for a dynamic IP with expire times larger then 10 minutes is highly
+  discouraged: The user will already have dialed in via another IP or account
+  and the G-line would only harm other, innocent, users.
+
+Allow Opers to connect from a remote site
+OPER_REMOTE
+  If you select 'n' for this, clients must be on the 'same network' as
+  the server in order to gain oper privledges.  If you're not sure, just
+  select 'y'.
+
+Allow local opers to use the REHASH command
+LOCOP_REHASH
+  Allows a local operator (defined by a lowercase o:line in ircd.conf)
+  to cause the server to reload its configuration file (ircd.conf) with
+  the REHASH command.  If unsure, select 'n'.
+
+Allow local opers to use the RESTART command
+LOCOP_RESTART
+  Allows a local operator (defined by a lowercase o:line in ircd.conf)
+  to use the RESTART command.  If unsure, select 'n'.
+
+Allow local opers to use the DIE command
+LOCOP_DIE
+  Allows a local operator (defined by a lowercase o:line in ircd.conf)
+  to use the DIE command.  If unsure, select 'n'.
+
+Allow local opers to add local G-lines
+LOCOP_LGLINE
+  Allows a local operator (defined by a lowercase o:line in ircd.conf)
+  to add local G-lines with the GLINE command.  This is like a *local* KILL,
+  except that the user being killed can't immediately reconnect: He will
+  have to wait for the G-line to expire.
+
+Do you want to have a default LIST parameter
+CONFIG_LIST
+  Pre-Undernet, the LIST command could either be given with one channel
+  name, or without any parameter.  In the last case it would simply list
+  all channels.  In time the IRC networks grew big, until the output of
+  the LIST command always filled up the sendQ of the client (and dis-
+  connected it).  This was fixed by Carlo Wood (Run@IRC) on request of a
+  Dutch ISP whose users complained about this:  The LIST output is now
+  generated in small chunks, generating more each time when there is room
+  in the clients sendQ.  However, then it turned out that LIST (now it
+  worked) used 50% of all cpu (not even mentioning the bandwidth)...
+  This was unacceptable and the mentioned patch was disabled.  On the
+  other hand we wanted LIST to work at least partly, so a few new
+  parameters have been added to LIST: <,>,C<,C>,T<,T> each followed by
+  a number they filter respectively the number of users on the channel,
+  the creation time of the channel (or age, depended on the value of
+  the number) and the topic set time.
+  If you specify 'y' here, then each time a "/LIST" (without parameter)
+  is issued by a client, a default parameter is used.  Note that when
+  a parameter is used, the client can still max. sendq out - the send
+  flood control only works without any parameter.
+  If you specify 'n' here then a "/LIST" without parameters will list
+  all channels (and work), but as just said: it uses a LOT of cpu and
+  bandwidth on a large net.work.
+  If you specify 'y' you will be prompted for the default parameter.
+
+Give default LIST parameter
+DEFAULT_LIST
+  Here you need to specify the default LIST parameter which is used
+  when the server receives a LIST without any parameter.
+  You should use something that limits the output to a maximum of a
+  few hundred channels; for instance "T<10" (topic is set less then
+  10 seconds ago) or ">10" (more then 10 users on the channel) or even
+  a combination of this.  Note that you should not include quotes here.
+
+K: line comments treated as a file
+COMMENT_IS_FILE
+  If you specify 'y' here, then K: line comments (see doc/example.conf
+  for more details on the K: line syntax) will be treated as a filename
+  by default. The file needs to exist and will be written to clients
+  that match that K: line.
+  If you specify 'n' here, then K: line comments will be treated as
+  a comments by default.
+  In both cases you can override the default by prepending a filename
+  with a '!' or enclose a comment between double quotes.
+  If unsure, use the default.
+
+Only nullify idle-time on PRIVMSG
+IDLE_FROM_MSG
+  The IRC command WHOIS gives an idle time for clients.  If you want that
+  this idle time is set to zero only when the clients send a PRIVMSG,
+  then you should specify a 'y' here.
+  If you specify a 'n' then the idle time will be nullified on all messages
+  except the server PING/PONG.
+
+Check clone limit (2!)
+CHECK_CLONE_LIMIT
+  Set this to 2.
+
+Check clone period (20!)
+CHECK_CLONE_PERIOD
+  Set this to 20.
+
+Check clone delay (600!)
+CHECK_CLONE_DELAY
+  Set this to 600.
+
+Max auto connects per class (1!)
+MAXIMUM_LINKS
+  Set this to 1.
+
+Enable message logging
+MSGLOG_ENABLED
+  Define this if you want the server to log received messages in static memory
+  at parsing time.  -This is for debugging purposes only-.  You might want to
+  redefine LOG_MASK_TYPE in s_debug.h and LOG_MASK_LEVEL in s_debug.c too.
+  The default is to log all messages that change some status in server's data
+  structures.  Select 'n' unless you are debugging the server code.
+  DO NOT SELECT THIS ON PRODUCTION SERVERS!
+
+Message log size
+MSGLOG_SIZE
+  Number of messages to log. Keep this low as raising it to 1024 will use 800k
+  of _static_ memory! Recommended value is 128. You must include this even if
+  you selected 'n' for MSGLOG_ENABLED.
+
+Only allow KILLs of local clients
+LOCAL_KILL_ONLY
+  This only allows operators of this server to KILL clients directly connected
+  to this server.  Operators will not be able to issue KILLs for clients on
+  other servers.  Some networks (not Undernet) require that this be defined
+  for newly linking servers, but if you haven't been told to do otherwise,
+  specify 'n' here.
+
+Max server idle time (60)
+TIMESEC
+  This is the maximum idle time for the server. If no messages are received
+  in TIMSEC seconds, PINGFREQUENCY and CONNECTFREQUENCY are checked.
+  Recommended value is 60 seconds.
+
+KILL nick chase time limit (30)
+KILLCHASETIMELIMIT
+  This is the maximum amount of time a KILL command will automatically change
+  to the current nick of a user that has just changed nicks from the one given
+  with the original KILL.  Don't change this unless you really need to.
+
+Max number of channels per user (recommended: 5)
+MAXCHANNELSPERUSER
+  This is the maximum number of channels a user can be in at a time.
+  The "mandatory" value on Undernet is currently 10.  Since it only
+  influences the local server when you decrease it, its up to you to decide
+  if you want to use a smaller value.  Do not use a larger value however,
+  because it DOES cost more memory and bandwidth on all other servers
+  when you allow users to join more channels simultaneously.
+  One of the most important reasons to choose a smaller value is the fact
+  that the now-a-days 'GUI' clients tend to stay on every channel they
+  join (they aren't bothered by flooding in other channels).  It DOES take
+  your bandwidth however to send all those messages for 10 different
+  channels to all your users.
+
+Max number of silence masks (15!)
+MAXSILES
+  This is the maximum number of masks a user can silence at a time.
+  The silence command allows users to filter messages directed at them
+  from certain users or domains, at the source server.  Increasing this
+  number allows users to use up more memory with inefficient use of the
+  command.  If your not sure, don't change this.
+
+Expected average banmask length (40!)
+AVBANLEN
+  This is the expected average banmask length.  Leave it at 40.
+
+Use .config of THIS source tree as your upgrade default
+CONFIG_NEW
+  Each source tree keeps its *own* config/.config file with the default
+  values for all questions (those that you gave the last time you did a
+  'make config').  Whenever you do 'make config' again in this source tree,
+  you will get these defaults.
+  However, when you *upgrade* to a new version (and get a NEW source tree),
+  it doesn't have a .config yet and will (try to) use a .config of one of
+  your previous source trees.
+  If you specify 'y' here, then the last defaults of THIS source tree will
+  be used in your next upgrade.  Note that any changes you make later to
+  config/.config (by running 'make config' again) will also take effect
+  on this later upgrade.
+  You can always change this by making a new hard link from .config ->
+  ircu2.10.xx/config/.config in the directory where you keep the source
+  trees.
+  If unsure, and you are not currently installing a test source tree,
+  specify 'y'.  If this is a second source tree that you will only be
+  experimenting with, specify 'n'.
+
+Class 0 ping frequency (120)
+PINGFREQUENCY
+  If the daemon doesn't receive anything from any of its links within
+  PINGFREQUENCY seconds, then the it 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.  This value is overridden by
+  a Y:line in "ircd.conf" if the connections I/C/N: line in "ircd.conf" assigns
+  a specific class to the connection (recommended).
+
+Class 0 connect frequency (600)
+CONNECTFREQUENCY
+  This is the default frequency that the server attempts to reconnect with
+  its uplink server if it is set to auto connect to it. Note that this value
+  is overridden by a Y:line in ircd.conf if the C/N:lines in ircd.conf
+  assigns a specific class to the connection (recommended).
+
+Min time before a link is good (300)
+HANGONGOODLINK
+  Often the net breaks for a short time and its useful to reestablish the
+  same connection faster than CONNECTFREQUENCY would allow, but to keep
+  from trying again on a bad connection, we require that the connection be
+  open for a certain minimum time. The recommended value is 300 seconds.
+
+Wait before reconnecting to good link (10!)
+HANGONRETRYDELAY
+  When attempting to quickly reestablish a connection to a good link, we
+  give the net a few seconds to steady. This time must be long enough for
+  the other end to notice it broke too. The recommended value is 10 seconds.
+
+connect(2) timeout (90!)
+CONNECTTIMEOUT
+  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
+  lookup to complete. It is recommended you don't change this value, but if
+  you do, consider the fact that users whose clients do not support NOSPOOF
+  will have to type /QUOTE PING <big number> before registration.
+
+Max send queue (40000)
+DEFAULTMAXSENDQLENGTH
+  This is the default value of the "max. sendq length" of Y: line classes
+  (see doc/example.conf for details on Y: lines).  You will probably
+  always override this value in your "ircd.conf" with the Y: lines.
+  The given value used to be an often used value for client sendqs.
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100644 (file)
index 0000000..6e3a405
--- /dev/null
@@ -0,0 +1,57 @@
+# doc/Makefile for the Undernet IRC Daemon.
+# Copyright (C) 1997, Carlo Wood <carlo@runaway.xs4all.nl>
+
+# 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., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+prefix=@prefix@
+INSTALL=@INSTALL@
+SHELL=@SHPROG@
+RM=@RMPROG@
+@SET_MAKE@
+# The following variables are replaced by what you give during configuration :
+
+MANDIR=
+
+#### End of system configuration section. ####
+
+all:
+
+distclean:
+       ${RM} -f Makefile stamp-m
+
+maintainer-clean: distclean
+
+install:
+       (test -d ${MANDIR}/man8 || mkdir ${MANDIR}/man8 || mkdir -p ${MANDIR}/man8) 2> /dev/null && ${INSTALL} -m 644 ircd.8 ${MANDIR}/man8
+
+uninstall:
+       ${RM} -f ${MANDIR}/man8/ircd.8
+
+# You need GNU make for this to work.
+Makefile: ../config/config.status Makefile.in ../config/gen.doc.Makefile \
+         ../config/.config stamp-m
+       @echo "recreating doc/Makefile"
+       @cd ../config; \
+       CONFIG_FILES=../doc/Makefile CONFIG_HEADERS= ./config.status > /dev/null; \
+       RM=${RM} ${SHELL} ./gen.doc.Makefile
+
+stamp-m:
+       echo timestamp > stamp-m
+
+../config/config.status:
+       @cd ../config; ${MAKE} config.status
diff --git a/doc/example.conf b/doc/example.conf
new file mode 100644 (file)
index 0000000..c1893ee
--- /dev/null
@@ -0,0 +1,353 @@
+# ircd.conf  configuration file for ircd version ircu2.9.mu and ircu2.10
+#
+# Written by Niels <niels@undernet.org>, based on the original example.conf,
+# server code and some real-life (ahem) experience.
+#
+# Thanks and credits to: Run, Trillian, Cym, Morrissey, Chaos, Flynn,
+#                        Xorath, WildThang, Mmmm, SeKs, Ghostwolf and
+#                        all other Undernet IRC Admins and Operators,
+#                        and programmers working on the Undernet ircd.
+
+# This is an example of the configuration file used by the Undernet ircd.
+#
+# This document is based on a (fictious) server in Europe with a
+# connection to the Undernet IRC network. It is primarily a leaf server,
+# but if all the other hubs in Europe aren't in service, it can connect
+# to one in the US by itself.
+#
+# All configuration options start with a letter identifying the option,
+# and a colon separated list of options. An asterisk indicates an
+# unused field.
+#
+# Please note that when ircd puts the configuration lines into practice,
+# it parses them exactly the other way round than they are listed here.
+# This means that you should start your I: lines with the "fall through",
+# most vanilla one and end with the most detailed.
+#
+# There is a difference between the ``hostname'' and the ``server name''
+# of the machine that the server is run on. For example, the host can
+# have ``veer.cs.vu.nl'' as FQDN, and ``Amsterdam.NL.EU.undernet.org'' as
+# server name.
+# A ``server mask'' is something like '*.EU.UnderNet.org'', which is
+# matched by 'Amsterdam.NL.EU.undernet.org' but not by
+# 'Manhattan.KS.US.undernet.org'.
+
+#
+# First some information about the server.
+# M:<server name>:*:<description>:<server port>:<server numeric>
+#
+# The <server port> is the port that other servers can connect to.
+# Client ports need to be specified with a P: line, see below.
+#
+# Note that <server numeric> has to be unique on the network your server
+# is running on, must be between 1 and 64, and is not updated on a rehash.
+
+M:London.UK.Eu.UnderNet.org:*:[127.0.0.1] University of London, England:4400:1
+
+#
+# This sets information that can be retrieved with the /ADMIN command.
+# It should contain at least an admin Email contact address.
+# A:<line 1>:<line 2>:<line 3>
+
+A:The University of London:Undernet IRC server:IRC Admins <irc@london.ac.uk>
+
+#
+# All connections to the server are associated with a certain ``connection
+# class'', be they incoming or outgoing (initiated by the server), be they
+# clients, servers or Martians. (Note that ircd doesn't have direct support
+# for Martians (yet?); they will have to register as normal users. ;-)
+# Take the following Y: lines only as a guide.
+# Y:<class>:<ping freq>:<connect freq>:<maximum links>:<sendq size>
+
+# Server classes: 90 = all your uplinks for who you do not wish to hub;
+# else in classes 80 and/or 70.
+# 50 = leaf servers (only used if your server is a hub)
+
+Y:90:90:300:1:1700000
+Y:80:90:300:1:1700000
+Y:70:90:300:1:1700000
+Y:50:90:300:10:1700000
+
+# Client classes. 10 = locals; 2 = for all .net and .com that are not
+# in Europe; 1 = for everybody.
+
+Y:10:90:0:100:160000
+Y:2:90:0:5:80000
+Y:1:90:0:400:160000
+
+#
+# To allow clients to connect, they need authorization. This can be
+# done based on hostmask, address mask, and/or with a password.
+# With intelligent use of classes and the maxconnections field in the
+# Y: lines, you can let in a specific domain, but get rid of all other
+# domains in the same toplevel, thus setting up some sort of 'reverse
+# K: line'.
+# I:<IP mask or crap to force resolving>:<opt passwd>:<hostmask>::<class>
+
+# Technical description (for examples, see below):
+# For every connecting client, the IP-number is know.  A reverse lookup
+# on this IP-number is done to get the (/all) hostname(s).
+# Each hostname that belongs to this IP-number is matched to <hostmask>,
+# and the I: line is used when any matches; the client will then show
+# with this particular hostname.  If none of the hostnames matches, then
+# the IP-number is matched against the <IP mask ...> field, if this matches
+# then the I: line is used nevertheless and the client will show with the
+# first (main) hostname if any; if the IP-number did not resolve then the
+# client will show with the dot notation of the IP-number.
+# There is a special case for the UNIX domain sockets and localhost connections
+# though; in this case the <IP mask ...> field is compared with the
+# name of the server (thus not with any IP-number representation). The name
+# of the server is the one returned in the numeric 002 reply, for example:
+# 002 Your host is 2.undernet.org[jolan.ppro], running version ...
+# Then the "jolan.ppro" is the name used for matching.
+# Therefore, unix domain sockets, and connections to localhost would
+# match this I: line:
+# I:jolan.ppro::foobar::1
+# Finally, I: lines with empty <IP mask ..> or <hostmask> fields are skipped.
+
+# This is the 'fallback' entry. All .uk, .nl, and all unresolved are
+# in these two lines.
+# By using two different lines, multiple connections from a single IP
+# are only allowed from hostnames which have both valid forward and
+# reverse DNS mappings.
+
+I:*@*:1:Unresolved::1
+I:Resolved::*@*::1
+
+# If you don't want unresolved dudes to be able to connect to your
+# server, use just:
+# I:NotMatchingCrap::*@*::1
+
+# Here, take care of all American ISPs.
+I:Resolved::*@*.com::2
+I:Resolved::*@*.net::2
+
+# Now list all the .com / .net domains that you wish to have access...
+# actually it's less work to do it this way than to do it the other
+# way around - K: lining every single ISP in the US.
+# I wish people in Holland just got a .nl domain, and not try to be
+# cool and use .com...
+I:Resolved::*@*.wirehub.net::1
+I:Resolved::*@*.planete.net::1
+I:Resolved::*@*.ivg.com::1
+I:Resolved::*@*.ib.com::1
+I:Resolved::*@*.ibm.net::1
+I:Resolved::*@*.hydro.com::1
+I:Resolved::*@*.NL.net::1
+
+# You can request a more complete listing, including the "list of standard
+# K-lines" from the Routing Committee; it will also be sent to you if
+# you apply for a server and get accepted.
+
+# Ourselves - this makes sure that we can get in, no matter how full
+# the server is (hopefully).
+I:*@193.37.*::*@*.london.ac.uk::10
+
+# You can put a digit (0..9) in the password field, which will make ircd
+# only accept a client when the total number of connections to the network
+# from the same IP number doesn't exceed this number.
+# The following example would accept at most one connection per IP number
+# from "*.swipnet.se" and at most two connections from dial up accounts
+# that have "dial??.*" as host mask:
+# I:Resolved:1:*@*.swipnet.se::1
+# I:Resolved:2:*@dial??.*::1
+
+#
+# It is possible to show a different Message of the Day to a connecting
+# client depending on its origin.
+# T:<hostmask>:<path to motd file>
+
+# DPATH/net_com.motd contains a special MOTD where users are encouraged
+# to register their domains and get their own I: lines if they're in
+# Europe, or move to US.UnderNet.org if they're in the USA.
+T:*.net:net_com.motd
+T:*.com:net_com.motd
+
+# A different MOTD for ourselves, where we point out that the helpdesk
+# better not be bothered with questions regarding irc...
+T:*.london.ac.uk:london.motd
+
+#
+# One of the many nice features of Undernet is ``Uworld'', a program
+# connected to the net as a server. This allows it to broadcast any mode
+# change, thus allowing opers to, for example, 'unlock' a channel that
+# has been taken over.
+# There is only one slight problem: the TimeStamp protocol prevents this.
+# So there is a configuration option to allow them anyway from a certain
+# server.
+# Note: (1) These lines are agreed on by every server admin on Undernet;
+# (2) These lines must be the same on every single server, or results
+# will be disasterous; (3) This is a useful feature, not something that
+# is a liability and abused regularly (well... :-)
+# If you're on Undernet, you MUST have these lines. I cannnot stress
+# this enough.
+# As of ircu2.10.05 is it possible to Jupe nicks. Juped nicks need to be
+# added to U: lines.  As per CFV-0095, the following nicks must be juped,
+# it is not allowed to jupe others as well.
+
+U:Uworld.EU.undernet.org:EuWorld,E,protocol,StatServ,NoteServ,Undernet:*
+U:Uworld2.undernet.org:UWorld2,W,ChanSvr,ChanSaver,ChanServ,COM1,COM2,COM3,COM4:
+*
+U:Uworld.undernet.org:Uworld,X,NickSvr,NickSaver,NickServ,LPT1,LPT2,AUX:*
+
+#
+# While running your server, you will most probably encounter individuals
+# or groups of persons that you do not wish to have access to your server.
+#
+# For this purpose, the ircd understands "kill lines".
+# K:<host/IP mask>:"<opt reason>":<username mask>
+#
+# It is possible to use a file as comment for the ban.
+# K:<host/IP mask>:!<path to file>:<usermask>
+#
+# The default reason is: "You are banned from this server"
+# Note that K: lines are local to the server; if you ban a person or a
+# whole domain from your server, they can get on IRC via any other server
+# that doesn't have them K: lined (yet).
+
+# With a simple comment, using quotes:
+K:*.au:"Please use a nearer server":*
+K:*.edu:"Please use a nearer server":*
+
+# With a file, prepending a '!' before the filename.
+# The file can contain for example, a reason, a link to the
+# server rules and a contact address.
+K:unixbox.flooder.co.uk:!kline/youflooded.txt:*luser
+
+#
+# IP-based kill lines are designated with a lowercase 'k'.  These lines
+# use the same format as normal K: lines, except they apply to all hosts,
+# even if an IP address has a properly resolving host name.
+k:192.168.*:!klines/martians:*
+
+#
+# A more flexible way of restricting access to your server is the use
+# of "restriction lines". These tell the server to start up an (external)
+# program, upon whose output is decided whether the client is allowed
+# access. The program should print "Y" or "N <reason>" on its stdout.
+# Note that the use of R: lines is discouraged and deprecated, needs a
+# compile-time define, eats CPU cycles and may well be taken out in
+# future releases of ircd.
+# R:<host/IP mask>:<program name>:<username mask>
+
+#
+# You probably want your server connected to other servers, so your users
+# have other users to chat with.
+# IRC servers connect to other servers forming a network with a star or
+# tree topology. Loops are not allowed.
+# In this network, two servers can be distinguished: "hub" and "leaf"
+# servers. Leaf servers connect to hubs; hubs connect to each other.
+# Of course, many servers can't be directly classified in one of these
+# categories. Both a fixed and a rule-based decision making system for
+# server links is provided for ircd to decide what links to allow, what
+# to let humans do themselves, and what links to (forcefully) disallow.
+#
+# The Connection and Allowing connection lines (also known as C/N lines)
+# define what servers the server connect to, and which servers are
+# allowed to connect. Note that they come in pairs; they do not work if
+# one if present and the other is absent.
+# C:<remote hostname or IP>:<password>:<remote server name>:<port>:<class>
+# N:<remote hostname or IP>:<password>:<remote server name>:<hostmask>:<class>
+#
+# If you wish to use ident, prepend "username@" to the hostname or IP
+# address (the first field).
+# If the "port" field is omitted, the server will not attempt to
+# establish a link with that server ("not autoconnecting").
+# The (optional) "host mask" field tells the server to represent itself
+# with "hostmask" dot-seperateed fields stripped from its servername
+# and replace it with "*.".
+# For example, if hostmask == 2 and the local server name is
+# "irc.sub.domain.com" it would be sent as "*.domain.com". This allows
+# for easier routing and linking of new servers.
+# This feature is not used on Undernet.
+
+# Our primary uplink.
+C:1.2.3.4:passwd:Amsterdam.NL.Eu.UnderNet.org:4400:90
+N:1.2.3.4:passwd:Amsterdam.NL.Eu.UnderNet.org::90
+
+#
+# If your server starts on a bit larger network, you'll probably get
+# assigned one or two uplinks to which your server can connect.
+# If your uplink(s) also connect to other servers than yours (which is
+# probable), you need to define your uplink as being allowed to "hub".
+# H:<allowed hostmask>::<server name>
+H:*.*::Amsterdam.NL.Eu.UnderNet.org
+
+#
+# Of course, the opposite is also possible: forcing a server to be
+# a leaf. L: lines follow Murphy's Law: if you use them, there's a big
+# chance that routing will be screwed up afterwards.
+# L:<opt disallowed hostmask>::<server mask>:<opt max depth>
+
+#
+# For an advanced, real-time rule-based routing decision making system
+# you can use Disallow lines. For more information, see doc/readme.crules.
+# D:<server mask that ircd will refuse to connect to>::<rule>
+# d:<server mask that ircd will not autoconnect to>::<rule>
+D:*.US.UnderNet.org::connected(*.US.UnderNet.org)
+d:*.EU.UnderNet.org::connected(Amsterdam.NL.EU.*)
+
+# The following line is recommended for leaf servers:
+d:*::directcon(*)
+
+#
+# Inevitably, you have reached the part about "IRC Operators". Oper status
+# grants some special privileges to a user, like the power to make the
+# server break or (try to) establish a connection with another server,
+# and to "kill" users off IRC.
+# I can write many pages about this; I will restrict myself to saying that
+# if you want to appoint somebody as IRC Operator on your server, that
+# person should be aware of his/her responsibilities, and that you, being
+# the admin, will be held accountable for their actions.
+#
+# There are two sorts of IRC Operators: "local" and "global". Local opers
+# can squit, connect and kill - but only locally: their +o user mode
+# is not not passed along to other servers. On Undernet, this prevents
+# them from using Uworld as well.
+# Depending on some defines in include/config.h, local operators are also
+# not allowed to /DIE and /RESTART the server.
+# Local operators are designated with a lowercase 'o'
+# O:<host/IP mask>:<encrypted password>:<Nick>::<connection class>
+# o:<host/IP mask>:<encrypted password>:<Nick>::<connection class>
+
+O:*@*.cs.vu.nl:VRKLKuGKn0jLs:Niels::10
+
+# Note that the <connection class> is optional, but leaving it away
+# puts the O: lines in class 0, which usually only accepts one connection
+# at a time.  If you want users to Oper up more then once per O: line,
+# then use a connection class that allows more then one connection,
+# for example (using class 10 as in the example above):
+# Y:10:90:0:100:160000
+#
+# When your server gets fuller, you will notice delays when trying to
+# connect to your server's primary listening port. Via the Port lines
+# it is possible to specify additional ports (both AF_UNIX and AF_INET)
+# for ircd to listen to.
+# De facto ports are: 6667 - standard; 6660-6669 - additional client
+# ports;
+#
+# These are just hints, they are in no way official IANA or IETF policies.
+#
+# On a side note, the /UPING command uses port 7007/udp. If your server
+# is located behind a firewall, you may want to make another hole in it
+# for this port.
+#
+# P:<hostmask, or path>:::<client port number>
+
+P::::6667
+P::::6668
+P:*.nl:::6666
+P:/tmp/.ircd:::6667
+
+#
+# Well, you have now reached the end of this sample configuration file
+# If you have any questions, feel free to mail <doco-com@undernet.org>
+# or <wastelanders@undernet.org>.
+# If you are interested in linking your server to the Undernet IRC network
+# visit http://www.routing-com.undernet.org/, and if there are any problems
+# then contact <routing-com@undernet.org> asking for information.
+# Upgrades of the Undernet ircd can be found on http://coder-com.undernet.org/.
+#
+# For the rest:  Good Luck!
+#
+#      -- Niels.
diff --git a/doc/history/2.4.notes b/doc/history/2.4.notes
new file mode 100644 (file)
index 0000000..0321b35
--- /dev/null
@@ -0,0 +1,478 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, 2.4.notes
+ *   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.
+ */
+
+IRC 2.4 release notes  6 May 1990/msa (Markku.Savela@vtt.fi)
+============================================================
+
+   This document explains the changes I have done up to this
+point. Some additional changes and packaging has been made by
+Chelsea (chelsea@earth.cchem.berkeley.edu). This is personal
+view of the changes.
+
+CHANGES TO LAST THE OFFICIAL RELEASE (2.2PL1)
+
+   This release of irc2.4 is based to 2.2PL1 release (see the
+HISTORY chapter later in this document). Aside from fixing the
+bugs, this version is in many ways different from the 2.2PL1.
+The purpose of the most changes is to make it easier to run an
+IRC server. Normal users benefit from these changes indirectly
+by getting a better maintained server.
+
+1. Changes visible to normal users
+
+   Even while mainly fixing bugs, some user visible changes have
+crept in too.
+
+1.1 General note on wildcards
+
+   Many commands accept now wildcard matching where applicable. All
+compares are case insensitive (e.g. "a" == "A"). The wild cards are
+
+       ?       matches any single character
+
+       *       matches any number of characters, also empty
+               string. [PL1 had a bug, which caused "*du*"
+               not match "....edu"].
+
+1.2 Server supported wildcards for "/who mask" command.
+
+   Protocol message is "WHO mask", where mask can be
+
+       empty
+       0       List all users [No change from PL1]
+
+       *       List all users on the same channel where the user is
+               (or all, if user is on 0) [No change from PL1].
+
+       number  List all users on the specified channel [No change
+               from PL1]. Note, if the "mask" begings with a digit,
+               this form is assumed, and the remainder of mask is
+               ignored, e.g. "/who 12*.fi" gives all people from
+               channel 12 and ignores the "*.fi" part.
+
+       mask    If the mask is any string, it will be compared
+               *separately* to each information field of the user
+               and if a match is found in any field, that user
+               is included into the list. The fields searched
+               are
+
+                       nickname
+                       loginname (account name)
+                       real name (text shown in parenthesis)
+                       hostname (users machine)
+                       servername (server he/she is using)
+
+               Note: servername is not usually shown on WHO output,
+               but is included in anyway. Example: finding all users
+               somehow connected with Finnish sites, can be achieved
+               with mask "*.fi".
+
+1.3 Changes to /whois command
+
+   As WHO, also /whois accepts wild cards as a parameters. WHOIS
+returns information for all users whose nickname matches the specified
+mask.
+
+   WHOIS automaticly calls WHOWAS [see below], if the attempted nickname
+is not found.
+
+1.4 Short term "WHOWAS" history
+
+   The server has a short in memory cache of the recent nickname changes
+(the current default is set to 200 last changes). The design goal of
+this is that it remembers changes in last few minutes, there is no
+intention of this to be a long term history. That must be a separate
+project, although it could use the hooks provided by this service.
+
+   "WHOWAS nickname" queries this cache and returns about the same
+information that WHOIS would do, if the nickname is found. Wildcards
+are not accepted here, this is a specifically designed feature. If
+the name is not found, WHOWAS doesn't reply anything. This is because
+the most useful use of WHOWAS is implicitly through "WHOIS".
+
+   This history is also implicitly utilized by KILL command.
+
+1.5 New SERVER-SERVER/SERVER-CLIENT protocol message WALLOPS
+
+   The message ":source WALLOPS :Message" sends the message text
+to all operators that are currently online. Any user can use this
+command, it's not restricted. How this function is activated, depends
+on the client, but if nothing else works, "/quote wallops text" should.
+
+   NOTE:This function will not be fully operational until *ALL*
+       servers have upgraded to version 2.4. Also, operators
+       must be using a client that recognizes this command.
+
+   This is really a hasty addition. But, done this way it follows
+the general IRC message philosophy, where messages are sent only
+to links where they are needed (e.g. WALLOPS goes only to servers
+that have opers online--it's not broadcast to every server).
+
+1.6 General use of wildcarding in server queries
+
+   All commands that previously took a servername as a parameter,
+now accept also a wildcarded mask. The mask is replaced with
+the first matching servername. The following user level commands
+are affected
+
+       /admin server   -- administrative info
+       /time server    -- local time
+       /version server -- the server version
+       /motd server    -- "the message of the day"
+       /info server    -- info (usually same on same server version)
+       /stat f server  -- statistics information
+       /users server   -- users logged on server machine
+
+   Note: Remote capability is a new feature for "info" and "stat"
+commands. Until all servers have upgraded, these commands may not
+reach the intended target and may return the information from some
+intermediate server.
+
+1.7 Marking user AWAY
+
+   v2.2PL1 version and earlier showed the AWAY-state (G) only for
+the local users of the same server. AWAY status could be queried
+only by sending a message to a user. This release (or since msa.4)
+broadcasts the away status to every server and the commands /WHO and
+/WHOIS give this information reliably.
+
+   A side effect of this change is: when a user marks himself/herself
+as AWAY, all pre-msa.4 servers that are reached will send back an
+acknowlegde message. Until all servers are upgraged, use of AWAY
+is somewhat inconvenient. If you get extra messages from AWAY,
+they also contain the server information. Use /admin command and
+send a *friendly* request for the admin to upgrage his/her server
+to a working version, namely 2.4 :)
+
+1.8 Servers don't restrict characters within messages
+
+   The parameter fields of the messages can now contain any characters
+in range 1-255, except '\r', '\n' and '\0'. The client programs should
+by default filter away the "dangerous" control characters, but intelligent
+clients can utilize this change and allow exchanges with foreign
+8-bit (or wider) charactersets. (The actual command parts must still be
+represented with the ordinary 7-bit characters.)
+
+2. Changes visible to the server administrator
+
+2.1 Identifying servers
+
+   Servers/clients have now always two names (it was this way in
+PL1, but I think this version makes the idea more clear):
+
+       Announced Name:
+
+       The official name of the server (the name you use in
+       /time, /quote connect, etc) or users nickname. Servers
+       name is usually the hostname, but can actually be almost
+       any string of characters resembling hostname. This one
+       is given in M-line of ircd.conf.
+
+       Socket hostname:
+
+       Socket hostname of the server or client. This is the hostname
+       of the connecting server/client and this is resolved from the
+       connection. If resolve cannot be done, ircd defaults to using
+       numeric IP-address. *ALL* access checks are based on this
+       name, especially noteworthy fact, if your resolver cannot find
+       hostnames by IP-address, you must allow the access by IP-numbers
+       in your ircd.conf.
+
+   In many places, where servers name is shown, actually both are
+shown. The general format of the displayed name is
+
+       AnnouncedName[SocketHostName]
+
+When a connection is yet unkown, there is no AnnouncedName, and if the
+AnnouncedName is the same as SocketHostName, the "[..]"-part is omitted.
+
+2.2 Many notices to local operators
+
+   If an oper is signed on the server, he/she will receive many
+notices about exceptional conditions and servers actions. When
+something goes wrong, it should be much easier to fix the problems.
+
+   Few often occurring, inportant error messages are
+
+   "Write error to SERVERNAME, closing link"
+
+       write() to socket returned with an error. Server is
+       closing the link. This means usually network problems
+       which you can do nothing about.
+
+   "Max buffering limit exceeded for SERVERNAME"
+
+       This is the situation where old server would have been
+       "frozen". The socket buffers in your OS have been filled and
+       even servers own predefined internal buffering MAX for a link
+       has been exceeded. Exceeding this limit most likely means
+       that the link is really dead, so the server closes the link
+       and scratches all queued output for it. If the limit is
+       set high ( > 20000 bytes), you won't usually see this, but
+       just "No responce from SERVERNAME, closing link" as the
+       server does not reply to PING as it should.
+
+   "Link SERVERNAME cancelled, server SERVERNAME already exits"
+
+       Two different servers from your net fragment attempted
+       to connect same other net fragment about the same time
+       and this collision is detected at your server. IRC routing
+       does not allow loops, the link causing the loop is closed.
+       (Which of the two links gets closed is mostly determined
+       by pure chance and timing--you may lose a better link this
+       way. Collisions should be rare in normal operation, if
+       the timers in "config.h" are not messed up too much...)
+
+       Of course, you get this too, if you try to connect to a
+       server that is already connected by some other route. In
+       that case your attempted connection is just safely cancelled.
+
+   The notices attempt to be self explaining.
+
+2.3 Links statistics collecting
+
+   IRCD now counts the bytes and messages transmitted to each open
+link. This information can be output with a command "/stats l"
+("/stats" or "/stat m" will give the old message count statistics).
+
+   Sample output
+
+Link           SendQ  SendM SendBytes  RcveM RcveBytes Open since
+oddjob.uchicago    0    203      8067    772     34321 Sun May  6 02:15:45 1990
+cs.hut.fi[sauna    0   1916     79798     94      3082 Sun May  6 01:51:25 1990
+otax.tky.hut.fi    0   3722    151511    426     22690 Sun May  6 00:25:54 1990
+nada.kth.se        0   8775    355811   5333    223853 Sat May  5 14:11:49 1990
+vehka.cs.uta.fi    0  23816    882000    901     41156 Fri May  4 22:50:23 1990
+lut.fi             0  25145    943765   1068     35020 Fri May  4 22:34:16 1990
+kreeta.helsinki    0  24286    899191    957     47085 Fri May  4 22:33:28 1990
+naakka.tut.fi      0  27754   1067302   8288    362960 Fri May  4 22:33:14 1990
+joyx.joensuu.fi    0  30003   1172949   2300     80053 Fri May  4 22:33:05 1990
+tel4.tel.vtt.fi    04083771 167473890 863475  35022755 Mon Apr 23 00:15:17 1990
+                   |    |       |       |       |      |
+                   |    |       |       |       |      Link established
+                   |    |       |       |       The number of bytes received
+                   |    |       |       The number protocol messages received
+                   |    |       The number of bytes transmitted
+                   |    The number of protocol messages transmitted
+                   The amount of queued data in bytes (if socket is hung)
+
+   The last row (with the local servername) contains the total
+cumulative counts for all connections since the server was started.
+
+   One can query the statistics of a remote server by adding the servers
+name to the command "/stat l servername". Of course, this only works,
+if all intermediate servers have upgraged. The first "old" server
+will stop the propagation and return the message counts by default.
+
+2.4 Connecting servers
+
+   An oper can manually activate a connection phase to any server
+defined in ircd.conf C-lines (to successfully complete the connection,
+the N-line must be present too). The message achieving this is
+
+       CONNECT servername portnumber
+
+   where servername may be a mask string containing wildcards. This
+name is matched against entries in ircd.conf (notice: the testing
+is made in reverse order, e.g. the last C-line in ircd.conf is tested
+first).  If portnumber is omitted, the ircd uses the one given in the
+found C-line. If the C-line does not have the portnumber, the compiled
+default will be used (PORTNUM from config.h).
+
+   This release allows also for remote connecting. An oper can send
+a connect request to remote server with
+
+       CONNECT servername portnumber remoteserver
+
+This command is passed to the 'remoteserver' and it then tries to
+execute it like it was given locally. (If there are opers online on
+that server, they will get a notice about this happening.) Note, that
+one can remotely connect only what is defined in ircd.conf. Usually
+one needs and should use this only for immediate your neighbours. Nobody
+should randomly go and give connect requests to distant servers, unless
+one knows it's absolutely necessary and is very familiar about the
+linking setup there.
+
+2.4 Terminating connections
+
+   The SQUIT command in PL1 was not intended to be used manually and
+was very dangerous to use (it also created so called "ghost servers").
+Since msa.4, the SQUIT has been safer to use manually.
+
+
+   "SQUIT z" s                          a
+              \                        /
+               \                      /
+         ------- x ------- y --| |-- z ------- b
+                /               ^     \
+              /                 |       \
+            p                            c
+
+       "SQUIT z" will break the link between "y" and "z" if injected
+       into system from "s". After that the net will be in two fragmets,
+       broken between "y" and "z". Server "z" never sees the actual
+       SQUIT, all it observers is that the link to "y" suddenly closes
+       (opers on z would see it as "Server y closed the connection"
+       notice. Opers on y would see it as "Received remote SQUIT from
+       x", note that the actual source "s" is not identified in the
+       current version--for reasons too complicated to be explained
+       here).
+
+       *WARNING* *WARNING* If the server "y" is still running pre-msa.4
+       (like PL1), don't *EVER* issue a SQUIT for its links (unless the
+       link is to a leaf node or verifiably a "ghost server").
+
+       Note, that when the link between "y" and "z" breaks, y will spit
+       out SQUIT's for "z", "a", "b" and "c" to "x". At same time "z"
+       is sending SQUIT's for "x", "s", "p", etc to "a", "b" and "c".
+       SQUIT is normally generated by servers automaticly, it's just
+       a later modification (msa.4) that allows an OPER to use this
+       same message to "simulate" a link break at certain point.
+
+       *IMPORTANT* If server "z" has configuration "C:y::y:6667", it
+       automaticly attempts to reconnect after a short delay (currently
+       10 seconds), but only *if* the connection has been up long enough
+       reliably (currently set to 10 minutes). If the thus formed link is
+       squit another time, it will not attempt to come back immeatedly.
+       This gives an oper time to reconfigure the links if that first
+       short delay is not enough.
+       
+    As in all commands, also SQUIT accepts wildcards, but be careful to
+give sufficient identification. SQUIT of wrong server is not nice...
+
+2.5 KILL message
+
+    KILL will implicitly use the history database. If a KILL is
+issued for a nick that has been changed to another, the server
+will automaticly re-issue the kill with the new nickname, if
+the change has happened recently (current value should be 90
+seconds). If a "terrorist" is clearly distrupting channel by
+bombarding it with garbage from negative channels and changing
+nick all time, there is no need to consult the "WHOWAS" data
+base, just use the nickname that was used to send the garbage
+and ircd hunts the culprit down. When this change of target
+happens, the oper issuing the kill is notified.
+
+NOTE:  With automatic, kill-proof-reconnecting clients, the
+       value of KILL is becoming insignificant...
+
+2.6 Changing the server defaults from the command line
+
+    The servers activation command is now
+
+ircd[ -f configfile][ -h servername][ -p portnumber][ -x debuglevel]
+
+where parameters can be given in any order. If the "configfile"
+is defined, it will override the default specified in the file
+"config.h". If "servername" is defined, it will override the
+one defined in the M-line on the configuration line. "portnumber"
+will override the compiled default (from "config.h") or the
+one from the M-line of the configuration file. The "debuglevel"
+will determine the amout of logging the server does into a
+log file that has been define in "config.h". The "debuglevel"
+should never be defined for a server running normally, it can
+quickly generate megabytes of trace. Usually needed only when
+the server is incapable of starting properly at all, then one
+run with "-x9" usually is enough to reveal the problem.
+
+3. General cleaning up and commenting the code
+
+   This issue is controversial. My way of fixing bugs is not just
+fix them, I also want to program defensively, make it difficult to
+make new errors. Thus I have heavily reformatted and reorganized
+those files that I have had to touch. Some functions have been
+renamed intentionally to catch all uses of those functions [because
+the functions semantics or calling sequences have been changed].
+
+   This release (2.4) will be the last IRC version I'm contributing
+to. If you have any wishes or complains about the code or functioining
+of IRC, use the source or ask whomever it happens to be the current
+developer.
+
+HISTORY
+
+   There have been many different versions of IRC and many of those
+versions are still in use. The following attempt to bring some
+clarification to the versions. This starts from 2.01.6, hopefully
+no servers are running older versions...
+
+       ...
+       ...
+       2.01.6  A version from WiZ in summer 1989
+       ...
+               2.01t6  A series of releases, which contained minor
+               2.01T6  adjustements and bug fixes to the base version.
+               2.01u6  Some of those fixes caused extra errors, of
+               2.01U6  this series versions 2.01U6 and 2.01v6 are at
+               2.01v6  least known to be rather stable.
+
+       2.1.0   Mike Bolotski created these versions from the sources
+       2.1.1   of 2.01U6, but unfortunale some devious bug crept in
+               and caused a lots of linking problems (the nasty "ghost
+               server problem" splintered the net constantly). These
+               versions must be deleted on sight :) [Autumn 1989]
+
+       2.2     This version is the 2.01v6 sources repackaged into
+               multiple directories by Mike again. Probably nobody
+               is running this base version, because is was promptly
+               followed by two patch releases [Autumn 1989]
+
+               2.2PL0  These two are the last major "official" releases
+               2.2PL1  and most of the servers upgraged to either of
+                       these.
+
+       2.2msa  Unfortunately 2.2PL1 version had a tendency to die
+               mysteriously very often. So, I started to look into the
+               code from March 1990 and that resulted a series of
+               patches to the 2.2PL1 server code, but finally
+               decided to release full server code releases of which
+               few have got wider distribution
+
+               2.2msa.4
+                       Has most of the known PL1 bugs fixed and seems
+                       to be very reliable. But once servers started
+                       staying up, a new problem appeared: socket
+                       buffers started getting full and servers tended
+                       to freeze very often for long intervals.
+
+       2.3alpha
+       2.3     Is an attempt to make an official release from 2.2msa.4
+               code, but hassles with changed copyrights make this
+               version unacceptable. Besides, 2.3alpha or 2.2msa.4 are
+               now obsolete, old versions :)
+
+       2.2msa.x
+               To solve the freezing problems, the server code is changed
+               to use non-blocking sockets.
+
+               2.2msa.7
+               2.2msa.9
+                       Are intermediate test versions, of which .9 seems
+                       to have most of the problems solved.
+
+               2.2msa.10
+                       Never released. This is slightly improved version
+                       of msa.9, some new features.
+
+       2.4     Is a release which combines 2.2msa.10 and Chelsea's
+               modifications to the server. Also, this release has
+               once again reorganized the directories and makefiles.
+
+
+-- msa (Markku.Savela@vtt.fi)
diff --git a/doc/history/2.7-New b/doc/history/2.7-New
new file mode 100644 (file)
index 0000000..7980d77
--- /dev/null
@@ -0,0 +1,128 @@
+       * WHOREPLY and NAMREPLY become numberics instead of strings.
+
+       * msa's patches to kick/mode to attempt to follow nick name changes
+
+       * spike's patches to get SUMMON to find last idle tty
+
+       * melazy's various DNS improvements.
+
+       * pjg's saber C check
+
+       * prefix changed for all server->client messages in which the origin
+         of the sender is a client to appear as follows:
+               nick!user@host
+
+       * TRACE output changed.
+
+       * # channel topics broadcast
+
+       * +-numeric channels removed from server
+
+       * numerics for TRACE output 201-209
+
+       * new switches for STATS: i,k,q,y
+
+       * numerics for stats output 211-219
+
+       * MODE changed to also operate on users
+
+       * deoper added as both mode and direct command. deoper sends out
+         ":user OPER -" to other servers. (from Kaizzu)
+
+       * XTRA/VOICE/GRAPH removed.
+
+       * MODE +a scrapped.
+
+       * added custom ANSI-compatible ctype macros to ensure speed
+
+       * user modes i,w,s,o implemented as follows:
+               i - invisible. over rides any channel mode for those external
+                   to your channel. you are invisible.
+
+               w - receive wallops
+
+               s - receive local service notices (errors, etc)
+
+               o - operator flag. (can not be set currently except using the
+                   OPER command).
+
+       * MODE +b added to ban a user from a channel using "nick!user@host" as
+         the mask to match to the user. If the user matches the ban mask they
+         are not allowed in. MODE +b with no parameters returns the list of
+         ban masks currently in place. MODE +b <mask> and MODE -b <mask>
+         add/delete a ban mask respectively.
+         Thanks to HulkHogan (andy@lingua.cltr.uq.oz.au) for defining what
+         we needed here and the 'BlackBall' approach.
+
+       * Operator passwords may now be stored in the ircd.conf file as the
+         encrypted plaintext. Crypt(3) is used to generate the matching
+         plaintext from the OPER command. Thanks to the following people
+         for help with this:
+               Sean Batt (sean@coombs.anu.edu.au),
+               Andy. M. Jones (andy@lingua.cltr.uq.oz.au),
+               Nelson Minar (minar@reed.edu).
+
+       * Server now creates "ircd.pid" file when booted. Holds the current
+         pid of the server.
+
+       * Each O and I line in the ircd.conf file may be linked only a number
+         of times equal to or the max. links value for the class they belong
+         to.
+
+       * Server stores results of any successful DNS lookups for servers so
+         that future lookups are not needed. A rehash will wipe all previous
+         lookup results and cause the server to start over. The server will
+         attempt to lookup each hostname in a C/N line on booting. This may
+         cause a delay during starting the server.
+
+       * All functions should be of the form "function_name", macros of the
+         form "MacroName" and constants as CONSTANT.
+
+       * Services are now treated with some respect. A service is associated
+         with an S-line in the ircd.conf. A service must send a NICK/SERVICE
+         pair on connecting to achieve service status.
+
+       * JOIN/PART now accept a list of channels in the first parameter with
+         each separated by a ",". eg "JOIN #foo,#bar,#foobar"
+
+       * A hopcount for the distance to nicknames and servers has been
+         introduced (for better or worse). It is passed as the second
+         parameter to both NICK and SERVER.
+         WHO and LINKS both report the hopcount in the info field.
+
+       * Default for WALL and WALLOPS set "off"
+
+       * rearranged config.h
+
+       * ISON now returns nicknames with the actual case, i.e.
+          ISON wiz will answer WiZ
+
+New into 2.7.1
+
+       * STATS u, r, z
+               u - uptime
+               r - CPU useage stats
+               z - counts and shows current memory useage
+         r & z are only available if DEBUGMODE is defined in config.h
+
+       * GETHOST which forces all reverse lookups of ip#'s to also match
+         when doing a forward lookup of the hostname returned.
+
+       * SENDQ_ALWAYS buffering policy for sending data over links.
+         (Server tries to buffer as much data as possible before attempting
+          a write).
+
+New into 2.7.2
+
+       * NOTE (once again appears and in much better state)
+
+       * WHOWAS gives a list of known users of the nick in question
+         rather than just the most recent.
+
+       * Server can accept both server and client connections via a unix
+         domain socket. This provides greater security and reliability for
+         connections between the host and itself. (#define UNIXPORT)
+
+       * STATS C reports L-lines: New Numeric 241
+
+       * #define for showing all users the invisible count from LUSERS
diff --git a/doc/history/Manual b/doc/history/Manual
new file mode 100644 (file)
index 0000000..37a31a1
--- /dev/null
@@ -0,0 +1,316 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, doc/MANUAL
+ *   Copyright (C) 1990, Karl Kleinpaste
+ *
+ *   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.
+ */
+
+                                        Date: 04 Apr 1989
+                                      Author: Karl Kleinpaste
+                                              karl@cis.ohio-state.edu
+                                                
+                           Last modification: 15 May 1992
+                                          by  Mauri Haikola 
+                                              mjh@stekt.oulu.fi
+
+                      Modified for undernet: 7 Feb 1995
+                                         by  Carlo Wood
+                                             carlo@runaway.xs4all.nl 
+
+
+                     INTERNET RELAY CHAT (IRC)
+                 a real-time conversational system
+
+
+* 1: IRC - replacement for talk(1)
+
+IRC is a functional replacement for and improvement to talk(1).  Talk
+is an old, primitive, atrocious, minimalist sort of keyboard/screen
+conversation tool, using a grotesque, machine-dependent protocol.
+IRC does everything talk does, but with a better protocol, allowing
+more than 2 users to talk at once, with access across the aggregate
+Internet, and providing a whole raft of other useful features.
+
+Note (added Apr 7, 1998): The above statement has been left there for
+historical reasons.  It should be noted however that IRC is not any
+longer a replacement for talk(1).  At the time IRC was first developed,
+people connected to internet all were using accounts on UNIX Operating
+Systems, which almost all did run a non-restricted fingerd and a talkd.
+This allowed to see if someone was logged in (with finger) and then
+summon him to talk by connecting to his talk daemon.  For IRC however it
+is necessary to already be connected to an IRC server and one needs
+to pay attention to the window of the IRC client in order to see if someone
+wants to talk to you.  Therefore IRC has become more of a 'chat box':
+a Real Time Chat environment for chatting, making friends and exchanging
+information.  It has little resemblance anyore with talk(1).
+
+* 2: Entering Internet Relay Chat
+
+To enter Internet Relay Chat you need to run a client, which will start
+connecting to its default server.  More info on clients can be achieved
+from ftp://ftp.undernet.org/pub/irc/docs/faq/underfaq.1.  A lot of clients
+for all kinds of Operating Systems and (programming) languages can be
+found in ftp://ftp.undernet.org/pub/irc/clients/index.html.
+
+* 3: How much can be seen from here
+
+The universe - seriously.
+
+This is most formally called Internet Relay Chat.  Server hosts are
+connected via a tree structure.  The various servers relay control and
+message data among themselves to advertise the existence of other
+servers, users, and the channels and other resources being occupied by
+those users.
+
+* 4: Structure
+
+There is quite a lot of structure to the operation of IRC, as compared
+to crufty old talk(1).  Since so little could be done with talk(1), it
+needed little structure.  But to keep track of people spread literally
+around the world, the structure is useful so that one can speak to exactly
+those people with whom one wishes to speak.  The structure is outlined in
+more detail in the paragraphs below.
+
+** 4.1: Nicknames
+
+All users of IRC are known to the system by a `nickname.'  A nickname
+can be chosen at the moment the client connects, but can be changed at
+any time.  Nickname clashes are not allowed; this is enforced by the servers.
+If one's intended nickname clashes with someone else as one enters chat, one
+will not be able to complete entry to IRC until one changes one's nickname
+to something else.
+
+** 4.2: Presence on a channel
+
+Fundamental to the operation of IRC is the concept of a channel.  All
+users are `on a channel' while inside IRC.  One enters the `null
+channel' first.  One cannot send any messages while not in any
+chatting channel unless one has set up a private conversation in some
+way.  The number of channels is virtually unlimited - whatever will
+fit in a string of 200 characters and starts with a #, & or + sign.
+A channel which is prefixed with a '#' (pound sign) is a global channel;
+available to everyone on the network.  A channel prefixed with a
+'&' (ampersand) is a local channel; only available to users on the server
+you are connected to.  While a channel prefixed with a + (addition sign)
+are global and modeless; those channels do accept mode changes.
+
+** 4.3: Main modes of #channels
+
+Public
+
+This is the default mode for a channel.  When one is on a public
+channel, one can be seen by all other users (if one's own user mode
+permits this).  Anyone can notice users on a public channel and join
+such a channel's conversation.
+
+Private
+
+This means that, although anyone can see that one is using chat, no
+one can tell what channel one is using unless one is already on that
+channel with oneself.  Since the number of potential channels is in
+the billions, this is quite some security - all one gives away is the
+acknowledgement that one is using chat.
+
+Secret
+
+While one is on a secret channel, no one who is not on one's channel
+with oneself can even see that one is there.  One's name does not show
+up in a wildcard search of active users.  Of course, making a channel
+like '#test' secret gives a huge change to be discovered anyway.
+
+Changing the mode
+
+The mode of a channel (private, secret, invite-only, moderated,
+topic-limited, person-number-limited, no-messages-to-channel, ban
+someone from channel, etc.) is set by a channel operator, who is the
+first person to join a channel, or someone who has had channel
+operatorship bestowed on them by another channel operator. 
+
+Local channels
+
+Channels which are prefixed with the ampersand (&) sign are local
+channels which mean they can only be accessed to users who are on
+the same server.  For example, &help may exist on every server on
+the network, however each of them are different channels whereas
+global (#) channels are just one channel for the entire network.
+
+Modeless channels
+
+Channels that have a name that start with a plus sign (+) instead,
+are modeless.  This means that nobody is channel operator and hence
+no mode changes can be done.  The default mode of a +channel is "+nt".
+The intention of modeless channels is to avoid channel wars by making
+all users on that channel a-priori equal.  The only possible abuse,
+channel flooding, should be solved with /ignore.
+
+*** 4.4: Conversations not using channels
+
+It is possible to conduct conversations with others without using the
+formalized channel structure.  Doing so requires that two people set
+themselves up for private conversation using special commands; see
+User Commands below.
+
+** 5: Getting help
+
+Type "/help."  Follow the instructions.  Since this is a client feature
+it might not work for you, in which case you'd have to consult your
+local IRC guru or someone on the net.
+
+** 5.1: User commands
+
+In most clients, commands must start with a '/' (for example: /join #test).
+The most important commands supported by IRC are:
+
+      help      quit       who     whois
+      list     topic      join      part
+     links       msg    invite   silence
+     names     stats      nick      away
+      info     clear     query    ignore
+      mode
+
+Also read the file ADD-TO-IRCRC for a description of Undernet specific
+commands and an example script for the ircII client.
+
+*** 5.1.1: /quit [comment]
+
+/quit exits chat.  Optional comment may be included; see above.
+
+*** 5.1.2: /who [channelname_mask | user@host.mask]
+
+/who returns information on who is using chat.  Users of public channels
+show up with one of their channels identified, if any.  Users of private
+channels appear, but they are specified as being on a private, unspecified
+channel.  Users of secret channels and users whose user mode is +i (invisible)
+do not appear at all.
+
+Giving a channel name as an argument to /who returns only those users of the
+specified channel.  This still doesn't show users of secret channel or
+invisible users one is actually on the same channel with them.  Users
+of private channels are shown, if an exact channel name is given.
+
+For a detailed explanation of the many options of /who, see doc/readme.who !
+
+*** 5.1.3: /whois <nickname>
+
+This returns information about individual users.  Type "/whois nickname"
+to get information on the login name and host from which the nicknamed
+user comes.  You can specify multiple nicknames to query by seperating
+each with a comma.
+
+*** 5.1.4: /topic <some topic string>
+
+Channels can be given off-the-cuff "topics."  Saying "/topic some
+string of text" will associate that topic with the current channel.
+
+*** 5.1.5: /list [options] [channel.mask]
+
+/list will give lists of active channels, the number of users of each,
+and the topics therewith associated.  Again, secret channels do not
+appear and private channels only appear as Prv.
+
+[options] is a comma seperated list of one or more of the following options:
+
+       >nnn
+       <nnn
+       C<ccc
+       C>ccc
+       T<ttt
+    and T>ttt
+
+This comma seperated list may not contain spaces.
+Here `nnn' is the minimum or maximum number of users on a channel,
+`ccc' is the minimum or maximum age or creation time of a channel, in
+respectively seconds or UTC.  And `ttt' is the minimum or maximum age
+or creation time of the topic of the channel, in respectively seconds
+or UTC.
+
+On most servers, if no options are given, the server will use a
+default option (like "T<10") in order to strongly reduce the of number
+of listed channels.
+
+*** 5.1.6: /join <channel> [key]
+
+/join <channel_name> is the means to enter a channel.  Give the channel
+name as an argument.  If this is a secret or hidden channel, /who
+commands will show oneself and any other users of one's channel.
+
+One's arrival on a channel is announced to the rest of the users
+already on that channel.  Silent, anonymous "lurking" is not
+supported.
+
+If the channel is locked with a key, you need to add the [key]
+parameter which acts as a password (cannot contain spaces).
+
+*** 5.1.7: /msg <nick> <some text string>
+
+A single message can be sent privately to a certain user with /msg.
+Type /msg nickname and the text to be sent.  It will be sent privately
+to the indicated nickname.
+
+*** 5.1.8: /invite <nick> <#channel>
+
+If there is a user online to whom one wishes to speak, one may invite
+that user to join oneself on a certain channel.  One types "/invite
+nickname" with an optional channel name.  The receiving user gets a
+one-line message indicating the sender and the invitation.  The
+receiving user is free to ignore the invitation, of course.  You
+cannot invite users to a modeless channel.
+
+*** 5.1.9: /ignore <nick!user@host.mask>
+
+If one wants to ignore messages sent by some other user or users, it
+may be done with /ignore command.  One can ignore someone by their
+nickname, or by their user@host data.  Wildcards may be used.  /ignore
+is only intended to ignore annoying public messages (messages sent to
+a channel), to stop flooding (a huge number of messages per second)
+you have to use banning for channel messages, and /silence for private
+messages.  /mode <your nick> +d stops all messages from ALL channels.
+
+*** 5.1.12: /silence [nick!user@host.mask]
+
+This command effectively stops private message flooding at the server
+of the flooder.  You can use "/silence nick" to get a list of the
+silence masks of 'nick'.  This command is undernet specific and therefor
+not supported by all clients unless you add specifically a line to your
+clients configuration file.
+
+*** 5.1.11: /nick <new_nick>
+
+One can change nicknames by issuing "/nick new-nickname."  All users
+on one's channel will be informed about the change.  NOTE: If one enters
+chat with a nickname clash (e.g., one's login name is the same as
+someone else's, and the other user got there first), the system will
+not let one enter until one issues a /nick command with a unique
+nickname.  Nicknames are limited to nine characters in length on the
+Undernet.
+
+*** 5.1.12: /mode #channel [lots of parameters]
+
+This command can be used for altering the various modes of a channel
+(see the explanation of channel modes above).  /mode command can only
+be issued by channel operators.  Please use /help, or the manual of
+your client to find out about this command.
+
+If you would like a list of the current modes in the channel, type
+/mode <channel> (you do not need to be a channel operator to do this).
+For a list of channel bans, type /mode <channel> +b.
+
+* 6: Questions, problems, troubles?
+
+If you have problems, please get and read the FAQs from
+ftp.undernet.org:/pub/irc/docs/underfaq.1 and underfaq.2.
+You can also ask for help on some of the operator channels on IRC,
+for example #help.  They will be able to assist you in whatever
+problems you are having with IRC.
diff --git a/doc/history/README-2.6 b/doc/history/README-2.6
new file mode 100644 (file)
index 0000000..6ba8ada
--- /dev/null
@@ -0,0 +1,71 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, README
+ *   Copyright (C) 1990
+ *
+ *   For the list of authors and their e-mail addresses, see
+ *   file doc/AUTHORS
+ *
+ *   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.
+ */
+
+To install the new server, there is just a few changes to be made.
+
+* General Comments
+
+  Tue Nov 13 12:43:46 1990  Armin Gruner <gruner@informatik.tu-muenchen.de>
+
+      (APOLLO: Be sure to have NEED_INET_NETOF, NEED_INET_ADDR and
+       NEED_INET_NTOA undefined.)
+
+  Mon Nov 26 20:10:37 1990  Armin Gruner <gruner@informatik.tu-muenchen.de>
+
+      Comment: I just found that compiling on our SUN SPARC with GCC produces
+      code that dumps core.. (that might not happen on your machine, try it,
+      we may have out-of-date libraries). See netnews gnu.gcc.bug for a dis-
+      cussion about the matter. It seems that gcc passes struct's in a
+      different manner.
+
+* Server configuration
+
+  Edit the include/config.h to your hearts content, avoiding going
+  beyond the warning line, unless you are *absolute* sure you know
+  what you are doing.
+  If you happen to not take to this warning, you may end up with
+  a server that will not function properly and annoy not only you,
+  but users all around the world.
+
+  Old irc client can be found in this package and if you want to use it,
+  check Makefile in directory irc before compiling. There are other,
+  better irc clients in distribution and the client distributed with
+  this version is simply something to begin with if you don't happen
+  to have other clients available.
+
+  This version was brought to you by jto@tolsun.oulu.fi and send any
+  bug fixes and suggestions to me.
+
+NOTE: This server does *NOT* have MAIL system installed by default.
+      The reason is that it doesn't work with many systems.
+
+This version introduces you string channels, starting with
+a plus (+) sign. The first person joining a string channel
+claims it's ownership and after that is entitiled to use /mode
+command. Ownerships can be given and taken away with
+/mode <channel> +o <nickname>  and /mode <channel> -o <nickname>
+Other flags are: s - secret, p - private, l - limited, i - inviteonly.
+                 m - moderated, n - no private messages to channel,
+                 t - topic settable by operator only
+New command /KICK <channel> <user> kicks a user off channel.
+
+--Jarkko (jto@oulu.fi)
diff --git a/doc/history/README.patches b/doc/history/README.patches
new file mode 100644 (file)
index 0000000..cfc917b
--- /dev/null
@@ -0,0 +1,1901 @@
+
+The available patches for 2.8*mu servers are:
+
+Tp8 = TimeStampPre8 - A protocol which disallows netsplit ops and channel
+                      desynchs.
+
+Bquiet - does not allow a person who is banned to speak over the channel
+
+Silence - Cuts off flooding at local server
+
+Anc = Anti-Nick collide - *Intentional* nick collides are impossible with
+                          this wonderful patch.
+
+W = Wallops - lets you send messages to other IRC ops
+
+ban = BanInformation - Lets you see who did a ban and when, as well
+
+sw = /stats w - lets you gather statistics on average client connects
+
+To = TopicInformation - Lets you see who set the topic for a channel and when
+
+S = Signon Time - Tells you when a local user signed onto IRC
+
+Cl = Client connect - Notifies opers on your server of client connects/
+                      disconnects (with disconnect reason)
+
+TT = Trace Times - displays the time (in secs) since your server last heard
+                   anything from a client/server, when you do /trace.
+
+KL = K-line comments - Allows you to modify the lame "no ghosts allowed" default 
+                       comment to whatever you wish, or alternately, display a 
+                        file to a rejected client.
+
+OF = Oper fail patch - displays a warning to current ops when someone fails
+                       in entering the right oper password.
+
+MC = Mixed case patch - useful for those pesky clone bots and hacked logins;
+               disallows userids which have mixed case or control chars
+
+N = Note - allows you keep a "note" for other users, amongst other things
+
+-----------------------------------------------------------------------------
+Explanations for these patches follow.....
+
+Help on patches written by Mandar Mirashi (mmmirash@mailhost.ecn.uoknor.edu)
+                           Mmmm on IRC.
+
+
+=============================================================================
+                                TIMESTAMP 
+=============================================================================
+Author: Carlo, carlo@sg.tn.tudelft.nl, Run on IRC.
+Info on TS protocol:
+
+                               TSpre7
+
+------
+Effects of the TS protocol:
+
+> No mode wars possible.
+  When you take someones op there are three possibilities:
+  - You were too late (You was already de-opped on your server).
+  - You take it (On the *whole* net).
+  - You start taking it (on your server, but it is 'bounced' (ie your mode
+    change is cancelled); This occurs when you try to do mode change
+    direct after a net re-connection in a situation were you hacked op by
+    net-break riding.
+> No desynchronisation possible.
+> No unnecessary MODE msg send. You can't send 'double' mode's that don't
+  make sense. Servers don't send unnecessary MODE's either.
+> Hacking op is from now on *only* possible by admins that hacked their
+  servercode, and therefor easier to prosecute. Also you can 'hack' op still
+  exactly like now (by riding net break) on an *opless* channel.
+> The protocol is fully compatible with older servers: It is invisible
+  and it works with old servers like this: Every 'block' of direct connected
+        2.8.x.TS servers will stay synchronized, Hacking op is impossible by means
+        of riding a net break between two TS-servers on channels that were created
+        within that block. In all other cases the same happens as without TS.
+
+Here follow technical details implemented in TSpre7:
+
+------
+
+Reference: 2.8.14/15.TSpre7
+Full list of TS-MODE-Change protocol:
+
+-Mode changes (originating by clients) are refused only:
+ 1) from a client that is directly connected and has no chan ops on 
+    the channel on its server.
+ 2) when not being a change of the internal state of a server (Well, refused
+    is to strong, propagation of the mode is just unnecessary and thus not
+    done).
+ 3) from someone flagged as de-opped-by-server. (flag is reset when this
+    person is opped again by anyone) (The server detecting this mode change
+    cancels the mode change, by bouncing it upstream, thus keeping the net
+    synchronized).
+
+-An extra parameter is added behind MODE changes *done* by servers, sended
+ *to* other servers. It is a Universal Time in ascii seconds. This extra
+ parameter is NOT sended when it is 0 (can happen with old servers on the
+ net), 0 means <NONE> rather then Jan 1st 1970 :).
+ This parameter is the creationtime of the channel being: the universal time at
+ which the channel was created by a *local* client; or the non-zero received
+ creation time from an other server (as parameter with a mode change) that
+ was earlier then its own; or equal 0 when the channel was created by
+ a non-local client and no MODE with TS was received (yet).
+
+-Of the channel_flags is 1 bit more used: CHFL_DEOPPED, set when de-opped
+ by a server (compare CHFL_CHANOP, set when channel operator). It's reset
+ when opped. (And starts reset on joining (creation?) of a channel, this
+ will be changed to 'set' on join, when all servers have TS; making detection
+ of op hacking by admins a bit easier).
+
+-Timestamps (sended by TS-servers) are handled as follows:
+ Received TS      Own TS      Bounced/Propagated
+    equal          equal       propagated
+    later          >0,earlier  if ops: bounced with own TS
+                               if no ops: propagated with own TS
+                               (own TS is sended upstream too, to make sure
+                                TS stays synchronized).
+    earlier        later       TS copied, propagated
+    none           any         propagated, own TS sended.
+    >0             none        if ops: propagated, no TS sended, own TS stays 0.
+                               if no ops: TS copied, propagated.
+
+-A mode change +/-o nick (+/- v) from a person that is deopped by a server
+ results in a -/+o nick back up stream (and is not propagated) if it was
+ an attempt to change the internal state of the receiving server.
+
+-kick is handled as mode -o, internal state 'not on channel' being 'already
+ de-opped'. Bounce includes JOIN and restoring o and v flags.
+ (Effect: You don't even *see* the kick on one side, on the other side
+  the person joins again and gets his flags back from the bouncing server).
+
+-A received TimeStamp that claims a creation time *earlier* then that
+ this server dissapeared from the net results in a HACK: notice (with
+ time difference in seconds). Bye OPER.. (This meaning, to hack op
+ on an existing channel that was created 15 minutes before you disconnected
+ your server, you will have generated a HACK notice: Clock set back at least
+ 900 seconds by <nick>.) (Not yet implemented, prob. in pre8).
+
+
+                               TSpre8
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: *** IMPORTANT; RFC
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Thu, 14 Apr 94 18:03:38 METDST
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+Hi, please read this carefully and respond if you think it will result
+in INCORRECT behaviour under any circumstances:
+
+Here follow technical details implemented in TSpre8:
+
+------
+
+Reference: 2.8.17.TSpre8
+Full list of TS-MODE-Change protocol:
+
+-Mode changes (originating by clients) are refused only:
+ 1) from a client that is directly connected and has no chan ops on 
+    the channel on its server.
+ 2) when not being a change of the internal state of a server (Well, refused
+    is to strong, propagation of the mode is just unnecessary and thus not
+    done).
+ 3) from someone flagged as de-opped-by-server. (flag is reset when this
+    person is opped again by anyone) (The server detecting this mode change
+    cancels the mode change, by bouncing it upstream, thus keeping the net
+    synchronized).
+ 4) When a '0' as timestamp is received, originating from a server (see below).
+    Then the whole mode is ignored, a HACK notice is sent to all ops and the
+                string is propagated as received.
+
+-An extra parameter is added behind MODE changes *done* by servers, sent
+ *to* other servers *containing* a +o, -o, +v or -v.
+ It is a Universal Time in ascii seconds.
+ Whenever a HACK is detected, a HACK: notice is sent to all local opers,
+ and the full MODE is propagated with a '0' as timestamp, generating
+ a HACK notice on all other servers.
+ Otherwise this parameter is the creationtime of the channel being: the
+ universal time at which the channel was created by a *local* client;
+ or the non-zero received creation time from an other server (as parameter
+ with a mode change) that was earlier then its own; or equal 0 when the
+ channel was created by a non-local client and no MODE with TS was received
+ (yet).
+
+-Of the channel_flags is 1 bit more used: CHFL_DEOPPED, set when de-opped
+ by a server (compare CHFL_CHANOP, set when channel operator). It's reset
+ when opped. It starts *set* on joining (creation?) of a channel, making
+ detection of op hacking by admins a bit easier.
+
+-Timestamps (sent by TS-servers) are handled as follows:
+ Received TS      Own TS      Bounced/Propagated
+    equal          equal       propagated
+    later          >0,earlier  if ops: bounced with own TS
+                               if no ops: TS copied, propagated
+    earlier        later       TS copied, propagated
+    0 or none      any         HACK generated, 0 propagated, own TS is kept
+    >0             none        TS copied, propagated.
+
+-A mode change +/-o nick (+/- v) from a person that is deopped by a server
+ results in a -/+o nick back up stream (and is not propagated) if it was
+ an attempt to change the internal state of the receiving server.
+
+-kick is handled as mode -o, internal state 'not on channel' being 'already
+ de-opped'. Bounce includes JOIN and restoring o and v flags.
+ (Effect: You don't even *see* the kick on one side, on the other side
+  the person joins again and gets his flags back from the bouncing server).
+
+-A received TimeStamp that claims a creation time *earlier* then that
+ this server dissapeared from the net results in a HACK: notice (with
+ time difference in seconds). Bye OPER.. (This meaning, to hack op
+ on an existing channel that was created 15 minutes before you disconnected
+ your server, you will have generated a HACK notice: Clock set back at least
+ 900 seconds by <nick>.)
+
+
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: TSpre8 can work! :)
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Wed, 20 Apr 94 11:44:39 METDST
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+Well... it took me a few days (a night and some dreams actually), but
+I think I found a solution for the problem I mentioned during the meeting :)
+
+Let me first repeat the problem:
+
+- I stated that TSpre8 would prevent op hacking by admins, but... later
+  I realized that that was impossible the way wanted it :(
+  My idea was at first: Simply generate a HACK notice when a server
+  comes on the net with a creation time earlier then when it did split off
+  (and earlier then my own creation time). This sounds nice, but in
+  even this simple case it doesn't work:
+
+Server A and B, users a and b:
+
+  A -- B 
+  |    
+ @a       TS=100
+
+Split at t=200
+
+  A    B
+  |    
+ @a 
+
+b joins at t=300
+
+  A(TS=100)  B(TS=300)
+  |          |
+ @a         @b
+
+Net joins:
+
+  A -- B
+  |    |
+  a    b
+
+Both are de-opped: b because he sends a TS of 300 with is greater (later)
+then 100 (correctly: he used the netbreak). And a is deopped with a
+HACK notice by B, because he introduces 1) a TS earlier then the existing
+TS (100<300) and 2) the 100 is earlier then the time the split occured.
+
+The reason why this goes wrong is simply because B *forgets* the channel
+AND the TS of 100, after the split... If B would *keep* in memory that
+the channel existed on A and with what TS, it would be possible, but only
+at cost of a lot of extra memory usage...
+
+Now my new idea :) It allows hacking, but only in not so very interesting
+cases... And at least it makes it extremely difficult for a newbee, so we
+might at least catch 99% before they understand how it works :)
+
+(This explanation should not be on a public ftp site anymore after a while :)
+
+New rules:
+
+- Servers that are OFF the net for more then one day are forgotten.
+- New servers (or forgotten servers), are always bounced except on channels
+  that have no ops (when they create a channel of their own thus :) unless
+  the receiving server is younger then one day and the introduced TS is
+  earlier then the start up time (minus 10 minutes :/) of the receiving server.
+  'Birthdays' of those servers are also kept.
+- A server that splitted off while a channel already existed, and thus
+  has a creation time earlier then the "received squit time" of that
+  server, is not allowed to introduce an earlier timestamp then the
+  creationtime of the channel (HACK), and also not an equal TS when
+  younger then one day.
+- A server introducing a server with an earlier "time of received squit"
+  inherrits that time as its own "time of received squit".
+
+This allows to hack op on a channel that didn't exist when you splitted
+(not interesting). You also can't keep a server off the net till you need
+it (a telnet connection), because those can't do anything for one day long,
+unless they send the TS *equal* to the existing TS (The only exception :(),
+having to connect between two and one days before the hack, break between
+zero and one day before the hack but before the channel existed, connect
+and hack with equal TS.
+
+What do you think? Just for fun? :)
+
+Apart from that it would be suspicious when someone connects/breaks every
+24 hours a "test" server, channels that exist longer then one day are
+unhackable.
+
+The "disadvantages" are: servers that break off the net for *longer* then
+one day, but keep a channel up with an op, on *both sides of the net, will
+be completely de-opped after reconnection.
+
+*** IMPORTANT:
+
+I am absolutely not sure ;) if there aren't any other disadvantages or
+unwanted effects :) Please, think this over and mail me if you find some
+objection...
+
+Run
+
+
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: 2.8.19.U3 RELEASED
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Sun, 22 May 94 14:15:41 METDST
+Cc: carlo@sg.tn.tudelft.nl
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+Hi all :)
+
+Proud to present: 2.8.19.U3 :)
+
+I have spend *enormous* amounts of time in TESTING this version,
+and I really hope it is completely bug free, but the changes are
+very big, so maybe persons who only want to upgrade/compile ONCE
+should wait a little longer then the compile cracks we have here ;)
+
+For real testing we need the HUBs though! So please, don't hesitate,
+Delft (a HUB) is running it already for a long time, also linked to
+other 2.8.19.U3 test servers.
+
+Before I'll tell about whats new in U3, I want to especially thank
+President for the tremendous help in testing TSpre8 -- I would never
+have been able bring up the stength to go through the difficult
+periods without him being there to listen to my technical complaints ;)
+
+=======================================================================
+
+NEW in .U3
+----------
+
+First all, TSpre8.
+
+It did not become what I hoped/expected to be possible :(
+Hacking will still be possible, but at least it will be a LOT
+more difficult when you don't know what you are doing, and I think
+we WILL catch (new) admins that think they can abuse their powers
+to be GOD on "their" channel.
+
+Especially, nobody will be able to hack *anything* with a normal nick.
+And because server modes are more obvious a hack, this alone is a
+step forward against admin hacking prevention imho.
+
+The .patch file is 
+-rw-------   1 carlo    users      65142 May 22 01:29 irc2.8.19-TSpre8.patch
+big.
+
+I'll now brows through it and mentions changes in the order they appear
+in the .patch file, arbitrary order thus ;)
+
+Zombies
+-------
+
+As mentioned before on 'wastelanders', I changed the internal way a KICK
+is handled, to be able to stop illegal -hacked- kicks from *outside* the
+channel. This has no effect on server-server protocol nor on server-client
+protocol. But because this way it is possible to keep (a little) memory
+for channels you're not on (being kicked from) I thought it would be no
+more then logical to stop people from joining the usual 10 ten channels
+at the same time, *including* the ones you are kicked from (because they
+occupy memory). This memory is released when you 1) Try to rejoin (so with
+all people having a auto-rejoin-on kick NOTHING changed at all), or 2)
+when you do a part - this is new and only intended to use when you do
+NOT have auto-rejoin, when you do not even WANT to rejoin, or try, assuming
+you might not be banned, when you have been kicked like this of a lot of
+channels and all together are "on" 10 channels so you NEED to leave one
+before you can join another... For this rare case, you must know on
+*which* channels you "are", therefor this is visible when you do a
+/names, or /who or /whois to yourself. It is never visible for others.
+Example:
+
+12:07 * Run (Daryl@sg.tn.tudelft.nl) has joined channel #wasteland
+*** Mode change "+o Run" on channel #wasteland by Wasted
+*** #wasteland : created Fri May 13 17:08:34 1994
+<Macro> Hi Run !
+*** You have been kicked off channel #wasteland by Run (Test)
+*** Run is Daryl@sg.tn.tudelft.nl (/msg Run profile)
+*** on channels: !#wasteland 
+*** on irc via server Delft.NL.EU.undernet.org (Runaway Server
++[130.161.188.188])
+*** Run is away: Writting E-mail
+*** Run is an IRC Operator
+*** Run has been idle for 642 seconds.
+
+As you can see, the channel is marked with a '!' to show you are NOT
+not that channel... Both, a part #wasteland as well as a join (being
+not able to actually join because of ban, invite-only, key or limit), will
+remove you from this channel. The part will be sent back to (only) you, and
+everything has turned out to be 100% compatible with ircii protocol.
+Finally, of course the channel is removed when everyone is kicked and/or
+left the channel (a channel with only zombies is removed immedeately).
+
+#define RPL_CREATIONTIME     329
+--------------------------------
+
+A new numeric is sent when you ask for a MODE of a channel, by doing
+/MODE #channel
+without parameters.
+The reply is the same as before, but followed by a new numeric 329:
+
+/MODE #wasteland
+Delft.NL.EU.undernet.org 324 Run #wasteland +t
+Delft.NL.EU.undernet.org 329 Run #wasteland 768845314
+
+To supress this, you'll have to add something like:
+ON ^329 *
+to your .ircrc file. If you want to see this new numeric, you would
+add
+On ^329 "*" echo *** $1 : created $stime($2)
+
+It turns out that ircii clients ask for this mode when you join a
+channel, therefor you will see the creationtime when you join a channel,
+I'll leave it as an exercise to suppress this, but still being able to
+see it when you specifically ask for it :)
+
+New ircd.conf line
+------------------
+
+This is IMPORTANT :
+In order for Uworld to work you MUST add those lines to your ircd.conf file:
+
+U:Uworld.undernet.org::*
+U:Underworld.nl::*
+
+The later to allow the backup Underworld.nl to still function.
+If you forget this, or do it wrong, your server might refuse to accept
+certain mode changes from Uworld.undernet.org and start *bouncing*
+modes done by lusers that got op from it. The name of servers allowed
+to hack have to be agreed upon totally, by all admins. If one admin
+removes his U: line, the service will not work always correctly.
+
+When a server does a mode change that is detected to be a hack, you
+will see -as an oper, or +s luser- this notice:
+
+-> *uworld* opcom MODE #wasteland +o Mmmm
+!Uworld.undernet.org! Run is using Uworld to : MODE #wasteland +o Mmmm
+*** Notice -- HACK: Uworld.undernet.org MODE #wasteland +o Mmmm 
+*** Mode change "+o Mmmm" on channel #wasteland by Uworld.undernet.org
+
+Normally, this HACK notice would NOT take effect! You still *see* the
+HACK notice for the U: line server(s) but then they DO take effect.
+
+Every other message (some including the word HACK) do also take effect,
+and are only a warning that someone is MAYBE hacking...
+I didn't see it occur yet.
+
+Removed bugs
+------------
+
+I did find some bugs in TSpre7, never thought that was possible :)
+I forgot what it exactly was, but under (very rare) circumstances it
+could be pretty serious :/
+
+One rather important thing is that now the TimeStamp is sent during a
+net re.join when there are no ops. Before it was possible to create
+a partly TimeStamp less net on an opless channel. TSpre8 garantees
+that the TS is synchronized on the whole net at any time.
+
+Other messages
+--------------
+
+Apart from the (true) HACK notice, you can get a:
+
+BOUNCE or HACK: notice, which does take effect and is most probably
+just a bounce of a mode done by an attacker: someone immedeately after
+a net re.join with his net.ride ops trying to de-op the others.
+I don't think this will happen often because it will be clear to all lusers
+that it is useless to try.
+
+NET.RIDE on opless #channel notice, you'll see this if someone does
+really abuses a net break to get ops on some opless channel. The mode
+does take effect however.
+
+FULL bounce of modes
+--------------------
+
+When before someone would ride a net break, and try something, ONLY
+his +/- o/v modes. Other modes like +mlk 1 \\|/\|/  would still take
+effect. With TSpre8 this changed... All modes (except bans) are bounced
+when someone rides a net break. Also the bouncing is more compact, while
+with TSpre7 every o and v mode took one line, now all modes are kept into
+one line.
+
+More allowed
+------------
+
+Before you was (how lame) not allowed to mix things like k, o and v...
+Now you are allowed, why not? Also you can use up to six parameters,
+really gives you a power kick ;)
+
+*** Mode change "+vvvvvv flux epa Skip Run Mmmm gyn" on channel #wasteland by
++Run
+
+User friendly mask fixing
+-------------------------
+
+The lame way Avalon fixes a mask (for a ban) is like this:
+
+/mode * +bb *.tudelft.nl Daryl@sg*.tn.tudelft.nl
+
+becomes:
+
+*** Mode change "+bb *.tudelft!*@* Daryl!*@sg*.tn.tudelft.nl" on channel
++#wasteland by Run
+
+The same on a TSpre8 server gives:
+
+*** Mode change "+b *!*@*.tudelft.nl" on channel #wasteland by Run
+
+While just Daryl@sg*.tn.tudelft.nl results in:
+
+*** Mode change "+b *!Daryl@sg*.tn.tudelft.nl" on channel #wasteland by Run
+
+which what one would expect!
+
+
+----------------------------------------------------------------
+
+Goodluck with compiling,
+
+Run
+
+PS If you encounter any problems, realize it is VERY unlikely that
+   it is .U3, but if you really think so, then first try to compile
+   plain 2.8.19. If you succeed, save the Makefile the include/config.h
+   and the include/setup.h. Unpack .U3, replace those files and recompile.
+   With this I assume you don't put your ircd.conf inside the source
+   directories of course, that would still have the paths set wrong then.
+
+   A smart move is to make patch file once for your Makefile/config.h :
+   First ONLY edit the Makefile and config.h (or copy the them from a
+   working source tree to a empty directory), and then make a diff with:
+   diff -rc irc2.8.19.clean irc2.8.19.my.makefile > Makefile.config.h.patch
+
+   That really speeds up upgrading with later versions.
+   (irc2.8.19.my.makefile only needs to contain
+    irc2.8.19.my.makefile/Makefile
+    irc2.8.19.my.makefile/include/config.h )
+   Of course, keep the include/setup.h seperately.
+
+### KILL for Mmm. Mmmm (stop it lamer (unnecessary flooding of alexbot))
+
+
+=============================================================================
+                               BQUIET
+=============================================================================
+Author: Carlo, carlo@sg.tn.tudelft.nl, Run on IRC.
+Helpful ideas by: Aaron, agifford@sci.dixie.edu, Karll on IRC
+
+
+In order to fight flooding, and as discussed on wastelanders, banning
+someone on a channel will now also stop him from doing anything visible
+on the channel. This allows to let someone see what you think of him
+without even kicking him, he will leave by himself.
+He will still be able to appologise by private msgs of course and then
+he can be de-banned. A ban is this way a short cut for +m+vvvv everyone
+excpet the flooder. Note that even NICK floods are stopped: When you are
+on a channel where you are banned, you are not allowed to change your nick.
+
+=============================================================================
+                               SILENCE 
+=============================================================================
+Author: Carlo, carlo@sg.tn.tudelft.nl, Run on IRC.
+Helpful ideas by: Aaron, agifford@sci.dixie.edu, Karll on IRC
+
+My solution to flooders with clone bots etc :)
+Let the user that GETS flooded decide he doesn't want that and stop
+the flooder right at his own server (the server of the flooder).
+This is a new command, and the clients will need unfortunately a few
+lines in their .ircrc before it can work.
+Moreover, before this works, ALL servers must have .U3.
+
+The lines I use at the moment are:
+
+ALIAS SILENCE QUOTE SILENCE
+ALIAS SILE QUOTE SILENCE
+ON ^RAW_IRC "% SILENCE %" echo *** $*
+
+It turns out that some auto-rejoin on kick lines, like Davemans toolbox,
+disables the use of ON RAW_IRC, or at least makes it work incorrectly.
+You should disable this auto-rejoin line and you could add the one I use
+instead:
+
+ON ^RAW_IRC "% KICK % % *" {
+    IF ([$3]==[$N]) {
+        //QUOTE JOIN $2
+        ECHO $MID(11 5 $STIME($TIME())) * You have been kicked off channel $2 by $LEFT($INDEX(! $0) $0) \($MID(1 256 $4-)\) } {
+        ECHO $MID(11 5 $STIME($TIME())) * $3 has been kicked off channel $2 by $LEFT($INDEX(! $0) $0) \($MID(1 256 $4-)\) }
+}
+
+which are 6 lines, not 8.
+
+The way the silence patch works is as follows:
+
+When you add a silence mask (using the same user friendly mask fixing)
+like:
+
+/SILENCE Tsunami*@
+
+It will echo back to you how it is added:
+
+*** Run!Daryl@sg.tn.tudelft.nl SILENCE +*!Tsunami*@*
+
+Note that there is a '+' infront of the mask now.
+You can also type:
+
+/SILENCE +bot*
+
+*** Run!Daryl@sg.tn.tudelft.nl SILENCE +bot*!*@*
+
+If you want to silence one particular nick, you must add the '+', because
+when you do:
+
+/SILENCE nick
+
+and 'nick' exists, you will get the silence list of nick. Just using
+/SILENCE gives your own silence list:
+
+*** Run bot*!*@*
+*** Run *!Tsunami*@*
+*** End of Silence List
+
+However, check the silence list of someone ELSE make only really sense
+when you already did sent a message to this person. Because a silence
+mask only propagates to the server of the flooder when it is actually
+necessary. For instance: You can add up to 5 silence masks and delete them
+again and it will only be local to your own server. Only when someone
+would message you, matching a mask, the mask propagates to the server of
+the flooder. And stays there till you signoff, or till you delete it.
+If you delete a mask, it follows the same path as the +masks...
+
+As a result of this, +s lusers and opers will see the message:
+
+*** SILENCE : Unknown command (from Lausanne.CH.EU.UnderNet.org)
+
+When someone from *behind* a non .U3 server sends you a message
+(Lausanne is this case). So, you will STILL be flooded ;) UNTIL ALL
+servers are upgraded... (Or must do -s, but at least the net is flooded).
+
+
+To: wastelanders@rush.cc.edu
+From: agifford@sci.dixie.edu (Aaron Gifford)
+Subject: HELP with HELP for SILENCE
+Status: RO
+
+Hey, here's a VERY VERY VERY rough draft of a HELP entry for SILENCE,
+assuming it becomes a new command for ircII and not a replacement for
+IGNORE either via new code, or aliases like:
+    ALIAS SILENCE { QUOTE SILENCE $* }
+
+Anyway, PLEASE PLEASE PLEASE alter, modify, correct, add, hack-up, etc this
+rough draft and send me your alterations.  I just typed this up VERY
+quickly because StGeorge is now running irc2.8.19.U3.1.  The bug I mention
+in the file will hopefully disappear very soon, so that text will have to
+do likewise and vanish.  :)
+
+Here it is, draft #1 HELP for SILENCE:
+
+Usage: SILENCE [<nick>]
+       SILENCE [+|-<pattern>]
+
+  SILENCE allows you to TOTALLY ignore all private messages (PRIVMSG's)
+  and notices (NOTICE's) from any user whose nick!user@host matches
+  the <pattern> parameter.  The characters * and ? can be used
+  as wildcards in the pattern.
+
+  If you wanted to ignore all users from "somewhere.com" you would use:
+    SILENCE +*!*@somewhere.com
+
+  To ignore some with the nickname "Jerk" you would use:
+    SILENCE +Jerk
+  NOTE: The server will complete the pattern, storing it as "Jerk!*@*"
+
+  The only drawback of just SILENCE'ing someone by nickname only is
+  that the user could quickly change nicknames to avoid your pattern.
+
+  To remove a pattern, use a - before the pattern you want to remove.
+  When the command is used without any parameters, the server will list
+  all stored patterns you are currently ignoring with the SILENCE
+  command.
+
+  When used in the first form listed, you will see the SILENCE list for
+  the specified user.  This list is not necessarily complete nor accurate
+  because of how servers share SILENCE information internally.  You can
+  check to see if someone is ignoring you with SILENCE by first sending
+  that user a private message, then using the command to see the user's
+  SILENCE list.
+
+  Currently there is a bug in the servers (hopefully to be fixed soon)
+  that will add the nickname you specify to your SILENCE list when you
+  attempt to see that user's SILENCE list if that user is not currently
+  online.
+
+  Because SILENCE is a part of the IRC server protocol (on the Undernet)
+  it works much more efficiently than IGNORE, but is limited to a very
+  brief list of patterns.  Also, you don't have as may options as you
+  do with IGNORE.  If a user is flooding you, SILENCE is many times
+  more efficient than IGNORE because the offending user's PRIMSG's or
+  NOTICE's (including CTCP) are stopped at the server and never even
+  sent to your client.
+
+See Also:
+  IGNORE
+
+
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: Re: HELP with HELP for SILENCE
+To: agifford@sci.dixie.edu (Aaron Gifford) (Aaron Gifford)
+Date: Wed, 25 May 94 12:29:37 METDST
+Cc: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+In-Reply-To: <9405250313.AA18446@sci.dixie.edu>; from "Aaron Gifford" at May 24, 94 9:20 pm
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+> Here it is, draft #1 HELP for SILENCE:
+> 
+> Usage: SILENCE [<nick>]
+>        SILENCE [+|-<pattern>]
+> 
+
+As it is now (I do not consider what you mentioned as a bug, I was aware
+of this effect and didn't choose to alter it), it really is:
+
+Usage: SILENCE [+|-]<pattern>
+Or   : SILENCE <nick>
+
+When you leave the '+|-' away A '+' is assumed.
+
+The second possibility allows you to check your own silence lists, or
+allows to check if you are silenced by someone after you did message
+him but didn't get a respond (did ping him for instance).
+Indeed, this could be interpreted as a pattern, when <nick> doesn't
+exists.
+
+>   If you wanted to ignore all users from "somewhere.com" you would use:
+>     SILENCE +*!*@somewhere.com
+
+SILENCE somewhere.com
+
+would do.
+
+>   When used in the first form listed, you will see the SILENCE list for
+>   the specified user.  This list is not necessarily complete nor accurate
+>   because of how servers share SILENCE information internally.  You can
+>   check to see if someone is ignoring you with SILENCE by first sending
+>   that user a private message, then using the command to see the user's
+>   SILENCE list.
+
+Mention that a EVAL CTCP <nick> PING $TIME() is better...
+It would not be necessary to do the silence if the ping returns...
+To determine between huge lag and silence, you have to do the silence
+check after that.
+If you add something like this in the manual, people will automatically
+wait a while after the 'message' (ping), so that the servers have time
+to send the silence mask back. Otherwise they might think they can do
+a quick msg+silence at the same time. Nah... Not too important, unless
+with huge lag + silence combination.
+
+> 
+>   Currently there is a bug in the servers (hopefully to be fixed soon)
+>   that will add the nickname you specify to your SILENCE list when you
+>   attempt to see that user's SILENCE list if that user is not currently
+>   online.
+
+I didn't consider this as too bad...
+But if people want it, I can change it so that you MUST add a '+' to
+add a mask that doesn't contain a '.', '!' or '@'.
+That would lead to 2.8.19.U3.2 and a very tiny patch for the people who
+already compiled .U3
+
+Run
+
+
+=============================================================================
+                       U3 - required additions to .ircrc
+=============================================================================
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: Re: .ircrc codes for the new patches :)
+To: lamberdc@dad.cs.tuns.ca
+Date: Mon, 23 May 94 11:41:31 METDST
+Cc: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+In-Reply-To: <9405222118.AA02415@dad.cs.tuns.ca>; from "Donald "WHIZZARD" Lambert" at May 22, 94 6:18 pm
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+> hiya All
+>       I was wondering if some one could send me a copy of the script/
+>  for the new patches including the ban , topic and client connecting patches.
+> 
+>       And whatever is now out with the new .19 code :)
+> 
+>       thanks 
+> 
+>               -- Donnie
+> 
+>               aka WHIZZARD
+
+The ftp.undernet.org:/pub/undernet/servers/Patches/README file:
+
+These are lines you need to add to your .ircrc file in order
+to use all posibilities .U3 profides you:
+
+To see when a channel was created:
+
+On ^329 * echo *** $1 : created $stime($2)
+
+When your server has the .ban patch use this to see who did a ban and when:
+
+On ^367 * if ([$4] != []) {echo *** $1 \($3 - $stime($4)) $2} {echo *** $1-}
+
+---------------------------
+When ALL servers upgraded to .U3, you can use this command:
+
+ALIAS SILENCE QUOTE SILENCE
+On ^RAW_IRC "% SILENCE %" echo *** $*
+
+Use this as:
+/SILENCE +mask
+
+To add 'mask' to your silence list (will completely stop all private
+messages from people matching 'mask' with their nick!user@host).
+You can VIEW your silence list by typing:
+/SILENCE
+
+If you message someone and he doesn't react (like with ping), then you
+can check if you match a silence mask he added by viewing his silence list
+with:
+/SILENCE nick
+
+A mask can be deleted by using the command:
+/SILENCE -mask
+
+When the some messages you from behind a NON-.U3 server you will not
+receive his message but the line:
+*** Unknown command (SILENCE) (From server.name.undernet.org)
+instead, so you will still be flooded.
+We hope all servers will be upgraded within a few months.
+
+------
+And my ircd.motd from Delft* :
+
+*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%*%
+ N E W : - This server now runs the official released
+           beta version 2.8.19.U3.1.ban
+ For you as users this means that:
+ -More security : .U3 contains the .TSpre8 patch with
+  disallows even ADMINs of servers to hack op on your
+  channel with a nick, most server modes are detected.
+ -The possibility to see the *creationtime* of a channel
+  (used with the TimeStamp (TS) protocol - unique on
+   undernet (disables the possibility of 'net.riding'))
+ -The possibility to stop EVERY kind of channel flooding
+  by *banning* someone : Now a ban stops not only
+  part/join floods, but also message floods and even
+  nick floods!
+ -The possibility to see who did when a certain ban.
+ -The possibility to stop anyone flooding you with
+  any kind of private messages at his *own* server!
+  (This will only work when ALL servers have upgraded)
+To be able to use all of this, ftp to sg.tn.tudelft.nl
+login: ftp ; password : anything ; file: /pub/README
+Put those lines in your .ircrc initialisation file !
+Have fun, Run.
+
+----
+
+Run
+
+=============================================================================
+                       U3.1 -> U3.2    
+=============================================================================
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: *BUG* .U3.1 found !!
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Wed, 25 May 94 16:45:39 METDST
+In-Reply-To: <457.9405250732@ccws-09.brunel.ac.uk>; from "James T Lowe" at May 25, 94 8:32 am
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+> :-> 
+> :-> Hiya..
+> :-> 
+> :->     Here's what I observed tonight:
+> :-> 
+> :-> *** Mmmm (mandar@roosevelt.ecn.uoknor.edu) has joined channel #friendly
+> :-> *** Users on #friendly: @Mmmm 
+> :-> *** Mode change "-o Mmmm" on channel #friendly by Uxbridge.*
+> 
+> Not surprising : 
+> 
+> #friendly  RedRum    H*  cs93jtl@ccws-09.brunel.ac.uk
+> #friendly  Emmy      H   lamphear@cheshire.oxy.edu
+> #friendly  ChemBot   H@  cmrobert@hellcat.ecn.uoknor.edu
+> 
+> 
+> 
+> >From Norman : 
+> 
+> *** ChemBot is cmrobert@hellcat.ecn.uoknor.edu (Charles Michael Roberts)
+> *** on channels: @#ChatZone 
+> *** on irc via server Norman.OK.US.undernet.org
+> *** ChemBot has been idle 10 minutes
+> 
+> 
+> and from Uxbridge : 
+> 
+> ** ChemBot is cmrobert@hellcat.ecn.uoknor.edu (Charles Michael Roberts)
+> *** on channels: @#chatZone @#friendly 
+> *** on irc via server Norman.OK.US.undernet.org
+> 
+> :-> But,
+> :-> 
+> :-> *** Mmmm has left channel #friendly
+> :-> *** Mmmm (mandar@roosevelt.ecn.uoknor.edu) has joined channel #test
+> :-> *** Users on #test: @Mmmm 
+> :-> 
+> :-> works fine..
+> :-> 
+> :-> Is this due to the U lines?  Uworld was in no way involved though :-(
+> :-> 
+> :-> I personally feel that uxbridge's retaining timestamps of old channels - 
+> :-> Run, can ya take a look asap. It can prove most distressing for our users :(
+> :-> 
+> :->                           Thanks!!
+> :-> 
+> :->                                                   Mmmm
+> 
+> 
+
+Weeehhhw, yeah a real bug :/
+
+RedRum and I looked for it for almost 4 hours before it was found...
+
+I will release .U3.2  and a patch for .U3.1-U3.2 asap...
+
+Description of bug:
+
+When someone gets kicked (and doesn't (try to) rejoin), it is flagged
+as a zombie. After a net-break, users are mutual re-joined on both
+sides of the net. It turned out that a zombie is also rejoined after
+a net rejoin.
+
+What happened with ChemBot:
+
+ChemBot was on #friendly via Norman (non TSpre8). It was banned and then
+kicked. It tried to rejoin, but Norman didn't allow that (ban).
+Delft never saw this try, and ChemBot stayed as a zombie on #friendly.
+Then Delft-UxBridge broke and reconnected... Delft did send a JOIN for
+ChemBot to UxBridge, ending up in a nick-desynced state.
+When Mmmm joined #friendly, he was the first on #friendly on all of the
+net except UxBridge... He was opped by Norman, but that is considered
+as a HACK by UxBridge and was bounced (ChemBot was still there *with*
+ops (those flags aren't reset when someone is marked zombie)).
+The second time Mmmm joined, he again got ops from Norman which now
+was accepted by UxBridge because this +o could be a BOUNCE of the de-op
+by UxBridge (Generating a BOUNCE or HACK: notice on UxBridge).
+
+Run
+
+
+
+From: Carlo Kid - Runaway <carlo@sg.tn.tudelft.nl>
+Subject: Release 2.8.19.U3.2
+To: wastelanders@rush.cc.edu (New Wastelanders MailingList)
+Date: Wed, 25 May 94 23:30:57 METDST
+Mailer: Elm [revision: 66.33]
+Status: RO
+
+Hi all,
+
+I released 2.8.19.U3.2
+
+Fixed:
+
+        - Rejoining of zombies after net break :/  (ChemBot case)
+        - Silence command now give: No such nick, when doing /silence nick
+        - I fixed the way a kick is handle, because in a last minute
+          thought I realized MURC would get trouble otherwise, see below.
+
+As usual you can get it from ftp.undernet.org:/pub/undernet/servers
+
+Patches/irc2.8.19.U3.1-2.patch     : If you already had .U3.1
+
+irc2.8.19.U3.2.tar.gz              : If start from scratch (DO SO!!!)
+
+For those who use the irc2.8.19.U3.1-2.patch ...
+
+------------------------------------------
+*** EDIT include/patchlevel.h !!!!!!!! ***
+------------------------------------------
+
+This patch will change your version to irc2.8.19.U3.2  so if you run
+ .ban  EDIT it !!!
+
+=========================================================================
+
+The change in KICK handling is as follows:
+
+- A kick received from a local client, or for a local client or
+  from a direction in which the kicked client is located, is
+  simply handled as before: completely removed from channel, no zombie.
+  This means also that there is no client-server protocol change anymore:
+  /who, /whois and /names won't change.
+
+- A kick received for a local client originating from a remote client
+  lets the server sent a PART upstream. Since this results for non TSpre8
+  servers in a remote "You're not on that channel" message, this
+  message is suppressed (would never occur anyway now we are completely
+  synced).
+
+The reason why this was needed is mainly because MURC constantly kicks
+people who have auto-rejoin disabled from different channels. With U3.1
+they would get into problems after ten channels (needed to send extra
+PART's).
+
+Run
+
+--
+-------------------------------------------------------------------------------
+|  carlo@sg.tn.tudelft.nl           |  Run @ IRC                              |
+|                                   |  Admin of Delft.NL.EU.undernet.org      |
+| * Don't expect anything of live,  |  and      Ircserver.et.tudelft.nl       |
+| or you'll miss all the rest of it.|                                         |
+-------------------------------------------------------------------------------
+
+
+
+=============================================================================
+                       U3->U4, ANTI NICK COLLIDE 
+=============================================================================
+Author: Carlo, carlo@sg.tn.tudelft.nl, Run on IRC.
+
+Hi all...
+
+After we dealt with channel msg-, join/part- and nick-floods (.bquiet),
+and with private message flooding (.silence), now in a sort of follow up
+to the anti net.break.ride (.TSpre7) and anti admin.hacks (TSpre8) we are
+about to deal with one of the last vulnerabilities of our net:
+nick-collision bots.
+Called .anc (anti nick collision).
+             -    -    -
+
+Socially spoken it does the following (I hope):
+
+- Only kills the RIGHT person, when a nick collision occurs.
+
+This would mean:
+
+A) If someone tries to harash by connecting to servers that just broke off
+and then take the nick of a person on the other side, both would be
+killed on reconnection. But with the .anc patch on both connecting servers,
+only the net.break rider will be killed.
+
+B) Secondly, when your server (or side) breaks off and you connect to some
+other server on the other side, it happens sometimes that due to lag you get
+killed by a nick collision after reconnection of the net.
+
+A and B differ strongly in the sense that in case A the *new* -the youngest-
+nick must be killed, while in case B the *old* (lagged) nick must be
+killed.
+Technically this means that we have to look at the user@host.name too.
+This gives rise to some incompatible changes, and therefor, this patch
+must be done in two steps: First we deal with the nick-collision bots and
+make the server compatible with both - the old and new protocol. And then
+once all server upgraded, we deal with the last step: the nick collision
+with yourself.
+
+In the future there will be a third possible condition in which we can have
+a nick collision: (long example follows, can be skipped)
+
+C) The net breaks, and reconnects else where, and somehow a race condition
+occurs between the 'SERVER' messages of the servers of one side.
+For example:
+
+Servers:        Part A                  Part B1                 PartB2
+Nicks           a(A),b(B)               a(A),b(B)               a(A),b(B)
+Part A breaks off Part B:
+                <-- :b QUIT             --> :a QUIT
+                <-- SQUIT serversB      --> SQUIT serversA
+Result:         a(A)                    b(B)                    b(B)
+A reconnects to Part B1, but immedeately breaks off again:
+                        -->SERVERs A
+                        -->NICK a
+                        -->:a USER ...
+Break: 
+                                                -->SERVERs A
+                                                -->NICK a
+                                                -->:a USER ...
+                                        --> :a QUIT
+                                        --> SQUIT serversA
+A connects to part B2, note that 'part B1 --> part B2' is lagged and the
+"SERVERs A ... etc" didn't arrive yet on partB2.
+Servers:        Part B1                 Part B2                 Part A
+Nicks:          b(B)                    b(B)                    a(A)
+                        -->SERVERs A
+                        -->NICK a
+                        -->:a USER ...
+                --> :a QUIT
+                --> SQUIT serversA
+                                                --> SERVERs B
+                                                --> NICK b
+                                                --> :b USER ...
+                                                <-- SERVERs A
+                                                <-- NICK a
+                                                <-- :a USER ...
+Result *before* the lagged messages arive on Part B2:
+Nicks:          b(B)                    b(B),a(A)               b(B),a(A)
+                        -->SERVERs A
+                        -->NICK a
+                        -->:a USER ...
+                        -->:a QUIT
+                        -->SQUIT serversA
+And when the lagged messages arrive on Part B2, the
+'SERVERs A' get all ignored: "server exists", even more, Part B2 disconnects 
+Part B1... :/. Now we are going to deal with the "server exists" problem
+*once* (attaching a timestamp to SERVER I think), in which case 'SERVERs A'
+would only be ignored by Part B2. Then the 'NICK a' would cause a nick
+collision with 1) Same user@host.name, 2) same server A, and 3) same
+nick-TS ! This means: We need to ignore 'NICK nick' when is has the same 
+TimeStamp and the same user@host. But when the user@host differ, two people 
+signed on at exactly the same time with the same nick and we must kill 
+*both* to avoid a desync.
+----
+
+How to handle a nick collision, general
+---------------------------------------
+
+Up till now when a nick collision occurs, both nicks (when a nick change
+from 'old' to 'new' is involved) are KILLed in ALL directions.. wiped off the
+net thus.
+But even with wiping off the net in mind, it doesn't make sense to kill in
+old direction, it is sufficient to deal with "our side" of the net, because
+every nick collision occurs on two servers, both can deal with their side.
+As an example:
+
+Servers:        A               B
+Nicks:          a(A)            a(B)
+Reconnection:
+                <-- NICK a
+                    NICK a -->
+
+As you see, if A receives the 'NICK a' from B, it only has to deal with
+its own side, because it is certain that B will receive the 'NICK a' from
+A and handle it too as a nick collision (and therefore this 'NICK a' *is*
+already a 'KILL' message).
+
+Thus, when we receive a 'NICK' that gives rise to the need of purging
+a nick on *our* side, we deal with it by doing:
+sendto_serv_butone(cptr,":%s KILL ...
+which sends the KILL to all server connections except the link 'cptr' that
+generated the nick collision.
+Also then we have to destroy the memory usage of the killed client on our
+own server, and disconnect him if it is ours, so we call exit_client().
+
+Summary so far
+--------------
+
+Ok, we discussed when to kill who. Resulting rules are:
+
+We receive a "NICK new" or ":old NICK new" from a server direction, and
+we already have a registered 'new'. Then we have a nick collison and deal
+with it as follows:
+1) If the user@host differ,
+        and our 'new' is younger or equal, KILL our 'new'.
+        and our 'new' is older, ignore the 'NICK new', but kill 'old' on
+                our side if it was a nick change.
+2) If user@host is the same:
+        and our 'new' is older, KILL our 'new'.
+        and our 'new' is younger, ignore the 'NICK new', but kill
+                'old' on our side if it was a nick change.
+        and our 'new' is equal, KILL our 'new',
+                and kill 'old' on our side too if it was a nick change.
+
+Remarks:
+        The last case, where have a ':old NICK new' collission with
+the same user@host and TimeStamp, can't be case C), but it
+is possible that *we* did send a 'NICK new', and the server at
+the other side kills 'old' off... So we have to do it too.
+        With this protocol we *ignore* 'NICK new' message, but of course
+in most cases they will be followed by at least a ':new USER ...' and
+probably
+more like ':new JOIN #...'. This would cause 'Fake direction' errors and
+the current protocol KILLs such a nick in *ALL* direction (???). Now, we
+DON'T want to KILL the nick in the right direction do we? And killing the
+fake direction nick makes no sense: it will be killed automatically due to
+the fact we did send a 'NICK new' in that direction. Thus: we can/have to
+remove the Fake Direction kills.
+        Of course, when we receive a 'NICK new hopcount :TimeStamp', we
+*can't* compare with the user@host, because it takes some time before we
+will receive the 'USER'... We also can't store the nick, because it must
+be unique. To solve this, we need to change the protocol so that 'NICK new'
+contains all information and the USER message will be redundant and removed
+in a later patch. This also reduces net.bursts.
+        
+Attaching a TimeStamp (TS) to nicks
+-----------------------------------
+
+Whenever someone takes a new nick, which is available of course, either by
+changing nick or by signing on, the local server attaches a TimeStamp (TS)
+to the nick. Now there will be sent:
+
+NICK new hopcount TS user host.name server.name :Real name
+or
+:old NICK new :TS
+
+This is 100% compatible with the existing protocol.
+
+When a server receives such a nick from a neighbouring server it copies the
+TS, keeping track of the local change time. (When not all servers have
+upgraded, and no TS is received, the .anc server will fill in the time
+itself - being a slight advantage over keeping it 0. It also will assume 
+that the host.names are equal or not equal resulting a as many kills as 
+needed in the worst case).
+
+
+Examples
+--------
+
+Servers:    A                     B
+Nicks:      a(A),b(B)             b(B),a(A)
+Both change simultaneously to nick 'c', but 'a' slightly faster (at time=1,
+and b at time=2):
+            c(A),b(B)             c(B),a(A)
+                 -> :a NICK c :1
+                 :b NICK c :2 <-
+Then A receives a ':b NICK c :2' where 2 > 1, and KILLs b on its own side.
+B however receives ':a NICK c :1' where 1 < 2, and KILLs c on its own side.
+Result:     c(A)                  c(A)
+
+Due to 'lag', more :c PRIVMSG .. from B to A can follow, resulting in a
+fake direction. 'A' will simply ignore them and not kill c (kills for
+fake direction are nonsense anyway).
+
+In the case that someone signs on, taking the same nick as a nick change
+we have almost the same:
+
+Servers:    A                     B
+Nicks:      a(A)                  a(A)
+'a' changes simultaneously to nick 'c', but slightly faster (at time=1),
+as a new 'c' signs on at B (time=2).
+            c(A)                  a(A),c(B)
+                -> :a NICK c :1
+                  NICK c 1 :2 <-
+Then A receives a 'NICK c 1 :2' where 2 > 1, and ignores it simply.
+B however receives ':a NICK c :1' where 1 < 2, and KILLs c on its own side.
+Result:     c(A)                  c(A)
+
+If the new 'c' was a little bit earlier, we get:
+
+Servers:    A                     B
+Nicks:      a(A)                  a(A)
+'a' changes simultaneously to nick 'c', and slightly slower (at time=2),
+as a new 'c' signs on at B (time=1).
+            c(A)                  a(A),c(B)
+                -> :a NICK c :2
+                  NICK c 1 :1 <-
+Then A receives a 'NICK c 1 :1' where 1 < 2, and KILLs c on its own side.
+B however receives ':a NICK c :2' where 2 > 1, and KILLs a on its own side.
+
+Result:     c(B)                  c(B)
+
+Last case, two people sign on (or during a net reconnection):
+
+Server:     A                     B
+Sign on:    c(A)                  c(B)
+                -> NICK c 1 :1
+                   NICK c 1 :2 <-
+Then A receives 'NICK c 1 :2' where 2 > 1, and ignores it.
+and B receives a 'NICK c 1 :1' where 1 < 2, and KILLs c on its own side.
+Result:     c(A)                  c(A)
+
+Note: the above didn't take equal TS's into account, and I assumed
+user@hosts to be different: the nick collision bot cases.
+
+A last possibility when someone starts hacking... a 'NICK new' twice
+from the same direction, should not be accepted: we kill the earlier one
+always.
+
+Compatibility problems
+----------------------
+
+In the future, we want to also take 'user@host' into account... Therefor,
+we need to start to ignore 'NICK new' and only act upon ':new USER ...'.
+We can only do that if also 'USER contains the hopcount and TimeStamp'...
+If we change the protocol immedeately to say:
+:nick USER user host.name server.name hopcount TimeStamp :Real name
+the 'hopcount' would be treated as realname, and we the rest would be
+lost :(
+
+We can also transfer the info to 'NICK', with:
+
+:server.name NICK nick hopcount user host.name TimeStamp :Real name
+
+and later forget the USER message. Although someone lamer uses
+the C source line " if (sptr == cptr) ..." in m_nick() to determine if
+it was a 'NICK new' or a ':old NICK new' :/ Geesh (unlogical). He should
+have used " if (IsServer(sptr)) ...". You would need three upgrade steps
+or we will have to do with:
+NICK nick hopcount user host.name server.name TimeStamp :Real name
+
+The nice thing about this is, that we can check how many parameters we
+receive and then immedeately use the user@host if it is there... That way
+the .acn will immedeately work once everyone upgraded once, and the second
+step would only get rid of the 'USER' line between servers.
+
+Run
+
+
+=============================================================================
+                                WALLOPS
+=============================================================================
+Usage: /WALLOPS <message>
+
+Sends a message to IRC ops on-line. Remember that users who are /umode +w
+can see wallops as well.
+
+
+=============================================================================
+                                STATS W
+=============================================================================
+Author: Michael Vanloon (michaelv@iastate.edu) - mlv on IRC  
+Help on /stats w :
+
+I've been working on something I think would be quite a useful
+addition to the ircd.  It keeps track of the average number of local
+clients, total clients, and total connections (including servers) over
+various periods of time, currently over the last minute, hour, day and
+week.  It is invoked by "/stats w server.name".  You may try it
+yourself by "/stats w *.iastate.edu".  A sample from
+ircserver.iastate.edu looks like this:
+
+*** Minute    Hour      Day       Week      Userload for:
+***  44.91     39.4      33        33       iastate.edu clients
+*** 114.40    103.2      69        65       total clients
+*** 120.40    109.0      73        70       total connections
+*** * End of /STATS report
+
+I'm debating changing it to show average connections over the last
+minute, hour, day, prev. day, and prev. day, as I think the data
+doesn't change enough after that to really be useful to justify
+keeping it over an entire week.
+
+On smaller, less used servers, it should add a negligible amount to
+the resident memory consumed by the ircd.  On a large hub such as the
+*.bu.edu servers, penfold, or ircserver.iastate.edu, it might add as
+much as 300k to the amount of memory the ircd attempts to keep
+resident.  The amount is determined solely by the number of
+connects/disconnects the server processes over the span of time
+measured.
+
+The code is bulletproof and has undergone *extensive* debugging and
+testing before I announced it here.
+
+The reason I'm posting this is because I would like to know how many
+people think this would be a useful addition to the server.  In
+addition, I'd like feedback on whether you think this should be a
+standard part of the distributed server code.  I think it should, but
+Avalon wants me to post here first and see how others feel about it.
+I'd appreciate your input.
+
+I will be making a patched 2.7.2 server available with this included,
+for those who would like to have this and stability too.  I'll also be
+hooking it into 2.8.x soon, and giving it back to Av to include in the
+standard 2.8 distribution as it matures, if he sees fit to do so.
+
+Thanks for your time...
+
+                                --Michael (mlv)
+
+IRC log started Wed Aug 18 21:52
+*** Value of LOG set to ON
+*mlv* it's the usage of your server
+*mlv* average number of users on your server over the last minute, hour, day, yesterday, and the day before
+*mlv* local clients, total clients, and total connections (clients + servers)
+-ircserver.iastate.edu- Minute   Hour  Day  Yest.  YYest.  Userload for:
+-ircserver.iastate.edu-  23.00   23.0   16    17      11   iastate.edu clients
+-ircserver.iastate.edu-  52.00   52.8   37    35      23   total clients
+-ircserver.iastate.edu-  61.00   61.7   45    42      21   total connections
+-> *mlv* hmm...so iastate had 23 local clients in the last minute?
+*mlv* right... in the last minute the average number of local users on our server was 23
+*mlv* 23.45 actually
+-> *mlv* okie...gotcha... thanks :)   one other thing
+*mlv* there were an average of 23.1 local users on here over the last hour
+*mlv* shoot
+-> *mlv* is it possible to specify multiple domains?
+-> *mlv* for e.g.  uoknor.edu  and  okstate.edu    cos those will be local to midway
+*mlv* it could be coded in, but 1) my code doesn't support it out of the box, and 2) that would add more state info which would increase the memory usage a bit
+-> *mlv* hmm...
+*mlv* it's purely informational... i wouldn't worry about it, really that much
+-> *mlv* okay...also, the Makefile on the ftp site seems hosed.....there's junk at the end...I tried both the .Z and the .gz
+*mlv* i'm thinking about making it log by connection class... but i'll have to use a more efficient statistical algorithm (less precise) if i do that
+*mlv* that way you could put all the local domains into certain classes
+*mlv* oh yeah... it's harmless, just weird
+-> *mlv* that'll work :)
+-> *mlv* well...thanks for your help....will have a look at the stats w patch when you're finished with it :)
+IRC Log ended *** Wed Aug 18 22:22
+
+
+=============================================================================
+                        BAN/TOPIC/SIGNON INFO
+=============================================================================
+Author: Paul Foley (pfoley@kauri.vuw.ac.nz)  SIO on IRC
+
+Help on Ban/Topic/Signon :
+
+Since these patches allow the server to maintain additional information, the
+server replies have been changes for the /mode #channel +b (#367), /whois
+(#317) and an additional reply #333 has been added for topic info. The time
+is returned as a long integer in UTC format in all cases. Since the existing
+clients will ignore this additional information, you will need to either
+patch the client, or in case you're using ircII, use the foll. /on statements
+to take benefit of these patches :
+
+on ^367 * if ([$4] != []) {echo *** $1 \($3 - $stime($4)) $2} {echo *** $1-}
+on ^333 * echo *** Topic for $1 set by $2 on $stime($3)
+on ^317 * if (index(012345679 $3) != -1) {echo *** $1 has been idle for $2 seconds.  Signon at $stime($3)} {echo *** $1 has been idle for $2 seconds.}
+
+
+Once you have done this, you can see info as follows:
+/mode #wasteland +b
+*** #wasteland (Mmmm - Thu Aug 19 04:44:24 1993) test!*@*
+
+/topic #wasteland
+*** Topic for #wasteland: We all love Axl Rose!
+*** Topic for #wasteland set by rbarnes on Thu Aug 19 04:26:56 1993
+
+/whois Mmmm
+*** Mmmm is mandar@essex.ecn.uoknor.edu (Mmmm,I get high with a little help
++from my friends)
+*** on channels: @#wasteland
+*** on irc via server essex.ecn.uoknor.edu (MIDWEST HUB..HELPS YOU GET THERE
++SOONER)
+*** Mmmm is an IRC Operator
+*** Mmmm has been idle for 454 seconds.  Signon at Wed Aug 18 23:47:19 1993
+
+
+=============================================================================
+                        CLIENT NOTIFY
+=============================================================================
+Authors: Patrick Ashmore (pda@engr.engr.uark.edu) - Twilight1 on IRC
+         Mandar Mirashi  (mmmirash@mailhost.ecn.uoknor.edu) - Mmmm on IRC
+         Tony Vencill    (vencill@iastate.edu) - Tony/Tonto on IRC
+
+Help on Client Notify:
+
+This patch allows all opers on your server to see clients connecting to your
+server and disconnecting from it (with the reason why they disconnected). 
+This can help you identify troublesome clients, or redirect remote clients
+to closer servers, or troubleshoot the reason why a client disconnected.
+
+A sample output:
+
+*** Notice -- Client connecting : Karll (agifford@sci.dixie.edu).
+
+*** Notice -- Client exiting : Karll (agifford@sci.dixie.edu) [Bad link?].
+
+PS: if you wish to selectively decide when you wish to see these connection
+notices, use the foll. script
+
+on ^server_notice "% % NOTICE -- CLIENT*" if (hideit != 1) {echo *** $2-}
+alias show @ hideit = 0;echo *** You can now see clients connecting/exiting
+alias hide @ hideit = 1;echo *** You will no longer see clients connecting/exiting 
+
+If you then wish to not see client connects/exits when you are opered, simply
+type /hide. If you wish to see them again, type /show.
+
+=============================================================================
+                        TRACE TIMES
+=============================================================================
+Author: Tony Vencill    (vencill@iastate.edu) - Tony/Tonto on IRC
+
+Help on Trace Time:
+
+  This useful patch lets you identify the times since your server last
+heard from any connected servers or clients. This time is displayed in
+seconds, appended to each line of your /trace output. It can be very
+helpful in identifying which servers are going to drop off or which
+clients may ping timeout from your server.
+
+A sample output:
+
+/trace essex*
+*** Serv [30] ==> 10S 8C cancun.caltech.edu *!*@essex.ecn.uoknor.edu 73
+*** Serv [30] ==> 9S 6C imageek.york.cuny.edu *!*@essex.ecn.uoknor.edu 27
+*** Serv [0] ==> 1S 0C inga1.acc.stolaf.edu[130.71.192.16]
++*!*@essex.ecn.uoknor.edu 58
+*** Serv [0] ==> 1S 0C shadow.acc.iit.edu *!*@essex.ecn.uoknor.edu 97
+*** Serv [0] ==> 1S 2C curie.ualr.edu Mmmm!mmmirash@essex.ecn.uoknor.edu 98
+*** Serv [0] ==> 1S 1C piaget.phys.ksu.edu *!*@essex.ecn.uoknor.edu 117
+*** Oper [0] ==> Mmmm[essex.ecn.uoknor.edu] 0
+*** Serv [50] ==> 1S 0C pv1629.vincent.iastate.edu *!*@essex.ecn.uoknor.edu 7
+*** Class 0 Entries linked: 6
+*** Class 50 Entries linked: 1
+*** Class 30 Entries linked: 2
+
+
+=============================================================================
+                       K- line comments
+=============================================================================
+Author: Mandar Mirashi (mmmirash@mailhost.ecn.uoknor.edu) - Mmmm on IRC
+
+This extremely useful patch allows you to specify either a comment or an
+interval in the 2nd field of the K line (instead of the previous format
+of just a restricted interval). The "comment" can even be configured to
+be a *file* name which can then be displayed to the client rejected via
+the K line. All existing K lines will work as they are. This patch is
+an *enhancement* to the K-lines.
+
+e.g. (of a comment field)
+
+K:*.sdsu.edu:Flooding.is.not.cool.lamer:masc0482
+
+As far as possible, do not use a white space in the comment field, because
+this breaks ircII's /stats k handling. You can use the period (.) or the
+underscore instead (_).
+
+e.g (of a file to be output to a rejected client 
+     -   #define COMMENT_IS_FILE  in config.h)
+
+K:*.netcom.com:/ecn/staff0/irc/servers/vinson/sorry.txt:*
+
+
+=============================================================================
+                               OPER FAIL
+=============================================================================
+Authors: Michael Vanloon (michaelv@iastate.edu) - mlv on IRC  
+         Jon C Green (jcgreen@iastate.edu) - Jon2 on IRC
+        Bryan Collins (b@ctpm.org) - bwy on IRC
+
+This patch notifies you if someone tries to gain oper on your server and
+fails. The notice goes out only to the operators on the server.
+
+*** Notice -- Failed OPER attempt by M (mmmirash@lincoln.ecn.uoknor.edu)
+[crackit]
+
+
+=============================================================================
+                               MIXED CASE
+=============================================================================
+Authors: Michael Vanloon (michaelv@iastate.edu) - mlv on IRC
+         Jon C Green (jcgreen@iastate.edu) - Jon2 on IRC
+
+"Here's a patch mlv and I wrote that prevents clients with mixed-case usernames
+from connecting.  I don't know of many sites that allow mixed-case, so it
+is effective for stopping many clonebot attacks and also stops many
+would-be username hackers."  - Jon2
+
+This has been slightly modified by Mmmm to give an option of ignoring the
+case of the first character if desired (useful for those PC users who
+intuitevely enter their first name with the first letter capitalised).
+
+e.g.
+*** Notice -- Invalid username: buankBOT[saucer.cc.umr.edu]
+
+                               
+=============================================================================
+                               NOTE
+=============================================================================
+
+Usage:
+  \ 2NOTE\ 2 USER [&passwd] [+-flags] [+-maxtime] <nick!username@host> <msg>
+-   or  SEND|SPY|FIND|WAITFOR|NEWS <same as USER args.>
+*   or  SEND|SPY|FIND|WAITFOR|WALL|WALLOPS|DENY|NEWS|KEY <see USER args.>
+  \ 2NOTE\ 2 LS|COUNT|RM|LOG [&pwd][+-flags][#ID] <nick!user@host> [date]
+  \ 2NOTE\ 2 FLAG [&passwd] [+-flags] [#ID] <nick!username@host> <+-flags>
+*  \ 2NOTE\ 2 SENT [NAME|COUNT|USERS] <f.nick!f.name@host> <date> [RM]
+-  \ 2NOTE\ 2 STATS [MSM|MSW|MUM|MUW|MST|MSF|USED]
+-  \ 2NOTE\ 2 SENT [NAME|COUNT]
+*  \ 2NOTE\ 2 STATS [MSM|MSW|MUM|MUW|MST|MSF|USED|RESET] [value]
+*  \ 2NOTE\ 2 SAVE
+
+  The Note system have two main functions:
+  1. Let you send one line messages to irc users which 
+     they will get when they next sign on to irc.
+     Example: NOTE SEND <nick> Hi, this is a note to you.
+  2. Let you spy on people, to see when they sign on or off,
+     change nick name or join channels.
+     Example: NOTE SPY +100 <nick>  (spy on nick for 100 days)
+
+  You may fill in none or any of the arguments listed above, including
+  * or ? at any place, as nick@*.edu, !username, ni?k!username etc...
+  Other usefull features may be NOTE WAIT <nick>, making nick and
+  you get a message when you both are on at the same time.
+  Note was developed 1990 by jarle@stud.cs.uit.no (Wizible on IRC).
+
+
+*Usage: NOTE [<server>] ANTIWALL
+*  Switch off b flag for wall's which you have received on specified
+*  server. The person who queued the wall would be notified about
+*  the antiwall, and who requested this.
+
+
+Usage: NOTE COUNT [&<passwd>] [+|-flags] [#<ID>] <nick!username@host>
+  Displays the number of messages sent from your nick and username. See
+  HELP LS for more info. See HELP FLAG for more info about flag setting.
+
+
+*Usage: NOTE DENY [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+*              <nick!user@host> <msg>
+*  DENY is an alias for USER +RZ (default max 1 day)
+*  This command makes it impossible for any matching recipient to
+*  queue any Note requests until timeout.
+
+
+Usage: NOTE FIND [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+               <nick!username@host> <msg>
+  FIND is an alias for USER +FLR (default max 1 day)
+  This command makes the server search for any matching recipient, and
+  send a note message back if this is found. If <msg> field is used, this 
+  should specify the realname of the person to be searched for. Examples:
+    FIND -4 foo*!username@host
+    FIND @host Internet*
+    FIND nicky Annie*       
+    FIND +7 * Annie*
+
+
+Usage: NOTE FLAG [&<passwd>] [+|-<flags>] [#<ID>]
+               <nick!username@host> <+|-flags>
+  You can add or delete as many flags as you wish with +/-<flag>.
+  + switch the flag on, and - switch it off. Example: -S+RL
+  Following flags with its default set specified first are available:
+    -S > News flag for subscribing.
+    -M > Request is removed after you sign off.
+    -Q > Ignore request if recipient's first nick is equal to username.
+    -I > Ignore request if recipient is not on same server as request.
+    -W > Ignore request if recipient is not an operator.
+    -Y > Ignore request if sender is not on IRC.
+    -N > Let server send a note to you if message is delivered.
+    -D > Same as N, except you only get a message (no queued note to you).
+    -R > Repeat processing the request until timeout.
+    -F > Let server send note info for matching recipient(s). Any message
+         part specify what to match with the realname of the recipient. 
+    -L > Same as F, except you only get a message (no queued note to you).
+    -C > Make sender's nicks be valid in all cases username@host is valid.
+    -V > Make sender's "nick*" be valid in all cases username@host is valid.
+    -X > Let server display if recipient signs on/off IRC or change
+         nickname. Any message specified is returned to sender.
+    -A > Show what server matching user is on using X flag.
+    -J > Show what channel matching user is on using X flag.
+    -U > Do not display nick-change using X flag.
+    -E > Ignore request if nick, name and host matches the message text
+         starting with any number of this format: 'nick!name@host nick!... '
+*    -B > Send a message to every account who match the nick!user@host 
+*         This creates a received list with flag H set. (see LS +h)
+*    -T > Send a message not all nicks on same accounts too using B flag.
+*    -K > Give keys to unlock privileged flags by setting that flags on.
+*         The recipient does also get privileges to queue unlimited 
+*         numer of requests, list privileged flags and see all stats.
+*    -Z > Make it impossible for recipient to queue anything at all.
+  Other flags which are only displayed but can't be set by user:
+    -O > Request is queued by an operator.
+    -G > Notice message is generated by server.
+-    -B > Broadcasting message.
+*    -H > Flag list for who's received Broadcast message (B flag).
+-  Notice: Message is not sent to recipient using F, L, R or X flag.
+-  Using this flags, no message needs to be specified.
+*  Notice: Message is not sent to recip. using F, L, R, X, K, Z or H
+*  flag (except if B flag is set for R). For this flags, no msg. needed.
+
+  Examples:
+    FLAG * +cj     : Switch on c and j flag for all requests.
+    FLAG +x * +c   : Switch on c flag for all req. which have x flag.
+    FLAG nick -c+j : Switch off c flag and which on j flag for nick
+
+
+*Usage: NOTE KEY [&<passwd>] [+|-<flags>] [+|-<maxtime>] <nick!user@host>
+*  KEY is an alias for USER +KR (default max 1 day)
+*  This command is for allowing no-opers to use oper-options by specifying
+*  the flag to unlock. Be careful with this option!
+*  Example: KEY +365 +s * would make it possible for everybody to use s flag.
+
+
+Usage: NOTE LOG [&<passwd>] [+|-<flags>] [#<ID>] <nick!username@host>
+  Displays how long time since matching person was on IRC. This works 
+  only after use of NOTE SPY. The log is protected to be seen for other
+  users than the person who queued the SPY request. To get short
+  output, do not specify any arguments. Example:
+    LOG      : Print short log of all
+    LOG *    : Print long log including real names of all
+    LOG nick : Print long log for user nick.
+
+
+Usage: NOTE LS [&<passwd>] [+|-<flags>] [#<ID>]
+               <nick!username@host> [<date>]
+  Displays requests you have queued. No arguments would show you
+  all requests without the message field.
+  Use flags for matching all messages which have the specified flags set
+  on or off. See HELP NOTE FLAG for more info about flag settings. Time 
+  can be specified on the form day.month.year or only day, or day/month, 
+  and separated with one of the three '.,/' characters. You can also 
+  specify -n for n days ago. Examples: 1.jan-90, 1/1.90, 3, 3/5, 3.may.
+  If only '-' and no number is specified max time is set to all days.
+  The time specified is always the local time on your system. Example:
+    LS !user    would show you all requests to username@*
+    LS +x       would show all your SPY requests.
+    LS #300     would show you only request #300.
+
+
+Usage: NOTE NEWS [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+               <group!username@host>
+  NEWS with no message is an alias for USER +RS (default max 60 days)
+  This command is for subscribing on a specified newsgroup from any
+  user(s) or host(s). Wildcards may be used anywhere. Example:
+    NEWS irc.note       : Subscribe on irc.note
+*    NEWS irc.note@*.no  : Send to group irc.note, but only for
+*                          users at host *.no
+*    NEWS irc.note       : Send to all for group irc.note
+*    NEWS Users@*.edu Hi : Send Hi to all users using note in your
+*                          server located at host *.edu.
+   (Advanced users may use User +rs <...> <filter> where filter is a 
+   string which must matches with field in received news message)
+-  Only opers can send news as default.
+*  To send news add message and use same format as subscribing, except 
+*  that username field must matches with subscribed group as alt.irc!*.irc - 
+*  everybody subscribing on a*.irc or *.irc or alt.irc... would get the news.
+*  A speciall case is the group Users which everybody using note in the server
+*  are member of.
+
+
+Usage: NOTE RM [&<passwd>] [+|-<flags>] [#<ID>] <nick!username@host>
+  Deletes any messages sent from your nick and username which matches
+  with nick and username@host. Use flags for matching all messages which
+  have the specified flags set on or off. See HELP FLAG for more info
+  about flag settings, and HELP LS for info about time.
+
+
+*Usage: NOTE SAVE
+*  SAVE saves all messages with the save flag set. Notice that the
+*  messages are automatically saved (see HELP STATS). Each time server is
+*  restarted, the save file is read and messages are restored. If no users
+*  are connected to this server when saving, the ID number for each
+*  message is renumbered.
+
+
+Usage: NOTE SEND [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+               <nick!username@host> <msg>
+  SEND is an alias for USER +D (default max 60 days)
+  This command is for sending a message to recipient, and let the server
+  send a note + a notice to sender if this is on IRC - if message is sent.
+    SEND foobar Hello, this is a test.
+    SEND +7 !username@*.edu Hello again!
+
+
+-Usage: NOTE SENT [NAME|COUNT]
+*Usage: NOTE SENT [NAME|COUNT|USERS] <f.nick!f.name@host> <date> [RM]
+  Displays host and time for messages which are queued without specifying
+  any password. If no option is specified SENT displays host/time for
+  messages sent from your nick and username.
+  NAME displays host/time for messages sent from your username
+  COUNT displays number of messages sent from your username
+*  USERS Displays the number of messages in [], and names for all users
+*  who have queued any message which matches with spec. nick/name/host.
+*  RM means that the server removes the messages from the specified user.
+
+
+*Usage: NOTE SERVICE <nick> <note command>
+*  Useful in robots. Note will take the requests as if from <nick>
+
+
+Usage: NOTE SPY [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+               <nick!username@host> [msg]
+  SPY is an alias for USER +RX (default 1 max day)
+  SPY makes the server tell you if any matching recipient sign(s)
+  on/off IRC or change nick name. No message needs to be specified.
+  However, if a message is specified this is returned to sender including
+  with the message about recipient. Message could for example be one or
+  more Ctrl-G characters to activate the bell on senders machine.
+  As an alternative for using C flag, <msg> field could start with
+  any number of nicks on this format: %nick1 %nick2... %nickn, with
+  no space between % and the nick name you use. Spy messages would be
+  valid for any of the nicks specified.
+  SPY with no argument would tell you what users you have spy on who are 
+  currently on IRC. The system logs last time the last matching person was 
+  on IRC for each SPY request is queued in the server. See NOTE LOG for this.
+  You may use flag +A to see what server matching user is on, 
+  and/or +J flag to see what channel. (Read HELP NOTE USER for more). Example:
+    SPY foobar!username@host <ctrl-G>
+    SPY +10 foobar
+    SPY +aj &secret * <ctrl-G>
+    SPY +365 +e !user nick!*@* <ctrl-G>
+    SPY % +7 foo!user
+    SPY +5 nicky %mynick %meenick
+
+
+-Usage: NOTE STATS [MSM|MSW|MUM|MUW|MST|MSF|USED]
+*Usage: NOTE STATS [MSM|MSW|MUM|MUW|MST|MSF|USED|RESET] [value]
+  STATS with no option displays the values of the following variables:
+    MSM: Max number of server messages.
+    MSW: Max number of server messages with username-wildcards.
+    MUM: Max number of user messages.
+    MUW: Max number of user messages with username-wildcards.
+    MST: Max server time.
+    MSF: Note save frequency (checks for save only when an user register)
+  Notice that 'dynamic' mark after MSM means that if there are more
+  messages in the server than MSM, the current number of messages are
+  displayed instead of MSM.
+  Only one of this variables are displayed if specified.
+*  You can change any of the stats by specifying new value after it.
+*  RESET sets the stats to the same values which is set when starting the
+*  server daemon if no note file exist. Notice that this stats are saved
+*  in same file as the other messages.
+
+
+Usage: NOTE USER [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+               <nick!username@host> <msg>
+  With USER you can queue a message in the server, and when the recipient
+  signs on/off IRC, change nick or join any channel, note checks for
+  valid messages. This works even if the sender is not on IRC. See HELP
+  FLAGS for more info. 
+  Password can be up to ten characters long. You may specify password 
+  using the &, % or $ character. & is equal to to $, except working much
+  better cause client use $ for other things...
+  The % character works like &, except it makes the queuing silent. It
+  makess also sense to use this without any following password.
+  If any request queued is equal to any previous except time and maxtime,
+  only maxtime is changed as specified. You then get "Joined" instead of
+  "Queued". 
+  HELP FLAGS for info about flag settings. Username can be specified
+  without @host. Do not use wildcards in username if you know what it
+  is, even if it's possible. Max time before the server automatically
+  remove the message from the queue, is specified with hours with a
+  '-' character first, or days if a '+' character is specified, as
+  -5 hours, or +10 days. Default maxtime is 7 days.
+  Note: The received message is *directly* displayed on the screen,
+  without the need for a read or remove request.
+    NOTE USER &secret +WN +10 Wizible!jarlek@ifi.uio.no Howdy!
+  is an example of a message sent only to the specified recipient if
+  this person is an operator, and after receiving the message, the server
+  sends a note message back to sender to inform about the delivery.
+    NOTE USER +XR -5 Anybody <ctrl-G>
+  is an example which makes the server to tell when Anybody signs
+  on/off irc, change nick etc. This process repeats for 5 hours.
+    NOTE USER +FL @*.edu *account*
+  is an example which makes the server send a message back if any real-
+  name of any user matches *account*. Message is sent back as note from
+  server, or directly as a notice if sender is on IRC at this time.
+
+
+Usage: NOTE WAITFOR [&<pwd>] [+|-<flags>] [+|-<maxtime>]
+               <nick!username@host> [<msg>]
+  WAITFOR is an alias for USER +YD (default max 1 day)
+  Default message is [Waiting]
+  This command is for telling the recipient if this appears on IRC that
+  you are waiting for him/her and notice that this got that message. Example:
+    WAITFOR foobar
+    WAITFOR -2 foobar!username@*
+    WAITFOR foobar Waiting for you until pm3:00..
+
+
+*Usage: NOTE WALL [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+*                      <nick!user@host> <msg>
+*  WALL is an alias for USER +BR (default max 1 day)
+*  This command is for sending a message once to every matching user
+*  on IRC. Be careful using this command. WALL creates a list of 
+*  persons received the message (and should not have it once more time)
+*  with H flag set. This list can be displayed using ls +h from the
+*  nick and username@host which the WALL request is queued from.
+*  Removing the headers (H) before WALL request is removed would cause
+*  the message to be sent once more to what users specified in list.
+*  WALL +7 @*.edu Do not do this! - Makes it clear for all users using 
+*  IRC on host @*.edu the next 7 days how stupid it is to send such WALL's ;) 
+
+
+*Usage: NOTE WALLOPS [&<passwd>] [+|-<flags>] [+|-<maxtime>]
+*              <nick!user@host> <msg>
+*  WALLOPS is an alias for USER +BRW (default max 1 day)
+*  This command is same as WALL, except only opers could receive it.
+=============================================================================
diff --git a/doc/history/history.pre24 b/doc/history/history.pre24
new file mode 100644 (file)
index 0000000..cac4d30
--- /dev/null
@@ -0,0 +1,51 @@
+/************************************************************************
+ *   IRC - Internet Relay Chat, doc/HISTORY
+ *   Copyright (C) 1990
+ *
+ *   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.
+ */
+
+HISTORY of Recent IRC Versions.
+
+Previous version numbering schemes have caused some confusion, which this
+document attempts to resolve.
+
+The original test versions released by WiZ were numbered 2.01?6 where
+the ? refers to a letter.  The last known stable version was U6.
+Version 2.1.0/1 was rewritten by Mike Bolotski from the U6 sources but 
+several bugs were introduced during the rewrite.  After several weeks, 
+almost all servers backed up to U6.  Version v6 contained comparatively 
+minor modifications from U6.  
+
+Version 2.2 consists of the v6 source repackaged into multiple directories, 
+and with modified documentation.  From now on, the version number will
+stay relatively constant.  As minor changes and bug fixes are added, they
+will be distributed in the form of context diffs, to be applied with the
+'patch' command.  Each bugfix will bump the patchlevel (PL) of the release
+by 1.  The PL is documented in the version.c file in the lib/misc directory.
+
+Version 2.3 was unfortunate mistake containing copyright violations
+so it was soon taken off distribution.
+
+Version 2.4 contains *very* many bug fixes, enhancements, and "hooks" for
+use in future releases. The source tree has been restructured, and the
+Makefiles rewritten to be recursive and follow the new source tree layout.
+
+Version 2.5 contains string channels and channel modes (as well as
+channel operators). Also Wizible's MAIL system was included as an option.
+
+Hopefully, whoever provides a fix will also update the respective
+ChangeLogs to summarize the changes, as well as adding a description of
+the bug to the BugList file.
diff --git a/doc/history/overview.u2.9 b/doc/history/overview.u2.9
new file mode 100644 (file)
index 0000000..e677058
--- /dev/null
@@ -0,0 +1,206 @@
+Hi fellow undernetters,
+
+I forgot if it was requested on routing-com or here, but you won't see me
+cross posting, so I did choose 'wastelanders'.
+
+The request was to mail an overview of the changes 2.8 ==> u2.9,
+especially for the new Opers, but also as a reminder for others.
+
+The patch file from irc2.8.21.mu3.1 to ircu2.9.17.mu is 446652 bytes.
+So you will understand I can't cover every little change.
+
+New commands
+------------
+
+/UPING <server.to> [<port>] [<server.from>] [<number of packets>]
+
+Sends <number of packets> (default 5, max 20) size 1024 bytes, from (remote)
+server <server.from> (default local server) to <server.to>. The default
+port is 7007 and the same on all servers. If a server is down, you can
+still use port 7 (echo). Also 2.8 echo's, on port PORTNUM (config.h).
+UPING uses udp, you need CN lines (masks as server names are allowed) but
+the connection doesn't have to exist already.
+
+/RPING <server.to> [<server.from>] [<Optional remark>]
+
+Sends one 'RPING' message using the irc protocol over an existing link.
+It allows to measure the lag of remote links, respons is in ms accuracy.
+The <Optional remark> can be used to measure to total pingtime to your
+client (like the CTCP PING) or to add a serial number for automation.
+
+/MAP [server.mask]
+
+Shows a map in the layout as Router.
+
+/SETTIME
+
+Only for debugging, isn't needed. (Oper only).
+
+Changed commands
+----------------
+
+/CONNECT
+
+No doubt the biggest impact of 2.9 is on connecting:
+When the link is physically possible, your /connect ALWAYS succeeds
+except when an H: or L: line somewhere on the net forbids it, or when
+*after* your connect another connect is done that cause a loop. The only
+restriction is that you are not allowed to make deliberately a loop:
+you must first squit. Loops only happen when to connects are done
+simultaneously and the SERVER messages had not yet time to propagate
+over the whole net.
+When a connect (manual or automatically) is done for a link that used
+to get "server exists", with 2.9 the Ghost is squitted off the net,
+making it possible to recover faster from breaks caused by bad links.
+If on the other hand a loop occurs because two parts connect at two
+points, the servers that detect the server nick collision will squit
+the most logical link to break the loop, and only one link. This results
+thus in a connected net one way or the other (for this all 2.8 servers need
+to be off the net! Till that time the net will connect and then break
+at two places, giving more messages then right now with only 2.8).
+2.9 servers also notify the Opers (or users with +s) about net.junctions
+and net.breaks. It does this even better then Router: A lot faster, always
+correct (REAL junctions), and independent of Router: You will also see
+them when Router is 'on the other side'.
+
+/TIME
+
+Has changed. Now also shows the system clock / TimeStamp clock offset.
+
+/MODE +b
+
+You only have to be joined, not be chan op anymore.
+
+/MODE <nick> +d
+
+Makes the user 'Deaf'. Needed for the channel registration service.
+Channel messages are not routed to a Deaf person decreasing bandwidth use.
+
+/LINKS
+
+Output also shows used protocol for that link.
+
+New numerics
+------------
+
+RPL_MAP, RPL_MAPMORE, RPL_MAPEND and RPL_TRACEPING.
+
+Bug fixes
+---------
+
+- A handshaking link doesn't pingtime out; That can interfere with
+  slow nameserver lookups.
+
+- U: lines (and K: lines) now active directly after a /rehash
+
+- Don't bind() a socket before connect(), thats useless on machines
+  with just one ip number (like we all have), and can confuse
+  some OS's I found out.
+
+Significant Patches
+-------------------
+
+The following patches have been the objective. To realize them I needed to
+rewrite and change huge other parts of the code also, because lot of the code
+in 2.8 is under great tension of re-re-re-patches.
+
+- Rewrote m_server. Objective:
+  = Allow ghosted servers to reconnect (solution "server exists").
+  To allow for this:
+  - Added a timestamp to SQUIT, this timestamp functions as a label
+    which matches the corresponding SERVER (connect).
+  - Added a prefix for every message, absolutely necessary to keep track
+    of the correct order (direction actually).
+
+- The oci has been added (oper sees invisibles on own server).
+
+- A new NOTE is added, many bugs removed and extremely speeded up due to
+  a better interface with the rest of the code.
+
+- The TimeStamp clocks are now automatically synchronized, so a wrong
+  system clock isn't a problem anymore.
+
+- Added a Protocol-version and detection. This allows protocol changes
+  with a *MUCH* higher backwards compatibility.
+
+- Server now keeps track of the server map. This allowed for /MAP and
+  a lot of speed ups (don't have to scan through all clients to find a
+  server) but much more important: The disconnect burst could be brought
+  back to ONE message (instead of a QUIT for ever single client).
+  Apart from decreasing bandwidth use, this was necessary for other
+  important protocol changes, and even more to allow important future
+  changes that will reduce the connect.burst as well. The most important
+  current impact is that it allows SQUIT to travel down stream AND up stream.
+  Because directionless messages can loose the order, the timestamp on
+  SQUIT was needed to check the validity.
+
+- In the client structure a pointer to the server structure is used
+  rather then the full servername, using less memory AND speeding up
+  several places because you don't need to lookup the servername
+  anymore.
+
+- USER removed from the connect burst (now all in NICK).
+
+Other patches
+-------------
+
+- exit_client() is rewritten.
+  Added are exit_client_msg() and exit_new_server().
+  This has especially impact on the possibilities within the protocol.
+  The old exit_client() was clumsy and therefor already used in an incorrect
+  way at several places. The kludges around this part of the code made it
+  impossible to make any changes without breaking something else. Only after
+  the rewrite it was possible to make changes described else where.
+  This also allowed to improve the error message handling to the point that
+  Opers see *always* the error messages involved with routing (also those
+  from remote /connects, delayed errors and squit reasons 'from the other
+  side').
+
+- send.c is more or less rewritten. varargs are fixed now and send.c is
+  highly optimized for speed (possible because of new internal server map). 
+
+- All useable dog3 code speed ups have been added.
+  These include:
+  - Added a head pointer in the dyn buffer.
+  - several code optimisations
+  - continious kill line checking removed (I added the check at
+    the place where it belongs: after a /rehash).
+
+- Useable patches from dl:
+  - Stop as much as possible flooding from unregistered connections.
+  - VERSION and ADMIN available for unregistered users.
+  - syslog (if defined) KILLs of local clients.
+
+- Many compile warnings have been removed. Also a special fix for DYNIX to
+  make UPING/RPING also work there (needed gettimeofday()).
+
+Package changes
+---------------
+
+- The irc client is removed from the package as are several old files with
+  incorrect old useless info (Like 'WHATSNEW', ChangeLog that stopped at 1992).
+- A Makefile.dist is added.
+- Slighty changed doc/Manual
+- New doc/NOTE manual
+- NO_DEFAULT_INVISIBLE removed; users are always visible by default.
+- Last but not least: patchlevel.h is rewritten so any additional patch
+  can do the version update itself, without interfering: No need to edit
+  this by hand anymore.
+
+Summary
+=======
+
+- Less memory usage
+- Speeded up code
+- Less bandwidth use, especially disconnect burst
+- "Server exists" solved
+- Error messages concerning (remote) /connects now always visible.
+- New tools to do (remote) link testing
+- Intelligent and improved SQUIT handling, should stop unwanted breaks.
+- squits comments visible everywhere.
+- net.junction and net.break notices
+- Overall protocol streamlining allowing for future improvements
+  of the protocol.
+
+Run
+
diff --git a/doc/irc.1 b/doc/irc.1
new file mode 100644 (file)
index 0000000..09b3ad3
--- /dev/null
+++ b/doc/irc.1
@@ -0,0 +1,82 @@
+.\" @(#)irc.1 2.6 7 Oct 90
+.TH IRC 1 "7 October 1990"
+.SH NAME
+irc \- User Interface to Internet Relay Chat Protocol
+.SH SYNOPSIS
+\fBirc\fP [\fB-p\fP \fIportnum\fP] [\fB-c\fP \fIchannel\fP] [ \fInickname\fP [ \fIserver\fP ]]
+.SH DESCRIPTION
+.LP
+\fBIrc\fP is a user interface to the Internet Relay Chat, a CB-like
+interactive discussion environment.  It is structured into \fIchannels\fP,
+which are public discussion forums, and also allows for private intercommunication.
+Each participant has a \fInickname\fP, which is the one specified in the command
+line or else his login name.
+.LP
+Once invoked, \fBirc\fP connects as a client to the specified server,
+\fIserver\fP or to the default one (see below).  The screen splits into a dialogue
+window (the major part
+of the screen) and a command line, from which messages can be sent and
+commands given to control irc.
+.SH COMMAND SYNTAX
+The syntax of irc commands is of the form \fB/COMMAND\fP.  The most notable
+ones are listed below.  For an uptodate list, use the \fBHELP\fP command
+of \fBirc\fP.  Case is ignored.
+.IP "\fB/ADMIN\fR [\fIserver\fP]"
+Prints administrative information about an IRC \fIserver\fP.
+.IP "\fB/AWAY\fP [\fImessage\fP]"
+Mark yourself as being away (with an automatic reply \fImessage\fP
+if specified)
+.IP "\fB/BYE\fR, \fB/EXIT\fR, \fB/QUIT\fR"
+Terminate the session
+.IP "\fB/CHANNEL\fR [\fIchannel\fP]"
+Join another \fIchannel\fP
+.IP "\fB/CLEAR\fR"
+Clear the screen
+.IP "\fB/HELP\fR [\fIcommand\fP]"
+Display a brief description of the \fIcommand\fP (or list all commands, if none
+specified).
+.IP "\fB/SUMMON\fR \fIuser\fP"
+Allows to summon a \fIuser\fP specified as a full Internet address, i.e.,
+\fIlogin@host.domain\fP, to an IRC dialogue session (in much the same
+way as the talk(1) command).  It is usable ONLY if the irc daemon runs on
+the target machine (host.domain).
+.IP "\fB/TOPIC\fR \fItopic\fP"
+Sets the \fItopic\fP for the current channel
+.IP "\fB/WHO\fR [\fIchannel\fP|*]"
+Lists all users of IRC if no argument, of the specified \fIchannel\fP or of the
+current channel (*).
+.SH ARGUMENTS
+.IP "\fB-p\fP \fIportnum\fP"
+TCP/IP "port number.  Default is 6667 and this option should seldom if ever"
+be used.
+.IP "\fB-c\fP \fIchannel\fP"
+\fIChannel\fP number to join upon beginning of the session.  Default is no channel.
+.IP "\fInickname\fP"
+\fINickname\fP used in the session (can be changed with the \fB/NICK\fP command).
+Default is user login name.
+.IP "\fIserver\fP"
+\fIServer\fP to connect to.  Default is specified in the irc system configuration
+file, and can be superseded with the environment variable IRCSERVER.
+.SH EXAMPLE
+.RS
+.nf
+tolmoon% \fBirc -p6667 Wizard tolsun\fP
+.fi
+.RE
+.LP
+connects you to irc server in host tolsun (port 6667) with nickname Wizard
+.SH COPYRIGHT
+Copyright (c) 1988 University of Oulu, Computing Center, Finland.
+.nf
+Copyright (c) 1988,1989,1990 Jarkko Oikarinen
+.nf
+All rights reserved.
+For full COPYRIGHT see LICENSE file with IRC package.
+.SH "SEE ALSO"
+ircd(8)
+.SH BUGS
+What bugs ?
+.SH AUTHOR
+Jarkko Oikarinen <jto@tolsun.oulu.fi>
+.nf
+Manual page updated by Michel Fingerhut <Michel.Fingerhut@ircam.fr>
diff --git a/doc/ircd.8 b/doc/ircd.8
new file mode 100644 (file)
index 0000000..be36b88
--- /dev/null
@@ -0,0 +1,147 @@
+.\" @(#)ircd.8 2.0 (beta version) 29 Mar 1989 
+.TH IRCD 8 "29 March 1989"
+.SH NAME
+ircd \- The Internet Relay Chat Program Server 
+.SH SYNOPSIS
+.hy 0
+.IP \fBircd\fP
+[-a] [-c] [-i] [-o] [-q] [-t] [-d directory]
+[-f configfile] [-w interface] [-x debuglevel] [-h hostname] [-p portnum]
+.SH DESCRIPTION
+.LP
+\fIircd\fP is the server (daemon) program for the Internet Relay Chat
+Program.  The \fIircd\fP is a server in that its function is to "serve"
+the client program \fIirc(1)\fP with messages and commands.  All commands
+and user messages are passed directly to the \fIircd\fP for processing
+and relaying to other ircd sites.  The \fIirc(1)\fP program depends upon
+there being an \fIircd\fP server running somewhere (either on your local
+UNIX site or a remote ircd site) so that it will have somewhere to connect
+to and thus allow the user to begin talking to other users. 
+.SH OPTIONS
+.TP
+.B \-d directory
+This option tells the server to change to that directory and use
+that as a reference point when opening \fIircd.conf\fP and other startup
+files.
+.TP
+.B \-o
+Starts up a local ircdaemon. Standard input can be used to send IRC
+commands to the daemon. The user logging in from standard input will
+be given operator privileges on this local ircd. If ircd is a setuid program,
+it will call setuid(getuid()) before going to local mode. This option
+can be used in inetd.conf to allow users to open their own irc clients
+by simply connecting their clients to the correct ports. For example:
+.TP
+.B
+irc stream tcp nowait irc /etc/ircd ircd \\-f/etc/ircd.conf \\-o
+
+allows users connecting to irc port (specified in /etc/services) to start
+up their own ircdaemon. The configuration file should be used to check from
+which hosts these connections are allowed from. This option also turns
+on the autodie option -a.
+.TP
+.B \-a
+Instructs the server to automatically die off if it loses all it's clients.
+.TP
+.B \-t
+Instructs the server run in the foreground and to direct debugging output to standard output.
+.TP
+.B \-x#
+Defines the debuglevel for ircd. The higher the debuglevel, the more stuff
+gets directed to debugging file (or standard output if -t option was used
+as well).
+.TP
+.B \-i
+The server was started by inetd and it should start accepting connections
+from standard input. The following inetd.conf-line could be used to start
+up ircd automatically when needed:
+.TP
+.B
+ircd stream tcp wait irc /etc/ircd ircd \-i
+
+allows inetd to start up ircd on request.
+.TP
+.B \-w interface
+If the server was compiled with VIRTUAL_HOST (run 'make config' to toggle
+this compile option), then \fIinterface\fP is passed to gethostbyname(3) in
+order to retrieve the IP-number of the interface to bind to. An example
+would be to use '-w localhost', after which the server only listens on the
+loopback interface.  Run `ifconfig -a' to see which interfaces you have.
+.TP
+.B \-f filename
+Specifies the ircd.conf file to be used for this ircdaemon. The option
+is used to override the default ircd.conf given at compile time.
+.TP
+.B \-c
+This flag must be given if you are running ircd from \fI/dev/console\fP or
+any other situation where fd 0 isnt a tty and you want the server to fork
+off and run in the background. This needs to be given if you are starting
+\fIircd\fP from an \fIrc\fP (such as \fI/etc/rc.local\fP) file.
+.TP
+.B \-q
+Using the -q option stops the server from doing DNS lookups on all the
+servers in your \fIircd.conf\fP file when it boots. This can take a lengthy
+amount of time if you have a large number of servers and they are not all
+close by.
+.TP
+.B \-h hostname
+Allows the user to manually set the server name at startup. The default
+name is hostname.domainname.
+.B \-p portname
+Specifies the server port where the daemon should start waiting for connections
+from other servers. Clients should connect to ports as specified in the ircd.conf file by means of a P: line.
+.TP
+.SH
+If you plan to connect your \fIircd\fP server to an existing Irc-Network,
+you will need to alter your local IRC CONFIGURATION FILE (typically named
+"ircd.conf") so that it will accept and make connections to other \fIircd\fP
+servers.  This file contains the hostnames, Network Addresses, and sometimes
+passwords for connections to other ircds around the world.  Because 
+description of the actual file format of the "ircs.conf" file is beyond the
+scope of this document, please refer to the file INSTALL in the IRC source
+files documentation directory.
+.LP
+BOOTING THE SERVER:  The \fIircd\fP server can be started as part of the
+UNIX boot procedure or just by placing the server into Unix Background.
+Keep in mind that if it is *not* part of your UNIXES Boot-up procedure 
+then you will have to manually start the \fIircd\fP server each time your
+UNIX is rebooted.  This means if your UNIX is prone to crashing
+or going for for repairs a lot it would make sense to start the \fIircd\fP
+server as part of your UNIX bootup procedure.  In some cases the \fIirc(1)\fP
+will automatically attempt to boot the \fIircd\fP server if the user is
+on the SAME UNIX that the \fIircd\fP is supposed to be running on.  If the
+\fIirc(1)\fP cannot connect to the \fIircd\fP server it will try to start
+the server on it's own and will then try to reconnect to the newly booted
+\fIircd\fP server.
+.SH EXAMPLE
+.RS
+.nf
+tolsun% \fBircd\fP
+.fi
+.RE
+.LP
+Places \fIircd\fP into UNIX Background and starts up the server for use.
+Note:  You do not have to add the "&" to this command, the program will
+automatically detach itself from tty.
+.SH COPYRIGHT
+(c) 1988,1989 University of Oulu, Computing Center, Finland,
+.LP
+(c) 1988,1989 Department of Information Processing Science,
+University of Oulu, Finland
+.LP
+(c) 1988,1989,1990,1991 Jarkko Oikarinen
+.LP
+For full COPYRIGHT see LICENSE file with IRC package.
+.LP
+.RE
+.SH FILES
+ /etc/utmp
+ "irc.conf"
+.SH "SEE ALSO"
+irc(1)
+.SH BUGS
+None... ;-) if somebody finds one, please inform author
+.SH AUTHOR
+Jarkko Oikarinen, currently jto@tolsun.oulu.fi,
+manual page written by Jeff Trim, jtrim@orion.cair.du.edu,
+later modified by jto@tolsun.oulu.fi.
diff --git a/doc/readme.crules b/doc/readme.crules
new file mode 100644 (file)
index 0000000..803f06f
--- /dev/null
@@ -0,0 +1,126 @@
+SmartRoute
+Rule based connects
+Draft 4 - Aug 19, 1994
+by Tony Vencill
+
+Rule based connects allow an admin to specify under what conditions
+a connect should not be allowed.  If no rules are specified for a
+given C and/or N line it will be allowed under any condition.
+
+A rule may consist of any legal combination of the following functions
+and operators.
+
+Functions
+---------
+connected(targetmask)     - true if a server other than that processing
+                            the rule is connected that matches the
+                            target mask
+directcon(targetmask)     - true if a server other than that processing
+                            the rule is directly connected that matches
+                            the target mask
+via(viamask, targetmask)  - true if a server other than that processing
+                            the rule matches the target mask and is
+                            connected via a directly connected server
+                            that matches the via mask
+directop()                - true if an oper is directly connected
+
+Unary operators
+---------------
+!    eg: !argument        - true if the argument is false
+
+Binary operartors
+-----------------
+&&   eg: arg1&&arg2       - true if arg1 and arg2 are both true
+||   eg: arg1||arg2       - true if arg1, arg2, or both are true
+
+Parenthesis () are allowed for grouping arguments, but if no parenthesis
+are included, && will take precedence over ||, ! will take precedence
+over both && and ||, and the function will be evaluated from left to
+right.  White space in a rule is ignored.  Invalid characters in a rule
+will lead to the rule being ignored.
+
+Examples
+--------
+
+A simple example of a connect rule might be:
+
+connected(*eu.under*)
+
+This might be used in a US undernet server for a Europe CN pair to
+insure that a second Europe link is not allowed if one US-EU link
+already exists.  Note that on the undernet, US server names are
+city.state.us.undernet.org and Europe server names are
+city.country.eu.undernet.org.
+
+A more interesting example might be:
+
+connected(*eu.under*) && 
+  ( !direct(*eu.under*) || via(manhat*, *eu.under*) )
+
+Imagine the Boston undernet server uses this rule on its Europe CN
+pairs.  This says that if a Europe server is already connected, a
+Boston-Europe connect will not be allowed.  It also says that if a
+Europe server does already exist and Boston is not directly connected
+to one or more Europe servers or Manhattan is, the Boston-Europe
+connect will not be allowed.  This has the effect of allowing multiple
+US-EU links but attempting to limit these links to one server (ie:
+Boston will not initiate its first Europe link if another server is
+already linking Europe).  This rule will also prefer to let Manhattan
+handle the US-EU link by disallowing Boston-Europe links if a Europe
+server is already linked to Manhattan.
+
+A example of the remaining function, directop(), is:
+
+connected(*eu.under*) || directop()
+
+If this line is used on Boston for the Paderborn CN pair, it will allow
+connects to Paderborn only if another Europe server is not already
+connected and there is not an oper on Boston.  If this rule is
+overrideable (ie: is applied only to autoconnects as described below),
+then it will disallow Boston autoconnects to Paderborn while a Boston
+oper is online, but allow oper-initiated connects to Paderborn under any
+circumstance.  This directop() function could be used to invoke less
+prefered routes only when an oper is not present to handle routing, or
+conversly to allow use of less preferable routes only when an oper is
+present to monitor their performance.
+
+ircd.conf entries
+-----------------
+
+A rule is listed in the ircd.conf file using a D or d line (which can
+be thought of as a "disallow" line).  D lines will apply to all oper
+and server originated connects, while d lines will apply only to
+autoconnects (ie: they are overrideable by opers).  The formats are:
+
+D:targetmask::rule
+d:targetmask::rule
+
+Remember that newlines are not allowed in conf lines.  Two examples
+(from above) are:
+
+D:*eu.under*::connected(*eu.under*)
+d:*eu.under*::connected(*eu.under*) || directop()
+
+Connects originating from other servers will be checked against and
+matching D lines, while matching d lines will be ignored as it will not
+be clear whether or not the connection attempt is oper initiated.
+
+Checking and viewing rules
+--------------------------
+
+The chkconf program that comes with the servers has been modified to
+also check your connect rules.  If running in debug mode, parsing errors
+will show up at debug level 8.  To view rules online, "/stats d" can be
+used to see all rules and "/stats D" can be used to view those rules
+which affect oper initiated connects and accepts.
+
+Processing and storage
+----------------------
+
+The rules are parsed when the conf file is read and transformed into a
+more efficiently computed form, then all applicable rules are
+evaluated each time a connect command is given or an autoconnect is
+due.  If more than one applicable rule is given, only one need
+evaluate to true for the connect to be allowed (ie: the rules are ored
+together).  Note that conditions that exist when the connect is
+initiated might differ from conditions when the link is established.
diff --git a/doc/readme.indent b/doc/readme.indent
new file mode 100644 (file)
index 0000000..6e02bc0
--- /dev/null
@@ -0,0 +1,9 @@
+If you want to indent this source file, in order to convert
+the source tree to the used programming style, you should use
+`make indent' in the base directory.
+
+For this to work you need to have `indent' version 2.1.0 or higher
+in your PATH.  GNU indent 2.1.0 is available from all GNU sites,
+its main ftp site is ftp://ftp.gnu.org/indent/.  Or you can download
+it directly from the webpage of its maintainer at
+http://www.xs4all.nl/~carlo17/indent/
diff --git a/doc/readme.who b/doc/readme.who
new file mode 100644 (file)
index 0000000..419abbe
--- /dev/null
@@ -0,0 +1,289 @@
+WHO documentation, updated on 02 Jan 1999.
+
+Since ircu2.10.02 the WHO command had been changed from what
+described in RFC1459, while still keeping backward compatibility,
+actually it has been changed again in u2.10.05 so that since this
+release the format of the who query is now:
+
+[:source] WHO <mask1> [<options> [<mask2>]]
+
+<mask2> is optional, if mask2 is present it's used for matching and
+mask1 is ignored, otherwise mask1 is used for matching, since mask2
+is the last parameter it *can* contain a space and this can help
+when trying to match a "realname".
+
+When matching IP numbers the <mask> can be in 3 forms:
+
+- The old and well known IRC masks using * and ? as wanted
+- The IPmask form a.b.c.d/e.f.g.h as used in most firewalls and
+  system configurations, where what is before the / are the bits
+  we expect in the IP number and what is after the / is the 
+  "filter mask" telling wich bits whould be considered and wich 
+  should be ignored.
+- The IPmask form a.b.c.d/bitcount where bitcount is an integer 
+  between 0 and 31 (inclusive), the matching will be for the IPs
+  whose first "bitcount" bits are equal to those in a.b.c.d
+
+Note that:
+. The bitcount must be between 0 and 31, 32 is NOT good (and
+  makes no sense to use it... just match against the static IP
+  a.b.c.d)
+. The missing pieces of both the bitmask and the ipnumber in
+  the forms ipnumber/bitmask and ipnumber/bitcount default to
+  zero from right to left, this is NOT what inet_aton and most 
+  tools do but makes more sense here IMO, in example /who 194.243/16
+  is taken as /who 194.243.0.0/255.255.0.0 (inet_aton whould
+  take 194.243 as 194.0.0.243).
+. For the above reason and specified validity limits 1.2.3.4/31
+  becomes 1.2.3.4/255.255.255.254 while 1.2.3.4/32 becomes
+  1.2.3.4/32.0.0.0 :)
+
+For all the other fields th match happens as has always been,
+i.e. it's only considered the IRC mask with * and ? (that is:
+don't expect to catch an user with "realname" = "1.2.3.4" when
+doing "/who 1.2/16 h" :)
+
+For both the masks and the options (and thus for all flags) case is
+NOT significative (so "/who <any> o" is exactly the same as 
+"/who <ANY> O".
+
+The "options2 part can be as follows:
+
+ [<flags>][%[<fields>[,<querytype>]]]
+
+in wich:
+
+ <flags>: can be a sequence of field matching flags, use mode matching
+          flags and special purpose flags
+
+   Field matching flags, when one of these is specified the field in 
+   question is matched against the mask, otherwise it's not matched.
+
+   n   Nick (in nick!user@host)
+   u   Username (in nick!user@host)
+   h   Hostname (in nick!user@host)
+   i   Numeric IP (the unresolved host)
+   s   Servername (the canonic name of the server the guy is on)
+   r   Info text (formerly "Realname")
+
+   If no field-matching flags are specified they default to what
+   old servers used to do: nuhsr (= everything except the numeric IP)
+
+   User mode matching flags (specifying one of these means that only 
+   clients with that umode are considered, what is not specified
+   is always matched):
+
+   o   Irc operator
+       [In the future more flags will be supported, basically all
+        usermodes plus the +/- specificators to revert the filtering]
+
+   Special purpose flags:
+
+   x   If this is specified the extended visibility of information 
+       for opers is applied, what this means depends on the fact that
+       you are local or global operator and on how the admin configured
+       the server (global and eventually local irc opers might be
+       allowed with this flag to see +i local users, to see all +i users,
+       to see users into +p and/or +s channels, and so on). Using the 'x'
+       flag while not beeing irc operator is meaningless (it will be
+       ignored), using it while oper'd means that the query is almost
+       certainly logged and the admin might (rightfully) ask you an
+       explanation on why you did.
+
+   The rest, what follows the %, that is [%[fields[,<querytype>]]],
+   is as it has always been since the first who.patch, the <fields> part
+   specifies wich fields to include in the output as:
+
+   c : Include (first) channel name
+   d : Include "distance" in hops (hopcount)
+   f : Include flags (all of them)
+   h : Include hostname
+   i : Include IP
+   n : Include nick
+   r : Include real name
+   s : Include server name
+   t : Include the querytype in the reply
+   u : Include userID with eventual ~
+
+And the ,<querytype> final option can be used to specify what you want
+the server to say in the querytype field of the output, useful to
+filter the output in scripts that do a kind of "on 354 ..."
+
+If no %fields are specified the reply is _exactly_ the same as
+has always been, numeric 352, same fields, same order.
+
+If one or more %fields are specified the reply uses a new numeric,
+since an out-of-standard 352 crashes EPIC and confuses several other
+clients. I used 354.
+
+:"source" 354 "target" ["querytype"] ["channel"] ["user"] 
+                       ["IP"] ["host"] ["server"] ["nick"] 
+                       ["flags"] ["hops"] [:"realname"]
+
+Where only the fields specified in the %fields options are present.
+
+"querytype" is the same value passed in the /who command, it
+is provided to simplify scripting, in example one could pass
+a certain value in the query and have that value "signal" back
+what is to be done with those replies.
+
+The number of lines in the reply is still limited to avoid self-flooding
+and sooner or later another limitation will be added: you will be forced
+to do no more than one /who query every 'n' seconds where 'n' depends
+on the number of fields you actually match (the field-match flags specified
+before % in the option, defaulting to 6 if you don't specify an option
+at all), infact matching against many fields as the default query does
+severely affects the CPU usage of the server and is *much* better to
+specify with the field-atching flags what you are looking for, in example
+when you are looking for all french users a "/who *.fr h" is A LOT
+better than just "/who *.fr" (and actually you want users that have the
+_hostname_ matching *.fr, you wouldn't want to match a japanese user
+that has the realname "ku fung-kay aj.fr" in example...)
+
+Note that:
+
+- An user doing a "/who whatever" or a "/who whatever o"
+  will not see any change (except for the anti-flood limit
+  and sooner or later the CPU usage limit)
+
+- An user doing a "/who #wasteland %n" will get just a list
+  of nicks (lame, very lame way of doing it :-)
+
+- An user doing a "/who 0 o%nuhs" will get a list of the opers
+  with Nick, userID, server and hostname like:
+  
+:Amst* 354 Nemesi #wasteland nbakker pc73.a.sn.no Oslo*.org Niels
+
+- An user doing a "/who 0 o%tnuhs,166" will get a list of the opers
+  with Nick, userID, server and hostname like the above but with a
+  request type field of 166 like:
+
+  :Amst* 354 Nemesi 166 #wasteland nbakker pc73.a.sn.no 
+         Oslo-R.NO.EU.Undernet.org Niels
+  
+  So that he can have in example a script that does 
+  "on 354 * 166" display "There is an oper ..."
+
+- The client will have to sort/format the fields by itself,
+  the _order_ in wich flags are passed is not significant,
+  the fields in the reply will always have the same order.
+
+- The maximum number of _lines_ reported as reply for a query
+  is 2048/(n+4) where 'n' is the number of flags "enabled"
+  that is the number of fields included in each reply.
+
+  Actually:   1 field  returned = maximum 409 replies
+              2 fields returned = maximum 341 replies
+              3 fields returned = maximum 292 replies
+              4 fields returned = maximum 256 replies
+              5 fields returned = maximum 227 replies
+              6 fields returned = maximum 204 replies
+              7 fields returned = maximum 186 replies (default query)
+              8 fields returned = maximum 170 replies
+              9 fields returned = maximum 157 replies
+             10 fields returned = maximum 146 replies
+
+  If the limit is reached before completing the query the
+  reply is truncated and a new numeric error is issued after
+  the "End of WHO", anyway the "end of" numeric is _always_ 
+  sent (otherwise some scripts and clients get crazy).
+
+The actual "mask" to match can have one of the two following forms:
+
+- A comma-separated list of elements: in this case each element
+  is treated as a flat channel or nick name and is not matched
+  to the other elements. Nicks do count in the limit of output
+  lines (they should not be that many anyway), channels count
+  if who asks the query is not on the channel. (That is: a /who
+  #channel gives unlimited output if you are in there).
+
+- A _single_ mask: in this case (no commas, only one element) the
+  mask is first checked to be a full channel or nickname, then
+  it is matched against all relevant fiels as already known.
+  These happens in different steps with replicates-removal so
+  that if one has (?) something like "#wasteland" as "real name"
+  or is on a channel named "#***MyChan***" it all works nicely.
+
+Miscellaneous bug fixes / "undocumented feature" changes:
+
+- /who NickName did not show the user with nick = NickName  when it
+  was invisible, even if the nick was given completely (without 
+  wildchars) now it does, since one could always see him as /whois 
+  NickName.
+  It does not report him twice if he also has in example the 
+  userID == NickName and is -i.
+
+- ":source WHO :The Black Hacker" did not report an user having
+  "The Black Hacker" as real name, now it does. Since this can only
+  be done without the flags/format specificator because that would
+  become the "last parameter" an escape has been provided: if you
+  pass to m_who _3_ parameters the first one will be ignored and the
+  last one used for matching, like in example
+  ":source WHO foo %nuh :*Black Hacker*" where "foo" will not
+  be used and the match will happen on "*Black Hacker*".
+  (It was passed through clean_channelname() that prevented the mask
+  from containing spaces and such...)
+
+- When one user was umode -i he was shown or not depending on the 
+  fact he was on a +p or +s channel... since we are doing a lookup
+  on the _user_ this makes no sense to me, example:
+  Neme1 : umode -i, on no channels, was SEEN with a /who 0
+  Neme2 : umode -i, on channel #p with chmode +p, was NOT SEEN by /who 0
+  Neme3 : umode -i, on channel #s with chmode +s, was NOT SEEN by /who 0
+
+  Now all users "-i" are matched with a "/who mask", the +i users 
+  instead must bee on a _common_ channel to be seen. 
+
+  Basically beeing on "one" +s|p channel "forced" a +i status while
+  one might want to be on #secret (mode +s) and have nobody know that
+  he is in there but on the other side stay -i so others can find him.
+  Of course a +s|p channel is never shown in the reply unless who asks 
+  the query is in there, if no "visible" channels are available for 
+  a -i user he is shown on "channel *".
+
+- When one user is +i is shown _only_ if there is a common channel,
+  the first common channel found is shown in the reply.
+
+- As requested by many persons an escape has been provided for opers,
+  when #defined SHOW_ALL_CHANNELS opers can /who #channel from outside
+  and see users in there even if the channel is +s|+p
+  Each admin decides locally if this feature is enabled to his opers.
+
+- As requested by many admins an escape from the query-size limit
+  has been provided for opers, by #defining UNLIMIT_OPER_QUERY opers
+  can do unlimited sized /who-s (until they get disconnected by max
+  SendQ exceeded ;)
+  Again admins will decide if enable or not this feature.
+
+- A /who a,c,b,d,e,f used to return as many ** END OF WHO as there 
+  were elements in the list, since now the command is supposed to be
+  _efficient_ for /who nick1,nick2,nick3 .. I return a _single_ end
+  of query message.
+
+- /who did not work for a channel named in example #**StarWars**  
+  now it does handle it properly (the mask was passed through
+  collapse() and then.. did not find that channel, fixed).
+
+- "/who #John" did not report an user having '#John' as "Real name",
+  now it does (and does NOT report him twice if he is ALSO on a
+  channel named #John, strange but true: this can happen).
+
+- "/who a,b,c,d" where a b c and d are channelnames/nicks now uses 
+  an hash lookup and therefore is extremely efficient, if _only_ one
+  field is specified it is looked in all the fields; who really wants
+  _only_ users on a specific channel or a single nick (without looking 
+  for a match in the other fields) can force the server to consider
+  the parameter as a list adding a comma somewhere, like:
+
+  "/who #Italia," or "/who ,Nemesi"
+
+  Or even better to avoid misbehaviour with other servers:
+  "/who #Italia %... #Italia,"   or "/who Nemesi %... Nemesi,"
+
+  This will make old servers act properly and new ones and should 
+  be the reccomended way for GUI based clients to get
+  a channel's userlist and all the infos they want about users
+  on the channel.
+
+Regards, Andrea aka Nemesi
+
diff --git a/doc/readme.www b/doc/readme.www
new file mode 100644 (file)
index 0000000..f3c25e7
--- /dev/null
@@ -0,0 +1,23 @@
+More, and up to date, information can be retrieved from the following
+world wide web pages:
+
+* Undernet Documents Project:
+               http://www.user-com.undernet.org/documents/
+
+* Release Notes & Patch Repository:
+               http://coder-com.undernet.org/
+
+* ircII scripts to support the undernet extentions:
+               http://coder-com.undernet.org/ircii/
+
+* Undernet Use Policy:
+               http://www.user-com.undernet.org/documents/aup.html
+
+* Operator Etiquette:
+               http://http://www.user-com.undernet.org/documents/operman.html
+
+* New server links & Routing info:
+               http://routing-com.undernet.org/
+
+* Information about large number of file descriptors per process:
+  linux:       ftp://ftp.linux.org.za/linux/local/kernel/
diff --git a/include/IPcheck.h b/include/IPcheck.h
new file mode 100644 (file)
index 0000000..f274996
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef IPCHECK_H
+#define IPCHECK_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int IPcheck_local_connect(aClient *cptr);
+extern void IPcheck_connect_fail(aClient *cptr);
+extern void IPcheck_connect_succeeded(aClient *cptr);
+extern int IPcheck_remote_connect(aClient *cptr, const char *hostname,
+    int is_burst);
+extern void IPcheck_disconnect(aClient *cptr);
+extern unsigned short IPcheck_nr(aClient *cptr);
+
+#endif /* IPCHECK_H */
diff --git a/include/bsd.h b/include/bsd.h
new file mode 100644 (file)
index 0000000..98ed790
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef BSD_H
+#define BSD_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern RETSIGTYPE dummy(HANDLER_ARG(int sig));
+extern int deliver_it(aClient *cptr, const char *str, int len);
+
+extern int writecalls;
+extern int writeb[10];
+
+#endif
diff --git a/include/channel.h b/include/channel.h
new file mode 100644 (file)
index 0000000..53feffe
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * IRC - Internet Relay Chat, ircd/channel.h
+ * Copyright (C) 1990 Jarkko Oikarinen
+ * Copyright (C) 1996 - 1997 Carlo Wood
+ *
+ * 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.
+ */
+
+#include "list.h"
+
+#ifndef CHANNEL_H
+#define CHANNEL_H
+
+/*=============================================================================
+ * General defines
+ */
+
+#define MAXMODEPARAMS  6
+#define MODEBUFLEN     200
+
+#define KEYLEN         23
+#define TOPICLEN       160
+#define CHANNELLEN     200
+#define MAXBANS                30
+#define MAXBANLENGTH   1024
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define ChannelExists(n)       (FindChannel(n) != NullChn)
+#define NullChn ((aChannel *)0)
+#define CREATE 1               /* whether a channel should be
+                                * created or just tested for existance */
+
+#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 */
+#define CHFL_BAN               0x0020  /* ban channel flag */
+#define CHFL_BAN_IPMASK                0x0040  /* ban mask is an IP-number mask */
+#define CHFL_BAN_OVERLAPPED    0x0080  /* ban overlapped, need bounce */
+#define CHFL_OVERLAP   (CHFL_CHANOP|CHFL_VOICE)
+#define CHFL_BURST_JOINED      0x0100  /* Just joined by net.junction */
+#define CHFL_BURST_BAN         0x0200  /* Ban part of last BURST */
+#define CHFL_BURST_BAN_WIPEOUT 0x0400  /* Ban will be wiped at end of BURST */
+#define CHFL_BANVALID          0x0800  /* CHFL_BANNED bit is valid */
+#define CHFL_BANNED            0x1000  /* Channel member is banned */
+#define CHFL_SILENCE_IPMASK    0x2000  /* silence mask is an IP-number mask */
+
+/* Channel Visibility macros */
+
+#define MODE_CHANOP    CHFL_CHANOP
+#define MODE_VOICE     CHFL_VOICE
+#define MODE_PRIVATE   0x0004
+#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_SENDTS    0x0800  /* TS was 0 during a local user /join; send
+                                * temporary TS; can be removed when all 2.10 */
+#define MODE_LISTED    0x10000
+
+/*
+ * mode flags which take another parameter (With PARAmeterS)
+ */
+#define MODE_WPARAS    (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT)
+
+#define HoldChannel(x)         (!(x))
+/* name invisible */
+#define SecretChannel(x)       ((x) && ((x)->mode.mode & MODE_SECRET))
+/* channel not shown but names are */
+#define HiddenChannel(x)       ((x) && ((x)->mode.mode & MODE_PRIVATE))
+/* channel visible */
+#define ShowChannel(v,c)       (PubChannel(c) || IsMember((v),(c)))
+#define PubChannel(x)          ((!x) || ((x)->mode.mode & \
+                                   (MODE_PRIVATE | MODE_SECRET)) == 0)
+#define is_listed(x)           ((x)->mode.mode & MODE_LISTED)
+
+#define IsLocalChannel(name)   (*(name) == '&')
+#define IsModelessChannel(name)        (*(name) == '+')
+#define IsChannelName(name)    (*(name) == '#' || \
+                               IsModelessChannel(name) || IsLocalChannel(name))
+
+/* 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
+
+/*=============================================================================
+ * Structures
+ */
+
+struct SMode {
+  unsigned int mode;
+  unsigned int limit;
+  char key[KEYLEN + 1];
+};
+
+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;
+  unsigned int users;
+  struct SLink *members;
+  struct SLink *invites;
+  struct SLink *banlist;
+  char chname[1];
+};
+
+struct ListingArgs {
+  time_t max_time;
+  time_t min_time;
+  unsigned int max_users;
+  unsigned int min_users;
+  unsigned int topic_limits;
+  time_t max_topic_time;
+  time_t min_topic_time;
+  struct Channel *chptr;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_names(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern Link *IsMember(aClient *cptr, aChannel *chptr);
+extern void remove_user_from_channel(aClient *sptr, aChannel *chptr);
+extern int is_chan_op(aClient *cptr, aChannel *chptr);
+extern int is_zombie(aClient *cptr, aChannel *chptr);
+extern int has_voice(aClient *cptr, aChannel *chptr);
+extern int can_send(aClient *cptr, aChannel *chptr);
+extern void send_channel_modes(aClient *cptr, aChannel *chptr);
+extern int m_mode(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern char *pretty_mask(char *mask);
+extern void del_invite(aClient *cptr, aChannel *chptr);
+extern void list_next_channels(aClient *cptr, int nr);
+extern int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_create(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_destruct(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_burst(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_part(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_kick(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_topic(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_invite(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_list(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern void send_user_joins(aClient *cptr, aClient *user);
+
+extern aChannel *channel;
+
+#endif /* CHANNEL_H */
diff --git a/include/class.h b/include/class.h
new file mode 100644 (file)
index 0000000..97e4832
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * IRC - Internet Relay Chat, include/class.h
+ * Copyright (C) 1990 Darren Reed
+ * Copyright (C) 1996 - 1997 Carlo Wood
+ *
+ * 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.
+ */
+
+#ifndef CLASS_H
+#define CLASS_H
+
+/*=========================================================================
+ * Structures
+ */
+
+struct ConfClass {
+  unsigned int conClass;
+  unsigned int conFreq;
+  unsigned int pingFreq;
+  unsigned int maxLinks;
+  unsigned int maxSendq;
+  unsigned int links;
+  struct ConfClass *next;
+};
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define ConClass(x)    ((x)->conClass)
+#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)   ((x)->confClass->links)
+#define ConfMaxLinks(x) ((x)->confClass->maxLinks)
+#define ConfClass(x)   ((x)->confClass->conClass)
+#define ConfConFreq(x) ((x)->confClass->conFreq)
+#define ConfPingFreq(x) ((x)->confClass->pingFreq)
+#define ConfSendq(x)   ((x)->confClass->maxSendq)
+
+#define FirstClass()   classes
+#define NextClass(x)   ((x)->next)
+
+#define MarkDelete(x)  do { MaxLinks(x) = (unsigned int)-1; } while(0)
+#define IsMarkedDelete(x) (MaxLinks(x) == (unsigned int)-1)
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern aConfClass *find_class(unsigned int cclass);
+extern aConfClass *make_class(void);
+extern void free_class(aConfClass * tmp);
+extern unsigned int get_con_freq(aConfClass * clptr);
+extern unsigned int get_client_ping(aClient *acptr);
+extern unsigned int get_conf_class(aConfItem *aconf);
+extern unsigned int get_client_class(aClient *acptr);
+extern void add_class(unsigned int conclass, unsigned int ping,
+    unsigned int confreq, unsigned int maxli, size_t sendq);
+extern void check_class(void);
+extern void initclass(void);
+extern void report_classes(aClient *sptr);
+extern size_t get_sendq(aClient *cptr);
+
+extern aConfClass *classes;
+
+#endif /* CLASS_H */
diff --git a/include/common.h b/include/common.h
new file mode 100644 (file)
index 0000000..beff780
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * IRC - Internet Relay Chat, include/common.h
+ * Copyright (C) 1998 Andrea Cocito
+ *
+ * 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.
+ */
+
+ /*
+    All the code in common.h/common.c is taken from the NTL
+    (Nemesi's Tools Library), adapted for ircu's character set 
+    and thereafter released under GNU GPL, from there comes the
+    NTL_ prefix of all macro and object names.
+    Removed isXdigit() to leave space to other char sets in the
+    bitmap, should give the same results as isxdigit() on any
+    implementation and isn't used in IRC anyway.
+  */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+/*=============================================================================
+ * System's headers needed in this header file
+ */
+
+#include "sys.h"
+#include <limits.h>
+
+/*=============================================================================
+ * Macros and constants for internal use, 
+ * WARNING: match.c depends on these macros, don't change them
+ *          without looking at that part of the code too !
+ */
+
+#define NTL_ALNUM 0x0001       /*    (NTL_ALPHA|NTL_DIGIT)               */
+#define NTL_ALPHA 0x0002       /*    (NTL_LOWER|NTL_UPPER)               */
+#define NTL_CNTRL 0x0004       /*    \000 - \037 == 0x00 - 0x1F          */
+#define NTL_DIGIT 0x0008       /*    0123456789                          */
+#define NTL_GRAPH 0x0010       /*    (NTL_ALNUM|NTL_PUNCT)               */
+#define NTL_LOWER 0x0020       /*    abcdefghijklmnopqrstuvwxyz{|}~      */
+#define NTL_PRINT 0x0040       /*    (NTL_GRAPH|' ')                     */
+#define NTL_PUNCT 0x0080       /*    !"#$%&'()*+,-./:;<=>?@_`            */
+#define NTL_SPACE 0x0100       /*    \011\012\013\014\015\040            */
+#define NTL_UPPER 0x0200       /*    ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^      */
+#define NTL_IRCCH 0x0400       /*    Channel's names charset             */
+#define NTL_IRCCL 0x0800       /*    Force toLower() in ch-name          */
+#define NTL_IRCNK 0x1000       /*    Nick names charset, aka isvalid()   */
+#define NTL_IRCUI 0x2000       /*    UserIDs charset, IRCHN plus tilde   */
+#define NTL_IRCHN 0x4000       /*    Hostnames charset (weak, RFC 1033)  */
+#define NTL_IRCIP 0x8000       /*    Numeric IPs charset (DIGIT and .)   */
+#define NTL_EOL  0x10000       /*    \r\n                                */
+
+/*=============================================================================
+ * Structures
+ */
+
+/*=============================================================================
+ * Externally visible function-like macros
+ */
+
+#define DupString(x, y)   (strcpy((x = (char *)RunMalloc(strlen(y) + 1)), y))
+
+#define toLower(c)        (NTL_tolower_tab[(c)-CHAR_MIN])
+#define toUpper(c)        (NTL_toupper_tab[(c)-CHAR_MIN])
+
+/* Char classification pseudo-functions, when others are needed add them */
+#define isAlnum(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_ALNUM)
+#define isAlpha(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_ALPHA)
+#define isDigit(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_DIGIT)
+#define isLower(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_LOWER)
+#define isSpace(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_SPACE)
+#define isUpper(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_UPPER)
+#define isCntrl(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_CNTRL)
+
+#define isIrcCh(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCCH)
+#define isIrcCl(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCCL)
+#define isIrcNk(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCNK)
+#define isIrcUi(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCUI)
+#define isIrcHn(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCHN)
+#define isIrcIp(c)        (NTL_char_attrib[(c)-CHAR_MIN] & NTL_IRCIP)
+#define isEol(c)          (NTL_char_attrib[(c)-CHAR_MIN] & NTL_EOL)
+
+/* String classification pseudo-functions, when others are needed add them,
+   strIsXxxxx(s) is true when IsXxxxx(c) is true for every char in s */
+
+#define strIsAlnum(s)     (strChattr(s) & NTL_ALNUM)
+#define strIsAlpha(s)     (strChattr(s) & NTL_ALPHA)
+#define strIsDigit(s)     (strChattr(s) & NTL_DIGIT)
+#define strIsLower(s)     (strChattr(s) & NTL_LOWER)
+#define strIsSpace(s)     (strChattr(s) & NTL_SPACE)
+#define strIsUpper(s)     (strChattr(s) & NTL_UPPER)
+
+#define strIsIrcCh(s)     (strChattr(s) & NTL_IRCCH)
+#define strIsIrcCl(s)     (strChattr(s) & NTL_IRCCL)
+#define strIsIrcNk(s)     (strChattr(s) & NTL_IRCNK)
+#define strIsIrcUi(s)     (strChattr(s) & NTL_IRCUI)
+#define strIsIrcHn(s)     (strChattr(s) & NTL_IRCHN)
+#define strIsIrcIp(s)     (strChattr(s) & NTL_IRCIP)
+
+/*=============================================================================
+ * Externally visible static memory stuff
+ */
+#ifdef MAKETABLES
+extern char NTL_tolower_tab[]; /* 256 bytes */
+extern char NTL_toupper_tab[]; /* 256 bytes */
+extern unsigned int NTL_char_attrib[]; /* 256 ints = 0.5 to 2 kilobytes */
+#else
+extern const char NTL_tolower_tab[];   /* 256 bytes */
+extern const char NTL_toupper_tab[];   /* 256 bytes */
+extern const unsigned int NTL_char_attrib[];   /* 256 ints = 0.5 to 2 kilobytes */
+#endif
+
+/*=============================================================================
+ * Critical small functions to inline even in separate compilation
+ * when FORCEINLINE is defined (provided you have a compiler that supports
+ * `inline').
+ */
+
+#define NTL_HDR_strChattr   int strChattr(const char *s)
+
+#define NTL_SRC_strChattr   register const char *rs = s; \
+                            register int x = ~0; \
+                            while(*rs) \
+                              x &= NTL_char_attrib[*rs++ - CHAR_MIN]; \
+                            return x;
+
+#define NTL_HDR_strCasediff int strCasediff(const char *a, const char *b)
+
+#define NTL_SRC_strCasediff register const char *ra = a; \
+                            register const char *rb = b; \
+                            while(toLower(*ra) == toLower(*rb++)) \
+                              if(!*ra++) \
+                                return 0; \
+                            return 1;
+
+#ifndef FORCEINLINE
+extern NTL_HDR_strChattr;
+extern NTL_HDR_strCasediff;
+#else /* FORCEINLINE */
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+inline NTL_HDR_strChattr { NTL_SRC_strChattr }
+inline NTL_HDR_strCasediff { NTL_SRC_strCasediff }
+#else
+static __inline__ NTL_HDR_strChattr { NTL_SRC_strChattr }
+static __inline__ NTL_HDR_strCasediff { NTL_SRC_strCasediff }
+#endif
+/* *INDENT-ON* */
+#endif /* FORCEINLINE */
+
+/*=============================================================================
+ * Proto types of other externally visible functions
+ */
+
+extern int strnChattr(const char *s, const size_t n);
+extern int strCasecmp(const char *a, const char *b);
+extern int strnCasecmp(const char *a, const char *b, const size_t n);
+
+#endif /* COMMON_H */
diff --git a/include/crule.h b/include/crule.h
new file mode 100644 (file)
index 0000000..d436378
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef CRULE_H
+#define CRULE_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void crule_free(char **elem);
+extern int crule_eval(char *rule);
+extern char *crule_parse(char *rule);
+
+#endif /* CRULE_H */
diff --git a/include/dbuf.h b/include/dbuf.h
new file mode 100644 (file)
index 0000000..03b45d1
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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 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.
+ */
+#ifndef INCLUDED_dbuf_h
+#define INCLUDED_dbuf_h
+#ifndef INCLUDED_sys_types_h
+#include <sys/types.h>         /* size_t */
+#define INCLUDED_sys_types_h
+#endif
+
+/*
+ * These two globals should be considered read only
+ */
+extern int DBufAllocCount;     /* GLOBAL - count of dbufs allocated */
+extern int DBufUsedCount;      /* GLOBAL - count of dbufs in use */
+
+struct DBufBuffer;
+
+struct DBuf {
+  size_t length;               /* Current number of bytes stored */
+  struct DBufBuffer *head;     /* First data buffer, if length > 0 */
+  struct DBufBuffer *tail;     /* last data buffer, if length > 0 */
+};
+
+/*
+ * DBufLength - Returns the current number of bytes stored into the buffer.
+ */
+#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))
+
+/*
+ * Prototypes
+ */
+extern void dbuf_delete(struct DBuf *dyn, size_t length);
+extern int dbuf_put(struct DBuf *dyn, const char *buf, size_t length);
+extern const char *dbuf_map(const struct DBuf *dyn, size_t *length);
+extern size_t dbuf_get(struct DBuf *dyn, char *buf, size_t length);
+extern size_t dbuf_getmsg(struct DBuf *dyn, char *buf, size_t length);
+extern void dbuf_count_memory(size_t *allocated, size_t *used);
+
+
+#endif /* INCLUDED_dbuf_h */
diff --git a/include/fileio.h b/include/fileio.h
new file mode 100644 (file)
index 0000000..ad7ad7a
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef INCLUDED_fileio_h
+#define INCLUDED_fileio_h
+
+#ifndef INCLUDED_sys_types_h
+#include <sys/types.h>         /* size_t */
+#define INCLUDED_sys_types_h
+#endif
+#ifndef INCLUDED_sys_stat_h
+#include <sys/stat.h>          /* struct stat */
+#define INCLUDED_sys_stat_h
+#endif
+
+/*
+ * FileBuf is a mirror of the ANSI FILE struct, but it works for any
+ * file descriptor. FileBufs are allocated when a file is opened with
+ * fbopen, and they are freed when the file is closed using fbclose.
+ */
+typedef struct FileBuf FBFILE;
+
+/*
+ * open a file and return a FBFILE*, see fopen(3)
+ */
+extern FBFILE *fbopen(const char *filename, const char *mode);
+/*
+ * associate a file descriptor with a FBFILE*
+ * if a FBFILE* is associated here it MUST be closed using fbclose
+ * see fdopen(3)
+ */
+extern FBFILE *fdbopen(int fd, const char *mode);
+/*
+ * close a file opened with fbopen, see fclose(3)
+ */
+extern void fbclose(FBFILE * fb);
+/* 
+ * return the next character from the file, EOF on end of file
+ * see fgetc(3)
+ */
+extern int fbgetc(FBFILE * fb);
+/*
+ * return next string in a file up to and including the newline character
+ * see fgets(3)
+ */
+extern char *fbgets(char *buf, size_t len, FBFILE * fb);
+/*
+ * write a null terminated string to a file, see fputs(3)
+ */
+extern int fbputs(const char *str, FBFILE * fb);
+/*
+ * return the status of the file associated with fb, see fstat(3)
+ */
+extern int fbstat(struct stat *sb, FBFILE * fb);
+
+#endif /* INCLUDED_fileio_h */
diff --git a/include/h.h b/include/h.h
new file mode 100644 (file)
index 0000000..c8d9f95
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * IRC - Internet Relay Chat, include/h.h
+ * Copyright (C) 1996 - 1997 Carlo Wood
+ *
+ * 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.
+ */
+
+#ifndef H_H
+#define H_H
+
+/* Typedefs */
+
+typedef struct Client aClient;
+typedef struct Server aServer;
+typedef struct User anUser;
+typedef struct Channel aChannel;
+typedef struct SMode Mode;
+typedef struct ConfItem aConfItem;
+typedef struct Message aMessage;
+typedef struct MessageTree aMessageTree;
+typedef struct Gline aGline;
+typedef struct ListingArgs aListingArgs;
+typedef struct MotdItem aMotdItem;
+typedef struct trecord atrecord;
+typedef unsigned int snomask_t;
+typedef struct ConfClass aConfClass;
+typedef struct hashentry aHashEntry;
+typedef struct SLink Link;
+typedef struct DSlink Dlink;
+typedef struct Whowas aWhowas;
+
+#include "s_debug.h"
+
+#endif /* H_H */
diff --git a/include/hash.h b/include/hash.h
new file mode 100644 (file)
index 0000000..47149bb
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * IRC - Internet Relay Chat, include/hash.h 
+ * Copyright (C) 1998 by Andrea "Nemesi" Cocito
+ *
+ * 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_H
+#define HASH_H
+
+#include "s_serv.h"            /* For STAT_* values and StatusMask() macro */
+
+/*=============================================================================
+ * general defines
+ */
+
+/* Now client and channel hash table must be of the same size */
+#define HASHSIZE               32000
+
+/*=============================================================================
+ * Structures
+ */
+
+/*=============================================================================
+ * Macros for internal use
+ */
+
+/*=============================================================================
+ * Externally visible pseudofunctions (macro interface to internal functions)
+ */
+
+/* Raw calls, expect a core if you pass a NULL or zero-length name */
+#define SeekChannel(name)      hSeekChannel((name))
+#define SeekClient(name)       hSeekClient((name), ~StatusMask(STAT_PING))
+#define SeekUser(name)         hSeekClient((name), StatusMask(STAT_USER))
+#define SeekServer(name)       hSeekClient((name), StatusMask(STAT_ME) | \
+                                                    StatusMask(STAT_SERVER) )
+
+/* Safer macros with sanity check on name, WARNING: these are _macros_,
+   no side effects allowed on <name> ! */
+#define FindChannel(name)      (BadPtr((name))?NULL:SeekChannel(name))
+#define FindClient(name)       (BadPtr((name))?NULL:SeekClient(name))
+#define FindUser(name)         (BadPtr((name))?NULL:SeekUser(name))
+#define FindServer(name)       (BadPtr((name))?NULL:SeekServer(name))
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void hash_init(void);   /* Call me on startup */
+extern int hAddClient(aClient *cptr);
+extern int hAddChannel(aChannel *chptr);
+extern int hRemClient(aClient *cptr);
+extern int hChangeClient(aClient *cptr, char *newname);
+extern int hRemChannel(aChannel *chptr);
+extern aClient *hSeekClient(char *name, int TMask);
+extern aChannel *hSeekChannel(char *name);
+
+extern int m_hash(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+extern int isNickJuped(char *nick);
+extern int addNickJupes(char *nicks);
+extern void clearNickJupes(void);
+
+#endif /* HASH_H */
diff --git a/include/ircd.h b/include/ircd.h
new file mode 100644 (file)
index 0000000..e62ea9d
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef IRCD_H
+#define IRCD_H
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define TStime() (now + TSoffset)
+#define BadPtr(x) (!(x) || (*(x) == '\0'))
+
+/* Miscellaneous defines */
+
+#define UDP_PORT       "7007"
+#define MINOR_PROTOCOL "09"
+#define MAJOR_PROTOCOL "10"
+#define BASE_VERSION   "u2.10"
+
+/* 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
+
+/*=============================================================================
+ * Proto types
+ */
+
+#ifdef PROFIL
+extern RETSIGTYPE s_monitor(HANDLER_ARG(int sig));
+#endif
+extern RETSIGTYPE s_die(HANDLER_ARG(int sig));
+extern RETSIGTYPE s_restart(HANDLER_ARG(int sig));
+
+extern void restart(char *mesg);
+extern void server_reboot(void);
+
+extern aClient me;
+extern time_t now;
+extern aClient *client;
+extern time_t TSoffset;
+extern unsigned int bootopt;
+extern time_t nextdnscheck;
+extern time_t nextconnect;
+extern int dorehash;
+extern time_t nextping;
+extern unsigned short int portnum;
+extern char *configfile;
+extern int debuglevel;
+extern char *debugmode;
+
+#endif /* IRCD_H */
diff --git a/include/list.h b/include/list.h
new file mode 100644 (file)
index 0000000..ef61f3a
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef LIST_H
+#define LIST_H
+
+/*=============================================================================
+ * General defines
+ */
+
+/*=============================================================================
+ * Macro's
+ */
+
+/* ============================================================================
+ * Structures
+ */
+
+struct SLink {
+  struct SLink *next;
+  union {
+    aClient *cptr;
+    struct Channel *chptr;
+    struct ConfItem *aconf;
+    char *cp;
+    struct {
+      char *banstr;
+      char *who;
+      time_t when;
+    } ban;
+  } value;
+  unsigned int flags;
+};
+
+struct DSlink {
+  struct DSlink *next;
+  struct DSlink *prev;
+  union {
+    aClient *cptr;
+    struct Channel *chptr;
+    struct ConfItem *aconf;
+    char *cp;
+  } value;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void free_link(Link *lp);
+extern Link *make_link(void);
+extern Link *find_user_link(Link *lp, aClient *ptr);
+extern void initlists(void);
+extern void outofmemory(void);
+extern aClient *make_client(aClient *from, int status);
+extern void free_client(aClient *cptr);
+extern struct User *make_user(aClient *cptr);
+extern struct Server *make_server(aClient *cptr);
+extern void free_user(struct User *user, aClient *cptr);
+extern void remove_client_from_list(aClient *cptr);
+extern void add_client_to_list(aClient *cptr);
+extern Dlink *add_dlink(Dlink **lpp, aClient *cp);
+extern void remove_dlink(Dlink **lpp, Dlink *lp);
+extern struct ConfItem *make_conf(void);
+extern void delist_conf(struct ConfItem *aconf);
+extern void free_conf(struct ConfItem *aconf);
+extern aGline *make_gline(int is_ipmask, char *host, char *reason, char *name,
+    time_t expire);
+extern aGline *find_gline(aClient *cptr, aGline **pgline);
+extern void free_gline(aGline *agline, aGline *pgline);
+extern void send_listinfo(aClient *cptr, char *name);
+
+#endif /* LIST_H */
diff --git a/include/map.h b/include/map.h
new file mode 100644 (file)
index 0000000..5d539d7
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef MAP_H
+#define MAP_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_map(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+#endif /* MAP_H */
diff --git a/include/match.h b/include/match.h
new file mode 100644 (file)
index 0000000..7a328a7
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef MATCH_H
+#define MATCH_H
+
+/*=============================================================================
+ * System headers used by this header file
+ */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/*=============================================================================
+ * Structures
+ */
+
+struct in_mask {
+  struct in_addr bits;
+  struct in_addr mask;
+  int fall;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int mmatch(const char *old_mask, const char *new_mask);
+extern int match(const char *ma, const char *na);
+extern char *collapse(char *pattern);
+
+extern int matchcomp(char *cmask, int *minlen, int *charset, const char *mask);
+extern int matchexec(const char *string, const char *cmask, int minlen);
+extern int matchdecomp(char *mask, const char *cmask);
+extern int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen);
+extern int matchcompIP(struct in_mask *imask, const char *mask);
+
+#endif /* MATCH_H */
diff --git a/include/msg.h b/include/msg.h
new file mode 100644 (file)
index 0000000..b6bf59c
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * 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_H
+#define MSG_H
+
+/*=============================================================================
+ * General defines
+ */
+
+#define MAXPARA           15
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+/*
+ * Tokenization:
+ * Each command must have a TOK_COMMAND and MSG_COMMAND definition.
+ * If you don't want one or the other, make them the same.
+ * Also each command has a "msgclass" used for debugging purposes.
+ */
+
+/* *INDENT-OFF* */
+
+#define MSG_PRIVATE            "PRIVMSG"       /* PRIV */
+#define TOK_PRIVATE            "P"
+#define CLASS_PRIVATE          LEVEL_PROPAGATE
+
+#define MSG_WHO                        "WHO"           /* WHO  -> WHOC */
+#define TOK_WHO                        "H"
+#define CLASS_WHO              LEVEL_QUERY
+
+#define MSG_WHOIS              "WHOIS"         /* WHOI */
+#define TOK_WHOIS              "W"
+#define CLASS_WHOIS            LEVEL_QUERY
+
+#define MSG_WHOWAS             "WHOWAS"        /* WHOW */
+#define TOK_WHOWAS             "X"
+#define CLASS_WHOWAS           LEVEL_QUERY
+
+#define MSG_USER               "USER"          /* USER */
+#define TOK_USER               "USER"
+#define CLASS_USER             LEVEL_CLIENT
+
+#define MSG_NICK               "NICK"          /* NICK */
+#define TOK_NICK               "N"
+#define CLASS_NICK             LEVEL_CLIENT
+
+#define MSG_SERVER             "SERVER"        /* SERV */
+#define TOK_SERVER             "S"
+#define CLASS_SERVER           LEVEL_MAP
+
+#define MSG_LIST               "LIST"          /* LIST */
+#define TOK_LIST               "LIST"
+#define CLASS_LIST             LEVEL_QUERY
+
+#define MSG_TOPIC              "TOPIC"         /* TOPI */
+#define TOK_TOPIC              "T"
+#define CLASS_TOPIC            LEVEL_PROPAGATE
+
+#define MSG_INVITE             "INVITE"        /* INVI */
+#define TOK_INVITE             "I"
+#define CLASS_INVITE           LEVEL_MODE
+
+#define MSG_VERSION            "VERSION"       /* VERS */
+#define TOK_VERSION            "V"
+#define CLASS_VERSION          LEVEL_QUERY
+
+#define MSG_QUIT               "QUIT"          /* QUIT */
+#define TOK_QUIT               "Q"
+#define CLASS_QUIT             LEVEL_CLIENT
+
+#define MSG_SQUIT              "SQUIT"         /* SQUI */
+#define TOK_SQUIT              "SQ"
+#define CLASS_SQUIT            LEVEL_MAP
+
+#define MSG_KILL               "KILL"          /* KILL */
+#define TOK_KILL               "D"
+#define CLASS_KILL             LEVEL_CLIENT
+
+#define MSG_INFO               "INFO"          /* INFO */
+#define TOK_INFO               "F"
+#define CLASS_INFO             LEVEL_QUERY
+
+#define MSG_LINKS              "LINKS"         /* LINK */
+#define TOK_LINKS              "LI"
+#define CLASS_LINKS            LEVEL_QUERY
+
+#define MSG_STATS              "STATS"         /* STAT */
+#define TOK_STATS              "R"
+#define CLASS_STATS            LEVEL_QUERY
+
+#define MSG_HELP               "HELP"          /* HELP */
+#define TOK_HELP               "HELP"
+#define CLASS_HELP             LEVEL_QUERY
+
+#define MSG_ERROR              "ERROR"         /* ERRO */
+#define TOK_ERROR              "Y"
+#define CLASS_ERROR            LEVEL_PROPAGATE
+
+#define MSG_AWAY               "AWAY"          /* AWAY */
+#define TOK_AWAY               "A"
+#define CLASS_AWAY             LEVEL_PROPAGATE
+
+#define MSG_CONNECT            "CONNECT"       /* CONN */
+#define TOK_CONNECT            "CO"
+#define CLASS_CONNECT          LEVEL_PROPAGATE
+
+#define MSG_UPING              "UPING"         /* UPIN */
+#define TOK_UPING              "UP"
+#define CLASS_UPING            LEVEL_PROPAGATE
+
+#define MSG_MAP                        "MAP"           /* MAP  */
+#define TOK_MAP                        "MAP"
+#define CLASS_MAP              LEVEL_QUERY
+
+#define MSG_PING               "PING"          /* PING */
+#define TOK_PING               "G"
+#define CLASS_PING             LEVEL_PROPAGATE
+
+#define MSG_PONG               "PONG"          /* PONG */
+#define TOK_PONG               "Z"
+#define CLASS_PONG             LEVEL_CLIENT
+
+#define MSG_OPER               "OPER"          /* OPER */
+#define TOK_OPER               "OPER"
+#define CLASS_OPER             LEVEL_PROPAGATE
+
+#define MSG_PASS               "PASS"          /* PASS */
+#define TOK_PASS               "PA"
+#define CLASS_PASS             LEVEL_CLIENT
+
+#define MSG_WALLOPS            "WALLOPS"       /* WALL */
+#define TOK_WALLOPS            "WA"
+#define CLASS_WALLOPS          LEVEL_PROPAGATE
+
+#define MSG_DESYNCH             "DESYNCH"       /* DESY */
+#define TOK_DESYNCH             "DS"
+#define CLASS_DESYNCH           LEVEL_PROPAGATE
+
+#define MSG_TIME               "TIME"          /* TIME */
+#define TOK_TIME               "TI"
+#define CLASS_TIME             LEVEL_QUERY
+
+#define MSG_SETTIME            "SETTIME"       /* SETT */
+#define TOK_SETTIME            "SE"
+#define CLASS_SETTIME          LEVEL_PROPAGATE
+
+#define MSG_RPING              "RPING"         /* RPIN */
+#define TOK_RPING              "RI"
+#define CLASS_RPING            LEVEL_PROPAGATE
+
+#define MSG_RPONG              "RPONG"         /* RPON */
+#define TOK_RPONG              "RO"
+#define CLASS_RPONG            LEVEL_PROPAGATE
+
+#define MSG_NAMES              "NAMES"         /* NAME */
+#define TOK_NAMES              "E"
+#define CLASS_NAMES            LEVEL_QUERY
+
+#define MSG_ADMIN              "ADMIN"         /* ADMI */
+#define TOK_ADMIN              "AD"
+#define CLASS_ADMIN            LEVEL_QUERY
+
+#define MSG_TRACE              "TRACE"         /* TRAC */
+#define TOK_TRACE              "TR"
+#define CLASS_TRACE            LEVEL_PROPAGATE
+
+#define MSG_NOTICE             "NOTICE"        /* NOTI */
+#define TOK_NOTICE             "O"
+#define CLASS_NOTICE           LEVEL_PROPAGATE
+
+#define MSG_WALLCHOPS          "WALLCHOPS"     /* WC */
+#define TOK_WALLCHOPS          "WC"
+#define CLASS_WALLCHOPS                LEVEL_PROPAGATE
+
+#define MSG_CPRIVMSG           "CPRIVMSG"      /* CPRI */
+#define TOK_CPRIVMSG           "CP"
+#define CLASS_CPRIVMSG         LEVEL_CLIENT
+
+#define MSG_CNOTICE            "CNOTICE"       /* CNOT */
+#define TOK_CNOTICE            "CN"
+#define CLASS_CNOTICE          LEVEL_CLIENT
+
+#define MSG_JOIN               "JOIN"          /* JOIN */
+#define TOK_JOIN               "J"
+#define CLASS_JOIN             LEVEL_CHANNEL
+
+#define MSG_PART               "PART"          /* PART */
+#define TOK_PART               "L"
+#define CLASS_PART             LEVEL_CHANNEL
+
+#define MSG_LUSERS             "LUSERS"        /* LUSE */
+#define TOK_LUSERS             "LU"
+#define CLASS_LUSERS           LEVEL_QUERY
+
+#define MSG_MOTD               "MOTD"          /* MOTD */
+#define TOK_MOTD               "MO"
+#define CLASS_MOTD             LEVEL_QUERY
+
+#define MSG_MODE               "MODE"          /* MODE */
+#define TOK_MODE               "M"
+#define CLASS_MODE             LEVEL_MODE
+
+#define MSG_KICK               "KICK"          /* KICK */
+#define TOK_KICK               "K"
+#define CLASS_KICK             LEVEL_CHANNEL
+
+#define MSG_USERHOST           "USERHOST"      /* USER -> USRH */
+#define TOK_USERHOST           "USERHOST"
+#define CLASS_USERHOST         LEVEL_QUERY
+
+#define MSG_USERIP             "USERIP"        /* USER -> USIP */
+#define TOK_USERIP             "USERIP"
+#define CLASS_USERIP           LEVEL_QUERY
+
+#define MSG_ISON               "ISON"          /* ISON */
+#define TOK_ISON               "ISON"
+#define CLASS_ISON             LEVEL_QUERY
+
+#define MSG_SQUERY             "SQUERY"        /* SQUE */
+#define TOK_SQUERY             "SQUERY"
+#define CLASS_SQUERY           LEVEL_QUERY
+
+#define MSG_SERVLIST           "SERVLIST"      /* SERV -> SLIS */
+#define TOK_SERVLIST           "SERVSET"
+#define CLASS_SERVLIST         LEVEL_QUERY
+
+#define MSG_SERVSET            "SERVSET"       /* SERV -> SSET */
+#define TOK_SERVSET            "SERVSET"
+#define CLASS_SERVSET          LEVEL_CLIENT
+
+#define MSG_REHASH             "REHASH"        /* REHA */
+#define TOK_REHASH             "REHASH"
+#define CLASS_REHASH           LEVEL_MAP
+
+#define MSG_RESTART            "RESTART"       /* REST */
+#define TOK_RESTART            "RESTART"
+#define CLASS_RESTART          LEVEL_MAP
+
+#define MSG_CLOSE              "CLOSE"         /* CLOS */
+#define TOK_CLOSE              "CLOSE"
+#define CLASS_CLOSE            LEVEL_CLIENT
+
+#define MSG_DIE                        "DIE"           /* DIE  */
+#define TOK_DIE                        "DIE"
+#define CLASS_DIE              LEVEL_MAP
+
+#define MSG_HASH               "HASH"          /* HASH */
+#define TOK_HASH               "HASH"
+#define CLASS_HASH             LEVEL_QUERY
+
+#define MSG_DNS                        "DNS"           /* DNS  -> DNSS */
+#define TOK_DNS                        "DNS"
+#define CLASS_DNS              LEVEL_QUERY
+
+#define MSG_SILENCE            "SILENCE"       /* SILE */
+#define TOK_SILENCE            "U"
+#define CLASS_SILENCE          LEVEL_PROPAGATE
+
+#define MSG_GLINE              "GLINE"         /* GLIN */
+#define TOK_GLINE              "GL"
+#define CLASS_GLINE            LEVEL_CLIENT
+
+#define MSG_BURST              "BURST"         /* BURS */
+#define TOK_BURST              "B"
+#define CLASS_BURST            LEVEL_CHANNEL
+
+#define MSG_CREATE             "CREATE"        /* CREA */
+#define TOK_CREATE             "C"
+#define CLASS_CREATE           LEVEL_CHANNEL
+
+#define MSG_DESTRUCT           "DESTRUCT"      /* DEST */
+#define TOK_DESTRUCT           "DE"
+#define CLASS_DESTRUCT         LEVEL_CHANNEL
+
+#define MSG_END_OF_BURST       "END_OF_BURST"  /* END_ */
+#define TOK_END_OF_BURST       "EB"
+#define CLASS_END_OF_BURST     LEVEL_MAP
+
+#define MSG_END_OF_BURST_ACK   "EOB_ACK"       /* EOB_ */
+#define TOK_END_OF_BURST_ACK   "EA"
+#define CLASS_END_OF_BURST_ACK LEVEL_MAP
+
+/* *INDENT-ON* */
+
+/*=============================================================================
+ * Constants
+ */
+#define   MFLG_SLOW              0x01  /* Command can be executed roughly    *
+                                        * once per 2 seconds.                */
+#define   MFLG_UNREG             0x02  /* Command available to unregistered  *
+                                        * clients.                           */
+
+/*=============================================================================
+ * Structures
+ */
+
+struct Message {
+  unsigned int msgclass;
+  char *cmd;
+  char *tok;
+  int (*func) (aClient *cptr, aClient *sptr, int parc, char *parv[]);
+  /* cptr = Connected client ptr
+     sptr = Source client ptr
+     parc = parameter count
+     parv = parameter variable array */
+  unsigned int count;
+  unsigned int parameters;
+  unsigned char flags;         /* bit 0 set means that this command is allowed
+                                  to be used only on the average of once per 2
+                                  seconds -SRB */
+  unsigned int bytes;
+};
+
+struct MessageTree {
+  char *final;
+  struct Message *msg;
+  struct MessageTree *pointers[26];
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern struct Message msgtab[];
+
+#endif /* MSG_H */
diff --git a/include/numeric.h b/include/numeric.h
new file mode 100644 (file)
index 0000000..405067c
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * 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.
+ */
+
+#ifndef NUMERIC_H
+#define NUMERIC_H
+
+/*=============================================================================
+ * Macro's
+ */
+
+/*
+ * 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           1
+#define RPL_YOURHOST          2
+#define RPL_CREATED           3
+#define RPL_MYINFO            4
+#define RPL_MAP                       5        /* Undernet extension */
+#define RPL_MAPMORE           6        /* Undernet extension */
+#define RPL_MAPEND            7        /* Undernet extension */
+#define RPL_SNOMASK           8        /* Undernet extension */
+#define RPL_STATMEMTOT        9        /* Undernet extension */
+#define RPL_STATMEM          10        /* Undernet extension */
+/*      RPL_YOURCOOKIE        14           IRCnet extension */
+
+/*
+ * 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_NOORIGIN        409
+
+#define ERR_NORECIPIENT             411
+#define ERR_NOTEXTTOSEND     412
+#define ERR_NOTOPLEVEL      413
+#define ERR_WILDTOPLEVEL     414
+
+#define ERR_QUERYTOOLONG     416       /* Undernet extension */
+
+#define ERR_UNKNOWNCOMMAND   421
+#define ERR_NOMOTD          422
+#define ERR_NOADMININFO             423
+/*      ERR_FILEERROR        424           removed from RFC1459 */
+
+#define ERR_NONICKNAMEGIVEN  431
+#define ERR_ERRONEUSNICKNAME 432
+#define ERR_NICKNAMEINUSE    433
+#define ERR_NICKCOLLISION    436
+#define ERR_BANNICKCHANGE    437       /* Undernet extension */
+#define ERR_NICKTOOFAST             438        /* Undernet extension */
+#define ERR_TARGETTOOFAST    439       /* Undernet extension */
+
+#define ERR_USERNOTINCHANNEL 441
+#define ERR_NOTONCHANNEL     442
+#define ERR_USERONCHANNEL    443
+/*      ERR_NOLOGIN          444           removed from RFC1459 */
+/*      ERR_SUMMONDISABLED   445           removed from RFC1459 */
+/*      ERR_USERSDISABLED    446           removed from RFC1459 */
+
+#define ERR_NOTREGISTERED    451
+/*      ERR_IDCOLLISION      452           IRCnet extension */
+/*      ERR_NICKLOST         453           IRCnet extension */
+
+#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        /* Undernet extension */
+#define ERR_INVALIDUSERNAME  468       /* Undernet extension */
+
+#define ERR_CHANNELISFULL    471
+#define ERR_UNKNOWNMODE             472
+#define ERR_INVITEONLYCHAN   473
+#define ERR_BANNEDFROMCHAN   474
+#define ERR_BADCHANNELKEY    475
+#define ERR_BADCHANMASK             476        /* Undernet extension */
+/*      ERR_NEEDREGGEDNICK   477           DalNet Extention */
+#define ERR_BANLISTFULL             478        /* Undernet extension */
+/* #define ERR_BADCHANNAME      479        EFNet extension */
+
+#define ERR_NOPRIVILEGES     481
+#define ERR_CHANOPRIVSNEEDED 482
+#define ERR_CANTKILLSERVER   483
+#define ERR_ISCHANSERVICE    484       /* Undernet extension */
+/*      ERR_CHANTOORECENT    487           IRCnet extension */
+/*      ERR_TSLESSCHAN       488           IRCnet extension */
+#define ERR_VOICENEEDED             489        /* Undernet extension */
+
+#define ERR_NOOPERHOST      491
+
+#define ERR_UMODEUNKNOWNFLAG 501
+#define ERR_USERSDONTMATCH   502
+
+#define ERR_SILELISTFULL     511       /* Undernet extension */
+
+#define ERR_NOSUCHGLINE             512        /* Undernet extension */
+#define ERR_BADPING         513        /* Undernet extension */
+
+/*
+ * 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_USERIP          307        /* Undernet extension */
+
+#define RPL_WHOISUSER       311        /* See also RPL_ENDOFWHOIS */
+#define RPL_WHOISSERVER             312
+#define RPL_WHOISOPERATOR    313
+
+#define RPL_WHOWASUSER      314        /* See also RPL_ENDOFWHOWAS */
+#define RPL_ENDOFWHO        315        /* See RPL_WHOREPLY/RPL_WHOSPCRPL */
+
+/*      RPL_WHOISCHANOP      316           removed from RFC1459 */
+
+#define RPL_WHOISIDLE       317
+
+#define RPL_ENDOFWHOIS      318        /* See RPL_WHOISUSER/RPL_WHOISSERVER/
+                                          RPL_WHOISOPERATOR/RPL_WHOISIDLE */
+#define RPL_WHOISCHANNELS    319
+
+#define RPL_LISTSTART       321
+#define RPL_LIST            322
+#define RPL_LISTEND         323
+#define RPL_CHANNELMODEIS    324
+/*      RPL_CHANNELPASSIS    325           IRCnet extension */
+/*      RPL_NOCHANPASS       326           IRCnet extension */
+/*      RPL_CHPASSUNKNOWN    327           IRCnet extension */
+#define RPL_CREATIONTIME     329
+
+#define RPL_NOTOPIC         331
+#define RPL_TOPIC           332
+#define RPL_TOPICWHOTIME     333       /* Undernet extension */
+#define RPL_LISTUSAGE       334        /* Undernet extension */
+/*      RPL_CHANPASSOK       338           IRCnet extension */
+/*      RPL_BADCHANPASS      339           IRCnet extension */
+
+#define RPL_INVITING        341
+/*      RPL_SUMMONING        342           removed from RFC1459 */
+/*      RPL_EXCEPTLIST       348           IRCnet extension */
+/*      RPL_ENDOFEXCEPTLIST  349           IRCnet extension */
+
+#define RPL_VERSION         351
+
+#define RPL_WHOREPLY        352        /* See also RPL_ENDOFWHO */
+#define RPL_NAMREPLY        353        /* See also RPL_ENDOFNAMES */
+#define RPL_WHOSPCRPL        354       /* Undernet extension,
+                                          See also RPL_ENDOFWHO */
+
+#define RPL_KILLDONE        361
+#define RPL_CLOSING         362
+#define RPL_CLOSEEND        363
+#define RPL_LINKS           364
+#define RPL_ENDOFLINKS      365
+#define RPL_ENDOFNAMES      366        /* See RPL_NAMREPLY */
+#define RPL_BANLIST         367
+#define RPL_ENDOFBANLIST     368
+#define RPL_ENDOFWHOWAS             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_YOUREOPER       381
+#define RPL_REHASHING       382
+#define RPL_MYPORTIS        384
+#define RPL_NOTOPERANYMORE   385       /* Extension to RFC1459 */
+
+#define RPL_TIME            391
+
+#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_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_STATSPLINE      217        /* Undernet extenstion */
+#define RPL_STATSYLINE      218
+#define RPL_ENDOFSTATS      219        /* See also RPL_STATSDLINE */
+
+#define RPL_UMODEIS         221
+
+#define RPL_SERVICEINFO             231
+#define RPL_ENDOFSERVICES    232
+#define RPL_SERVICE         233
+#define RPL_SERVLIST        234
+#define RPL_SERVLISTEND             235
+
+#define RPL_STATSLLINE      241
+#define RPL_STATSUPTIME             242
+#define RPL_STATSOLINE      243
+#define RPL_STATSHLINE      244
+/*      RPL_STATSSLINE       245           Reserved */
+#define RPL_STATSTLINE      246        /* Undernet extension */
+#define RPL_STATSGLINE      247        /* Undernet extension */
+#define RPL_STATSULINE      248        /* Undernet extension */
+#define RPL_STATSDEBUG      249        /* Extension to RFC1459 */
+#define RPL_STATSCONN       250        /* Undernet extension */
+
+#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_TRACEPING       262        /* Extension to RFC1459 */
+
+#define RPL_SILELIST        271        /* Undernet extension */
+#define RPL_ENDOFSILELIST    272       /* Undernet extension */
+
+/*      RPL_STATSDELTA       274           IRCnet extension */
+#define RPL_STATSDLINE      275        /* Undernet extension */
+
+#define RPL_GLIST           280        /* Undernet extension */
+#define RPL_ENDOFGLIST      281        /* Undernet extension */
+
+#endif /* NUMERIC_H */
diff --git a/include/numnicks.h b/include/numnicks.h
new file mode 100644 (file)
index 0000000..5a435a4
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * IRC - Internet Relay Chat, include/h.h
+ * Copyright (C) 1996 - 1997 Carlo Wood
+ *
+ * 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.
+ */
+
+#ifndef INCLUDED_numnicks_h
+#define INCLUDED_numnicks_h
+#ifndef INCLUDED_sys_types_h
+#include <sys/types.h>
+#define INCLUDED_sys_types_h
+#endif
+
+/*=============================================================================
+ * General defines
+ */
+
+/*
+ * used for buffer size calculations in channel.c
+ */
+#define NUMNICKLEN 5           /* strlen("YYXXX") */
+
+/*=============================================================================
+ * Macros
+ */
+
+/*
+ * Use this macro as follows: sprintf(buf, "%s%s ...", NumNick(cptr), ...);
+ */
+#define NumNick(c) (c)->user->server->yxx, (c)->yxx
+
+/*
+ * Use this macro as follows: sprintf(buf, "%s ...", NumServ(cptr), ...);
+ */
+#define NumServ(c) (c)->yxx
+
+/*
+ * Use this macro as follows: sprintf(buf, "%s%s ...", NumServCap(cptr), ...);
+ */
+#define NumServCap(c) (c)->yxx, (c)->serv->nn_capacity
+
+/*=============================================================================
+ * Structures
+ */
+struct Client;
+
+/*=============================================================================
+ * Proto types
+ */
+extern int SetRemoteNumNick(struct Client *cptr, const char *yxx);
+extern void SetLocalNumNick(struct Client *cptr);
+extern void RemoveYXXClient(struct Client *server, const char *yxx);
+extern void SetServerYXX(struct Client *cptr,
+    struct Client *server, const char *yxx);
+extern void ClearServerYXX(const struct Client *server);
+
+extern void SetYXXCapacity(struct Client *myself, size_t max_clients);
+extern void SetYXXServerName(struct Client *myself, unsigned int numeric);
+
+extern int markMatchexServer(const char *cmask, int minlen);
+extern struct Client *find_match_server(char *mask);
+extern struct Client *findNUser(const char *yxx);
+extern struct Client *FindNServer(const char *numeric);
+
+extern unsigned int base64toint(const char *str);
+extern const char *inttobase64(char *buf, unsigned int v, size_t count);
+
+#ifndef NO_PROTOCOL9
+extern const char *CreateNNforProtocol9server(const struct Client *server);
+#endif
+
+#endif /* INCLUDED_numnicks_h */
diff --git a/include/opercmds.h b/include/opercmds.h
new file mode 100644 (file)
index 0000000..f16e549
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef OPERCMDS_H
+#define OPERCMDS_H
+
+/*=============================================================================
+ * General defines
+ */
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define GLINE_ACTIVE   1
+#define GLINE_IPMASK   2
+#define GLINE_LOCAL    4
+
+/*
+ * G-line macros.
+ */
+
+#define GlineIsActive(g)       ((g)->gflags & GLINE_ACTIVE)
+#define GlineIsIpMask(g)       ((g)->gflags & GLINE_IPMASK)
+#define GlineIsLocal(g)                ((g)->gflags & GLINE_LOCAL)
+
+#define SetActive(g)           ((g)->gflags |= GLINE_ACTIVE)
+#define ClearActive(g)         ((g)->gflags &= ~GLINE_ACTIVE)
+#define SetGlineIsIpMask(g)    ((g)->gflags |= GLINE_IPMASK)
+#define SetGlineIsLocal(g)     ((g)->gflags |= GLINE_LOCAL)
+
+/*=============================================================================
+ * Structures
+ */
+
+struct Gline {
+  struct Gline *next;
+  char *host;
+  char *reason;
+  char *name;
+  time_t expire;
+  unsigned int gflags;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+extern int m_rehash(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+#endif
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+extern int m_restart(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+#endif
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+extern int m_die(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+#endif
+extern int m_squit(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_stats(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_connect(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_wallops(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_time(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_settime(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_rping(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_rpong(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_trace(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_close(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_gline(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+#endif /* OPERCMDS_H */
diff --git a/include/packet.h b/include/packet.h
new file mode 100644 (file)
index 0000000..9c2c639
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef PACKET_H
+#define PACKET_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int dopacket(aClient *cptr, char *buffer, int length);
+extern int client_dopacket(aClient *cptr, size_t length);
+
+#endif /* PACKET_H */
diff --git a/include/parse.h b/include/parse.h
new file mode 100644 (file)
index 0000000..f6ac2c2
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef PARSE_H
+#define PARSE_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int parse_client(aClient *cptr, char *buffer, char *bufend);
+extern int parse_server(aClient *cptr, char *buffer, char *bufend);
+extern void initmsgtree(void);
+
+#endif /* PARSE_H */
diff --git a/include/patchlevel.h b/include/patchlevel.h
new file mode 100644 (file)
index 0000000..700d74f
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * 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: Since u2.9 there is a new format of this file. The reason
+ * is that this way it shouldn't be needed anymore for the user to edit
+ * this manually !!!
+ * If you do, be sure you know what you are doing!
+ *
+ * For patch devellopers:
+ * 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 \
+               \
+               \
+               \
+               ".07"
+
+/*
+ * 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
+ */
+
+#define PATCH9 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH10 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH11 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH12 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH13 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH14 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH15 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH16 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH17 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH18 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH19 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH20 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH21 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH22 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH23 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH24 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH25 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH26 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH27 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH28 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH29 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH30 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#define PATCH31 \
+               \
+               \
+               \
+               ""
+
+/*
+ * Deliberate empty lines
+ */
+
+#ifdef TESTNET
+#define PATCH32 \
+               \
+               \
+               \
+               ".testnet"
+#else
+#define PATCH32 ""
+#endif
+
+/*
+ * Deliberate empty lines
+ */
+
+/* Do NOT edit those: */
+
+#ifndef BASE_VERSION
+#define BASE_VERSION "u2.10"
+#endif
+
+#ifndef MAJOR_PROTOCOL
+#define MAJOR_PROTOCOL "10"
+#endif
diff --git a/include/querycmds.h b/include/querycmds.h
new file mode 100644 (file)
index 0000000..22a9c83
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef QUERYCMDS_H
+#define QUERYCMDS_H
+
+/*=============================================================================
+ * Structs
+ */
+
+struct lusers_st {
+  /* Local connections: */
+  unsigned int unknowns;       /* IsUnknown() || IsConnecting() || IsHandshake() */
+  unsigned int local_servers;  /* IsServer() && MyConnect() */
+  unsigned int local_clients;  /* IsUser() && MyConnect() */
+  /* Global counts: */
+  unsigned int servers;                /* IsServer() || IsMe() */
+  unsigned int clients;                /* IsUser() */
+  /* Global user mode changes: */
+  unsigned int inv_clients;    /* IsUser() && IsInvisible() */
+  unsigned int opers;          /* IsUser() && IsOper() */
+  /* Misc: */
+  unsigned int channels;
+};
+
+/*=============================================================================
+ * Macros
+ */
+
+/* Macros for remote connections: */
+#define Count_newremoteclient(nrof)            (++nrof.clients)
+#define Count_newremoteserver(nrof)            (++nrof.servers)
+#define Count_remoteclientquits(nrof)          (--nrof.clients)
+#define Count_remoteserverquits(nrof)          (--nrof.servers)
+
+/* Macros for local connections: */
+#define Count_newunknown(nrof)                 (++nrof.unknowns)
+#define Count_unknownbecomesclient(cptr, nrof) \
+  do { \
+    --nrof.unknowns; ++nrof.local_clients; ++nrof.clients; \
+    if (match("*" DOMAINNAME, cptr->sockhost) == 0) \
+      ++current_load.local_count; \
+    if (nrof.local_clients > max_client_count) \
+      max_client_count = nrof.local_clients; \
+    if (nrof.local_clients + nrof.local_servers > max_connection_count) \
+    { \
+      max_connection_count = nrof.local_clients + nrof.local_servers; \
+      if (max_connection_count % 10 == 0) \
+        sendto_ops("Maximum connections: %d (%d clients)", \
+           max_connection_count, max_client_count); \
+    } \
+  } while(0)
+#define Count_unknownbecomesserver(nrof)       do { --nrof.unknowns; ++nrof.local_servers; ++nrof.servers; } while(0)
+#define Count_clientdisconnects(cptr, nrof) \
+  do \
+  { \
+    --nrof.local_clients; --nrof.clients; \
+    if (match("*" DOMAINNAME, cptr->sockhost) == 0) \
+      --current_load.local_count; \
+  } while(0)
+#define Count_serverdisconnects(nrof)          do { --nrof.local_servers; --nrof.servers; } while(0)
+#define Count_unknowndisconnects(nrof)         (--nrof.unknowns)
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_version(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_info(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_links(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_help(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_lusers(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_admin(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_motd(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+extern struct lusers_st nrof;
+
+#endif /* QUERYCMDS_H */
diff --git a/include/random.h b/include/random.h
new file mode 100644 (file)
index 0000000..af18a28
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef RANDOM_H
+#define RANDOM_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern unsigned int ircrandom(void);
+
+#endif /* RANDOM_H */
diff --git a/include/res.h b/include/res.h
new file mode 100644 (file)
index 0000000..b808652
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef RES_H
+#define RES_H
+
+#include <netinet/in.h>
+#include <netdb.h>
+#ifdef HPUX
+#ifndef h_errno
+extern int h_errno;
+#endif
+#endif
+#include "list.h"
+
+/*=============================================================================
+ * General defines
+ */
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int init_resolver(void);
+extern time_t timeout_query_list(void);
+extern void del_queries(char *cp);
+extern void add_local_domain(char *hname, int size);
+extern struct hostent *gethost_byname(char *name, Link *lp);
+extern struct hostent *gethost_byaddr(struct in_addr *addr, Link *lp);
+extern struct hostent *get_res(char *lp);
+extern time_t expire_cache(void);
+extern void flush_cache(void);
+extern int m_dns(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern size_t cres_mem(aClient *sptr);
+
+#endif /* RES_H */
diff --git a/include/runmalloc.h b/include/runmalloc.h
new file mode 100644 (file)
index 0000000..5b39361
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * runmalloc.h
+ *
+ * (C) Copyright 1996 - 1997, Carlo Wood (carlo@runaway.xs4all.nl)
+ *
+ * Headerfile of runmalloc.c
+ *
+ */
+
+#ifndef RUNMALLOC_H
+#define RUNMALLOC_H
+
+#ifdef DEBUGMALLOC
+
+#if defined(MEMMAGICNUMS) && !defined(MEMSIZESTATS)
+#define MEMSIZESTATS
+#endif
+#ifndef MEMLEAKSTATS
+#undef MEMTIMESTATS
+#endif
+
+/*=============================================================================
+ * Proto types
+ */
+
+#ifdef MEMLEAKSTATS
+extern void *RunMalloc_memleak(size_t size, int line, const char *filename);
+extern void *RunCalloc_memleak(size_t nmemb, size_t size,
+    int line, const char *filename);
+extern void *RunRealloc_memleak(void *ptr, size_t size,
+    int line, const char *filename);
+struct Client;
+extern void report_memleak_stats(struct Client *sptr, int parc, char *parv[]);
+#define RunMalloc(x) RunMalloc_memleak(x, __LINE__, __FILE__)
+#define RunCalloc(x,y) RunCalloc_memleak(x,y, __LINE__, __FILE__)
+#define RunRealloc(x,y) RunRealloc_memleak(x,y, __LINE__, __FILE__)
+#else
+extern void *RunMalloc(size_t size);
+extern void *RunCalloc(size_t nmemb, size_t size);
+extern void *RunRealloc(void *ptr, size_t size);
+#endif
+extern int RunFree_test(void *ptr);
+extern void RunFree(void *ptr);
+#ifdef MEMSIZESTATS
+extern unsigned int get_alloc_cnt(void);
+extern size_t get_mem_size(void);
+#endif
+
+#else /* !DEBUGMALLOC */
+
+#include <stdlib.h>
+
+#undef MEMSIZESTATS
+#undef MEMMAGICNUMS
+#undef MEMLEAKSTATS
+#undef MEMTIMESTATS
+
+#define Debug_malloc(x)
+#define RunMalloc(x) malloc(x)
+#define RunCalloc(x,y) calloc(x,y)
+#define RunRealloc(x,y) realloc(x,y)
+#define RunFree(x) free(x)
+
+#endif /* DEBUGMALLOC */
+
+#endif /* RUNMALLOC_H */
diff --git a/include/s_auth.h b/include/s_auth.h
new file mode 100644 (file)
index 0000000..c325432
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef S_AUTH_H
+#define S_AUTH_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void start_auth(aClient *cptr);
+extern void send_authports(aClient *cptr);
+extern void read_authports(aClient *cptr);
+
+#endif /* S_AUTH_H */
diff --git a/include/s_bsd.h b/include/s_bsd.h
new file mode 100644 (file)
index 0000000..9fa24d1
--- /dev/null
@@ -0,0 +1,184 @@
+#ifndef S_BSD_H
+#define S_BSD_H
+
+#include <netdb.h>
+#include "s_conf.h"
+
+/*=============================================================================
+ * Macro's
+ */
+
+#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_OPER      0x0008 /* Operator */
+#define FLAGS_LOCOP     0x0010 /* Local operator -- SRB */
+#define FLAGS_INVISIBLE         0x0020 /* makes user invisible */
+#define FLAGS_WALLOP    0x0040 /* send wallops to them */
+#define FLAGS_SERVNOTICE 0x0080        /* server notices such as kill */
+#define FLAGS_BLOCKED   0x0100 /* socket is in a blocked condition */
+#define FLAGS_UNIX      0x0200 /* socket is in the unix domain, not inet */
+#define FLAGS_CLOSING   0x0400 /* set when closing to suppress errors */
+#define FLAGS_LISTEN    0x0800 /* used to mark clients which we listen() on */
+#define FLAGS_CHKACCESS         0x1000 /* ok to check clients access if set */
+#define FLAGS_DOINGDNS  0x2000 /* client is waiting for a DNS response */
+#define FLAGS_AUTH      0x4000 /* client is waiting on rfc931 response */
+#define FLAGS_WRAUTH    0x8000 /* set if we havent writen to ident server */
+#define FLAGS_LOCAL    0x00010000      /* set for local clients */
+#define FLAGS_GOTID    0x00020000      /* successful ident lookup achieved */
+#define FLAGS_DOID     0x00040000      /* I-lines say must use ident return */
+#define FLAGS_NONL     0x00080000      /* No \n in buffer */
+#define FLAGS_TS8              0x00100000      /* Why do you want to know? */
+#define FLAGS_PING     0x00200000      /* Socket needs to send udp pings */
+#define FLAGS_ASKEDPING        0x00400000      /* Client asked for udp ping */
+#define FLAGS_MAP      0x00800000      /* Show server on the map */
+#define FLAGS_JUNCTION 0x01000000      /* Junction causing the net.burst */
+#define FLAGS_DEAF     0x02000000      /* Makes user deaf */
+#define FLAGS_CHSERV   0x04000000      /* Disallow KICK or MODE -o on the user;
+                                          don't display channels in /whois */
+#define FLAGS_BURST    0x08000000      /* Server is receiving a net.burst */
+#define FLAGS_BURST_ACK        0x10000000      /* Server is waiting for eob ack */
+#define FLAGS_DEBUG    0x20000000      /* send global debug/anti-hack info */
+#define FLAGS_IPCHECK  0x40000000      /* Added or updated IPregistry data */
+
+#define SEND_UMODES \
+    (FLAGS_INVISIBLE|FLAGS_OPER|FLAGS_WALLOP|FLAGS_DEAF|FLAGS_CHSERV|FLAGS_DEBUG)
+#define ALL_UMODES (SEND_UMODES|FLAGS_SERVNOTICE|FLAGS_LOCOP)
+#define FLAGS_ID (FLAGS_DOID|FLAGS_GOTID)
+
+/*
+ * flags macros.
+ */
+#define IsOper(x)              ((x)->flags & FLAGS_OPER)
+#define IsLocOp(x)             ((x)->flags & FLAGS_LOCOP)
+#define IsInvisible(x)         ((x)->flags & FLAGS_INVISIBLE)
+#define IsDeaf(x)              ((x)->flags & FLAGS_DEAF)
+#define IsChannelService(x)    ((x)->flags & FLAGS_CHSERV)
+#define IsAnOper(x)            ((x)->flags & (FLAGS_OPER|FLAGS_LOCOP))
+#define IsPrivileged(x)                (IsAnOper(x) || IsServer(x))
+#define SendWallops(x)         ((x)->flags & FLAGS_WALLOP)
+#define SendDebug(x)            ((x)->flags & FLAGS_DEBUG)
+#define SendServNotice(x)      ((x)->flags & FLAGS_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 IsJunction(x)          ((x)->flags & FLAGS_JUNCTION)
+#define IsBurst(x)             ((x)->flags & FLAGS_BURST)
+#define IsBurstAck(x)          ((x)->flags & FLAGS_BURST_ACK)
+#define IsBurstOrBurstAck(x)   ((x)->flags & (FLAGS_BURST|FLAGS_BURST_ACK))
+#define IsIPChecked(x)         ((x)->flags & FLAGS_IPCHECK)
+
+#define SetOper(x)             ((x)->flags |= FLAGS_OPER)
+#define SetLocOp(x)            ((x)->flags |= FLAGS_LOCOP)
+#define SetInvisible(x)                ((x)->flags |= FLAGS_INVISIBLE)
+#define SetWallops(x)          ((x)->flags |= FLAGS_WALLOP)
+#define SetDebug(x)             ((x)->flags |= FLAGS_DEBUG)
+#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 DoingAuth(x)           ((x)->flags & FLAGS_AUTH)
+#define NoNewLine(x)           ((x)->flags & FLAGS_NONL)
+#define DoPing(x)              ((x)->flags & FLAGS_PING)
+#define SetAskedPing(x)                ((x)->flags |= FLAGS_ASKEDPING)
+#define AskedPing(x)           ((x)->flags & FLAGS_ASKEDPING)
+#define SetJunction(x)         ((x)->flags |= FLAGS_JUNCTION)
+#define SetBurst(x)            ((x)->flags |= FLAGS_BURST)
+#define SetBurstAck(x)         ((x)->flags |= FLAGS_BURST_ACK)
+#define SetIPChecked(x)                ((x)->flags |= FLAGS_IPCHECK)
+
+#define ClearOper(x)           ((x)->flags &= ~FLAGS_OPER)
+#define ClearLocOp(x)          ((x)->flags &= ~FLAGS_LOCOP)
+#define ClearInvisible(x)      ((x)->flags &= ~FLAGS_INVISIBLE)
+#define ClearWallops(x)                ((x)->flags &= ~FLAGS_WALLOP)
+#define ClearDebug(x)           ((x)->flags &= ~FLAGS_DEBUG)
+#define ClearDNS(x)            ((x)->flags &= ~FLAGS_DOINGDNS)
+#define ClearAuth(x)           ((x)->flags &= ~FLAGS_AUTH)
+#define ClearAccess(x)         ((x)->flags &= ~FLAGS_CHKACCESS)
+#define ClearPing(x)           ((x)->flags &= ~FLAGS_PING)
+#define ClearAskedPing(x)      ((x)->flags &= ~FLAGS_ASKEDPING)
+#define ClearBurst(x)          ((x)->flags &= ~FLAGS_BURST)
+#define ClearBurstAck(x)       ((x)->flags &= ~FLAGS_BURST_ACK)
+
+/* used for async dns values */
+
+#define ASYNC_NONE     0
+#define ASYNC_CLIENT   1
+#define ASYNC_CONNECT  2
+#define ASYNC_CONF     3
+#define ASYNC_PING     4
+
+/* server notice stuff */
+
+#define SNO_ADD                1
+#define SNO_DEL                2
+#define SNO_SET                3
+                               /* DON'T CHANGE THESE VALUES ! */
+                               /* THE CLIENTS DEPEND ON IT  ! */
+#define SNO_OLDSNO     0x1     /* unsorted old messages */
+#define SNO_SERVKILL   0x2     /* server kills (nick collisions) */
+#define SNO_OPERKILL   0x4     /* oper kills */
+#define SNO_HACK2      0x8     /* desyncs */
+#define SNO_HACK3      0x10    /* temporary desyncs */
+#define SNO_UNAUTH     0x20    /* unauthorized connections */
+#define SNO_TCPCOMMON  0x40    /* common TCP or socket errors */
+#define SNO_TOOMANY    0x80    /* too many connections */
+#define SNO_HACK4      0x100   /* Uworld actions on channels */
+#define SNO_GLINE      0x200   /* glines */
+#define SNO_NETWORK    0x400   /* net join/break, etc */
+#define SNO_IPMISMATCH 0x800   /* IP mismatches */
+#define SNO_THROTTLE   0x1000  /* host throttle add/remove notices */
+#define SNO_OLDREALOP  0x2000  /* old oper-only messages */
+#define SNO_CONNEXIT   0x4000  /* client connect/exit (ugh) */
+
+#define SNO_ALL                0x7fff  /* Don't make it larger then significant,
+                                * that looks nicer */
+
+#define SNO_USER       (SNO_ALL & ~SNO_OPER)
+
+#define SNO_DEFAULT (SNO_NETWORK|SNO_OPERKILL|SNO_GLINE)
+#define SNO_OPERDEFAULT (SNO_DEFAULT|SNO_HACK2|SNO_HACK4|SNO_THROTTLE|SNO_OLDSNO)
+#define SNO_OPER (SNO_CONNEXIT|SNO_OLDREALOP)
+#define SNO_NOISY (SNO_SERVKILL|SNO_UNAUTH)
+
+/*
+ * simple defines to differentiate between a tty and socket for
+ * add_connection()  -Simon
+ */
+
+#define ADCON_TTY 0
+#define ADCON_SOCKET 1
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int setsnomask(aClient *cptr, snomask_t newmask, int what);
+extern snomask_t umode_make_snomask(snomask_t oldmask, char *arg, int what);
+extern int connect_server(aConfItem *aconf, aClient *by, struct hostent *hp);
+extern void report_error(char *text, aClient *cptr);
+extern int inetport(aClient *cptr, char *name, unsigned short int port);
+extern int add_listener(aConfItem *aconf);
+extern void close_listeners(void);
+extern void init_sys(void);
+extern void write_pidfile(void);
+extern enum AuthorizationCheckResult check_client(aClient *cptr);
+extern int check_server(aClient *cptr);
+extern void close_connection(aClient *cptr);
+extern int get_sockerr(aClient *cptr);
+extern void set_non_blocking(int fd, aClient *cptr);
+extern aClient *add_connection(aClient *cptr, int fd, int type);
+extern int read_message(time_t delay);
+extern void get_my_name(aClient *cptr, char *name, size_t len);
+extern int setup_ping(void);
+
+extern int highest_fd, resfd;
+extern unsigned int readcalls;
+extern aClient *loc_clients[MAXCONNECTIONS];
+#ifdef VIRTUAL_HOST
+extern struct sockaddr_in vserv;
+#endif
+
+#endif /* S_BSD_H */
diff --git a/include/s_conf.h b/include/s_conf.h
new file mode 100644 (file)
index 0000000..e67aa4a
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef S_CONF_H
+#define S_CONF_H
+
+#include "list.h"
+#include <netinet/in.h>
+#include <netdb.h>
+
+/*=============================================================================
+ * General defines
+ */
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define CONF_ILLEGAL           0x80000000
+#define CONF_MATCH             0x40000000
+#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
+#ifdef R_LINES
+#define CONF_RESTRICT          0x0200
+#endif
+#define CONF_CLASS             0x0400
+#define CONF_LEAF              0x1000
+#define CONF_LISTEN_PORT       0x2000
+#define CONF_HUB               0x4000
+#define CONF_UWORLD            0x8000
+#define CONF_CRULEALL          0x00200000
+#define CONF_CRULEAUTO         0x00400000
+#define CONF_TLINES            0x00800000
+#define CONF_IPKILL            0x00010000
+
+#define CONF_OPS               (CONF_OPERATOR | CONF_LOCOP)
+#define CONF_SERVER_MASK       (CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER)
+#define CONF_CLIENT_MASK       (CONF_CLIENT | CONF_OPS | CONF_SERVER_MASK)
+#define CONF_CRULE             (CONF_CRULEALL | CONF_CRULEAUTO)
+#define CONF_KLINE             (CONF_KILL | CONF_IPKILL)
+
+#define IsIllegal(x)   ((x)->status & CONF_ILLEGAL)
+
+/*=============================================================================
+ * Structures
+ */
+
+struct ConfItem {
+  unsigned int status;         /* If CONF_ILLEGAL, delete when no clients */
+  unsigned int clients;                /* Number of *LOCAL* clients using this */
+  struct in_addr ipnum;                /* ip number of host field */
+  char *host;
+  char *passwd;
+  char *name;
+  unsigned short int port;
+  time_t hold;                 /* Hold action until this time
+                                  (calendar time) */
+#ifndef VMSP
+  struct ConfClass *confClass; /* Class of connection */
+#endif
+  struct ConfItem *next;
+};
+
+struct MotdItem {
+  char line[82];
+  struct MotdItem *next;
+};
+
+struct trecord {
+  char *hostmask;
+  struct MotdItem *tmotd;
+  struct tm tmotd_tm;
+  struct trecord *next;
+};
+
+enum AuthorizationCheckResult {
+  ACR_OK,
+  ACR_NO_AUTHORIZATION,
+  ACR_TOO_MANY_IN_CLASS,
+  ACR_TOO_MANY_FROM_IP,
+  ACR_ALREADY_AUTHORIZED,
+  ACR_BAD_SOCKET
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern aConfItem *find_conf_host(Link *lp, char *host, int statmask);
+extern void det_confs_butmask(aClient *cptr, int mask);
+extern enum AuthorizationCheckResult attach_Iline(aClient *cptr,
+    struct hostent *hp, char *sockhost);
+extern aConfItem *count_cnlines(Link *lp);
+extern int detach_conf(aClient *cptr, aConfItem *aconf);
+extern enum AuthorizationCheckResult attach_conf(aClient *cptr,
+    aConfItem *aconf);
+extern aConfItem *find_admin(void);
+extern aConfItem *find_me(void);
+extern aConfItem *attach_confs(aClient *cptr, const char *name, int statmask);
+extern aConfItem *attach_confs_host(aClient *cptr, char *host, int statmask);
+extern aConfItem *find_conf_exact(char *name, char *user, char *host,
+    int statmask);
+extern aConfItem *find_conf_name(char *name, int statmask);
+extern aConfItem *find_conf(Link *lp, const char *name, int statmask);
+extern aConfItem *find_conf_ip(Link *lp, char *ip, char *user, int statmask);
+extern int rehash(aClient *cptr, int sig);
+extern int initconf(int opt);
+extern void read_tlines(void);
+extern int find_kill(aClient *cptr);
+extern int find_restrict(aClient *cptr);
+extern int m_killcomment(aClient *sptr, char *parv, char *filename);
+extern aMotdItem *read_motd(char *motdfile);
+
+extern aConfItem *conf;
+extern aGline *gline;
+extern struct tm motd_tm;
+extern aMotdItem *motd;
+extern aMotdItem *rmotd;
+extern atrecord *tdata;
+
+#endif /* S_CONF_H */
diff --git a/include/s_debug.h b/include/s_debug.h
new file mode 100644 (file)
index 0000000..80872a1
--- /dev/null
@@ -0,0 +1,157 @@
+#ifndef S_DEBUG_H
+#define S_DEBUG_H
+
+#include <stdarg.h>
+#ifdef MSGLOG_ENABLED
+#include "struct.h"            /* Needed for HOSTLEN */
+#endif
+
+#ifdef DEBUGMODE
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define Debug(x) debug x
+#define LOGFILE LPATH
+
+/*
+ * 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 */
+
+/*=============================================================================
+ * proto types
+ */
+
+extern void vdebug(int level, const char *form, va_list vl);
+extern void debug(int level, const char *form, ...)
+    __attribute__ ((format(printf, 2, 3)));
+extern void send_usage(aClient *cptr, char *nick);
+
+#else /* !DEBUGMODE */
+
+#define Debug(x)
+#define LOGFILE "/dev/null"
+
+#endif /* !DEBUGMODE */
+
+extern void count_memory(aClient *cptr, char *nick);
+extern char serveropts[];
+
+/*=============================================================================
+ * Message logging service
+ */
+
+/*
+ * Message levels: these are inclusive, i.e. a message that is LEVEL_MAP
+ * affects also clients and channels and is propagated and needs a query of
+ * some status, and therefore belongs to all the classes, in the same way
+ * _every_ message is parsed so belongs to LEVEL_PARSED
+ */
+
+/* Messages that affect servers' map */
+#define LEVEL_MAP      6
+
+/* Messages that affect clients existance */
+#define LEVEL_CLIENT   5
+
+/* Messages that affect channel existance */
+#define LEVEL_CHANNEL  4
+
+/* Messages that affect channel modes */
+#define LEVEL_MODE     3
+
+/* Messages that are only to be propagated somewhere */
+#define LEVEL_PROPAGATE 2
+
+/*
+ * Messages that only perform queries
+ * note how every message may need some status query over data structs
+ * and at the same time every query might need to be propagated
+ * somewhere... so the distinction between levels PROPAGATE and
+ * QUERY is quite fuzzy
+ */
+#define LEVEL_QUERY    1
+
+/* Messages that only perform queries */
+#define LEVEL_PARSED   0
+
+#ifdef MSGLOG_ENABLED
+
+/*=============================================================================
+ * Macro's
+ */
+
+#define LogMessage(x) Log_Message x
+#define StoreBuffer(x) Store_Buffer x
+
+/* Logging mask, selection on type of connection */
+#define LOG_PING       (0x8000 >> (8 + STAT_PING))
+#define LOG_LOG                (0x8000 >> (8 + STAT_LOG))
+#define LOG_MASTER     (0x8000 >> (8 + STAT_MASTER))
+#define LOG_CONNECTING (0x8000 >> (8 + STAT_CONNECTING))
+#define LOG_HANDSHAKE  (0x8000 >> (8 + STAT_HANDSHAKE))
+#define LOG_ME         (0x8000 >> (8 + STAT_ME))
+#define LOG_UNKNOWN    (0x8000 >> (8 + STAT_UNKNOWN))
+#define LOG_SERVER     (0x8000 >> (8 + STAT_SERVER))
+#define LOG_CLIENT     (0x8000 >> (8 + STAT_USER))
+
+/*
+ * Define here the type of connection(s) that will be monitored.
+ * Default is to log messages coming from any connection.
+ */
+#define LOG_MASK_TYPE \
+    ( LOG_PING | LOG_LOG | LOG_MASTER | LOG_CONNECTING | \
+      LOG_HANDSHAKE | LOG_ME | LOG_UNKNOWN | LOG_SERVER | LOG_CLIENT )
+
+/*=============================================================================
+ * data structures
+ */
+
+struct log_entry {
+  int cptr_status;
+  char cptr_name[HOSTLEN + 1];
+  char cptr_yxx[3];
+  int cptr_fd;
+
+  int sptr_status;
+  char sptr_name[HOSTLEN + 1];
+  char sptr_yxx[4];
+  char sptr_from_name[HOSTLEN + 1];
+
+  char buffer[512];
+
+  /* The following may be lost before log gets used,
+     anyhow they are only here for usage through gdb */
+
+  aClient *cptr;
+  aClient *sptr;
+};
+
+/*=============================================================================
+ * proto types
+ */
+
+extern void Log_Message(aClient *sptr, int msgclass);
+extern void Store_Buffer(char *buf, aClient *cptr);
+
+#else /* !MSGLOG_ENABLED */
+
+#define LogMessage(x)
+#define StoreBuffer(x)
+
+#endif /* !MSGLOG_ENABLED */
+
+#endif /* S_DEBUG_H */
diff --git a/include/s_err.h b/include/s_err.h
new file mode 100644 (file)
index 0000000..ee746ae
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef S_ERR_H
+#define S_ERR_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern char *err_str(int numeric);
+extern char *rpl_str(int numeric);
+
+#endif /* S_ERR_H */
diff --git a/include/s_misc.h b/include/s_misc.h
new file mode 100644 (file)
index 0000000..e53b7df
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef S_MISC_H
+#define S_MISC_H
+
+/*=============================================================================
+ * General defines
+ */
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define CPTR_KILLED    -2
+
+/*=============================================================================
+ * 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 int is_cbs;   /* bytes sent to clients */
+  unsigned short int is_cbr;   /* bytes received to clients */
+  unsigned short int is_sbs;   /* bytes sent to servers */
+  unsigned short int is_sbr;   /* bytes received to servers */
+  unsigned int is_cks;         /* k-bytes sent to clients */
+  unsigned int is_ckr;         /* k-bytes received to clients */
+  unsigned int is_sks;         /* k-bytes sent to servers */
+  unsigned int 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_asuc;                /* successful auth requests */
+  unsigned int is_abad;                /* bad auth requests */
+  unsigned int is_udp;         /* packets recv'd on udp port */
+  unsigned int is_loc;         /* local connections made */
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int check_registered(aClient *sptr);
+extern int check_registered_user(aClient *sptr);
+extern int exit_client(aClient *cptr, aClient *bcptr,
+    aClient *sptr, char *comment);
+extern char *myctime(time_t value);
+extern char *get_client_name(aClient *sptr, int showip);
+extern int exit_client_msg(aClient *cptr, aClient *bcptr,
+    aClient *sptr, char *pattern, ...) __attribute__ ((format(printf, 4, 5)));
+extern void initstats(void);
+extern char *date(time_t clock);
+extern char *get_client_host(aClient *cptr);
+extern void get_sockhost(aClient *cptr, char *host);
+extern char *my_name_for_link(char *name, aConfItem *aconf);
+extern int vexit_client_msg(aClient *cptr, aClient *bcptr,
+    aClient *sptr, char *pattern, va_list vl);
+extern void checklist(void);
+extern void tstats(aClient *cptr, char *name);
+
+extern struct stats *ircstp;
+
+#endif /* S_MISC_H */
diff --git a/include/s_numeric.h b/include/s_numeric.h
new file mode 100644 (file)
index 0000000..0c148b3
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef S_NUMERIC_H
+#define S_NUMERIC_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int do_numeric(int numeric, int nnn, aClient *cptr, aClient *sptr,
+    int parc, char *parv[]);
+
+#endif /* S_NUMERIC_H */
diff --git a/include/s_ping.h b/include/s_ping.h
new file mode 100644 (file)
index 0000000..20ff1fd
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef S_PING_H
+#define S_PING_H
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int start_ping(aClient *cptr);
+extern void send_ping(aClient *cptr);
+extern void read_ping(aClient *cptr);
+extern int ping_server(aClient *cptr);
+extern int m_uping(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern void end_ping(aClient *cptr);
+extern void cancel_ping(aClient *sptr, aClient *acptr);
+
+#endif /* S_PING_H */
diff --git a/include/s_serv.h b/include/s_serv.h
new file mode 100644 (file)
index 0000000..4c7fdd1
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef S_SERV_H
+#define S_SERV_H
+
+#include "struct.h"
+
+/*=============================================================================
+ * General defines
+ */
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define STAT_PING              0
+#define STAT_LOG               1       /* logfile for -x */
+#define STAT_MASTER            2       /* Local ircd master before identification */
+#define STAT_CONNECTING                3
+#define STAT_HANDSHAKE         4
+#define STAT_ME                        5
+#define STAT_UNKNOWN           6
+#define STAT_UNKNOWN_USER      7       /* Connect to client port */
+#define STAT_UNKNOWN_SERVER    8       /* Connect to server port */
+#define STAT_SERVER            9
+#define STAT_USER              10
+
+/* 
+ * for when you wanna create a bitmask of status values
+ */
+#define StatusMask(T) (1<<(T))
+#define IsStatMask(x, s) (StatusMask((x)->status) & (s))
+
+/*
+ * status macros.
+ */
+#define IsRegistered(x)                (IsStatMask(x, \
+                                       StatusMask(STAT_SERVER)|\
+                                       StatusMask(STAT_USER)))
+#define IsConnecting(x)                ((x)->status == STAT_CONNECTING)
+#define IsHandshake(x)         ((x)->status == STAT_HANDSHAKE)
+#define IsMe(x)                        ((x)->status == STAT_ME)
+#define IsUnknown(x)           (IsStatMask(x, \
+                                       StatusMask(STAT_UNKNOWN)|\
+                                       StatusMask(STAT_UNKNOWN_USER)|\
+                                       StatusMask(STAT_UNKNOWN_SERVER)|\
+                                       StatusMask(STAT_MASTER)))
+#define IsServerPort(x)                ((x)->status == STAT_UNKNOWN_SERVER )
+#define IsUserPort(x)          ((x)->status == STAT_UNKNOWN_USER )
+#define IsMaster(x)            ((x)->status == STAT_MASTER)
+#define IsClient(x)            (IsStatMask(x, \
+                                       StatusMask(STAT_MASTER)|\
+                                       StatusMask(STAT_HANDSHAKE)|\
+                                       StatusMask(STAT_ME)|\
+                                       StatusMask(STAT_UNKNOWN)|\
+                                       StatusMask(STAT_UNKNOWN_USER)|\
+                                       StatusMask(STAT_UNKNOWN_SERVER)|\
+                                       StatusMask(STAT_SERVER)|\
+                                       StatusMask(STAT_USER)))
+#define IsTrusted(x)           (IsStatMask(x, \
+                                       StatusMask(STAT_PING)|\
+                                       StatusMask(STAT_LOG)|\
+                                       StatusMask(STAT_CONNECTING)|\
+                                       StatusMask(STAT_HANDSHAKE)|\
+                                       StatusMask(STAT_ME)|\
+                                       StatusMask(STAT_SERVER)))
+#ifdef DEBUGMODE               /* Coredump if we miss something... */
+#define IsServer(x)            ( ((x)->status == STAT_SERVER) && \
+                                  (((x)->serv) ? 1 : (*((char *) NULL) = 0)) )
+#define IsUser(x)              ( ((x)->status == STAT_USER) && \
+                                  (((x)->user) ? 1 : (*((char *) NULL) = 0)) )
+#else
+#define IsServer(x)            ((x)->status == STAT_SERVER)
+#define IsUser(x)              ((x)->status == STAT_USER)
+#endif
+#define IsLog(x)               ((x)->status == STAT_LOG)
+#define IsPing(x)              ((x)->status == STAT_PING)
+
+#define SetMaster(x)           ((x)->status = STAT_MASTER)
+#define SetConnecting(x)       ((x)->status = STAT_CONNECTING)
+#define SetHandshake(x)                ((x)->status = STAT_HANDSHAKE)
+#define SetServer(x)           ((x)->status = STAT_SERVER)
+#define SetMe(x)               ((x)->status = STAT_ME)
+#define SetUser(x)             ((x)->status = STAT_USER)
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_server_estab(aClient *cptr, aConfItem *aconf, aConfItem *bconf);
+extern int m_error(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_end_of_burst(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_end_of_burst_ack(aClient *cptr, aClient *sptr,
+    int parc, char *parv[]);
+extern int m_desynch(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+extern unsigned int max_connection_count, max_client_count;
+
+#endif /* S_SERV_H */
diff --git a/include/s_user.h b/include/s_user.h
new file mode 100644 (file)
index 0000000..47fee6c
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef S_USER_H
+#define S_USER_H
+
+/*=============================================================================
+ * Macro's
+ */
+
+/*
+ * Nick flood limit
+ * Minimum time between nick changes.
+ * (The first two changes are allowed quickly after another however).
+ */
+#define NICK_DELAY 30
+
+/*
+ * Target flood time.
+ * Minimum time between target changes.
+ * (MAXTARGETS are allowed simultaneously however).
+ * Its set to a power of 2 because we devide through it quite a lot.
+ */
+#define TARGET_DELAY 128
+
+/* 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
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_umode(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int is_silenced(aClient *sptr, aClient *acptr);
+extern int hunt_server(int, aClient *cptr, aClient *sptr,
+    char *command, int server, int parc, char *parv[]);
+extern aClient *next_client(aClient *next, char *ch);
+extern int m_nick(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_private(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_notice(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_wallchops(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_cprivmsg(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_cnotice(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_user(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_quit(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_away(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_ping(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_pong(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_oper(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_pass(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_userhost(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_userip(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_ison(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern char *umode_str(aClient *cptr);
+extern void send_umode(aClient *cptr, aClient *sptr, int old, int sendmask);
+extern int del_silence(aClient *sptr, char *mask);
+extern int m_silence(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern void set_snomask(aClient *, snomask_t, int);
+extern int is_snomask(char *);
+extern int check_target_limit(aClient *sptr, void *target, const char *name,
+    int created);
+extern void add_target(aClient *sptr, void *target);
+
+extern struct SLink *opsarray[];
+
+#endif /* S_USER_H */
diff --git a/include/send.h b/include/send.h
new file mode 100644 (file)
index 0000000..73be18c
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef SEND_H
+#define SEND_H
+
+/*=============================================================================
+ * Macros
+ */
+
+#define LastDeadComment(cptr) ((cptr)->info)
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void sendto_one(aClient *to, char *pattern, ...)
+    __attribute__ ((format(printf, 2, 3)));
+extern void sendbufto_one(aClient *to);
+extern void sendto_ops(const char *pattern, ...)
+    __attribute__ ((format(printf, 1, 2)));
+extern void sendto_channel_butserv(aChannel *chptr, aClient *from,
+    char *pattern, ...) __attribute__ ((format(printf, 3, 4)));
+extern void sendto_serv_butone(aClient *one, char *pattern, ...)
+    __attribute__ ((format(printf, 2, 3)));
+extern void sendto_match_servs(aChannel *chptr, aClient *from,
+    char *format, ...) __attribute__ ((format(printf, 3, 4)));
+extern void sendto_lowprot_butone(aClient *cptr, int p, char *pattern, ...)
+    __attribute__ ((format(printf, 3, 4)));
+extern void sendto_highprot_butone(aClient *cptr, int p, char *pattern, ...)
+    __attribute__ ((format(printf, 3, 4)));
+extern void sendto_prefix_one(Reg1 aClient *to, Reg2 aClient *from,
+    char *pattern, ...) __attribute__ ((format(printf, 3, 4)));
+extern void flush_connections(int fd);
+extern void send_queued(aClient *to);
+extern void vsendto_one(aClient *to, char *pattern, va_list vl);
+extern void sendto_channel_butone(aClient *one, aClient *from,
+    aChannel *chptr, char *pattern, ...) __attribute__ ((format(printf, 4, 5)));
+extern void sendto_lchanops_butone(aClient *one, aClient *from,
+    aChannel *chptr, char *pattern, ...) __attribute__ ((format(printf, 4, 5)));
+extern void sendto_chanopsserv_butone(aClient *one, aClient *from,
+    aChannel *chptr, char *pattern, ...) __attribute__ ((format(printf, 4, 5)));
+extern void sendto_common_channels(aClient *user, char *pattern, ...)
+    __attribute__ ((format(printf, 2, 3)));
+extern void sendto_match_butone(aClient *one, aClient *from, char *mask,
+    int what, char *pattern, ...) __attribute__ ((format(printf, 5, 6)));
+extern void sendto_lops_butone(aClient *one, char *pattern, ...)
+    __attribute__ ((format(printf, 2, 3)));
+extern void vsendto_ops(const char *pattern, va_list vl);
+extern void sendto_ops_butone(aClient *one, aClient *from, char *pattern, ...)
+    __attribute__ ((format(printf, 3, 4)));
+extern void sendto_g_serv_butone(aClient *one, char *pattern, ...)
+    __attribute__ ((format(printf, 2, 3)));
+extern void sendto_realops(const char *pattern, ...)
+    __attribute__ ((format(printf, 1, 2)));
+extern void vsendto_op_mask(register snomask_t mask,
+    const char *pattern, va_list vl);
+extern void sendto_op_mask(snomask_t mask, const char *pattern, ...)
+    __attribute__ ((format(printf, 2, 3)));
+extern void sendbufto_op_mask(snomask_t mask);
+extern void sendbufto_serv_butone(aClient *one);
+
+extern char sendbuf[2048];
+
+#endif /* SEND_H */
diff --git a/include/sprintf_irc.h b/include/sprintf_irc.h
new file mode 100644 (file)
index 0000000..1a8d19b
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef SPRINTF_IRC
+#define SPRINTF_IRC
+
+#include <stdarg.h>
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern char *vsprintf_irc(register char *str, register const char *format,
+    register va_list);
+extern char *sprintf_irc(register char *str, register const char *format, ...)
+    __attribute__ ((format(printf, 2, 3)));
+
+extern const char atoi_tab[4000];
+
+#endif /* SPRINTF_IRC */
diff --git a/include/struct.h b/include/struct.h
new file mode 100644 (file)
index 0000000..1a18590
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * IRC - Internet Relay Chat, include/struct.h
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ *                    University of Oulu, Computing Center
+ * Copyright (C) 1996 -1997 Carlo Wood
+ *
+ * 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.
+ */
+
+#ifndef STRUCT_H
+#define STRUCT_H
+
+#include <netinet/in.h>                /* Needed for struct in_addr */
+#include "whowas.h"            /* Needed for whowas struct */
+
+#ifndef INCLUDED_dbuf_h
+#include "dbuf.h"
+#endif
+
+
+/*=============================================================================
+ * General defines
+ */
+
+#define NICKLEN                9
+#define USERLEN                10
+#define HOSTLEN                63
+#define REALLEN                50
+#define PASSWDLEN      20
+#define BUFSIZE                512     /* WARNING: *DONT* CHANGE THIS!!!! */
+#define MAXTARGETS     20
+#define STARTTARGETS   10
+#define RESERVEDTARGETS 12
+
+/*-----------------------------------------------------------------------------
+ * Macro's
+ */
+
+#define CLIENT_LOCAL_SIZE sizeof(aClient)
+#define CLIENT_REMOTE_SIZE offsetof(aClient, count)
+
+#define MyConnect(x)   ((x)->from == (x))
+#define MyUser(x)      (MyConnect(x) && IsUser(x))
+#define MyOper(x)      (MyConnect(x) && IsOper(x))
+#define Protocol(x)    ((x)->serv->prot)
+
+/*=============================================================================
+ * Structures
+ *
+ * Only put structures here that are being used in a very large number of
+ * source files. Other structures go in the header file of there corresponding
+ * source file, or in the source file itself (when only used in that file).
+ */
+
+struct Client {
+  struct Client *next, *prev, *hnext;
+  struct User *user;           /* ...defined, if this is a User */
+  struct Server *serv;         /* ...defined, if this is a server */
+  struct Whowas *whowas;       /* Pointer to ww struct to be freed on quit */
+  char yxx[4];                 /* Numeric Nick: YMM if this is a server,
+                                  XX0 if this is a user */
+  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 */
+  int marker;                  /* /who processing marker */
+  unsigned int flags;          /* client flags */
+  struct Client *from;         /* == self, if Local Client, *NEVER* NULL! */
+  int fd;                      /* >= 0, for local clients */
+  unsigned int hopcount;       /* number of servers to this 0 = local */
+  short status;                        /* Client type */
+  struct in_addr ip;           /* Real ip# - NOT defined for remote servers! */
+  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 */
+  /*
+   *  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).
+   */
+  unsigned int count;          /* Amount of data in buffer, DON'T PUT
+                                  variables ABOVE this one! */
+  snomask_t snomask;           /* mask for server messages */
+  char buffer[BUFSIZE];                /* Incoming message buffer; or the error that
+                                  caused this clients socket to be `dead' */
+  unsigned short int lastsq;   /* # of 2k blocks when sendqueued called last */
+  time_t nextnick;             /* Next time that a nick change is allowed */
+  time_t nexttarget;           /* Next time that a target change is allowed */
+  unsigned char targets[MAXTARGETS];   /* Hash values of current targets */
+  unsigned int cookie;         /* Random number the user must PONG */
+  struct DBuf sendQ;           /* Outgoing message queue--if socket full */
+  struct DBuf recvQ;           /* Hold for data incoming yet to be parsed */
+  unsigned int sendM;          /* Statistics: protocol messages send */
+  unsigned int sendK;          /* Statistics: total k-bytes send */
+  unsigned int receiveM;       /* Statistics: protocol messages received */
+  unsigned int receiveK;       /* Statistics: total k-bytes received */
+  unsigned short int sendB;    /* counters to count upto 1-k lots of bytes */
+  unsigned short int receiveB; /* sent and received. */
+  struct Client *acpt;         /* listening client which we accepted from */
+  struct SLink *confs;         /* Configuration record associated */
+  int authfd;                  /* fd for rfc931 authentication */
+  unsigned short int port;     /* and the remote port# too :-) */
+  struct hostent *hostp;
+  struct ListingArgs *listing;
+#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];
+};
+
+struct Server {
+  struct Server *nexts;
+  struct Client *up;           /* Server one closer to me */
+  struct DSlink *down;         /* List with downlink servers */
+  struct DSlink *updown;       /* own Dlink in up->serv->down struct */
+  aClient **client_list;       /* List with client pointers on this server */
+  struct User *user;           /* who activated this connection */
+  struct ConfItem *nline;      /* N-line pointer for this server */
+  time_t timestamp;            /* Remotely determined connect try time */
+  time_t ghost;                        /* Local time at which a new server
+                                  caused a Ghost */
+  unsigned short prot;         /* Major protocol */
+  unsigned short nn_last;      /* Last numeric nick for p9 servers only */
+  unsigned int nn_mask;                /* [Remote] FD_SETSIZE - 1 */
+  char nn_capacity[4];         /* numeric representation of server capacity */
+#ifdef LIST_DEBUG
+  struct Client *bcptr;
+#endif
+  char *last_error_msg;                /* Allocated memory with last message receive with an ERROR */
+  char by[NICKLEN + 1];
+};
+
+struct User {
+  struct User *nextu;
+  struct Client *server;       /* client structure of server */
+  struct SLink *channel;       /* chain of channel pointer blocks */
+  struct SLink *invited;       /* chain of invite pointer blocks */
+  struct SLink *silence;       /* chain of silence pointer blocks */
+  char *away;                  /* pointer to away message */
+  time_t last;
+  unsigned int refcnt;         /* Number of times this block is referenced */
+  unsigned int joined;         /* number of channels joined */
+  char username[USERLEN + 1];
+  char host[HOSTLEN + 1];
+#ifdef LIST_DEBUG
+  struct Client *bcptr;
+#endif
+};
+
+#endif /* STRUCT_H */
diff --git a/include/support.h b/include/support.h
new file mode 100644 (file)
index 0000000..5629669
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef SUPPORT_H
+#define SUPPORT_H
+
+#include <netinet/in.h>
+
+/*=============================================================================
+ * Proto types
+ */
+
+#ifndef HAVE_STRTOKEN
+extern char *strtoken(char **save, char *str, char *fs);
+#endif
+#ifndef HAVE_STRERROR
+extern char *strerror(int err_no);
+#endif
+extern void dumpcore(const char *pattern, ...)
+    __attribute__ ((format(printf, 1, 2)));
+extern char *inetntoa(struct in_addr in);
+extern int check_if_ipmask(const char *mask);
+extern void write_log(const char *filename, const char *pattern, ...);
+
+#endif /* SUPPORT_H */
diff --git a/include/sys.h b/include/sys.h
new file mode 100644 (file)
index 0000000..47814b5
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * 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__
+
+#include "../config/config.h"
+#include "../config/setup.h"
+
+#ifdef __osf__
+#define _OSF_SOURCE
+#endif
+
+#ifdef __sun__
+#ifdef __svr4__
+#define SOL2
+#else
+#define SUNOS4
+#endif
+#endif
+
+#if WORDS_BIGENDIAN
+# define BIT_ZERO_ON_LEFT
+#else
+# define BIT_ZERO_ON_RIGHT
+#endif
+
+#ifdef _SEQUENT_               /* Dynix 1.4 or 2.0 Generic Define.. */
+#undef BSD
+#define SYSV                   /* Also #define SYSV */
+#endif
+
+#ifdef __hpux
+#define HPUX
+#endif
+
+#ifdef sgi
+#define SGI
+#endif
+
+#if defined(mips)
+#undef SYSV
+#undef BSD
+#define BSD 1                  /* mips only works in bsd43 environment */
+#endif
+
+#ifdef BSD_RELIABLE_SIGNALS
+#if defined(SYSV_UNRELIABLE_SIGNALS) || defined(POSIX_SIGNALS)
+#error You stuffed up config.h signals #defines use only one.
+#endif
+#define HAVE_RELIABLE_SIGNALS
+#endif
+
+#ifdef SYSV_UNRELIABLE_SIGNALS
+#ifdef POSIX_SIGNALS
+#error You stuffed up config.h signals #defines use only one.
+#endif
+#undef HAVE_RELIABLE_SIGNALS
+#endif
+
+#ifdef POSIX_SIGNALS
+#define HAVE_RELIABLE_SIGNALS
+#endif
+
+/*
+ * safety margin so we can always have one spare fd, for motd/authd or
+ * whatever else.  -24 allows "safety" margin of 10 listen ports, 8 servers
+ * and space reserved for logfiles, DNS sockets and identd sockets etc.
+ */
+#define MAXCLIENTS     (MAXCONNECTIONS-24)
+
+#ifdef HAVECURSES
+#define DOCURSES
+#else
+#undef DOCURSES
+#endif
+
+#ifdef HAVETERMCAP
+#define DOTERMCAP
+#else
+#undef DOTERMCAP
+#endif
+
+#ifndef UNIXPORT
+#undef UNIXPORTPATH
+#endif
+
+#if defined(CLIENT_FLOOD)
+#if (CLIENT_FLOOD > 8000) || (CLIENT_FLOOD < 512)
+#error CLIENT_FLOOD needs redefining.
+#endif
+#else
+#error CLIENT_FLOOD undefined
+#endif
+
+#ifndef CONFIG_SETUGID
+#undef IRC_UID
+#undef IRC_GID
+#endif
+
+#define Reg1 register
+#define Reg2 register
+#define Reg3 register
+#define Reg4 register
+#define Reg5 register
+#define Reg6 register
+#define Reg7 register
+#define Reg8 register
+#define Reg9 register
+#define Reg10 register
+
+/* Define FD_SETSIZE to what we want before including sys/types.h on BSD */
+#if  defined(__FreeBSD__) || defined(__NetBSD__) || defined(__bsdi__)
+#if ((!defined(USE_POLL)) && (!defined(FD_SETSIZE)))
+#define FD_SETSIZE ((MAXCONNECTIONS)+4)
+#endif
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#ifdef __osf__
+#undef _OSF_SOURCE
+/* Buggy header */
+#include <netdb.h>
+#define _OSF_SOURCE
+#endif
+
+#if HAVE_ERRNO_H
+# include <errno.h>
+#else
+# if HAVE_NET_ERRNO_H
+#  include <net/errno.h>
+# endif
+#endif
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && \
+    !defined(__bsdi__) && !defined(__alpha) && !defined(__GLIBC__)
+extern char *sys_errlist[];
+#endif
+
+/* See AC_HEADER_STDC in 'info autoconf' */
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if HAVE_STRING_H
+#  include <string.h>
+# endif
+# ifndef HAVE_STRCHR
+#  define strchr index
+#  define strrchr rindex
+# endif
+char *strchr(), *strrchr(), *strtok();
+# if HAVE_MEMORY_H             /* See AC_MEMORY_H in 'info autoconf' */
+#  include <memory.h>
+# endif
+# ifndef HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+#  define memset(a, b, c) bzero(a, c)  /* We ONLY use memset(x, 0, y) */
+# else
+#  if NEED_BZERO               /* This is not used yet - needs to be added to `configure' */
+#   define bzero(a, c) memset((a), 0, (c))     /* Some use it in FD_ZERO */
+#  endif
+# endif
+# ifndef HAVE_MEMMOVE
+#  define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#if defined(_AIX) || (defined(__STRICT_ANSI__) && __GLIBC__ >= 2)
+#include <sys/select.h>
+#endif
+
+/* See AC_HEADER_TIME in 'info autoconf' */
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef SOL2
+#define OPT_TYPE char          /* opt type for get/setsockopt */
+#else
+#define OPT_TYPE void
+#endif
+
+#ifdef SUNOS4
+#define LIMIT_FMT "%d"
+#else
+#if (defined(__bsdi__) || defined(__NetBSD__))
+#define LIMIT_FMT "%qd"
+#else
+#define LIMIT_FMT "%ld"
+#endif
+#endif
+
+/* Different name on NetBSD and FreeBSD --Skip */
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__bsdi__)
+#define dn_skipname  __dn_skipname
+#endif
+
+#if defined(DEBUGMODE) && !defined(DEBUGMALLOC)
+#define DEBUGMALLOC
+#endif
+
+#ifdef STDC_HEADERS
+#include <stdlib.h>
+#include <stddef.h>
+#else /* !STDC_HEADERS */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#else
+#ifdef HAVE_SYS_MALLOC_H
+#include <sys/malloc.h>
+#endif /* HAVE_SYS_MALLOC_H */
+#endif /* HAVE_MALLOC_H */
+#endif /* !STDC_HEADERS */
+
+#ifndef MAX
+#define MAX(a, b)      ((a) > (b) ? (a) : (b))
+#endif
+#ifndef MIN
+#define MIN(a, b)      ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE  (!FALSE)
+#endif
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#include "runmalloc.h"
+
+#define MyCoreDump *((int *)NULL)=0
+
+/* This isn't really POSIX :(, but we really need it -- can this be replaced ? */
+#if defined(__STRICT_ANSI__) && !defined(_AIX)
+extern int gettimeofday(struct timeval *tv, struct timezone *tz);
+#endif
+
+/*
+ * The following part is donated by Carlo Wood from his package 'libr':
+ * (C) Copyright 1996 by Carlo Wood. All rights reserved.
+ */
+
+/* GNU CC improvements: We can only use this if we have a gcc/g++ compiler */
+#ifdef __GNUC__
+
+#if (__GNUC__ < 2) || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+#define NO_ATTRIBUTE
+#endif
+
+#else /* !__GNUC__ */
+
+/* No attributes if we don't have gcc-2.7 or higher */
+#define NO_ATTRIBUTE
+
+#endif /* !__GNUC__ */
+
+#ifdef __cplusplus
+#define HANDLER_ARG(x) x
+#define UNUSED(x)
+#else
+#define HANDLER_ARG(x)
+#ifdef NO_ATTRIBUTE
+#define __attribute__(x)
+#define UNUSED(x) unused_##x
+#else
+#define UNUSED(x) x __attribute__ ((unused))
+#endif
+#endif
+
+#ifdef NO_ATTRIBUTE
+#define RCSTAG_CC(string) static char unused_rcs_ident[] = string
+#else
+#define RCSTAG_CC(string) static char rcs_ident[] __attribute__ ((unused)) = string
+#endif
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#else /* !HAVE_SYS_MALLOC_H */
+#undef __BEGIN_DECLS
+#undef __END_DECLS
+#ifdef  __cplusplus
+#define __BEGIN_DECLS   extern "C" {
+#define __END_DECLS     }
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+#endif /* !HAVE_SYS_CDEFS_H */
+
+#endif /* __sys_include__ */
diff --git a/include/userload.h b/include/userload.h
new file mode 100644 (file)
index 0000000..50adf2c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Userload module by Michael L. VanLoon (mlv) <michaelv@iastate.edu>
+ * Written 2/93.  Originally grafted into irc2.7.2g 4/93.
+ * Rewritten 9/97 by Carlo Wood for ircu2.10.01.
+ *
+ * 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.
+ */
+
+#ifndef USERLOAD_H
+#define USERLOAD_H
+
+/*=============================================================================
+ * Structures
+ */
+
+struct current_load_st {
+  unsigned int client_count;
+  unsigned int local_count;
+  unsigned int conn_count;
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern void update_load(void);
+extern void calc_load(aClient *sptr);
+extern void initload(void);
+
+extern struct current_load_st current_load;
+
+#endif /* USERLOAD_H */
diff --git a/include/version.h b/include/version.h
new file mode 100644 (file)
index 0000000..e9e3c74
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+extern const char *version;
+extern const char *creation;
+extern const char *infotext[];
+extern const char *generation;
+
+#endif /* VERSION_H */
diff --git a/include/whocmds.h b/include/whocmds.h
new file mode 100644 (file)
index 0000000..f471538
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef WHOCMDS_H
+#define WHOCMDS_H
+
+/*=============================================================================
+ * Macro's
+ */
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern int m_who(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern int m_whois(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+
+#endif /* WHOCMDS_H */
diff --git a/include/whowas.h b/include/whowas.h
new file mode 100644 (file)
index 0000000..e7def4e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef WHOWAS_H
+#define WHOWAS_H
+
+/*=============================================================================
+ * General defines
+ */
+
+#define BITS_PER_COL           3
+#define BITS_PER_COL_MASK      0x7
+#define WW_MAX_INITIAL         16
+
+#define MAX_SUB (1 << BITS_PER_COL)
+#define WW_MAX_INITIAL_MASK (WW_MAX_INITIAL - 1)
+#define WW_MAX (WW_MAX_INITIAL * MAX_SUB)
+
+/*=============================================================================
+ * Structures
+ */
+
+struct Whowas {
+  unsigned int hashv;
+  char *name;
+  char *username;
+  char *hostname;
+  char *servername;
+  char *realname;
+  char *away;
+  time_t logoff;
+  struct Client *online;       /* Needed for get_history() (nick chasing) */
+  struct Whowas *hnext;                /* Next entry with the same hash value */
+  struct Whowas **hprevnextp;  /* Pointer to previous next pointer */
+  struct Whowas *cnext;                /* Next entry with the same 'online' pointer */
+  struct Whowas **cprevnextp;  /* Pointer to previous next pointer */
+};
+
+/*=============================================================================
+ * Proto types
+ */
+
+extern aClient *get_history(const char *nick, time_t timelimit);
+extern void add_history(aClient *cptr, int still_on);
+extern void off_history(const aClient *cptr);
+extern void initwhowas(void);
+extern int m_whowas(aClient *cptr, aClient *sptr, int parc, char *parv[]);
+extern void count_whowas_memory(int *wwu, size_t *wwm, int *wwa, size_t *wwam);
+
+#endif /* WHOWAS_H */
diff --git a/ircd/.cvsignore b/ircd/.cvsignore
new file mode 100644 (file)
index 0000000..54bccd8
--- /dev/null
@@ -0,0 +1,5 @@
+Makefile
+stamp-m
+version.c
+ircd
+chkconf
diff --git a/ircd/IPcheck.c b/ircd/IPcheck.c
new file mode 100644 (file)
index 0000000..df668b6
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * IRC - Internet Relay Chat, ircd/IPcheck.c
+ * Copyright (C) 1998 Carlo Wood ( 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 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.
+ */
+
+/* This file should be edited in a window with a width of 141 characters */
+
+#include "sys.h"
+#include <netinet/in.h>
+#include "h.h"
+#include "IPcheck.h"
+#include "querycmds.h"
+#include "struct.h"
+#include "s_user.h"
+#include "s_bsd.h"
+#include "struct.h"
+#ifdef GODMODE
+#include "numnicks.h"
+#endif
+#include "send.h"
+
+RCSTAG_CC("$Id$");
+
+extern aClient me;
+extern time_t now;
+
+/*
+ * IP number and last targets of a user that just disconnected.
+ * Used to allow a user that shortly disconnected to rejoin
+ * the channels he/she was on.
+ */
+struct ip_targets_st {
+  struct in_addr ip;
+  unsigned char free_targets;
+  unsigned char targets[MAXTARGETS];
+};
+
+/* We keep one IPregistry for each IP number (for both, remote and local clients) */
+struct IPregistry {
+  union {
+    struct in_addr ip;         /* The IP number of the registry entry. */
+    struct ip_targets_st *ptr; /* The IP number of the registry entry, and a list of targets */
+  } ip_targets;
+  unsigned int last_connect:16;        /* Time of last connect (attempt), see BITMASK below,
+                                  or time of last disconnect when `connected' is zero. */
+  unsigned int connected:8;    /* Used for IP# throttling: Number of currently on-line clients with this IP number */
+  unsigned int connect_attempts:4;     /* Used for connect speed throttling: Number of clients that connected with this IP number
+                                          or `15' when then real value is >= 15.  This value is only valid when the last connect
+                                          was less then IPCHECK_CLONE_PERIOD seconds ago, it should considered to be 0 otherwise. */
+  unsigned int free_targets:4; /* Number of free targets that the next local client will inherit on connect,
+                                  or HAS_TARGETS_MAGIC when ip_targets.ptr is a pointer to a ip_targets_st. */
+};
+
+struct IPregistry_vector {
+  unsigned short length;
+  unsigned short allocated_length;
+  struct IPregistry *vector;
+};
+
+#define HASHTABSIZE 0x2000     /* Must be power of 2 */
+static struct IPregistry_vector IPregistry_hashtable[HASHTABSIZE];
+
+/*
+ * Calculate a `hash' value between 0 and HASHTABSIZE, from the internet address `in_addr'.
+ * Apply it immedeately to the table, effectively hiding the table itself.
+ */
+#define CALCULATE_HASH(in_addr) \
+  struct IPregistry_vector *hash; \
+  do { register unsigned int ip = (in_addr).s_addr; \
+       hash = &IPregistry_hashtable[((ip >> 14) + (ip >> 7) + ip) & (HASHTABSIZE - 1)]; } while(0)
+
+/*
+ * Fit `now' in an unsigned short, the advantage is that we use less memory `struct IPregistry::last_connect' can be smaller
+ * while the only disadvantage is that if someone reconnects after exactly 18 hours and 12 minutes, and NOBODY with the
+ * same _hash_ value for this IP-number did disconnect in the meantime, then the server will think he reconnected immedeately.
+ * In other words: No disadvantage at all.
+ */
+#define BITMASK 0xffff         /* Same number of bits as `struct IPregistry::last_connect' */
+#define NOW ((unsigned short)(now & BITMASK))
+#define CONNECTED_SINCE(x) ((unsigned short)((now & BITMASK) - (x)->last_connect))
+
+#define IPCHECK_CLONE_LIMIT 2
+#define IPCHECK_CLONE_PERIOD 20
+#define IPCHECK_CLONE_DELAY 600
+
+#define HAS_TARGETS_MAGIC 15
+#define HAS_TARGETS(entry) ((entry)->free_targets == HAS_TARGETS_MAGIC)
+
+#if STARTTARGETS >= HAS_TARGETS_MAGIC
+#error "That doesn't fit in 4 bits, does it?"
+#endif
+
+/* IP(entry) returns the `struct in_addr' of the IPregistry. */
+#define IP(entry) (HAS_TARGETS(entry) ? (entry)->ip_targets.ptr->ip : (entry)->ip_targets.ip)
+#define FREE_TARGETS(entry) (HAS_TARGETS(entry) ? (entry)->ip_targets.ptr->free_targets : (entry)->free_targets)
+
+static unsigned short count = 10000, average_length = 4;
+
+static struct IPregistry *IPregistry_add(struct IPregistry_vector *iprv)
+{
+  if (iprv->length == iprv->allocated_length)
+  {
+    iprv->allocated_length += 4;
+    iprv->vector =
+       (struct IPregistry *)RunRealloc(iprv->vector,
+       iprv->allocated_length * sizeof(struct IPregistry));
+  }
+  return &iprv->vector[iprv->length++];
+}
+
+static struct IPregistry *IPregistry_find(struct IPregistry_vector *iprv,
+    struct in_addr ip)
+{
+  if (iprv->length > 0)
+  {
+    struct IPregistry *i, *end = &iprv->vector[iprv->length];
+    for (i = &iprv->vector[0]; i < end; ++i)
+      if (IP(i).s_addr == ip.s_addr)
+       return i;
+  }
+  return NULL;
+}
+
+static struct IPregistry *IPregistry_find_with_expire(struct IPregistry_vector
+    *iprv, struct in_addr ip)
+{
+  struct IPregistry *last = &iprv->vector[iprv->length - 1];   /* length always > 0 because searched element always exists */
+  struct IPregistry *curr;
+  struct IPregistry *retval = NULL;    /* Core dump if we find nothing :/ - can be removed when code is stable */
+
+  for (curr = &iprv->vector[0]; curr < last;)
+  {
+    if (IP(curr).s_addr == ip.s_addr)
+      /* `curr' is element we looked for */
+      retval = curr;
+    else if (curr->connected == 0)
+    {
+      if (CONNECTED_SINCE(curr) > 600U)        /* Don't touch this number, it has statistical significance */
+      {
+       /* `curr' expired */
+       if (HAS_TARGETS(curr))
+         RunFree(curr->ip_targets.ptr);
+       *curr = *last--;
+       iprv->length--;
+       if (--count == 0)
+       {
+         /* Make ever 10000 disconnects an estimation of the average vector length */
+         count = 10000;
+         average_length =
+             (nrof.clients + nrof.unknowns + nrof.local_servers) / HASHTABSIZE;
+       }
+       /* Now check the new element (last) that was moved to this position */
+       continue;
+      }
+      else if (CONNECTED_SINCE(curr) > 120U && HAS_TARGETS(curr))
+      {
+       /* Expire storage of targets */
+       struct in_addr ip1 = curr->ip_targets.ptr->ip;
+       curr->free_targets = curr->ip_targets.ptr->free_targets;
+       RunFree(curr->ip_targets.ptr);
+       curr->ip_targets.ip = ip1;
+      }
+    }
+    /* Did not expire, check next element */
+    ++curr;
+  }
+  /* Now check the last element in the list (curr == last) */
+  if (IP(curr).s_addr == ip.s_addr)
+    /* `curr' is element we looked for */
+    retval = curr;
+  else if (curr->connected == 0)
+  {
+    if (CONNECTED_SINCE(curr) > 600U)  /* Don't touch this number, it has statistical significance */
+    {
+      /* `curr' expired */
+      if (HAS_TARGETS(curr))
+       RunFree(curr->ip_targets.ptr);
+      iprv->length--;
+      if (--count == 0)
+      {
+       /* Make ever 10000 disconnects an estimation of the average vector length */
+       count = 10000;
+       average_length =
+           (nrof.clients + nrof.unknowns + nrof.local_servers) / HASHTABSIZE;
+      }
+    }
+    else if (CONNECTED_SINCE(curr) > 120U && HAS_TARGETS(curr))
+    {
+      /* Expire storage of targets */
+      struct in_addr ip1 = curr->ip_targets.ptr->ip;
+      curr->free_targets = curr->ip_targets.ptr->free_targets;
+      RunFree(curr->ip_targets.ptr);
+      curr->ip_targets.ip = ip1;
+    }
+  }
+  /* Do we need to shrink the vector? */
+  if (iprv->allocated_length > average_length
+      && iprv->allocated_length - iprv->length >= 4)
+  {
+    struct IPregistry *newpos;
+    iprv->allocated_length = iprv->length;
+    newpos =
+       (struct IPregistry *)RunRealloc(iprv->vector,
+       iprv->allocated_length * sizeof(struct IPregistry));
+    if (newpos != iprv->vector)        /* Is this ever true? */
+    {
+      retval =
+         (struct IPregistry *)((char *)retval + ((char *)newpos -
+         (char *)iprv->vector));
+      iprv->vector = newpos;
+    }
+  }
+  return retval;
+}
+
+static void reset_connect_time(struct IPregistry *entry)
+{
+  unsigned int previous_free_targets;
+
+  /* Apply aging */
+  previous_free_targets =
+      FREE_TARGETS(entry) + CONNECTED_SINCE(entry) / TARGET_DELAY;
+  if (previous_free_targets > STARTTARGETS)
+    previous_free_targets = STARTTARGETS;
+  if (HAS_TARGETS(entry))
+    entry->ip_targets.ptr->free_targets = previous_free_targets;
+  else
+    entry->free_targets = previous_free_targets;
+
+  entry->last_connect = NOW;
+}
+
+/*
+ * IPcheck_local_connect
+ *
+ * Event:
+ *   A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
+ *
+ * Action:
+ *   Update the IPcheck registry.
+ *   Return < 0 if the connection should be rejected, otherwise 0.
+ *     -1 : Throttled
+ *     -2 : Too many connections from your host
+ *
+ * Throttling:
+ *
+ * A connection should be rejected when a connection from the same IP number was
+ * received IPCHECK_CLONE_LIMIT times before this connect attempt, with
+ * reconnect intervals of IPCHECK_CLONE_PERIOD seconds or less.
+ *
+ * Free target inheritance:
+ *
+ * When the client is accepted, then the number of Free Targets
+ * of the cptr is set to the value stored in the found IPregistry
+ * structure, or left at STARTTARGETS.  This can be done by changing
+ * cptr->nexttarget to be `now - (TARGET_DELAY * (FREE_TARGETS - 1))',
+ * where FREE_TARGETS may range from 0 till STARTTARGETS.
+ */
+int IPcheck_local_connect(aClient *cptr)
+{
+  struct IPregistry *entry;
+  CALCULATE_HASH(cptr->ip);
+  SetIPChecked(cptr);          /* Mark that we did add/update an IPregistry entry */
+  if (!(entry = IPregistry_find(hash, cptr->ip)))
+  {
+    entry = IPregistry_add(hash);
+    entry->ip_targets.ip = cptr->ip;   /* The IP number of registry entry */
+    entry->last_connect = NOW; /* Seconds since last connect (attempt) */
+    entry->connected = 1;      /* Number of currently connected clients with this IP number */
+    entry->connect_attempts = 1;       /* Number of clients that connected with this IP number */
+    entry->free_targets = STARTTARGETS;        /* Number of free targets that a client gets on connect */
+    return 0;
+  }
+#ifdef GODMODE
+  sendto_one(cptr,
+      "ERROR :I saw your face before my friend (connected: %u; connect_attempts %u; free_targets %u)",
+      entry->connected, entry->connect_attempts, FREE_TARGETS(entry));
+#endif
+  /* Note that this also connects server connects.  It is hard and not interesting, to change that. */
+  if (++(entry->connected) == 0)       /* Don't allow more then 255 connects from one IP number, ever */
+    return -2;
+  if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
+    entry->connect_attempts = 0;
+  reset_connect_time(entry);
+  if (++(entry->connect_attempts) == 0)        /* Check for overflow */
+    --(entry->connect_attempts);
+  if (entry->connect_attempts <= IPCHECK_CLONE_LIMIT)
+    cptr->nexttarget = now - (TARGET_DELAY * (FREE_TARGETS(entry) - 1));
+#ifdef DEBUGMODE
+  else
+#else
+  else if (now - me.since > IPCHECK_CLONE_DELAY)       /* Don't refuse connection when we just rebooted the server */
+#endif
+    return -1;
+  return 0;
+}
+
+/*
+ * IPcheck_remote_connect
+ *
+ * Event:
+ *   A remote client connected to Undernet, with IP number `cptr->ip.s_addr'
+ *   and hostname `hostname'.
+ *
+ * Action:
+ *   Update the IPcheck registry.
+ *   Return -1 on failure, 0 on success.
+ */
+int IPcheck_remote_connect(aClient *cptr, const char *UNUSED(hostname),
+    int is_burst)
+{
+  struct IPregistry *entry;
+  CALCULATE_HASH(cptr->ip);
+  SetIPChecked(cptr);          /* Mark that we did add/update an IPregistry entry */
+  if (!(entry = IPregistry_find(hash, cptr->ip)))
+  {
+    entry = IPregistry_add(hash);
+    entry->ip_targets.ip = cptr->ip;   /* The IP number of registry entry */
+    entry->last_connect = NOW; /* Seconds since last connect (attempt) */
+    entry->connected = 1;      /* Number of currently connected clients with this IP number */
+    entry->connect_attempts = is_burst ? 1 : 0;        /* Number of clients that connected with this IP number */
+    entry->free_targets = STARTTARGETS;        /* Number of free targets that a client gets on connect */
+  }
+  else
+  {
+#ifdef GODMODE
+    sendto_one(cptr,
+       "%s NOTICE %s%s :I saw your face before my friend (connected: %u; connect_attempts %u; free_targets %u)",
+       NumServ(&me), NumNick(cptr), entry->connected, entry->connect_attempts,
+       FREE_TARGETS(entry));
+#endif
+    if (++(entry->connected) == 0)     /* Don't allow more then 255 connects from one IP number, ever */
+      return -1;
+    if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
+      entry->connect_attempts = 0;
+    if (!is_burst)
+    {
+      if (++(entry->connect_attempts) == 0)    /* Check for overflow */
+       --(entry->connect_attempts);
+      reset_connect_time(entry);
+    }
+  }
+  return 0;
+}
+
+/*
+ * IPcheck_connect_fail
+ *
+ * Event:
+ *   This local client failed to connect due to legal reasons.
+ *
+ * Action:
+ *   Neutralize the effect of calling IPcheck_local_connect, in such
+ *   a way that the client won't be penalized when trying to reconnect
+ *   again.
+ */
+void IPcheck_connect_fail(aClient *cptr)
+{
+  struct IPregistry *entry;
+  CALCULATE_HASH(cptr->ip);
+  entry = IPregistry_find(hash, cptr->ip);
+  entry->connect_attempts--;
+}
+
+/*
+ * IPcheck_connect_succeeded
+ *
+ * Event:
+ *   A client succeeded to finish the registration.
+ *
+ * Finish IPcheck registration of a successfully, locally connected client.
+ */
+void IPcheck_connect_succeeded(aClient *cptr)
+{
+  struct IPregistry *entry;
+  const char *tr = "";
+  CALCULATE_HASH(cptr->ip);
+  entry = IPregistry_find(hash, cptr->ip);
+  if (HAS_TARGETS(entry))
+  {
+    memcpy(cptr->targets, entry->ip_targets.ptr->targets, MAXTARGETS);
+    tr = " tr";
+  }
+  sendto_one(cptr, ":%s NOTICE %s :on %u ca %u(%u) ft %u(%u)%s",
+      me.name, cptr->name, entry->connected, entry->connect_attempts,
+      IPCHECK_CLONE_LIMIT, FREE_TARGETS(entry), STARTTARGETS, tr);
+}
+
+/*
+ * IPcheck_disconnect
+ *
+ * Event:
+ *   A local client disconnected or a remote client left Undernet.
+ *
+ * Action:
+ *   Update the IPcheck registry.
+ *   Remove all expired IPregistry structures from the hash bucket
+ *     that belongs to this clients IP number.
+ */
+void IPcheck_disconnect(aClient *cptr)
+{
+  struct IPregistry *entry;
+  CALCULATE_HASH(cptr->ip);
+  entry = IPregistry_find_with_expire(hash, cptr->ip);
+  if (--(entry->connected) == 0)       /* If this was the last one, set `last_connect' to disconnect time (used for expiration) */
+  {
+    if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD)
+      entry->connect_attempts = 0;     /* Otherwise we'd penetalize for this old value if the client reconnects within 20 seconds */
+    reset_connect_time(entry);
+  }
+  if (MyConnect(cptr))
+  {
+    unsigned int inheritance;
+    /* Copy the clients targets */
+    if (HAS_TARGETS(entry))
+    {
+      entry->free_targets = entry->ip_targets.ptr->free_targets;
+      RunFree(entry->ip_targets.ptr);
+    }
+    entry->ip_targets.ptr =
+       (struct ip_targets_st *)RunMalloc(sizeof(struct ip_targets_st));
+    entry->ip_targets.ptr->ip = cptr->ip;
+    entry->ip_targets.ptr->free_targets = entry->free_targets;
+    entry->free_targets = HAS_TARGETS_MAGIC;
+    memcpy(entry->ip_targets.ptr->targets, cptr->targets, MAXTARGETS);
+    /*
+     * This calculation can be pretty unfair towards large multi-user hosts, but
+     * there is "nothing" we can do without also allowing spam bots to send more
+     * messages or by drastically increasing the ammount of memory used in the IPregistry.
+     *
+     * The problem is that when a client disconnects, leaving no free targets, then
+     * the next client from that IP number has to pay for it (getting no free targets).
+     * But ALSO the next client, and the next client, and the next client etc - until
+     * another client disconnects that DOES leave free targets.  The reason for this
+     * is that if there are 10 SPAM bots, and they all disconnect at once, then they
+     * ALL should get no free targets when reconnecting.  We'd need to store an entry
+     * per client (instead of per IP number) to avoid this.
+     */
+    if (cptr->nexttarget <= now)
+      inheritance = (now - cptr->nexttarget) / TARGET_DELAY + 1;       /* Number of free targets */
+    else
+      inheritance = 0;
+    /* Add bonus, this is pretty fuzzy, but it will help in some cases. */
+    if (now - cptr->firsttime > 600)   /* Was longer then 10 minutes online? */
+      inheritance += (now - cptr->firsttime - 600) / TARGET_DELAY;
+    /* Finally, store smallest value for Judgement Day */
+    if (inheritance < entry->ip_targets.ptr->free_targets)
+      entry->ip_targets.ptr->free_targets = inheritance;
+  }
+}
+
+/*
+ * IPcheck_nr
+ *
+ * Returns number of clients with the same IP number
+ */
+unsigned short IPcheck_nr(aClient *cptr)
+{
+  struct IPregistry *entry;
+  CALCULATE_HASH(cptr->ip);
+  entry = IPregistry_find(hash, cptr->ip);
+  return (entry ? entry->connected : 0);
+}
diff --git a/ircd/Makefile.in b/ircd/Makefile.in
new file mode 100644 (file)
index 0000000..4bf1c44
--- /dev/null
@@ -0,0 +1,460 @@
+# ircd/Makefile for the Undernet IRC Daemon.
+# Copyright (C) 1990 Jarkko Oikarinen
+# Copyright (C) 1997 Carlo Wood
+
+# 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., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#### Start of system configuration section. ####
+
+# Output variables of the 'configure' script:
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+srcdir=@srcdir@
+DEFS=@DEFS@
+INSTALL=@INSTALL@
+SHELL=@SHPROG@
+RM=@RMPROG@
+AWK=@AWK@
+LN_S=@LN_S@
+MV=mv
+CHMOD=chmod
+CHOWN=chown
+CHGRP=chgrp
+MKDIR=mkdir
+TOUCH=touch
+GREP=grep
+@SET_MAKE@
+# The following variables are replaced by what you give during configuration :
+
+BINDIR=
+SYMLINK=
+IRCDMODE=
+IRCDOWN=
+IRCDGRP=
+DPATH=
+MPATH=
+RPATH=
+
+CC=
+CFLAGS=
+CPPFLAGS=
+LDFLAGS=
+IRCDLIBS=
+
+#### End of system configuration section. ####
+
+OBJS=IPcheck.o bsd.o channel.o class.o common.o crule.o dbuf.o fileio.o ircd.o \
+     list.o map.o match.o numnicks.o opercmds.o packet.o parse.o querycmds.o \
+     random.o res.o runmalloc.o s_auth.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 send.o sprintf_irc.o \
+     support.o userload.o whocmds.o whowas.o hash.o
+
+SRC=${OBJS:%.o=%.c}
+
+all:
+       ( cd ..; make -f Makefile )
+
+.SUFFIXES: .c .o
+
+.c.o:
+       ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
+
+build: ircd chkconf
+
+ircd: ${OBJS} ../include/patchlevel.h
+       ${SHELL} version.c.SH
+       ${CC} ${CFLAGS} ${CPPFLAGS} -c version.c
+       ${CC} ${CFLAGS} ${OBJS} version.o ${LDFLAGS} ${IRCDLIBS} -o ircd
+       ${CHMOD} ${IRCDMODE} ircd
+
+chkcrule.o: crule.c ../include/sys.h ../include/../config/config.h \
+       ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+       ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+       ../include/whowas.h ../include/s_serv.h ../include/ircd.h \
+       ../include/match.h ../include/s_bsd.h ../include/s_conf.h \
+       ../include/list.h ../include/common.h ../include/crule.h
+       ${CC} ${CFLAGS} ${CPPFLAGS} -DCR_CHKCONF -o chkcrule.o -c crule.c
+
+chkconf: chkconf.o match.o common.o chkcrule.o runmalloc.o fileio.o
+       ${CC} ${CFLAGS} ${CPPFLAGS} \
+           chkconf.o match.o common.o chkcrule.o runmalloc.o fileio.o \
+           ${LDFLAGS} ${IRCDLIBS} -o chkconf
+
+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 `date +%y%m%d%H%M`.`cat ../.patches | \
+           ${AWK} -F . '{ if ($$(NF)~/\+$$/) { \
+               for(i=1;i<NF;i++) \
+                 printf("%s_",$$i); \
+                 gsub("\\\\+","",$$(NF)); \
+               }; \
+               print $$(NF) }'` > /tmp/ircd.tag;
+       @echo "Installing new ircd as ${BINDIR}/ircd.`cat /tmp/ircd.tag` :"
+       ${INSTALL} -m ${IRCDMODE} -o ${IRCDOWN} -g ${IRCDGRP} ircd ${BINDIR}/ircd.`cat /tmp/ircd.tag`
+       @( cd ${BINDIR}; \
+         ${RM} -f ${SYMLINK}; \
+         ${LN_S} ircd.`cat /tmp/ircd.tag` ${SYMLINK}; )
+       @${RM} /tmp/ircd.tag
+       ${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}; \
+         ${TOUCH} ${RPATH}; \
+         ${CHOWN} ${IRCDOWN} ${MPATH} ${RPATH}; \
+         ${CHGRP} ${IRCDGRP} ${MPATH} ${RPATH}; )
+
+uninstall:
+       @if [ "${BINDIR}" != "${DPATH}" ]; then \
+               echo "${RM} -f ${BINDIR}/${SYMLINK} ${BINDIR}/ircd.9* ${BINDIR}/chkconf"; \
+               ${RM} -f ${BINDIR}/${SYMLINK} ${BINDIR}/ircd.9* ${BINDIR}/chkconf; \
+       fi
+       @echo "Please remove the contents of ${DPATH} manually"
+
+clean:
+       ${RM} -f *.o ircd version.c chkconf
+
+distclean: clean
+       ${RM} -f Makefile stamp-m
+
+maintainer-clean: distclean
+
+ctables: common.c
+       ${CC} -I../include -DMAKETABLES common.c || exit 1
+       { ${GREP} -A1 -B1000 ^...NTL_TOK_START common.c ; ./a.out ; \
+         ${GREP} -A1000 -B1 ^...NTL_TOK_END common.c ; } > common.temp || exit 1;
+       ${MV} common.temp common.c
+       ${RM} a.out
+
+depend:
+       @if [ -f Makefile.in.bak ]; then \
+         echo "make depend: First remove ircd/Makefile.in.bak"; \
+       else \
+         ( ${MV} Makefile.in Makefile.in.bak; \
+           ${GREP} -A1 -B1000 '^# DO NOT DELETE THIS LINE' Makefile.in.bak > Makefile.in;\
+           ${CC} ${CFLAGS} -MM ${CPPFLAGS} ${SRC:hash.c=} >> Makefile.in; ) \
+       fi
+
+hash.o: hash.c ../include/sys.h ../config/config.h \
+ ../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/common.h ../include/match.h ../include/hash.h \
+ ../include/channel.h ../include/list.h ../include/send.h \
+ ../include/s_serv.h ../include/ircd.h \
+ ircd.c version.c.SH
+       @CC="${CC}" CFLAGS="${CFLAGS}" CPPFLAGS="${CPPFLAGS}" \
+       crypt/sums
+       ${CC} ${CFLAGS} ${CPPFLAGS} -c hash.c -o hash.o
+       @${RM} -f hash.c
+       @${MV} -f hash.c.old hash.c
+       @${TOUCH} hash.o
+
+# Coders: You need GNU make for this to work
+Makefile: ../config/config.status Makefile.in ../config/gen.ircd.Makefile \
+         ../config/config.h ../config/.config stamp-m
+       @echo "recreating ircd/Makefile"
+       @cd ../config; \
+         CONFIG_FILES=../ircd/Makefile CONFIG_HEADERS= \
+         ./config.status >/dev/null; \
+         RM=${RM} ${SHELL} ./gen.ircd.Makefile
+
+stamp-m:
+       echo timestamp > stamp-m
+
+../config/config.status:
+       @cd ../config; ${MAKE} config.status
+
+../config/config.h:
+       @cd ../config; ${MAKE} config.h
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
+IPcheck.o: IPcheck.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/IPcheck.h ../include/querycmds.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/s_user.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/numnicks.h ../include/send.h
+bsd.o: bsd.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/ircd.h ../include/bsd.h
+channel.o: channel.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/channel.h ../include/list.h \
+ ../include/parse.h ../include/send.h ../include/s_err.h \
+ ../include/numeric.h ../include/ircd.h ../include/common.h \
+ ../include/match.h ../include/hash.h ../include/s_serv.h \
+ ../include/s_misc.h ../include/s_user.h ../include/s_conf.h \
+ ../include/s_bsd.h ../include/msg.h ../include/support.h \
+ ../include/numnicks.h ../include/sprintf_irc.h ../include/querycmds.h
+class.o: class.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/class.h ../include/s_conf.h \
+ ../include/list.h ../include/s_serv.h ../include/send.h \
+ ../include/s_err.h ../include/numeric.h ../include/ircd.h
+common.o: common.c ../include/common.h ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h
+crule.o: crule.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/ircd.h \
+ ../include/match.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/common.h ../include/crule.h
+dbuf.o: dbuf.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/dbuf.h
+ircd.o: ircd.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/res.h ../include/list.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/s_serv.h ../include/send.h ../include/ircd.h \
+ ../include/s_conf.h ../include/class.h ../include/s_misc.h \
+ ../include/parse.h ../include/match.h ../include/s_bsd.h \
+ ../include/crule.h ../include/userload.h ../include/numeric.h \
+ ../include/hash.h ../include/bsd.h ../include/version.h \
+ ../include/numnicks.h
+list.o: list.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/send.h \
+ ../include/s_conf.h ../include/list.h ../include/class.h \
+ ../include/match.h ../include/ircd.h ../include/s_serv.h \
+ ../include/support.h ../include/s_misc.h ../include/s_bsd.h \
+ ../include/res.h ../include/common.h ../include/s_user.h \
+ ../include/opercmds.h
+map.o: map.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/send.h \
+ ../include/match.h ../include/list.h ../include/s_err.h \
+ ../include/ircd.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/s_misc.h ../include/map.h
+match.o: match.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/common.h ../include/match.h \
+ ../include/ircd.h
+numnicks.o: numnicks.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/s_serv.h ../include/struct.h \
+ ../include/dbuf.h ../include/whowas.h ../include/common.h \
+ ../include/numnicks.h ../include/ircd.h ../include/parse.h \
+ ../include/s_misc.h ../include/match.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/list.h
+opercmds.o: opercmds.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/opercmds.h ../include/struct.h \
+ ../include/dbuf.h ../include/whowas.h ../include/ircd.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/list.h \
+ ../include/send.h ../include/s_err.h ../include/numeric.h \
+ ../include/match.h ../include/s_misc.h ../include/class.h \
+ ../include/s_user.h ../include/common.h ../include/msg.h \
+ ../include/sprintf_irc.h ../include/userload.h ../include/parse.h \
+ ../include/numnicks.h ../include/crule.h ../include/version.h \
+ ../include/support.h ../include/s_serv.h ../include/hash.h
+packet.o: packet.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_misc.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/list.h ../include/ircd.h \
+ ../include/msg.h ../include/parse.h ../include/send.h \
+ ../include/packet.h ../include/s_serv.h
+parse.o: parse.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/send.h \
+ ../include/parse.h ../include/common.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/list.h ../include/msg.h \
+ ../include/s_user.h ../include/channel.h ../include/s_ping.h \
+ ../include/res.h ../include/map.h ../include/hash.h \
+ ../include/numeric.h ../include/ircd.h ../include/s_misc.h \
+ ../include/s_numeric.h ../include/numnicks.h ../include/opercmds.h \
+ ../include/querycmds.h ../include/whocmds.h
+querycmds.o: querycmds.c ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h ../include/h.h ../include/s_debug.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/parse.h ../include/send.h ../include/s_err.h \
+ ../include/numeric.h ../include/ircd.h ../include/s_user.h \
+ ../include/version.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/s_misc.h ../include/match.h \
+ ../include/s_serv.h ../include/msg.h ../include/channel.h \
+ ../include/numnicks.h ../include/userload.h ../include/support.h \
+ ../include/querycmds.h
+random.o: random.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h \
+ ../include/random.h
+res.o: res.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/res.h ../include/list.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/numeric.h ../include/send.h ../include/s_misc.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/ircd.h \
+ ../include/s_ping.h ../include/support.h ../include/common.h \
+ ../include/sprintf_irc.h
+runmalloc.o: runmalloc.c ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h ../include/h.h ../include/s_debug.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/send.h ../include/numeric.h ../include/s_err.h \
+ ../include/ircd.h ../include/s_serv.h ../include/numnicks.h
+s_auth.o: s_auth.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/res.h ../include/list.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/common.h ../include/send.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/s_misc.h ../include/support.h \
+ ../include/ircd.h ../include/s_auth.h ../include/sprintf_irc.h
+s_bsd.o: s_bsd.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/res.h ../include/list.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/s_serv.h \
+ ../include/numeric.h ../include/send.h ../include/s_misc.h \
+ ../include/hash.h ../include/s_err.h ../include/ircd.h \
+ ../include/support.h ../include/s_auth.h ../include/class.h \
+ ../include/packet.h ../include/s_ping.h ../include/channel.h \
+ ../include/version.h ../include/parse.h ../include/common.h \
+ ../include/bsd.h ../include/numnicks.h ../include/s_user.h \
+ ../include/sprintf_irc.h ../include/querycmds.h ../include/IPcheck.h
+s_conf.o: s_conf.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/opercmds.h \
+ ../include/numeric.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/class.h ../include/s_misc.h \
+ ../include/match.h ../include/common.h ../include/s_err.h \
+ ../include/s_bsd.h ../include/ircd.h ../include/crule.h \
+ ../include/res.h ../include/support.h ../include/parse.h \
+ ../include/numnicks.h ../include/sprintf_irc.h ../include/IPcheck.h \
+ ../include/hash.h
+s_debug.o: s_debug.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/numeric.h ../include/hash.h \
+ ../include/s_serv.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/class.h ../include/ircd.h \
+ ../include/s_bsd.h ../include/bsd.h ../include/res.h \
+ ../include/channel.h ../include/numnicks.h
+s_err.o: s_err.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/numeric.h ../include/s_err.h \
+ ../include/sprintf_irc.h
+s_misc.o: s_misc.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_serv.h ../include/numeric.h \
+ ../include/send.h ../include/s_conf.h ../include/list.h \
+ ../include/s_misc.h ../include/common.h ../include/match.h \
+ ../include/hash.h ../include/s_bsd.h ../include/res.h \
+ ../include/ircd.h ../include/s_ping.h ../include/channel.h \
+ ../include/s_err.h ../include/support.h ../include/userload.h \
+ ../include/parse.h ../include/s_user.h ../include/numnicks.h \
+ ../include/sprintf_irc.h ../include/querycmds.h ../include/IPcheck.h
+s_numeric.o: s_numeric.c ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h ../include/h.h ../include/s_debug.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/s_serv.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/send.h ../include/support.h \
+ ../include/parse.h ../include/numeric.h ../include/channel.h \
+ ../include/ircd.h ../include/hash.h ../include/numnicks.h \
+ ../include/s_numeric.h
+s_ping.o: s_ping.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/match.h ../include/res.h \
+ ../include/s_bsd.h ../include/s_serv.h ../include/ircd.h \
+ ../include/s_ping.h ../include/support.h ../include/numeric.h \
+ ../include/s_user.h ../include/s_err.h ../include/common.h \
+ ../include/numnicks.h
+s_serv.o: s_serv.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/ircd.h ../include/s_serv.h \
+ ../include/s_misc.h ../include/sprintf_irc.h ../include/send.h \
+ ../include/s_err.h ../include/numeric.h ../include/s_bsd.h \
+ ../include/s_conf.h ../include/list.h ../include/hash.h \
+ ../include/common.h ../include/match.h ../include/crule.h \
+ ../include/parse.h ../include/numnicks.h ../include/userload.h \
+ ../include/s_user.h ../include/channel.h ../include/querycmds.h \
+ ../include/IPcheck.h
+s_user.o: s_user.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/common.h ../include/s_serv.h \
+ ../include/numeric.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/s_misc.h ../include/match.h \
+ ../include/hash.h ../include/s_bsd.h ../include/s_err.h \
+ ../include/parse.h ../include/ircd.h ../include/s_user.h \
+ ../include/support.h ../include/channel.h ../include/random.h \
+ ../include/version.h ../include/msg.h ../include/userload.h \
+ ../include/numnicks.h ../include/sprintf_irc.h ../include/querycmds.h \
+ ../include/IPcheck.h
+send.o: send.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/s_bsd.h ../include/s_conf.h \
+ ../include/list.h ../include/s_serv.h ../include/send.h \
+ ../include/s_misc.h ../include/common.h ../include/match.h \
+ ../include/ircd.h ../include/channel.h ../include/bsd.h \
+ ../include/class.h ../include/s_user.h ../include/sprintf_irc.h
+sprintf_irc.o: sprintf_irc.c ../include/sys.h \
+ ../include/../config/config.h ../include/../config/setup.h \
+ ../include/runmalloc.h ../include/h.h ../include/s_debug.h \
+ ../include/sprintf_irc.h
+support.o: support.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/send.h ../include/ircd.h \
+ ../include/s_bsd.h ../include/s_conf.h ../include/list.h \
+ ../include/support.h ../include/sprintf_irc.h ../include/common.h
+userload.o: userload.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/send.h ../include/s_misc.h \
+ ../include/userload.h ../include/ircd.h ../include/numnicks.h \
+ ../include/s_serv.h ../include/querycmds.h
+whocmds.o: whocmds.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h ../include/h.h \
+ ../include/s_debug.h ../include/struct.h ../include/dbuf.h \
+ ../include/whowas.h ../include/common.h ../include/s_serv.h \
+ ../include/numeric.h ../include/send.h ../include/s_conf.h \
+ ../include/list.h ../include/s_misc.h ../include/match.h \
+ ../include/hash.h ../include/s_bsd.h ../include/s_err.h \
+ ../include/parse.h ../include/ircd.h ../include/s_user.h \
+ ../include/support.h ../include/channel.h ../include/random.h \
+ ../include/version.h ../include/msg.h ../include/userload.h \
+ ../include/numnicks.h ../include/sprintf_irc.h ../include/querycmds.h \
+ ../include/IPcheck.h
+whowas.o: whowas.c ../include/sys.h ../include/../config/config.h \
+ ../include/../config/setup.h ../include/runmalloc.h \
+ ../include/common.h ../include/h.h ../include/s_debug.h \
+ ../include/struct.h ../include/dbuf.h ../include/whowas.h \
+ ../include/numeric.h ../include/send.h ../include/s_misc.h \
+ ../include/s_err.h ../include/ircd.h ../include/list.h \
+ ../include/s_user.h ../include/support.h
diff --git a/ircd/bsd.c b/ircd/bsd.c
new file mode 100644 (file)
index 0000000..89467d0
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * 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 "sys.h"
+#include <signal.h>
+#include <sys/socket.h>                /* Needed for send() */
+#include "h.h"
+#include "struct.h"
+#include "s_bsd.h"
+#include "ircd.h"
+#include "bsd.h"
+
+RCSTAG_CC("$Id$");
+
+#ifdef DEBUGMODE
+int writecalls = 0;
+int writeb[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+RETSIGTYPE dummy(HANDLER_ARG(int UNUSED(sig)))
+{
+#ifndef HAVE_RELIABLE_SIGNALS
+  signal(SIGALRM, dummy);
+  signal(SIGPIPE, dummy);
+#ifndef HPUX                   /* Only 9k/800 series require this,
+                                  but don't know how to.. */
+#ifdef SIGWINCH
+  signal(SIGWINCH, dummy);
+#endif
+#endif
+#else
+#ifdef POSIX_SIGNALS
+  struct sigaction act;
+
+  act.sa_handler = dummy;
+  act.sa_flags = 0;
+  sigemptyset(&act.sa_mask);
+  sigaddset(&act.sa_mask, SIGALRM);
+  sigaddset(&act.sa_mask, SIGPIPE);
+#ifdef SIGWINCH
+  sigaddset(&act.sa_mask, SIGWINCH);
+#endif
+  sigaction(SIGALRM, &act, (struct sigaction *)NULL);
+  sigaction(SIGPIPE, &act, (struct sigaction *)NULL);
+#ifdef SIGWINCH
+  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...
+ *
+ *   We don't use blocking anymore, that is impossible with the
+ *      net.loads today anyway. Commented out the alarms to save cpu.
+ *      --Run
+ */
+int deliver_it(aClient *cptr, const char *str, int len)
+{
+  int retval;
+  aClient *acpt = cptr->acpt;
+
+#ifdef DEBUGMODE
+  writecalls++;
+#endif
+#ifdef VMS
+  retval = netwrite(cptr->fd, str, len);
+#else
+  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 ||
+#ifdef SOL2
+      errno == ENOMEM || errno == ENOSR ||
+#endif
+      errno == ENOBUFS))
+  {
+    retval = 0;
+    cptr->flags |= FLAGS_BLOCKED;
+  }
+  else if (retval > 0)
+  {
+#ifdef pyr
+    gettimeofday(&cptr->lw, NULL);
+#endif
+    cptr->flags &= ~FLAGS_BLOCKED;
+  }
+
+#endif
+#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/ircd/channel.c b/ircd/channel.c
new file mode 100644 (file)
index 0000000..c917cdc
--- /dev/null
@@ -0,0 +1,4506 @@
+/*
+ * 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 "sys.h"
+#include <stdlib.h>
+#include "h.h"
+#include "struct.h"
+#include "channel.h"
+#include "parse.h"
+#include "whowas.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "ircd.h"
+#include "common.h"
+#include "match.h"
+#include "list.h"
+#include "hash.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "s_conf.h"
+#include "s_bsd.h"
+#include "msg.h"
+#include "common.h"
+#include "s_serv.h"
+#include "channel.h"
+#include "support.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+
+RCSTAG_CC("$Id$");
+
+aChannel *channel = NullChn;
+
+static void sendmodeto_one(aClient *cptr, char *from, char *name,
+    char *mode, char *param, time_t creationtime);
+static void add_invite(aClient *, aChannel *);
+static int add_banid(aClient *, aChannel *, char *, int, int);
+static Link *next_overlapped_ban(void);
+static Link *next_removed_overlapped_ban(void);
+static int can_join(aClient *, aChannel *, char *);
+static void channel_modes(aClient *, char *, char *, aChannel *);
+static int del_banid(aChannel *, char *, int);
+static int is_banned(aClient *, aChannel *, Link *);
+static int number_of_zombies(aChannel *);
+static int is_deopped(aClient *, aChannel *);
+static int set_mode(aClient *, aClient *, aChannel *, int,
+    char **, char *, char *, char *, int *);
+static void sub1_from_channel(aChannel *);
+static void send_hack_notice(aClient *, aClient *, int, char *[], int, int);
+static void clean_channelname(char *);
+
+void del_invite(aClient *, aChannel *);
+
+static char *PartFmt1 = ":%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];
+static char nparabuf[MODEBUFLEN];
+
+/*
+ * Maximum acceptable lag time in seconds: A channel younger than
+ * this is not protected against hacking admins.
+ * Mainly here to check if the TS clocks really sync (otherwise this
+ * will start causing HACK notices.
+ * This value must be the same on all servers.
+ *
+ * This value has been increased to 1 day in order to distinguish this
+ * "normal" type of HACK wallops / desyncs, from possiblity still
+ * existing bugs.
+ */
+#define TS_LAG_TIME ((time_t)86400)
+
+/*
+ * A Magic TS that is used for channels that are created by JOIN,
+ * a channel with this TS accepts all TS without complaining that
+ * it might receive later via MODE or CREATE.
+ */
+#define MAGIC_REMOTE_JOIN_TS 1270080000
+
+/*
+ * return the length (>=0) of a chain of links.
+ */
+static int list_length(Link *lp)
+{
+  Reg2 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(aClient *sptr, char *user, int *chasing)
+{
+  Reg2 aClient *who = FindClient(user);
+
+  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;
+}
+
+/*
+ * Create a string of form "foo!bar@fubar" given foo, bar and fubar
+ * as the parameters.  If NULL, they become "*".
+ */
+static char *make_nick_user_host(char *nick, char *name, char *host)
+{
+  static char namebuf[NICKLEN + USERLEN + HOSTLEN + 3];
+  sprintf_irc(namebuf, "%s!%s@%s", nick, name, host);
+  return namebuf;
+}
+
+/*
+ * Create a string of form "foo!bar@123.456.789.123" given foo, bar and the
+ * IP-number as the parameters.  If NULL, they become "*".
+ */
+static char *make_nick_user_ip(char *nick, char *name, struct in_addr ip)
+{
+  static char ipbuf[NICKLEN + USERLEN + 16 + 3];
+  sprintf_irc(ipbuf, "%s!%s@%s", nick, name, inetntoa(ip));
+  return ipbuf;
+}
+
+/*
+ * add_banid
+ *
+ * `cptr' must be the client adding the ban.
+ *
+ * If `change' is true then add `banid' to channel `chptr'.
+ * Returns 0 if the ban was added.
+ * Returns -2 if the ban already existed and was marked CHFL_BURST_BAN_WIPEOUT.
+ * Return -1 otherwise.
+ *
+ * Those bans that overlapped with `banid' are flagged with CHFL_BAN_OVERLAPPED
+ * when `change' is false, otherwise they will be removed from the banlist.
+ * Subsequently calls to next_overlapped_ban() or next_removed_overlapped_ban()
+ * respectively will return these bans until NULL is returned.
+ *
+ * If `firsttime' is true, the ban list as returned by next_overlapped_ban()
+ * is reset (unless a non-zero value is returned, in which case the
+ * CHFL_BAN_OVERLAPPED flag might not have been reset!).
+ *
+ * --Run
+ */
+static Link *next_ban, *prev_ban, *removed_bans_list;
+
+static int add_banid(aClient *cptr, aChannel *chptr, char *banid,
+    int change, int firsttime)
+{
+  Reg1 Link *ban, **banp;
+  Reg2 int cnt = 0, removed_bans = 0, len = strlen(banid);
+
+  if (firsttime)
+  {
+    next_ban = NULL;
+    if (prev_ban || removed_bans_list)
+      MyCoreDump;              /* Memory leak */
+  }
+  if (MyUser(cptr))
+    collapse(banid);
+  for (banp = &chptr->banlist; *banp;)
+  {
+    len += strlen((*banp)->value.ban.banstr);
+    ++cnt;
+    if (((*banp)->flags & CHFL_BURST_BAN_WIPEOUT))
+    {
+      if (!strcmp((*banp)->value.ban.banstr, banid))
+      {
+       (*banp)->flags &= ~CHFL_BURST_BAN_WIPEOUT;
+       return -2;
+      }
+    }
+    else if (!mmatch((*banp)->value.ban.banstr, banid))
+      return -1;
+    if (!mmatch(banid, (*banp)->value.ban.banstr))
+    {
+      Link *tmp = *banp;
+      if (change)
+      {
+       if (MyUser(cptr))
+       {
+         cnt--;
+         len -= strlen(tmp->value.ban.banstr);
+       }
+       *banp = tmp->next;
+#if 0
+       /* Silently remove overlapping bans */
+       RunFree(tmp->value.ban.banstr);
+       RunFree(tmp->value.ban.who);
+       free_link(tmp);
+#else
+       /* These will be sent to the user later as -b */
+       tmp->next = removed_bans_list;
+       removed_bans_list = tmp;
+       removed_bans = 1;
+#endif
+      }
+      else if (!(tmp->flags & CHFL_BURST_BAN_WIPEOUT))
+      {
+       tmp->flags |= CHFL_BAN_OVERLAPPED;
+       if (!next_ban)
+         next_ban = tmp;
+       banp = &tmp->next;
+      }
+      else
+       banp = &tmp->next;
+    }
+    else
+    {
+      if (firsttime)
+       (*banp)->flags &= ~CHFL_BAN_OVERLAPPED;
+      banp = &(*banp)->next;
+    }
+  }
+  if (MyUser(cptr) && !removed_bans && (len > MAXBANLENGTH || (cnt >= MAXBANS)))
+  {
+    sendto_one(cptr, err_str(ERR_BANLISTFULL), me.name, cptr->name,
+       chptr->chname, banid);
+    return -1;
+  }
+  if (change)
+  {
+    char *ip_start;
+    ban = make_link();
+    ban->next = chptr->banlist;
+    ban->value.ban.banstr = (char *)RunMalloc(strlen(banid) + 1);
+    strcpy(ban->value.ban.banstr, banid);
+    ban->value.ban.who = (char *)RunMalloc(strlen(cptr->name) + 1);
+    strcpy(ban->value.ban.who, cptr->name);
+    ban->value.ban.when = now;
+    ban->flags = CHFL_BAN;     /* This bit is never used I think... */
+    if ((ip_start = strrchr(banid, '@')) && check_if_ipmask(ip_start + 1))
+      ban->flags |= CHFL_BAN_IPMASK;
+    chptr->banlist = ban;
+    /* Erase ban-valid-bit */
+    for (ban = chptr->members; ban; ban = ban->next)
+      ban->flags &= ~CHFL_BANVALID;    /* `ban' == channel member ! */
+  }
+  return 0;
+}
+
+static Link *next_overlapped_ban(void)
+{
+  Reg1 Link *tmp = next_ban;
+  if (tmp)
+  {
+    Reg2 Link *ban;
+    for (ban = tmp->next; ban; ban = ban->next)
+      if ((ban->flags & CHFL_BAN_OVERLAPPED))
+       break;
+    next_ban = ban;
+  }
+  return tmp;
+}
+
+static Link *next_removed_overlapped_ban(void)
+{
+  Reg1 Link *tmp = removed_bans_list;
+  if (prev_ban)
+  {
+    if (prev_ban->value.ban.banstr)    /* Can be set to NULL in set_mode() */
+      RunFree(prev_ban->value.ban.banstr);
+    RunFree(prev_ban->value.ban.who);
+    free_link(prev_ban);
+  }
+  if (tmp)
+    removed_bans_list = removed_bans_list->next;
+  prev_ban = tmp;
+  return tmp;
+}
+
+/*
+ * del_banid
+ *
+ * If `change' is true, delete `banid' from channel `chptr'.
+ * Returns `false' if removal was (or would have been) successful.
+ */
+static int del_banid(aChannel *chptr, char *banid, int change)
+{
+  Reg1 Link **ban;
+  Reg2 Link *tmp;
+
+  if (!banid)
+    return -1;
+  for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next))
+    if (strCasediff(banid, (*ban)->value.ban.banstr) == 0)
+    {
+      tmp = *ban;
+      if (change)
+      {
+       *ban = tmp->next;
+       RunFree(tmp->value.ban.banstr);
+       RunFree(tmp->value.ban.who);
+       free_link(tmp);
+       /* Erase ban-valid-bit, for channel members that are banned */
+       for (tmp = chptr->members; tmp; tmp = tmp->next)
+         if ((tmp->flags & (CHFL_BANNED | CHFL_BANVALID)) ==
+             (CHFL_BANNED | CHFL_BANVALID))
+           tmp->flags &= ~CHFL_BANVALID;       /* `tmp' == channel member */
+      }
+      return 0;
+    }
+  return -1;
+}
+
+/*
+ * IsMember - returns Link * if a person is joined and not a zombie
+ */
+Link *IsMember(aClient *cptr, aChannel *chptr)
+{
+  Link *lp;
+  return (((lp = find_user_link(chptr->members, cptr)) &&
+      !(lp->flags & CHFL_ZOMBIE)) ? lp : NULL);
+}
+
+/*
+ * is_banned - a non-zero value if banned else 0.
+ */
+static int is_banned(aClient *cptr, aChannel *chptr, Link *member)
+{
+  Reg1 Link *tmp;
+  char *s, *ip_s = NULL;
+
+  if (!IsUser(cptr))
+    return 0;
+
+  if (member)
+  {
+    if ((member->flags & CHFL_BANVALID))
+      return (member->flags & CHFL_BANNED);
+  }
+
+  s = make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host);
+
+  for (tmp = chptr->banlist; tmp; tmp = tmp->next)
+  {
+    if ((tmp->flags & CHFL_BAN_IPMASK))
+    {
+      if (!ip_s)
+       ip_s = make_nick_user_ip(cptr->name, cptr->user->username, cptr->ip);
+      if (match(tmp->value.ban.banstr, ip_s) == 0)
+       break;
+    }
+    else if (match(tmp->value.ban.banstr, s) == 0)
+      break;
+  }
+
+  if (member)
+  {
+    member->flags |= CHFL_BANVALID;
+    if (tmp)
+    {
+      member->flags |= CHFL_BANNED;
+      return 1;
+    }
+    else
+    {
+      member->flags &= ~CHFL_BANNED;
+      return 0;
+    }
+  }
+
+  return (tmp != NULL);
+}
+
+/*
+ * adds a user to a channel by adding another link to the channels member
+ * chain.
+ */
+static void add_user_to_channel(aChannel *chptr, aClient *who, int flags)
+{
+  Reg1 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(aClient *sptr, aChannel *chptr)
+{
+  Reg1 Link **curr;
+  Reg2 Link *tmp;
+  Reg3 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);
+}
+
+int is_chan_op(aClient *cptr, aChannel *chptr)
+{
+  Reg1 Link *lp;
+
+  if (chptr)
+    if ((lp = find_user_link(chptr->members, cptr)) &&
+       !(lp->flags & CHFL_ZOMBIE))
+      return (lp->flags & CHFL_CHANOP);
+
+  return 0;
+}
+
+static int is_deopped(aClient *cptr, aChannel *chptr)
+{
+  Reg1 Link *lp;
+
+  if (chptr)
+    if ((lp = find_user_link(chptr->members, cptr)))
+      return (lp->flags & CHFL_DEOPPED);
+
+  return (IsUser(cptr) ? 1 : 0);
+}
+
+int is_zombie(aClient *cptr, aChannel *chptr)
+{
+  Reg1 Link *lp;
+
+  if (chptr)
+    if ((lp = find_user_link(chptr->members, cptr)))
+      return (lp->flags & CHFL_ZOMBIE);
+
+  return 0;
+}
+
+int has_voice(aClient *cptr, aChannel *chptr)
+{
+  Reg1 Link *lp;
+
+  if (chptr)
+    if ((lp = find_user_link(chptr->members, cptr)) &&
+       !(lp->flags & CHFL_ZOMBIE))
+      return (lp->flags & CHFL_VOICE);
+
+  return 0;
+}
+
+int can_send(aClient *cptr, aChannel *chptr)
+{
+  Reg1 Link *lp;
+
+  lp = IsMember(cptr, chptr);
+
+  if ((!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE)) ||
+      (lp->flags & CHFL_ZOMBIE)) && MyUser(cptr) && is_banned(cptr, chptr, lp))
+    return (MODE_BAN);
+
+  if (chptr->mode.mode & MODE_MODERATED &&
+      (!lp || !(lp->flags & (CHFL_CHANOP | CHFL_VOICE)) ||
+      (lp->flags & CHFL_ZOMBIE)))
+    return (MODE_MODERATED);
+
+  if (!lp && ((chptr->mode.mode & MODE_NOPRIVMSGS) ||
+      IsModelessChannel(chptr->chname)))
+    return (MODE_NOPRIVMSGS);
+
+  return 0;
+}
+
+/*
+ * write the "simple" list of channel modes for channel chptr onto buffer mbuf
+ * with the parameters in pbuf.
+ */
+static void channel_modes(aClient *cptr, char *mbuf, char *pbuf,
+    aChannel *chptr)
+{
+  *mbuf++ = '+';
+  if (chptr->mode.mode & MODE_SECRET)
+    *mbuf++ = 's';
+  else if (chptr->mode.mode & MODE_PRIVATE)
+    *mbuf++ = 'p';
+  if (chptr->mode.mode & MODE_MODERATED)
+    *mbuf++ = 'm';
+  if (chptr->mode.mode & MODE_TOPICLIMIT)
+    *mbuf++ = 't';
+  if (chptr->mode.mode & MODE_INVITEONLY)
+    *mbuf++ = 'i';
+  if (chptr->mode.mode & MODE_NOPRIVMSGS)
+    *mbuf++ = 'n';
+  if (chptr->mode.limit)
+  {
+    *mbuf++ = 'l';
+    sprintf_irc(pbuf, "%d", chptr->mode.limit);
+  }
+  if (*chptr->mode.key)
+  {
+    *mbuf++ = 'k';
+    if (is_chan_op(cptr, chptr) || IsServer(cptr))
+    {
+      if (chptr->mode.limit)
+       strcat(pbuf, " ");
+      strcat(pbuf, chptr->mode.key);
+    }
+  }
+  *mbuf = '\0';
+  return;
+}
+
+static int send_mode_list(aClient *cptr, char *chname, time_t creationtime,
+    Link *top, int mask, char flag)
+{
+  Reg1 Link *lp;
+  Reg2 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)
+  {
+    if (!(lp->flags & mask))
+      continue;
+    if (mask == CHFL_BAN)
+      name = lp->value.ban.banstr;
+    else
+      name = lp->value.cptr->name;
+    if (strlen(parabuf) + strlen(name) + 11 < (size_t)MODEBUFLEN)
+    {
+      strcat(parabuf, " ");
+      strcat(parabuf, name);
+      count++;
+      *cp++ = flag;
+      *cp = '\0';
+    }
+    else if (*parabuf)
+      send = 1;
+    if (count == 6)
+      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 != 6)
+      {
+       strcpy(parabuf, name);
+       *cp++ = flag;
+      }
+      count = 0;
+      *cp = '\0';
+    }
+  }
+  return sent;
+}
+
+/*
+ * send "cptr" a full list of the modes for channel chptr.
+ */
+void send_channel_modes(aClient *cptr, aChannel *chptr)
+{
+  int sent;
+  if (IsLocalChannel(chptr->chname))
+    return;
+
+  *modebuf = *parabuf = '\0';
+  channel_modes(cptr, modebuf, parabuf, chptr);
+
+  if (Protocol(cptr) < 10)
+  {
+    sent = send_mode_list(cptr, chptr->chname, chptr->creationtime,
+       chptr->members, CHFL_CHANOP, 'o');
+    if (!sent && chptr->creationtime)
+      sendto_one(cptr, ":%s MODE %s %s %s " TIME_T_FMT, me.name,
+         chptr->chname, modebuf, parabuf, chptr->creationtime);
+    else if (modebuf[1] || *parabuf)
+      sendmodeto_one(cptr, me.name,
+         chptr->chname, modebuf, parabuf, chptr->creationtime);
+
+    *parabuf = '\0';
+    *modebuf = '+';
+    modebuf[1] = '\0';
+    send_mode_list(cptr, chptr->chname, chptr->creationtime,
+       chptr->banlist, CHFL_BAN, 'b');
+    if (modebuf[1] || *parabuf)
+      sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
+         parabuf, chptr->creationtime);
+
+    *parabuf = '\0';
+    *modebuf = '+';
+    modebuf[1] = '\0';
+    send_mode_list(cptr, chptr->chname, chptr->creationtime,
+       chptr->members, CHFL_VOICE, 'v');
+    if (modebuf[1] || *parabuf)
+      sendmodeto_one(cptr, me.name, chptr->chname, modebuf,
+         parabuf, chptr->creationtime);
+  }
+  else
+  {
+    static unsigned int current_flags[4] =
+       { 0, CHFL_CHANOP | CHFL_VOICE, CHFL_VOICE, CHFL_CHANOP };
+    int first = 1, full = 1, flag_cnt = 0, new_mode = 0;
+    size_t len, sblen;
+    Link *lp1 = chptr->members;
+    Link *lp2 = chptr->banlist;
+    for (first = 1; full; first = 0)   /* Loop for multiple messages */
+    {
+      full = 0;                        /* Assume by default we get it
+                                  all in one message */
+
+      /* (Continued) prefix: "<Y> BURST <channel> <TS>" */
+      sprintf_irc(sendbuf, "%s BURST %s " TIME_T_FMT, NumServ(&me),
+         chptr->chname, chptr->creationtime);
+      sblen = strlen(sendbuf);
+
+      if (first && modebuf[1]) /* Add simple modes (iklmnpst)
+                                  if first message */
+      {
+       /* prefix: "<Y> BURST <channel> <TS>[ <modes>[ <params>]]" */
+       sendbuf[sblen++] = ' ';
+       strcpy(sendbuf + sblen, modebuf);
+       sblen += strlen(modebuf);
+       if (*parabuf)
+       {
+         sendbuf[sblen++] = ' ';
+         strcpy(sendbuf + sblen, parabuf);
+         sblen += strlen(parabuf);
+       }
+      }
+
+      /* Attach nicks, comma seperated " nick[:modes],nick[:modes],..." */
+      /* Run 4 times over all members, to group the members with the
+       * same mode together */
+      for (first = 1; flag_cnt < 4;
+         lp1 = chptr->members, new_mode = 1, flag_cnt++)
+      {
+       for (; lp1; lp1 = lp1->next)
+       {
+         if ((lp1->flags & (CHFL_CHANOP | CHFL_VOICE)) !=
+             current_flags[flag_cnt])
+           continue;           /* Skip members with different flags */
+         if (sblen + NUMNICKLEN + 4 > BUFSIZE - 3)
+           /* The 4 is a possible ",:ov"
+              The -3 is for the "\r\n\0" that is added in send.c */
+         {
+           full = 1;           /* Make sure we continue after
+                                  sending it so far */
+           break;              /* Do not add this member to this message */
+         }
+         sendbuf[sblen++] = first ? ' ' : ',';
+         first = 0;            /* From now on, us comma's to add new nicks */
+
+         sprintf_irc(sendbuf + sblen, "%s%s", NumNick(lp1->value.cptr));
+         sblen += strlen(sendbuf + sblen);
+
+         if (new_mode)         /* Do we have a nick with a new mode ? */
+         {
+           new_mode = 0;
+           sendbuf[sblen++] = ':';
+           if (lp1->flags & CHFL_CHANOP)
+             sendbuf[sblen++] = 'o';
+           if (lp1->flags & CHFL_VOICE)
+             sendbuf[sblen++] = 'v';
+         }
+       }
+       if (full)
+         break;
+      }
+
+      if (!full)
+      {
+       /* Attach all bans, space seperated " :%ban ban ..." */
+       for (first = 2; lp2; lp2 = lp2->next)
+       {
+         len = strlen(lp2->value.ban.banstr);
+         if (sblen + len + 1 + first > BUFSIZE - 3)
+           /* The +1 stands for the added ' '.
+            * The +first stands for the added ":%".
+            * The -3 is for the "\r\n\0" that is added in send.c
+            */
+         {
+           full = 1;
+           break;
+         }
+         if (first)
+         {
+           first = 0;
+           sendbuf[sblen++] = ' ';
+           sendbuf[sblen++] = ':';     /* Will be last parameter */
+           sendbuf[sblen++] = '%';     /* To tell bans apart */
+         }
+         else
+           sendbuf[sblen++] = ' ';
+         strcpy(sendbuf + sblen, lp2->value.ban.banstr);
+         sblen += len;
+       }
+      }
+
+      sendbuf[sblen] = '\0';
+      sendbufto_one(cptr);     /* Send this message */
+    }                          /* Continue when there was something
+                                  that didn't fit (full==1) */
+  }
+}
+
+/*
+ * m_mode
+ * parv[0] - sender
+ * parv[1] - channel
+ */
+
+int m_mode(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  int badop, sendts;
+  aChannel *chptr;
+
+  /* Now, try to find the channel in question */
+  if (parc > 1)
+  {
+    chptr = FindChannel(parv[1]);
+    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 (MyUser(sptr))
+    clean_channelname(parv[1]);
+  else if (IsLocalChannel(parv[1]))
+    return 0;
+
+  /* sending an error wasnt good, lets just send an empty mode reply..  poptix */
+  if (IsModelessChannel(chptr->chname))
+  {
+    if (IsUser(sptr))
+      sendto_one(sptr, rpl_str(RPL_CHANNELMODEIS), me.name, parv[0],
+         chptr->chname, "+nt", "");
+    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;
+  }
+
+  if (!(sendts = set_mode(cptr, sptr, chptr, parc - 2, parv + 2,
+      modebuf, parabuf, nparabuf, &badop)))
+  {
+    sendto_one(sptr, err_str(IsMember(sptr, chptr) ? ERR_CHANOPRIVSNEEDED :
+       ERR_NOTONCHANNEL), me.name, parv[0], chptr->chname);
+    return 0;
+  }
+
+  if (badop >= 2)
+    send_hack_notice(cptr, sptr, parc, parv, 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",
+         parv[0], chptr->chname, modebuf, parabuf);
+    if (IsLocalChannel(chptr->chname))
+      return 0;
+    /* We send a creationtime of 0, to mark it as a hack --Run */
+    if (IsServer(sptr) && (badop == 2 || sendts > 0))
+    {
+      if (*modebuf == '\0')
+       strcpy(modebuf, "+");
+      if (badop != 2)
+      {
+       sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s %s " TIME_T_FMT,
+           parv[0], chptr->chname, modebuf, parabuf,
+           (badop == 4) ? (time_t) 0 : chptr->creationtime);
+       sendto_highprot_butone(cptr, 10, ":%s MODE %s %s %s " TIME_T_FMT,
+           parv[0], chptr->chname, modebuf, nparabuf,
+           (badop == 4) ? (time_t) 0 : chptr->creationtime);
+      }
+    }
+    else
+    {
+      sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s %s",
+         parv[0], chptr->chname, modebuf, parabuf);
+      sendto_highprot_butone(cptr, 10, ":%s MODE %s %s %s",
+         parv[0], chptr->chname, modebuf, nparabuf);
+    }
+  }
+  return 0;
+}
+
+static int DoesOp(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;
+}
+
+/* This function should be removed when all servers are 2.10 */
+static void sendmodeto_one(aClient *cptr, char *from, char *name,
+    char *mode, char *param, time_t creationtime)
+{
+  if (IsServer(cptr) && DoesOp(mode) && creationtime)
+    sendto_one(cptr, ":%s MODE %s %s %s " TIME_T_FMT,
+       from, name, mode, param, creationtime);
+  else
+    sendto_one(cptr, ":%s MODE %s %s %s", from, name, mode, param);
+}
+
+/*
+ * pretty_mask
+ *
+ * by Carlo Wood (Run), 05 Oct 1998.
+ *
+ * Canonify a mask.
+ *
+ * When the nick is longer then NICKLEN, it is cut off (its an error of course).
+ * When the user name or host name are too long (USERLEN and HOSTLEN
+ * respectively) then they are cut off at the start with a '*'.
+ *
+ * The following transformations are made:
+ *
+ * 1)   xxx             -> nick!*@*
+ * 2)   xxx.xxx         -> *!*@host
+ * 3)   xxx!yyy         -> nick!user@*
+ * 4)   xxx@yyy         -> *!user@host
+ * 5)   xxx!yyy@zzz     -> nick!user@host
+ */
+char *pretty_mask(char *mask)
+{
+  static char star[2] = { '*', 0 };
+  char *last_dot = NULL;
+  char *ptr;
+
+  /* Case 1: default */
+  char *nick = mask;
+  char *user = star;
+  char *host = star;
+
+  /* Do a _single_ pass through the characters of the mask: */
+  for (ptr = mask; *ptr; ++ptr)
+  {
+    if (*ptr == '!')
+    {
+      /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
+      user = ++ptr;
+      host = star;
+    }
+    else if (*ptr == '@')
+    {
+      /* Case 4: Found last '@' (without finding a '!' yet) */
+      nick = star;
+      user = mask;
+      host = ++ptr;
+    }
+    else if (*ptr == '.')
+    {
+      /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
+      last_dot = ptr;
+      continue;
+    }
+    else
+      continue;
+    for (; *ptr; ++ptr)
+    {
+      if (*ptr == '@')
+      {
+       /* Case 4 or 5: Found last '@' */
+       host = ptr + 1;
+      }
+    }
+    break;
+  }
+  if (user == star && last_dot)
+  {
+    /* Case 2: */
+    nick = star;
+    user = star;
+    host = mask;
+  }
+  /* Check lengths */
+  if (nick != star)
+  {
+    char *nick_end = (user != star) ? user - 1 : ptr;
+    if (nick_end - nick > NICKLEN)
+      nick[NICKLEN] = 0;
+    *nick_end = 0;
+  }
+  if (user != star)
+  {
+    char *user_end = (host != star) ? host - 1 : ptr;
+    if (user_end - user > USERLEN)
+    {
+      user = user_end - USERLEN;
+      *user = '*';
+    }
+    *user_end = 0;
+  }
+  if (host != star && ptr - host > HOSTLEN)
+  {
+    host = ptr - HOSTLEN;
+    *host = '*';
+  }
+  return make_nick_user_host(nick, user, host);
+}
+
+static char bmodebuf[MODEBUFLEN], bparambuf[MODEBUFLEN];
+static char nbparambuf[MODEBUFLEN];    /* "Numeric" Bounce Parameter Buffer */
+
+/*
+ * 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(aClient *cptr, aClient *sptr, aChannel *chptr, int parc,
+    char *parv[], char *mbuf, char *pbuf, char *npbuf, int *badop)
+{
+  static Link chops[MAXPARA - 2];      /* This size is only needed when a broken
+                                          server sends more then MAXMODEPARAMS
+                                          parameters */
+  static int flags[] = {
+    MODE_PRIVATE, 'p', MODE_SECRET, 's',
+    MODE_MODERATED, 'm', MODE_NOPRIVMSGS, 'n',
+    MODE_TOPICLIMIT, 't', MODE_INVITEONLY, 'i',
+    MODE_VOICE, 'v', MODE_KEY, 'k',
+    0x0, 0x0
+  };
+
+  Reg1 Link *lp;
+  Reg2 char *curr = parv[0], *cp = NULL;
+  Reg3 int *ip;
+  Link *member, *tmp = NULL;
+  unsigned int whatt = MODE_ADD, bwhatt = 0;
+  int limitset = 0, bounce, add_banid_called = 0;
+  size_t len, nlen, blen, nblen;
+  int keychange = 0;
+  unsigned int nusers = 0, newmode;
+  int opcnt = 0, banlsent = 0;
+  int doesdeop = 0, doesop = 0, hacknotice = 0, change, gotts = 0;
+  aClient *who;
+  Mode *mode, oldm;
+  static char numeric[16];
+  char *bmbuf = bmodebuf, *bpbuf = bparambuf, *nbpbuf = nbparambuf;
+  time_t newtime = (time_t) 0;
+  aConfItem *aconf;
+
+  *mbuf = *pbuf = *npbuf = *bmbuf = *bpbuf = *nbpbuf = '\0';
+  *badop = 0;
+  if (parc < 1)
+    return 0;
+
+  mode = &(chptr->mode);
+  memcpy(&oldm, mode, sizeof(Mode));
+  /*
+   * Mode is accepted when sptr is a channel operator
+   * but also when the mode is received from a server.
+   * At this point, let any member pass, so they are allowed
+   * to see the bans.
+   */
+  if (!(IsServer(cptr) || (tmp = IsMember(sptr, chptr))))
+    return 0;
+
+  newmode = 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++;
+       if (MyUser(sptr) && opcnt >= MAXMODEPARAMS)
+         break;
+       /*
+        * Check for nickname changes and try to follow these
+        * to make sure the right client is affected by the
+        * mode change.
+        * Even if we find a nick with find_chasing() there
+        * is still a reason to ignore in a special case.
+        * We need to ignore the mode when:
+        * - It is part of a net.burst (from a server and
+        *   a MODE_ADD). Ofcourse we don't ignore mode
+        *   changes from Uworld.
+        * - The found nick is not on the right side off
+        *   the net.junction.
+        * This fixes the bug that when someone (tries to)
+        * ride a net.break and does so with the nick of
+        * someone on the otherside, that he is nick collided
+        * (killed) but his +o still ops the other person.
+        */
+       if (MyUser(sptr) || Protocol(cptr) < 10)
+       {
+         if (!(who = find_chasing(sptr, parv[0], NULL)))
+           break;
+       }
+       else
+       {
+         if (!(who = findNUser(parv[0])))
+           break;
+       }
+       if (whatt == MODE_ADD && IsServer(sptr) && who->from != sptr->from &&
+           !find_conf_host(cptr->confs, sptr->name, CONF_UWORLD))
+         break;
+       if (!(member = find_user_link(chptr->members, who)) ||
+           (MyUser(sptr) && (member->flags & CHFL_ZOMBIE)))
+       {
+         sendto_one(cptr, err_str(ERR_USERNOTINCHANNEL),
+             me.name, cptr->name, who->name, chptr->chname);
+         break;
+       }
+       /* if the user is +k, prevent a deop from local user */
+       if (whatt == MODE_DEL && IsChannelService(who) &&
+           MyUser(cptr) && *curr == 'o')
+       {
+         sendto_one(cptr, err_str(ERR_ISCHANSERVICE), 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)))))
+           *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;
+       else
+       {
+         char *s = &(*parv)[-1];
+         unsigned short count = KEYLEN + 1;
+
+         while (*++s > ' ' && *s != ':' && --count);
+         *s = '\0';
+         if (!**parv)          /* nothing left in key */
+           break;
+       }
+       if (MyUser(sptr) && opcnt >= MAXMODEPARAMS)
+         break;
+       if (whatt == MODE_ADD)
+       {
+         if (*mode->key && !IsServer(cptr))
+           sendto_one(cptr, err_str(ERR_KEYSET),
+               me.name, cptr->name, chptr->chname);
+         else if (!*mode->key || IsServer(cptr))
+         {
+           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 (strCasediff(mode->key, *parv) == 0 || IsServer(cptr))
+         {
+           lp = &chops[opcnt++];
+           lp->value.cp = mode->key;
+           lp->flags = MODE_KEY | MODE_DEL;
+           keychange = 1;
+         }
+       }
+       break;
+      case 'b':
+       if (--parc <= 0)
+       {
+         if (banlsent)         /* Only send it once */
+           break;
+         for (lp = chptr->banlist; lp; lp = lp->next)
+           sendto_one(cptr, rpl_str(RPL_BANLIST), me.name, cptr->name,
+               chptr->chname, lp->value.ban.banstr, lp->value.ban.who,
+               lp->value.ban.when);
+         sendto_one(cptr, rpl_str(RPL_ENDOFBANLIST), me.name, cptr->name,
+             chptr->chname);
+         banlsent = 1;
+         break;
+       }
+       parv++;
+       if (BadPtr(*parv))
+         break;
+       if (MyUser(sptr))
+       {
+         if ((cp = strchr(*parv, ' ')))
+           *cp = 0;
+         if (opcnt >= MAXMODEPARAMS || **parv == ':' || **parv == '\0')
+           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 '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 (MyUser(sptr) && 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 '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)
+         {
+           if (*ip == MODE_PRIVATE)
+             newmode &= ~MODE_SECRET;
+           else if (*ip == MODE_SECRET)
+             newmode &= ~MODE_PRIVATE;
+           newmode |= *ip;
+         }
+         else
+           newmode &= ~*ip;
+       }
+       else if (!IsServer(cptr))
+         sendto_one(cptr, err_str(ERR_UNKNOWNMODE),
+             me.name, cptr->name, *curr);
+       break;
+    }
+    curr++;
+    /*
+     * Make sure mode strings such as "+m +t +p +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))
+      {
+       if (parc == 1 && isDigit(*curr))
+       {
+         newtime = atoi(curr);
+         if (newtime && chptr->creationtime == MAGIC_REMOTE_JOIN_TS)
+         {
+           chptr->creationtime = newtime;
+           *badop = 0;
+         }
+         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)
+           {
+             if (chptr->creationtime == 0 || doesop)
+               chptr->creationtime = newtime;
+             *badop = 0;
+           }
+           /* Bounce: */
+           else
+             *badop = 1;
+         }
+         /*
+          * 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 > (TStime() - TS_LAG_TIME))
+         {
+           if (newtime < chptr->creationtime)
+             chptr->creationtime = newtime;
+           *badop = 0;
+         }
+         break;
+       }
+      }
+      else
+       *badop = 0;
+    }
+  }                            /* end of while loop for MODE processing */
+
+  /* Now reject non chan ops */
+  if (!IsServer(cptr) && (!tmp || !(tmp->flags & CHFL_CHANOP)))
+  {
+    *badop = 0;
+    return (opcnt || newmode != mode->mode || limitset || keychange) ? 0 : -1;
+  }
+
+  if (doesop && newtime == 0 && IsServer(sptr))
+    *badop = 2;
+
+  if (*badop >= 2 &&
+      (aconf = find_conf_host(cptr->confs, sptr->name, CONF_UWORLD)))
+    *badop = 4;
+
+  bounce = (*badop == 1 || *badop == 2 || is_deopped(sptr, chptr)) ? 1 : 0;
+
+  whatt = 0;
+  for (ip = flags; *ip; ip += 2)
+    if ((*ip & newmode) && !(*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 & newmode))
+    {
+      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 = nblen = 0;
+  if (limitset && !nusers && mode->limit)
+  {
+    if (bounce)
+    {
+      if (bwhatt != MODE_ADD)
+      {
+       *bmbuf++ = '+';
+       bwhatt = MODE_ADD;
+      }
+      *bmbuf++ = 'l';
+      sprintf(numeric, "%-15d", mode->limit);
+      if ((cp = strchr(numeric, ' ')))
+       *cp = '\0';
+      strcat(bpbuf, numeric);
+      blen += strlen(numeric);
+      strcat(bpbuf, " ");
+      strcat(nbpbuf, numeric);
+      nblen += strlen(numeric);
+      strcat(nbpbuf, " ");
+    }
+    else
+    {
+      if (whatt != MODE_DEL)
+      {
+       *mbuf++ = '-';
+       whatt = MODE_DEL;
+      }
+      mode->mode &= ~MODE_LIMIT;
+      mode->limit = 0;
+      *mbuf++ = 'l';
+    }
+  }
+  /*
+   * Reconstruct "+bkov" chain.
+   */
+  if (opcnt)
+  {
+    Reg1 int i = 0;
+    Reg2 char c = 0;
+    unsigned 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);
+      nlen = strlen(npbuf);
+      /*
+       * 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_KEY:
+         c = 'k';
+         cp = lp->value.cp;
+         break;
+       case MODE_LIMIT:
+         c = 'l';
+         sprintf(numeric, "%-15d", nusers);
+         if ((cp = strchr(numeric, ' ')))
+           *cp = '\0';
+         cp = numeric;
+         break;
+      }
+
+      /* What could be added: cp+' '+' '+<TS>+'\0' */
+      if (len + strlen(cp) + 13 > (size_t)MODEBUFLEN ||
+         nlen + strlen(cp) + NUMNICKLEN + 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' ||
+             strCasediff(mode->key, cp) != 0)) ||
+             (whatt == MODE_DEL && (*mode->key != '\0')))
+         {
+           if (bounce)
+           {
+             if (*mode->key == '\0')
+             {
+               if (bwhatt != MODE_DEL)
+               {
+                 *bmbuf++ = '-';
+                 bwhatt = MODE_DEL;
+               }
+               strcat(bpbuf, cp);
+               blen += strlen(cp);
+               strcat(bpbuf, " ");
+               blen++;
+               strcat(nbpbuf, cp);
+               nblen += strlen(cp);
+               strcat(nbpbuf, " ");
+               nblen++;
+             }
+             else
+             {
+               if (bwhatt != MODE_ADD)
+               {
+                 *bmbuf++ = '+';
+                 bwhatt = MODE_ADD;
+               }
+               strcat(bpbuf, mode->key);
+               blen += strlen(mode->key);
+               strcat(bpbuf, " ");
+               blen++;
+               strcat(nbpbuf, mode->key);
+               nblen += strlen(mode->key);
+               strcat(nbpbuf, " ");
+               nblen++;
+             }
+             *bmbuf++ = c;
+             mbuf--;
+             if (*mbuf != '+' && *mbuf != '-')
+               mbuf++;
+             else
+               whatt = prev_whatt;
+           }
+           else
+           {
+             *mbuf++ = c;
+             strcat(pbuf, cp);
+             len += strlen(cp);
+             strcat(pbuf, " ");
+             len++;
+             strcat(npbuf, cp);
+             nlen += strlen(cp);
+             strcat(npbuf, " ");
+             nlen++;
+             if (whatt == MODE_ADD)
+               strncpy(mode->key, cp, KEYLEN);
+             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;
+               }
+               sprintf(numeric, "%-15d", mode->limit);
+               if ((cp = strchr(numeric, ' ')))
+                 *cp = '\0';
+               strcat(bpbuf, numeric);
+               blen += strlen(numeric);
+               strcat(bpbuf, " ");
+               blen++;
+               strcat(nbpbuf, numeric);
+               nblen += strlen(numeric);
+               strcat(nbpbuf, " ");
+               nblen++;
+             }
+             *bmbuf++ = c;
+             mbuf--;
+             if (*mbuf != '+' && *mbuf != '-')
+               mbuf++;
+             else
+               whatt = prev_whatt;
+           }
+           else
+           {
+             *mbuf++ = c;
+             strcat(pbuf, cp);
+             len += strlen(cp);
+             strcat(pbuf, " ");
+             len++;
+             strcat(npbuf, cp);
+             nlen += strlen(cp);
+             strcat(npbuf, " ");
+             nlen++;
+             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;
+             strcat(bpbuf, lp->value.cptr->name);
+             blen += strlen(lp->value.cptr->name);
+             strcat(bpbuf, " ");
+             blen++;
+             sprintf_irc(nbpbuf + nblen, "%s%s ", NumNick(lp->value.cptr));
+             nblen += strlen(nbpbuf + nblen);
+             change = 0;
+           }
+           else if (change)
+           {
+             tmp->flags |= lp->flags & CHFL_OVERLAP;
+             if (lp->flags & MODE_CHANOP)
+             {
+               tmp->flags &= ~CHFL_DEOPPED;
+               if (IsServer(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;
+             strcat(bpbuf, lp->value.cptr->name);
+             blen += strlen(lp->value.cptr->name);
+             strcat(bpbuf, " ");
+             blen++;
+             sprintf_irc(nbpbuf + nblen, "%s%s ", NumNick(lp->value.cptr));
+             blen += strlen(bpbuf + blen);
+             change = 0;
+           }
+           else
+           {
+             tmp->flags &= ~change;
+             if ((change & MODE_CHANOP) && IsServer(sptr))
+               tmp->flags |= CHFL_DEOPPED;
+           }
+         }
+         if (change || *badop == 2 || *badop == 4)
+         {
+           *mbuf++ = c;
+           strcat(pbuf, cp);
+           len += strlen(cp);
+           strcat(pbuf, " ");
+           len++;
+           sprintf_irc(npbuf + nlen, "%s%s ", NumNick(lp->value.cptr));
+           nlen += strlen(npbuf + nlen);
+           npbuf[nlen++] = ' ';
+           npbuf[nlen] = 0;
+         }
+         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.
+ *
+ * Since BURST - I *did* implement net.rejoin ban bouncing. So now it
+ * certainly makes sense to also bounce 'last second' bans (bans done
+ * after the net.junction). -- RunHardWorker
+ */
+         if ((change = (whatt & MODE_ADD) &&
+             !add_banid(sptr, chptr, cp, !bounce, !add_banid_called)))
+           add_banid_called = 1;
+         else
+           change = (whatt & MODE_DEL) && !del_banid(chptr, cp, !bounce);
+
+         if (bounce && change)
+         {
+           change = 0;
+           if ((whatt & MODE_ADD))
+           {
+             if (bwhatt != MODE_DEL)
+             {
+               *bmbuf++ = '-';
+               bwhatt = MODE_DEL;
+             }
+           }
+           else if ((whatt & MODE_DEL))
+           {
+             if (bwhatt != MODE_ADD)
+             {
+               *bmbuf++ = '+';
+               bwhatt = MODE_ADD;
+             }
+           }
+           *bmbuf++ = c;
+           strcat(bpbuf, cp);
+           blen += strlen(cp);
+           strcat(bpbuf, " ");
+           blen++;
+           strcat(nbpbuf, cp);
+           nblen += strlen(cp);
+           strcat(nbpbuf, " ");
+           nblen++;
+         }
+         if (change)
+         {
+           *mbuf++ = c;
+           strcat(pbuf, cp);
+           len += strlen(cp);
+           strcat(pbuf, " ");
+           len++;
+           strcat(npbuf, cp);
+           nlen += strlen(cp);
+           strcat(npbuf, " ");
+           nlen++;
+         }
+         else
+         {
+           mbuf--;
+           if (*mbuf != '+' && *mbuf != '-')
+             mbuf++;
+           else
+             whatt = prev_whatt;
+         }
+         break;
+      }
+    }                          /* for (; i < opcnt; i++) */
+  }                            /* if (opcnt) */
+
+  *mbuf++ = '\0';
+  *bmbuf++ = '\0';
+
+  /* Bounce here */
+  if (!hacknotice && *bmodebuf && chptr->creationtime)
+  {
+    if (Protocol(cptr) < 10)
+      sendto_one(cptr, ":%s MODE %s %s %s " TIME_T_FMT,
+         me.name, chptr->chname, bmodebuf, bparambuf,
+         *badop == 2 ? (time_t) 0 : chptr->creationtime);
+    else
+      sendto_one(cptr, "%s MODE %s %s %s " TIME_T_FMT,
+         NumServ(&me), chptr->chname, bmodebuf, nbparambuf,
+         *badop == 2 ? (time_t) 0 : chptr->creationtime);
+  }
+  /* If there are possibly bans to re-add, bounce them now */
+  if (add_banid_called && bounce)
+  {
+    Link *ban[6];              /* Max 6 bans at a time */
+    size_t len[6], sblen, total_len;
+    int cnt, delayed = 0;
+    while (delayed || (ban[0] = next_overlapped_ban()))
+    {
+      len[0] = strlen(ban[0]->value.ban.banstr);
+      cnt = 1;                 /* We already got one ban :) */
+      sblen = sprintf_irc(sendbuf, ":%s MODE %s +b",
+         me.name, chptr->chname) - sendbuf;
+      total_len = sblen + 1 + len[0];  /* 1 = ' ' */
+      /* Find more bans: */
+      delayed = 0;
+      while (cnt < 6 && (ban[cnt] = next_overlapped_ban()))
+      {
+       len[cnt] = strlen(ban[cnt]->value.ban.banstr);
+       if (total_len + 5 + len[cnt] > BUFSIZE) /* 5 = "b \r\n\0" */
+       {
+         delayed = cnt + 1;    /* != 0 */
+         break;                /* Flush */
+       }
+       sendbuf[sblen++] = 'b';
+       total_len += 2 + len[cnt++];    /* 2 = "b " */
+      }
+      while (cnt--)
+      {
+       sendbuf[sblen++] = ' ';
+       strcpy(sendbuf + sblen, ban[cnt]->value.ban.banstr);
+       sblen += len[cnt];
+      }
+      sendbufto_one(cptr);     /* Send bounce to uplink */
+      if (delayed)
+       ban[0] = ban[delayed - 1];
+    }
+  }
+  /* Send -b's of overlapped bans to clients to keep them synchronized */
+  if (add_banid_called && !bounce)
+  {
+    Link *ban;
+    char *banstr[6];           /* Max 6 bans at a time */
+    size_t len[6], sblen, psblen, total_len;
+    int cnt, delayed = 0;
+    Link *lp;
+    aClient *acptr;
+    if (IsServer(sptr))
+      psblen = sprintf_irc(sendbuf, ":%s MODE %s -b",
+         sptr->name, chptr->chname) - sendbuf;
+    else                       /* We rely on IsRegistered(sptr) being true for MODE */
+      psblen = sprintf_irc(sendbuf, ":%s!%s@%s MODE %s -b", sptr->name,
+         sptr->user->username, sptr->user->host, chptr->chname) - sendbuf;
+    while (delayed || (ban = next_removed_overlapped_ban()))
+    {
+      if (!delayed)
+      {
+       len[0] = strlen((banstr[0] = ban->value.ban.banstr));
+       ban->value.ban.banstr = NULL;
+      }
+      cnt = 1;                 /* We already got one ban :) */
+      sblen = psblen;
+      total_len = sblen + 1 + len[0];  /* 1 = ' ' */
+      /* Find more bans: */
+      delayed = 0;
+      while (cnt < 6 && (ban = next_removed_overlapped_ban()))
+      {
+       len[cnt] = strlen((banstr[cnt] = ban->value.ban.banstr));
+       ban->value.ban.banstr = NULL;
+       if (total_len + 5 + len[cnt] > BUFSIZE) /* 5 = "b \r\n\0" */
+       {
+         delayed = cnt + 1;    /* != 0 */
+         break;                /* Flush */
+       }
+       sendbuf[sblen++] = 'b';
+       total_len += 2 + len[cnt++];    /* 2 = "b " */
+      }
+      while (cnt--)
+      {
+       sendbuf[sblen++] = ' ';
+       strcpy(sendbuf + sblen, banstr[cnt]);
+       RunFree(banstr[cnt]);
+       sblen += len[cnt];
+      }
+      for (lp = chptr->members; lp; lp = lp->next)
+       if (MyConnect(acptr = lp->value.cptr) && !(lp->flags & CHFL_ZOMBIE))
+         sendbufto_one(acptr);
+      if (delayed)
+      {
+       banstr[0] = banstr[delayed - 1];
+       len[0] = len[delayed - 1];
+      }
+    }
+  }
+
+  return gotts ? 1 : -1;
+}
+
+/* We are now treating the <key> part of /join <channel list> <key> as a key
+ * ring; that is, we try one key against the actual channel key, and if that
+ * doesn't work, we try the next one, and so on. -Kev -Texaco
+ * Returns: 0 on match, 1 otherwise
+ * This version contributed by SeKs <intru@info.polymtl.ca>
+ */
+static int compall(char *key, char *keyring)
+{
+  register char *p1;
+
+top:
+  p1 = key;                    /* point to the key... */
+  while (*p1 && *p1 == *keyring)
+  {                            /* step through the key and ring until they
+                                  don't match... */
+    p1++;
+    keyring++;
+  }
+
+  if (!*p1 && (!*keyring || *keyring == ','))
+    /* ok, if we're at the end of the and also at the end of one of the keys
+       in the keyring, we have a match */
+    return 0;
+
+  if (!*keyring)               /* if we're at the end of the key ring, there
+                                  weren't any matches, so we return 1 */
+    return 1;
+
+  /* Not at the end of the key ring, so step
+     through to the next key in the ring: */
+  while (*keyring && *(keyring++) != ',');
+
+  goto top;                    /* and check it against the key */
+}
+
+static int can_join(aClient *sptr, aChannel *chptr, char *key)
+{
+  Reg1 Link *lp;
+
+  /* Now a banned user CAN join if invited -- Nemesi */
+  /* Now a user CAN escape channel limit if invited -- bfriendly */
+  if ((chptr->mode.mode & MODE_INVITEONLY) || (is_banned(sptr, chptr, NULL)
+      || (chptr->mode.limit && chptr->users >= chptr->mode.limit)))
+  {
+    for (lp = sptr->user->invited; lp; lp = lp->next)
+      if (lp->value.chptr == chptr)
+       break;
+    if (!lp)
+    {
+      if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
+       return (ERR_CHANNELISFULL);
+      /* This can return an "Invite only" msg instead of the "You are banned"
+         if _both_ conditions are true, but who can say what is more
+         appropriate ? checking again IsBanned would be _SO_ cpu-xpensive ! */
+      return ((chptr->mode.mode & MODE_INVITEONLY) ?
+         ERR_INVITEONLYCHAN : ERR_BANNEDFROMCHAN);
+    }
+  }
+
+  /* now using compall (above) to test against a whole key ring -Kev */
+  if (*chptr->mode.key && (BadPtr(key) || compall(chptr->mode.key, key)))
+    return (ERR_BADCHANNELKEY);
+
+  return 0;
+}
+
+/*
+ * Remove bells and commas from channel name
+ */
+
+static void clean_channelname(char *cn)
+{
+  for (; *cn; cn++)
+  {
+    if (!isIrcCh(*cn))
+    {
+      *cn = '\0';
+      return;
+    }
+    if (isIrcCl(*cn))
+#ifndef FIXME
+    {
+#endif
+      *cn = toLower(*cn);
+#ifndef FIXME
+      /* Missed the Icelandic letter ETH last time: */
+      if ((unsigned char)(*cn) == 0xd0)
+       *cn = (char)0xf0;
+    }
+#endif
+  }
+}
+
+/*
+ *  Get Channel block for i (and allocate a new channel
+ *  block, if it didn't exists before).
+ */
+static aChannel *get_channel(aClient *cptr, char *chname, int flag)
+{
+  Reg1 aChannel *chptr;
+  int len;
+
+  if (BadPtr(chname))
+    return NULL;
+
+  len = strlen(chname);
+  if (MyUser(cptr) && len > CHANNELLEN)
+  {
+    len = CHANNELLEN;
+    *(chname + CHANNELLEN) = '\0';
+  }
+  if ((chptr = FindChannel(chname)))
+    return (chptr);
+  if (flag == CREATE)
+  {
+    chptr = (aChannel *)RunMalloc(sizeof(aChannel) + len);
+    ++nrof.channels;
+    memset(chptr, 0, sizeof(aChannel));
+    strcpy(chptr->chname, chname);
+    if (channel)
+      channel->prevch = chptr;
+    chptr->prevch = NULL;
+    chptr->nextch = channel;
+    chptr->creationtime = MyUser(cptr) ? TStime() : (time_t) 0;
+    channel = chptr;
+    hAddChannel(chptr);
+  }
+  return chptr;
+}
+
+static void add_invite(aClient *cptr, aChannel *chptr)
+{
+  Reg1 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)
+    del_invite(cptr, cptr->user->invited->value.chptr);
+  /*
+   * Add client to channel invite list
+   */
+  inv = make_link();
+  inv->value.cptr = cptr;
+  inv->next = chptr->invites;
+  chptr->invites = inv;
+  /*
+   * Add channel to the end of the client invite list
+   */
+  for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next));
+  inv = make_link();
+  inv->value.chptr = chptr;
+  inv->next = NULL;
+  (*tmp) = inv;
+}
+
+/*
+ * Delete Invite block from channel invite list and client invite list
+ */
+void del_invite(aClient *cptr, aChannel *chptr)
+{
+  Reg1 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;
+    }
+}
+
+/* List and skip all channels that are listen */
+void list_next_channels(aClient *cptr, int nr)
+{
+  aListingArgs *args = cptr->listing;
+  aChannel *chptr = args->chptr;
+  chptr->mode.mode &= ~MODE_LISTED;
+  while (is_listed(chptr) || --nr >= 0)
+  {
+    for (; chptr; chptr = chptr->nextch)
+    {
+      if (!cptr->user || (SecretChannel(chptr) && !IsMember(cptr, chptr)))
+       continue;
+      if (chptr->users > args->min_users && chptr->users < args->max_users &&
+         chptr->creationtime > args->min_time &&
+         chptr->creationtime < args->max_time &&
+         (!args->topic_limits || (*chptr->topic &&
+         chptr->topic_time > args->min_topic_time &&
+         chptr->topic_time < args->max_topic_time)))
+      {
+       sendto_one(cptr, rpl_str(RPL_LIST), me.name, cptr->name,
+           ShowChannel(cptr, chptr) ? chptr->chname : "*",
+           chptr->users, ShowChannel(cptr, chptr) ? chptr->topic : "");
+       chptr = chptr->nextch;
+       break;
+      }
+    }
+    if (!chptr)
+    {
+      RunFree(cptr->listing);
+      cptr->listing = NULL;
+      sendto_one(cptr, rpl_str(RPL_LISTEND), me.name, cptr->name);
+      break;
+    }
+  }
+  if (chptr)
+  {
+    cptr->listing->chptr = chptr;
+    chptr->mode.mode |= MODE_LISTED;
+  }
+}
+
+/*
+ *  Subtract one user from channel i (and free channel
+ *  block, if channel became empty).
+ */
+static void sub1_from_channel(aChannel *chptr)
+{
+  Reg2 Link *tmp;
+  Link *obtmp;
+
+  if (chptr->users > 1)                /* Can be 0, called for an empty channel too */
+  {
+    --chptr->users;
+    return;
+  }
+
+  /* Channel became (or was) empty: Remove channel */
+  if (is_listed(chptr))
+  {
+    int i;
+    for (i = 0; i <= highest_fd; i++)
+    {
+      aClient *acptr;
+      if ((acptr = loc_clients[i]) && acptr->listing &&
+         acptr->listing->chptr == chptr)
+      {
+       list_next_channels(acptr, 1);
+       break;                  /* Only one client can list a channel */
+      }
+    }
+  }
+  /*
+   * Now, find all invite links from channel structure
+   */
+  while ((tmp = chptr->invites))
+    del_invite(tmp->value.cptr, chptr);
+
+  tmp = chptr->banlist;
+  while (tmp)
+  {
+    obtmp = tmp;
+    tmp = tmp->next;
+    RunFree(obtmp->value.ban.banstr);
+    RunFree(obtmp->value.ban.who);
+    free_link(obtmp);
+  }
+  if (chptr->prevch)
+    chptr->prevch->nextch = chptr->nextch;
+  else
+    channel = chptr->nextch;
+  if (chptr->nextch)
+    chptr->nextch->prevch = chptr->prevch;
+  hRemChannel(chptr);
+  --nrof.channels;
+  RunFree((char *)chptr);
+}
+
+/*
+ * m_join
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[2] = channel keys (client), or channel TS (server)
+ */
+int m_join(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  static char jbuf[BUFSIZE], mbuf[BUFSIZE];
+  Reg1 Link *lp;
+  Reg3 aChannel *chptr;
+  Reg4 char *name, *keysOrTS = NULL;
+  int i = 0, zombie = 0, sendcreate = 0;
+  unsigned int flags = 0;
+  size_t jlen = 0, mlen = 0;
+  size_t *buflen;
+  char *p = NULL, *bufptr;
+
+  if (parc < 2 || *parv[1] == '\0')
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "JOIN");
+    return 0;
+  }
+
+  for (p = parv[1]; *p; p++)   /* find the last "JOIN 0" in the line -Kev */
+    if (*p == '0'
+       && (*(p + 1) == ',' || *(p + 1) == '\0' || !isIrcCh(*(p + 1))))
+    {
+      /* If it's a single "0", remember the place; we will start parsing
+         the channels after the last 0 in the line -Kev */
+      parv[1] = p;
+      if (!*(p + 1))
+       break;
+      p++;
+    }
+    else
+    {                          /* Step through to the next comma or until the
+                                  end of the line, in an attempt to save CPU
+                                  -Kev */
+      while (*p != ',' && *p != '\0')
+       p++;
+      if (!*p)
+       break;
+    }
+
+  keysOrTS = parv[2];          /* Remember where our keys are or the TS is;
+                                  parv[2] needs to be NULL for the call to
+                                  m_names below -Kev */
+  parv[2] = p = NULL;
+
+  *jbuf = *mbuf = '\0';                /* clear both join and mode buffers -Kev */
+  /*
+   *  Rebuild list of channels joined to be the actual result of the
+   *  JOIN.  Note that "JOIN 0" is the destructive problem.
+   */
+  for (name = strtoken(&p, parv[1], ","); name; name = strtoken(&p, NULL, ","))
+  {
+    size_t len;
+    if (MyConnect(sptr))
+      clean_channelname(name);
+    else if (IsLocalChannel(name))
+      continue;
+    if (*name == '0' && *(name + 1) == '\0')
+    {
+      /* Remove the user from all his channels -Kev */
+      while ((lp = sptr->user->channel))
+      {
+       chptr = lp->value.chptr;
+       if (!is_zombie(sptr, chptr))
+         sendto_channel_butserv(chptr, sptr, PartFmt2,
+             parv[0], chptr->chname, "Left all channels");
+       remove_user_from_channel(sptr, chptr);
+      }
+      /* Just in case */
+      *mbuf = *jbuf = '\0';
+      mlen = jlen = 0;
+    }
+    else
+    {                          /* not a /join 0, so treat it as
+                                  a /join #channel -Kev */
+      if (!IsChannelName(name))
+      {
+       if (MyUser(sptr))
+         sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name);
+       continue;
+      }
+
+      if (MyConnect(sptr))
+      {
+       /*
+        * Local client is first to enter previously nonexistant
+        * channel so make them (rightfully) the Channel Operator.
+        * This looks kind of ugly because we try to avoid calling the strlen()
+        */
+       if (ChannelExists(name))
+       {
+         flags = CHFL_DEOPPED;
+         sendcreate = 0;
+       }
+       else if (strlen(name) > CHANNELLEN)
+       {
+         *(name + CHANNELLEN) = '\0';
+         if (ChannelExists(name))
+         {
+           flags = CHFL_DEOPPED;
+           sendcreate = 0;
+         }
+         else
+         {
+           flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP;
+           sendcreate = 1;
+         }
+       }
+       else
+       {
+         flags = IsModelessChannel(name) ? CHFL_DEOPPED : CHFL_CHANOP;
+         sendcreate = 1;
+       }
+
+       if (sptr->user->joined >= MAXCHANNELSPERUSER)
+       {
+         chptr = get_channel(sptr, name, !CREATE);
+         sendto_one(sptr, err_str(ERR_TOOMANYCHANNELS),
+             me.name, parv[0], chptr ? chptr->chname : name);
+         break;                /* Can't return, else he won't get on ANY
+                                  channels!  Break out of the for loop instead.
+                                  -Kev */
+       }
+      }
+      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;
+      }
+      name = chptr->chname;
+      if (!chptr->creationtime)        /* A remote JOIN created this channel ? */
+       chptr->creationtime = MAGIC_REMOTE_JOIN_TS;
+      if (parc > 2)
+      {
+       if (chptr->creationtime == MAGIC_REMOTE_JOIN_TS)
+         chptr->creationtime = atoi(keysOrTS);
+       else
+         parc = 2;             /* Don't pass it on */
+      }
+      if (!zombie)
+      {
+       if (!MyConnect(sptr))
+         flags = CHFL_DEOPPED;
+       if (sptr->flags & FLAGS_TS8)
+         flags |= CHFL_SERVOPOK;
+      }
+      if (MyConnect(sptr))
+      {
+       int created = chptr->users == 0;
+       if (check_target_limit(sptr, chptr, chptr->chname, created))
+       {
+         if (created)          /* Did we create the channel? */
+           sub1_from_channel(chptr);   /* Remove it again! */
+         continue;
+       }
+       if ((i = can_join(sptr, chptr, keysOrTS)))
+       {
+         sendto_one(sptr, err_str(i), me.name, parv[0], chptr->chname);
+         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);
+
+      if (MyUser(sptr))
+      {
+       del_invite(sptr, chptr);
+       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;
+       m_names(cptr, sptr, 2, parv);
+      }
+    }
+
+    /* Select proper buffer; mbuf for creation, jbuf otherwise */
+
+    if (*name == '&')
+      continue;                        /* Head off local channels at the pass */
+
+    bufptr = (sendcreate == 0) ? jbuf : mbuf;
+    buflen = (sendcreate == 0) ? &jlen : &mlen;
+    len = strlen(name);
+    if (*buflen < BUFSIZE - len - 2)
+    {
+      if (*bufptr)
+      {
+       strcat(bufptr, ",");    /* Add to join buf */
+       *buflen += 1;
+      }
+      strncat(bufptr, name, BUFSIZE - *buflen - 1);
+      *buflen += len;
+    }
+    sendcreate = 0;            /* Reset sendcreate */
+  }
+
+#ifndef NO_PROTOCOL9
+  if (*jbuf || *mbuf)          /* Propagate joins to P09 servers */
+    sendto_lowprot_butone(cptr, 9, (*jbuf && *mbuf) ? ":%s JOIN %s,%s" :
+       ":%s JOIN %s%s", parv[0], jbuf, mbuf);
+#endif
+
+  if (*jbuf)                   /* Propgate joins to P10 servers */
+#ifdef NO_PROTOCOL9
+    sendto_serv_butone(cptr,
+       parc > 2 ? ":%s JOIN %s %s" : ":%s JOIN %s", parv[0], jbuf, keysOrTS);
+#else
+    sendto_highprot_butone(cptr, 10,
+       parc > 2 ? ":%s JOIN %s %s" : ":%s JOIN %s", parv[0], jbuf, keysOrTS);
+#endif
+  if (*mbuf)                   /* and now creation events */
+#ifdef NO_PROTOCOL9
+    sendto_serv_butone(cptr, "%s%s CREATE %s " TIME_T_FMT,
+       NumNick(sptr), mbuf, TStime());
+#else
+    sendto_highprot_butone(cptr, 10, "%s%s CREATE %s " TIME_T_FMT,
+       NumNick(sptr), mbuf, TStime());
+#endif
+
+  if (MyUser(sptr))
+  {                            /* shouldn't ever set TS for remote JOIN's */
+    if (*jbuf)
+    {                          /* check for channels that need TS's */
+      p = NULL;
+      for (name = strtoken(&p, jbuf, ","); name; name = strtoken(&p, NULL, ","))
+      {
+       chptr = get_channel(sptr, name, !CREATE);
+       if (chptr && chptr->mode.mode & MODE_SENDTS)
+       {                       /* send a TS? */
+         sendto_serv_butone(cptr, ":%s MODE %s + " TIME_T_FMT, me.name,
+             chptr->chname, chptr->creationtime);      /* ok, send TS */
+         chptr->mode.mode &= ~MODE_SENDTS;     /* reset flag */
+       }
+      }
+    }
+
+    if (*mbuf)
+    {                          /* ok, send along modes for creation events to P9 */
+      p = NULL;
+      for (name = strtoken(&p, mbuf, ","); name; name = strtoken(&p, NULL, ","))
+      {
+       chptr = get_channel(sptr, name, !CREATE);
+       sendto_lowprot_butone(cptr, 9, ":%s MODE %s +o %s " TIME_T_FMT,
+           me.name, chptr->chname, parv[0], chptr->creationtime);
+      }
+    }
+  }
+  return 0;
+}
+
+/*
+ * m_destruct
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel channelname
+ * parv[2] = channel time stamp
+ *
+ * This function does nothing, it does passes DESTRUCT to the other servers.
+ * In the future we will start to use this message.
+ *
+ */
+int m_destruct(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  time_t chanTS;               /* Creation time of the channel */
+
+  if (parc < 3 || *parv[2] == '\0')
+    return 0;
+
+#ifdef GODMODE
+  /* Allow DESTRUCT from user */
+  if (MyUser(sptr))
+    sptr = &me;
+  else
+#endif
+
+    /* sanity checks: Only accept DESTRUCT messages from servers */
+  if (!IsServer(sptr))
+    return 0;
+
+  /* Don't pass on DESTRUCT messages for channels that exist */
+  if (FindChannel(parv[1]))
+    return 0;
+
+  chanTS = atoi(parv[2]);
+
+  /* Pass on DESTRUCT message */
+  sendto_highprot_butone(cptr, 10, "%s DESTRUCT %s " TIME_T_FMT,
+      NumServ(sptr), parv[1], chanTS);
+
+  return 0;
+}
+
+/*
+ * m_create
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel names
+ * parv[2] = channel time stamp
+ */
+int m_create(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  char cbuf[BUFSIZE];          /* Buffer for list with channels
+                                  that `sptr' really creates */
+  time_t chanTS;               /* Creation time for all channels
+                                  in the comma seperated list */
+  char *p, *name;
+  Reg5 aChannel *chptr;
+  int badop;
+
+  /* sanity checks: Only accept CREATE messages from servers */
+  if (!IsServer(cptr) || parc < 3 || *parv[2] == '\0')
+    return 0;
+
+  chanTS = atoi(parv[2]);
+
+  *cbuf = '\0';                        /* Start with empty buffer */
+
+  /* For each channel in the comma seperated list: */
+  for (name = strtoken(&p, parv[1], ","); name; name = strtoken(&p, NULL, ","))
+  {
+    badop = 0;                 /* Default is to accept the op */
+    if ((chptr = FindChannel(name)))
+    {
+      name = chptr->chname;
+      if (TStime() - chanTS > TS_LAG_TIME)
+      {
+       /* A bounce would not be accepted anyway - if we get here something
+          is wrong with the TS clock syncing (or we have more then
+          TS_LAG_TIME lag, or an admin is hacking */
+       badop = 2;
+       /* This causes a HACK notice on all upstream servers: */
+       if (Protocol(cptr) < 10)
+         sendto_one(cptr, ":%s MODE %s -o %s 0", me.name, name, sptr->name);
+       else
+         sendto_one(cptr, ":%s MODE %s -o %s%s 0",
+             me.name, name, NumNick(sptr));
+       /* This causes a WALLOPS on all downstream servers and a notice to our
+          own opers: */
+       parv[1] = name;         /* Corrupt parv[1], it is not used anymore anyway */
+       send_hack_notice(cptr, sptr, parc, parv, badop, 2);
+      }
+      else if (chptr->creationtime && chanTS > chptr->creationtime &&
+         chptr->creationtime != MAGIC_REMOTE_JOIN_TS)
+      {
+       /* We (try) to bounce the mode, because the CREATE is used on an older
+          channel, probably a net.ride */
+       badop = 1;
+       /* Send a deop upstream: */
+       if (Protocol(cptr) < 10)
+         sendto_one(cptr, ":%s MODE %s -o %s " TIME_T_FMT, me.name,
+             name, sptr->name, chptr->creationtime);
+       else
+         sendto_one(cptr, ":%s MODE %s -o %s%s " TIME_T_FMT, me.name,
+             name, NumNick(sptr), chptr->creationtime);
+      }
+    }
+    else                       /* Channel doesn't exist: create it */
+      chptr = get_channel(sptr, name, CREATE);
+
+    /* Add and mark ops */
+    add_user_to_channel(chptr, sptr,
+       (badop || IsModelessChannel(name)) ? CHFL_DEOPPED : CHFL_CHANOP);
+
+    /* Send user join to the local clients (if any) */
+    sendto_channel_butserv(chptr, sptr, ":%s JOIN :%s", parv[0], name);
+
+    if (badop)                 /* handle badop: convert CREATE into JOIN */
+      sendto_serv_butone(cptr, ":%s JOIN %s " TIME_T_FMT,
+         sptr->name, name, chptr->creationtime);
+    else
+    {
+      /* Send the op to local clients:
+         (if any; extremely unlikely, but it CAN happen) */
+      if (!IsModelessChannel(name))
+       sendto_channel_butserv(chptr, sptr, ":%s MODE %s +o %s",
+           sptr->user->server->name, name, parv[0]);
+
+      /* Set/correct TS and add the channel to the
+         buffer for accepted channels: */
+      chptr->creationtime = chanTS;
+      if (*cbuf)
+       strcat(cbuf, ",");
+      strcat(cbuf, name);
+    }
+  }
+
+  if (*cbuf)                   /* Any channel accepted with ops ? */
+  {
+#ifdef NO_PROTOCOL9
+    sendto_serv_butone(cptr, "%s%s CREATE %s " TIME_T_FMT,
+       NumNick(sptr), cbuf, chanTS);
+#else
+    /* send CREATEs to 2.10 servers */
+    sendto_highprot_butone(cptr, 10, "%s%s CREATE %s " TIME_T_FMT,
+       NumNick(sptr), cbuf, chanTS);
+
+    /* And JOIN + MODE to 2.9 servers; following
+       is not needed after all are 2.10 */
+    sendto_lowprot_butone(cptr, 9, ":%s JOIN %s", parv[0], cbuf);
+    p = NULL;
+    for (name = strtoken(&p, cbuf, ","); name; name = strtoken(&p, NULL, ","))
+      sendto_lowprot_butone(cptr, 9, ":%s MODE %s +o %s " TIME_T_FMT,
+         sptr->user->server->name, name, parv[0], chanTS);
+#endif
+  }
+
+  return 0;
+}
+
+static size_t prefix_len;
+
+static void add_token_to_sendbuf(char *token, size_t *sblenp, int *firstp,
+    int *send_itp, char is_a_ban, int mode)
+{
+  int first = *firstp;
+
+  /*
+   * Heh - we do not need to test if it still fits in the buffer, because
+   * this BURST message is reconstructed from another BURST message, and
+   * it only can become smaller. --Run
+   */
+
+  if (*firstp)                 /* First token in this parameter ? */
+  {
+    *firstp = 0;
+    if (*send_itp == 0)
+      *send_itp = 1;           /* Buffer contains data to be sent */
+    sendbuf[(*sblenp)++] = ' ';
+    if (is_a_ban)
+    {
+      sendbuf[(*sblenp)++] = ':';      /* Bans are always the last "parv" */
+      sendbuf[(*sblenp)++] = is_a_ban;
+    }
+  }
+  else                         /* Of course, 'send_it' is already set here */
+    /* Seperate banmasks with a space because
+       they can contain commas themselfs: */
+    sendbuf[(*sblenp)++] = is_a_ban ? ' ' : ',';
+  strcpy(sendbuf + *sblenp, token);
+  *sblenp += strlen(token);
+  if (!is_a_ban)               /* nick list ? Need to take care
+                                  of modes for nicks: */
+  {
+    static int last_mode = 0;
+    mode &= CHFL_CHANOP | CHFL_VOICE;
+    if (first)
+      last_mode = 0;
+    if (last_mode != mode)     /* Append mode like ':ov' if changed */
+    {
+      last_mode = mode;
+      sendbuf[(*sblenp)++] = ':';
+      if (mode & CHFL_CHANOP)
+       sendbuf[(*sblenp)++] = 'o';
+      if (mode & CHFL_VOICE)
+       sendbuf[(*sblenp)++] = 'v';
+    }
+    sendbuf[*sblenp] = '\0';
+  }
+}
+
+static void cancel_mode(aClient *sptr, aChannel *chptr, char m,
+    const char *param, int *count)
+{
+  static char *pb, *sbp, *sbpi;
+  int paramdoesntfit = 0;
+  if (*count == -1)            /* initialize ? */
+  {
+    sbp = sbpi =
+       sprintf_irc(sendbuf, ":%s MODE %s -", sptr->name, chptr->chname);
+    pb = parabuf;
+    *count = 0;
+  }
+  /* m == 0 means flush */
+  if (m)
+  {
+    if (param)
+    {
+      size_t nplen = strlen(param);
+      if (pb - parabuf + nplen + 23 > MODEBUFLEN)
+       paramdoesntfit = 1;
+      else
+      {
+       *sbp++ = m;
+       *pb++ = ' ';
+       strcpy(pb, param);
+       pb += nplen;
+       ++*count;
+      }
+    }
+    else
+      *sbp++ = m;
+  }
+  else if (*count == 0)
+    return;
+  if (*count == 6 || !m || paramdoesntfit)
+  {
+#ifndef NO_PROTOCOL9
+    Dlink *lp;
+    char *sbe;
+#endif
+    Link *member;
+    strcpy(sbp, parabuf);
+#ifndef NO_PROTOCOL9
+    sbe = sbp + strlen(parabuf);
+#endif
+    for (member = chptr->members; member; member = member->next)
+      if (MyUser(member->value.cptr))
+       sendbufto_one(member->value.cptr);
+#ifndef NO_PROTOCOL9
+    sprintf_irc(sbe, " " TIME_T_FMT, chptr->creationtime);
+    /* Send 'sendbuf' to all 2.9 downlinks: */
+    for (lp = me.serv->down; lp; lp = lp->next)
+      if (Protocol(lp->value.cptr) < 10)
+       sendbufto_one(lp->value.cptr);
+#endif
+    sbp = sbpi;
+    pb = parabuf;
+    *count = 0;
+  }
+  if (paramdoesntfit)
+  {
+    *sbp++ = m;
+    *pb++ = ' ';
+    strcpy(pb, param);
+    pb += strlen(param);
+    ++*count;
+  }
+}
+
+/*
+ * m_burst  --  by Run carlo@runaway.xs4all.nl  december 1995 till march 1997
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel name
+ * parv[2] = channel timestamp
+ * The meaning of the following parv[]'s depend on their first character:
+ * If parv[n] starts with a '+':
+ * Net burst, additive modes
+ *   parv[n] = <mode>
+ *   parv[n+1] = <param> (optional)
+ *   parv[n+2] = <param> (optional)
+ * If parv[n] starts with a '%', then n will be parc-1:
+ *   parv[n] = %<ban> <ban> <ban> ...
+ * If parv[n] starts with another character:
+ *   parv[n] = <nick>[:<mode>],<nick>[:<mode>],...
+ *   where <mode> is the channel mode (ov) of nick and all following nicks.
+ *
+ * Example:
+ * "S BURST #channel 87654321 +ntkl key 123 AAA,AAB:o,BAA,BAB:ov :%ban1 ban2"
+ *
+ * Anti net.ride code.
+ *
+ * When the channel already exist, and its TS is larger then
+ * the TS in the BURST message, then we cancel all existing modes.
+ * If its is smaller then the received BURST message is ignored.
+ * If it's equal, then the received modes are just added.
+ */
+int m_burst(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 aChannel *chptr;
+  time_t timestamp;
+  int netride = 0, wipeout = 0, n;
+  int send_it = 0, add_banid_not_called = 1;
+  Mode *current_mode;
+  size_t sblen, mblen = 0;
+  int mblen2, pblen2, cnt;
+  int prev_mode;
+  char prev_key[KEYLEN + 1];
+  Link *lp;
+#ifndef NO_PROTOCOL9
+  int ts_sent = 0;
+#endif
+
+  /* BURST is only for servers and has at least 4 parameters */
+  if (!IsServer(cptr) || parc < 4)
+    return 0;
+
+  if (!IsBurst(sptr))
+  {
+    int i;
+    char *p;
+    if (find_conf_host(cptr->confs, sptr->name, CONF_UWORLD))
+    {
+      p =
+         sprintf_irc(sendbuf,
+         ":%s NOTICE * :*** Notice -- HACK(4): %s BURST %s %s", me.name,
+         sptr->name, parv[1], parv[2]);
+      for (i = 3; i < parc - 1; ++i)
+       p = sprintf_irc(p, " %s", parv[i]);
+      sprintf_irc(p, " :%s", parv[parc - 1]);
+      sendbufto_op_mask(SNO_HACK4);
+    }
+    else
+    {
+#if 1                          /* FIXME: This should be removed after all HUBs upgraded to ircu2.10.05 */
+      SetBurst(sptr);
+      if (MyConnect(sptr))
+#endif
+       return exit_client_msg(cptr, cptr, &me,
+           "HACK: BURST message outside net.burst from %s", sptr->name);
+    }
+  }
+
+  /* Find the channel, or create it - note that the creation time
+   * will be 0 if it has to be created */
+  chptr = get_channel(sptr, parv[1], CREATE);
+  current_mode = &chptr->mode;
+  prev_mode = chptr->mode.mode;
+  if (*chptr->mode.key)
+  {
+    prev_mode |= MODE_KEY;
+    strcpy(prev_key, chptr->mode.key);
+  }
+  if (chptr->mode.limit)
+    prev_mode |= MODE_LIMIT;
+
+  timestamp = atoi(parv[2]);
+
+  /* Copy the new TS when the received creationtime appears to be older */
+  if (!chptr->creationtime || chptr->creationtime > timestamp)
+  {
+    /* Set the new timestamp */
+    chptr->creationtime = timestamp;
+    send_it = 1;               /* Make sure we pass on the different timestamp ! */
+    /* Mark all bans as needed to be wiped out */
+    for (lp = chptr->banlist; lp; lp = lp->next)
+      lp->flags |= CHFL_BURST_BAN_WIPEOUT;
+    /*
+     * Only the first BURST for this channel can have creationtime > timestamp,
+     * so at this moment ALL members are on OUR side, and thus all net.riders:
+     */
+    wipeout = 1;
+  }
+  for (lp = chptr->members; lp; lp = lp->next)
+    lp->flags &= ~CHFL_BURST_JOINED;   /* Set later for nicks in the BURST msg */
+  /* If `wipeout' is set then these will be deopped later. */
+
+  /* If the entering creationtime is younger, ignore the modes */
+  if (chptr->creationtime < timestamp)
+    netride = 1;               /* Only pass on the nicks (so they JOIN) */
+
+  /* Prepare buffers to pass the message */
+  *bparambuf = *bmodebuf = *parabuf = '\0';
+  pblen2 = 0;
+  *modebuf = '+';
+  mblen2 = 1;
+  cnt = 0;
+  prefix_len = sblen = sprintf_irc(sendbuf, "%s BURST %s " TIME_T_FMT,
+      NumServ(sptr), chptr->chname, chptr->creationtime) - sendbuf;
+
+  /* Run over all remaining parameters */
+  for (n = 3; n < parc; n++)
+    switch (*parv[n])          /* What type is it ? mode, nicks or bans ? */
+    {
+      case '+':                /* modes */
+      {
+       char *p = parv[n];
+       while (*(++p))          /* Run over all mode characters */
+       {
+         switch (*p)           /* which mode ? */
+         {
+             /*
+              * The following cases all do the following:
+              * - In case wipeout needed, reset 'prev_mode' to indicate this
+              *   mode should not be cancelled.
+              * - If wipeout or (not netride and the new mode is a change),
+              *   add it to bmodebuf and bparabuf for propagation.
+              * - Else ignore it.
+              * - Add it to modebuf and parabuf for propagation to the
+              *   clients when not netride and the new mode is a change.
+              * Special cases:
+              * - If a +s is received, cancel a +p and sent a -p to the
+              *   clients too (if +p was set).
+              * - If a +p is received and +s is set, ignore the +p.
+              */
+           case 'i':
+           {
+             register int tmp;
+             prev_mode &= ~MODE_INVITEONLY;
+             if (!(tmp = netride ||
+                 (current_mode->mode & MODE_INVITEONLY)) || wipeout)
+             {
+               bmodebuf[mblen++] = 'i';
+               current_mode->mode |= MODE_INVITEONLY;
+             }
+             if (!tmp)
+               modebuf[mblen2++] = 'i';
+             break;
+           }
+           case 'k':
+           {
+             register int tmp;
+             char *param = parv[++n];
+             prev_mode &= ~MODE_KEY;
+             if (!(tmp = netride || (*current_mode->key &&
+                 (!strcmp(current_mode->key, param) ||
+                 (!wipeout && strcmp(current_mode->key, param) < 0)))) ||
+                 wipeout)
+             {
+               bmodebuf[mblen++] = 'k';
+               strcat(bparambuf, " ");
+               strcat(bparambuf, param);
+               strncpy(current_mode->key, param, KEYLEN);
+             }
+             if (!tmp && !wipeout)
+             {
+               modebuf[mblen2++] = 'k';
+               parabuf[pblen2++] = ' ';
+               strcpy(parabuf + pblen2, param);
+               pblen2 += strlen(param);
+               cnt++;
+             }
+             break;
+           }
+           case 'l':
+           {
+             register int tmp;
+             unsigned int param = atoi(parv[++n]);
+             prev_mode &= ~MODE_LIMIT;
+             if (!(tmp = netride || (current_mode->limit &&
+                 (current_mode->limit == param ||
+                 (!wipeout && current_mode->limit < param)))) || wipeout)
+             {
+               bmodebuf[mblen++] = 'l';
+               sprintf_irc(bparambuf + strlen(bparambuf), " %d", param);
+               current_mode->limit = param;
+             }
+             if (!tmp)
+             {
+               modebuf[mblen2++] = 'l';
+               pblen2 = sprintf_irc(parabuf + pblen2, " %d", param) - parabuf;
+               cnt++;
+             }
+             break;
+           }
+           case 'm':
+           {
+             register int tmp;
+             prev_mode &= ~MODE_MODERATED;
+             if (!(tmp = netride ||
+                 (current_mode->mode & MODE_MODERATED)) || wipeout)
+             {
+               bmodebuf[mblen++] = 'm';
+               current_mode->mode |= MODE_MODERATED;
+             }
+             if (!tmp)
+               modebuf[mblen2++] = 'm';
+             break;
+           }
+           case 'n':
+           {
+             register int tmp;
+             prev_mode &= ~MODE_NOPRIVMSGS;
+             if (!(tmp = netride ||
+                 (current_mode->mode & MODE_NOPRIVMSGS)) || wipeout)
+             {
+               bmodebuf[mblen++] = 'n';
+               current_mode->mode |= MODE_NOPRIVMSGS;
+             }
+             if (!tmp)
+               modebuf[mblen2++] = 'n';
+             break;
+           }
+           case 'p':
+           {
+             register int tmp;
+
+             /* Special case: */
+             if (!netride && !wipeout && (current_mode->mode & MODE_SECRET))
+               break;
+
+             prev_mode &= ~MODE_PRIVATE;
+             if (!(tmp = netride ||
+                 (current_mode->mode & MODE_PRIVATE)) || wipeout)
+             {
+               bmodebuf[mblen++] = 'p';
+               current_mode->mode |= MODE_PRIVATE;
+             }
+             if (!tmp)
+               modebuf[mblen2++] = 'p';
+             break;
+           }
+           case 's':
+           {
+             register int tmp;
+             prev_mode &= ~MODE_SECRET;
+             if (!(tmp = netride ||
+                 (current_mode->mode & MODE_SECRET)) || wipeout)
+             {
+               bmodebuf[mblen++] = 's';
+               current_mode->mode |= MODE_SECRET;
+             }
+             if (!tmp)
+               modebuf[mblen2++] = 's';
+
+             /* Special case: */
+             if (!netride && !wipeout && (current_mode->mode & MODE_PRIVATE))
+             {
+               int i;
+               for (i = mblen2 - 1; i >= 0; --i)
+                 modebuf[i + 2] = modebuf[i];
+               modebuf[0] = '-';
+               modebuf[1] = 'p';
+               mblen2 += 2;
+               current_mode->mode &= ~MODE_PRIVATE;
+             }
+
+             break;
+           }
+           case 't':
+           {
+             register int tmp;
+             prev_mode &= ~MODE_TOPICLIMIT;
+             if (!(tmp = netride ||
+                 (current_mode->mode & MODE_TOPICLIMIT)) || wipeout)
+             {
+               bmodebuf[mblen++] = 't';
+               current_mode->mode |= MODE_TOPICLIMIT;
+             }
+             if (!tmp)
+               modebuf[mblen2++] = 't';
+             break;
+           }
+         }
+       }                       /* <-- while over all modes */
+
+       bmodebuf[mblen] = '\0';
+       sendbuf[sblen] = '\0';
+       if (mblen)              /* Anything to send at all ? */
+       {
+         send_it = 1;
+         strcpy(sendbuf + sblen, " +");
+         sblen += 2;
+         strcpy(sendbuf + sblen, bmodebuf);
+         sblen += mblen;
+         strcpy(sendbuf + sblen, bparambuf);
+         sblen += strlen(bparambuf);
+       }
+       break;                  /* Done mode part */
+      }
+      case '%':                /* bans */
+      {
+       char *pv, *p = NULL, *ban;
+       int first = 1;
+       if (netride)
+         break;                /* Ignore bans */
+       /* Run over all bans */
+       for (pv = parv[n] + 1; (ban = strtoken(&p, pv, " ")); pv = NULL)
+       {
+         int ret;
+         /*
+          * The following part should do the following:
+          * - If the new (un)ban is not a _change_ it is ignored.
+          * - Else, add it to sendbuf for later use.
+          * - If sendbuf is full, send it, and prepare a new
+          *   message in sendbuf.
+          */
+         ret = add_banid(sptr, chptr, ban, 1, add_banid_not_called);
+         if (ret == 0)
+         {
+           add_banid_not_called = 0;
+           /* Mark this new ban so we can send it to the clients later */
+           chptr->banlist->flags |= CHFL_BURST_BAN;
+         }
+         if (ret != -1)
+           /* A new ban was added or an existing one needs to be passed on.
+            * Also add it to sendbuf: */
+           add_token_to_sendbuf(ban, &sblen, &first, &send_it, '%', 0);
+       }
+       break;                  /* Done bans part */
+      }
+      default:                 /* nicks */
+      {
+       char *pv, *p = NULL, *nick, *ptr;
+       int first = 1;
+       /* Default mode: */
+       int default_mode = CHFL_DEOPPED;
+       /* Run over all nicks */
+       for (pv = parv[n]; (nick = strtoken(&p, pv, ",")); pv = NULL)
+       {
+         aClient *acptr;
+         if ((ptr = strchr(nick, ':')))        /* New default mode ? */
+         {
+           *ptr = '\0';        /* Fix 'nick' */
+           acptr = findNUser(nick);
+           if (!netride)
+           {
+             /* Calculate new mode change: */
+             default_mode = CHFL_DEOPPED;
+             while (*(++ptr))
+               if (*ptr == 'o')
+               {
+                 default_mode |= CHFL_CHANOP;
+                 default_mode &= ~CHFL_DEOPPED;
+               }
+               else if (*ptr == 'v')
+                 default_mode |= CHFL_VOICE;
+               else
+                 break;
+           }
+         }
+         else
+           acptr = findNUser(nick);
+         /*
+          * Note that at this point we already received a 'NICK' for any
+          * <nick> numeric that is joining (and possibly opped) here.
+          * Therefore we consider the following situations:
+          * - The <nick> numeric exists and is from the direction of cptr: ok
+          * - The <nick> numeric does not exist:
+          *   Apparently this previous <nick> numeric was killed (upstream)
+          *   or it collided with an existing <nick> name.
+          * - The <nick> numeric exists but is from another direction:
+          *   Apparently this previous <nick> numeric was killed,
+          *   and due to a reroute it signed on via another link (probably
+          *   a nick [numeric] collision).
+          * Note that it can't be a QUIT or SQUIT, because a QUIT would
+          * come from the same direction as the BURST (cptr) while an
+          * upstream SQUIT removes the source (server) and we would thus
+          * have this BURST ignored already.
+          * This means that if we find the nick and it is from the correct
+          * direction, it joins. If it doesn't exist or is from another
+          * direction, we have to ignore it. If all nicks are ignored, we
+          * remove the channel again when it is empty and don't propagate
+          * the BURST message.
+          */
+         if (acptr && acptr->from == cptr)
+         {
+           /*
+            * The following should do the following:
+            * - Add it to sendbuf for later use.
+            * - If sendbuf is full, send it, and prepare a new
+            *   message in sendbuf.
+            */
+           add_token_to_sendbuf(nick, &sblen, &first, &send_it, 0,
+               default_mode);
+           /* Let is take effect: (Note that in the case of a netride
+            * 'default_mode' is always CHFL_DEOPPED here). */
+           add_user_to_channel(chptr, acptr, default_mode);
+           chptr->members->flags |= CHFL_BURST_JOINED;
+         }
+       }                       /* <-- Next nick */
+       if (!chptr->members)    /* All nicks collided and channel is empty ? */
+       {
+         sub1_from_channel(chptr);
+         return 0;             /* Forget about the (rest of the) message... */
+       }
+       break;                  /* Done nicks part */
+      }
+    }                          /* <-- Next parameter if any */
+  if (!chptr->members)         /* This message only contained bans (then the previous
+                                  message only contained collided nicks, see above) */
+  {
+    sub1_from_channel(chptr);
+    if (!add_banid_not_called)
+      while (next_removed_overlapped_ban());
+    return 0;                  /* Forget about the (rest of the) message... */
+  }
+
+  /* The last (possibly only) message is always send here */
+  if (send_it)                 /* Anything (left) to send ? */
+  {
+    Dlink *lp;
+    Link *member;
+
+    /* send 'sendbuf' to all downlinks */
+    for (lp = me.serv->down; lp; lp = lp->next)
+    {
+      if (lp->value.cptr == cptr)
+       continue;
+      if (Protocol(lp->value.cptr) > 9)
+       sendbufto_one(lp->value.cptr);
+    }
+
+    /*
+     * Now we finally can screw sendbuf again...
+     * Send all changes to the local clients:
+     *
+     * First send all joins and op them, because 2.9 servers
+     * would protest with a HACK if we first de-opped people.
+     * However, we don't send the +b bans yes, because we
+     * DO first want to -b the old bans (otherwise it's confusing).
+     */
+
+    /* Send all joins: */
+    for (member = chptr->members; member; member = member->next)
+      if (member->flags & CHFL_BURST_JOINED)
+      {
+       sendto_channel_butserv(chptr, member->value.cptr, ":%s JOIN :%s",
+           member->value.cptr->name, chptr->chname);
+#ifndef NO_PROTOCOL9
+       /* And to 2.9 servers: */
+       sendto_lowprot_butone(cptr, 9, ":%s JOIN %s",
+           member->value.cptr->name, chptr->chname);
+#endif
+      }
+
+    if (!netride)
+    {
+      /* Send all +o and +v modes: */
+      for (member = chptr->members; member; member = member->next)
+      {
+       if ((member->flags & CHFL_BURST_JOINED))
+       {
+         int mode = CHFL_CHANOP;
+         for (;;)
+         {
+           if ((member->flags & mode))
+           {
+             modebuf[mblen2++] = (mode == CHFL_CHANOP) ? 'o' : 'v';
+             parabuf[pblen2++] = ' ';
+             strcpy(parabuf + pblen2, member->value.cptr->name);
+             pblen2 += strlen(member->value.cptr->name);
+             if (6 == ++cnt)
+             {
+               modebuf[mblen2] = 0;
+               sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s%s",
+                   parv[0], chptr->chname, modebuf, parabuf);
+#ifndef NO_PROTOCOL9
+               sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s%s " TIME_T_FMT,
+                   parv[0], chptr->chname, modebuf, parabuf,
+                   chptr->creationtime);
+               ts_sent = 1;
+#endif
+               *parabuf = 0;
+               pblen2 = 0;
+               mblen2 = 1;
+               cnt = 0;
+             }
+           }
+           if (mode == CHFL_CHANOP)
+             mode = CHFL_VOICE;
+           else
+             break;
+         }
+       }
+      }
+      /* Flush MODEs: */
+      if (cnt > 0 || mblen2 > 1)
+      {
+       modebuf[mblen2] = 0;
+       sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s%s",
+           parv[0], chptr->chname, modebuf, parabuf);
+#ifndef NO_PROTOCOL9
+       sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s%s " TIME_T_FMT,
+           parv[0], chptr->chname, modebuf, parabuf, chptr->creationtime);
+       ts_sent = 1;
+#endif
+      }
+#ifndef NO_PROTOCOL9
+      else if (send_it && !ts_sent)
+      {
+       sendto_lowprot_butone(cptr, 9, ":%s MODE %s + " TIME_T_FMT,
+           parv[0], chptr->chname, chptr->creationtime);
+       ts_sent = 1;
+      }
+#endif
+    }
+  }
+
+  if (wipeout)
+  {
+    Link *lp;
+    Link **ban;
+    int mode;
+    char m;
+    int count = -1;
+
+    /* Now cancel all previous simple modes */
+    if ((prev_mode & MODE_SECRET))
+      cancel_mode(sptr, chptr, 's', NULL, &count);
+    if ((prev_mode & MODE_PRIVATE))
+      cancel_mode(sptr, chptr, 'p', NULL, &count);
+    if ((prev_mode & MODE_MODERATED))
+      cancel_mode(sptr, chptr, 'm', NULL, &count);
+    if ((prev_mode & MODE_TOPICLIMIT))
+      cancel_mode(sptr, chptr, 't', NULL, &count);
+    if ((prev_mode & MODE_INVITEONLY))
+      cancel_mode(sptr, chptr, 'i', NULL, &count);
+    if ((prev_mode & MODE_NOPRIVMSGS))
+      cancel_mode(sptr, chptr, 'n', NULL, &count);
+    if ((prev_mode & MODE_LIMIT))
+    {
+      current_mode->limit = 0;
+      cancel_mode(sptr, chptr, 'l', NULL, &count);
+    }
+    if ((prev_mode & MODE_KEY))
+    {
+      *current_mode->key = 0;
+      cancel_mode(sptr, chptr, 'k', prev_key, &count);
+    }
+    current_mode->mode &= ~prev_mode;
+
+    /* And deop and devoice all net.riders on my side */
+    mode = CHFL_CHANOP;
+    m = 'o';
+    for (;;)
+    {
+      for (lp = chptr->members; lp; lp = lp->next)
+      {
+       if ((lp->flags & CHFL_BURST_JOINED))
+         continue;             /* This is not a net.rider from
+                                  this side of the net.junction */
+       if ((lp->flags & mode))
+       {
+         lp->flags &= ~mode;
+         if (mode == CHFL_CHANOP)
+           lp->flags |= CHFL_DEOPPED;
+         cancel_mode(sptr, chptr, m, lp->value.cptr->name, &count);
+       }
+      }
+      if (mode == CHFL_VOICE)
+       break;
+      mode = CHFL_VOICE;
+      m = 'v';
+    }
+
+    /* And finally wipeout all bans that are left */
+    for (ban = &chptr->banlist; *ban;)
+    {
+      Link *tmp = *ban;
+      if ((tmp->flags & CHFL_BURST_BAN_WIPEOUT))
+      {
+       cancel_mode(sptr, chptr, 'b', tmp->value.ban.banstr, &count);
+       /* Copied from del_banid(): */
+       *ban = tmp->next;
+       RunFree(tmp->value.ban.banstr);
+       RunFree(tmp->value.ban.who);
+       free_link(tmp);
+       /* Erase ban-valid-bit, for channel members that are banned */
+       for (tmp = chptr->members; tmp; tmp = tmp->next)
+         if ((tmp->flags & (CHFL_BANNED | CHFL_BANVALID)) ==
+             (CHFL_BANNED | CHFL_BANVALID))
+           tmp->flags &= ~CHFL_BANVALID;       /* `tmp' == channel member */
+      }
+      else
+       ban = &tmp->next;
+    }
+    /* Also wipeout overlapped bans */
+    if (!add_banid_not_called)
+    {
+      Link *ban;
+      while ((ban = next_removed_overlapped_ban()))
+       cancel_mode(sptr, chptr, 'b', ban->value.ban.banstr, &count);
+    }
+    cancel_mode(sptr, chptr, 0, NULL, &count); /* flush */
+  }
+
+  if (send_it && !netride)
+  {
+    Link *bl;
+    int deban;
+
+    if (add_banid_not_called || !(bl = next_removed_overlapped_ban()))
+    {
+      deban = 0;
+      bl = chptr->banlist;
+      *modebuf = '+';
+    }
+    else
+    {
+      deban = 1;
+      *modebuf = '-';
+    }
+
+    mblen2 = 1;
+    pblen2 = 0;
+    cnt = 0;
+    for (;;)
+    {
+      size_t nblen = 0;
+      if (bl)
+       nblen = strlen(bl->value.ban.banstr);
+      if (cnt == 6 || (!bl && cnt) || pblen2 + nblen + 12 > MODEBUFLEN)        /* The last check is to make sure
+                                                                          that the receiving 2.9 will
+                                                                          still process this */
+      {
+       /* Time to send buffer */
+       modebuf[mblen2] = 0;
+       sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s%s",
+           parv[0], chptr->chname, modebuf, parabuf);
+#ifndef NO_PROTOCOL9
+       sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s%s",
+           parv[0], chptr->chname, modebuf, parabuf);
+#endif
+       *modebuf = deban ? '-' : '+';
+       mblen2 = 1;
+       pblen2 = 0;
+       cnt = 0;
+      }
+      if (!bl)                 /* Done ? */
+       break;
+      if (deban || (bl->flags & CHFL_BURST_BAN))
+      {
+       /* Add ban to buffers and remove it */
+       modebuf[mblen2++] = 'b';
+       parabuf[pblen2++] = ' ';
+       strcpy(parabuf + pblen2, bl->value.ban.banstr);
+       pblen2 += nblen;
+       cnt++;
+       bl->flags &= ~CHFL_BURST_BAN;
+      }
+      if (deban)
+      {
+       if (!(bl = next_removed_overlapped_ban()))
+       {
+         deban = 0;
+         modebuf[mblen2++] = '+';
+         bl = chptr->banlist;
+       }
+      }
+      else
+       bl = bl->next;
+    }
+    /* Flush MODE [-b]+b ...: */
+    if (cnt > 0 || mblen2 > 1)
+    {
+      modebuf[mblen2] = 0;
+      sendto_channel_butserv(chptr, sptr, ":%s MODE %s %s%s",
+         parv[0], chptr->chname, modebuf, parabuf);
+#ifndef NO_PROTOCOL9
+      sendto_lowprot_butone(cptr, 9, ":%s MODE %s %s%s " TIME_T_FMT,
+         parv[0], chptr->chname, modebuf, parabuf, chptr->creationtime);
+#endif
+    }
+#ifndef NO_PROTOCOL9
+    else if (send_it && !ts_sent)
+      sendto_lowprot_butone(cptr, 9, ":%s MODE %s + " TIME_T_FMT,
+         parv[0], chptr->chname, chptr->creationtime);
+#endif
+  }
+
+  return 0;
+}
+
+/*
+ * m_part
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[parc - 1] = comment
+ */
+int m_part(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 aChannel *chptr;
+  Reg2 Link *lp;
+  char *p = NULL, *name, pbuf[BUFSIZE];
+  char *comment = (parc > 2 && !BadPtr(parv[parc - 1])) ? parv[parc - 1] : NULL;
+
+  *pbuf = '\0';                        /* Initialize the part buffer... -Kev */
+
+  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, !CREATE);
+    if (!chptr)
+    {
+      sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], name);
+      continue;
+    }
+    if (*name == '&' && !MyUser(sptr))
+      continue;
+    /* Do not use IsMember here: zombies must be able to part too */
+    if (!(lp = find_user_link(chptr->members, sptr)))
+    {
+      /* Normal to 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 (MyUser(sptr))
+       sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],
+           chptr->chname);
+      continue;
+    }
+    /* Recreate the /part list for sending to servers */
+    if (*name != '&')
+    {
+      if (*pbuf)
+       strcat(pbuf, ",");
+      strcat(pbuf, name);
+    }
+    if (can_send(sptr, chptr) != 0)    /* Returns 0 if we CAN send */
+      comment = NULL;
+    /* Send part to all clients */
+    if (!(lp->flags & CHFL_ZOMBIE))
+    {
+      if (comment)
+       sendto_channel_butserv(chptr, sptr, PartFmt2, parv[0], chptr->chname,
+           comment);
+      else
+       sendto_channel_butserv(chptr, sptr, PartFmt1, parv[0], chptr->chname);
+    }
+    else if (MyUser(sptr))
+    {
+      if (comment)
+       sendto_one(sptr, PartFmt2, parv[0], chptr->chname, comment);
+      else
+       sendto_one(sptr, PartFmt1, parv[0], chptr->chname);
+    }
+    remove_user_from_channel(sptr, chptr);
+  }
+  /* Send out the parts to all servers... -Kev */
+  if (*pbuf)
+  {
+    if (comment)
+      sendto_serv_butone(cptr, PartFmt2, parv[0], pbuf, comment);
+    else
+      sendto_serv_butone(cptr, PartFmt1, parv[0], pbuf);
+  }
+  return 0;
+}
+
+/*
+ * m_kick
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ * parv[2] = client to kick
+ * parv[parc-1] = kick comment
+ */
+int m_kick(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aClient *who;
+  aChannel *chptr;
+  char *comment;
+  Link *lp, *lp2;
+
+  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))
+    send_hack_notice(cptr, sptr, parc, parv, 1, 3);
+
+  comment = (BadPtr(parv[parc - 1])) ? parv[0] : parv[parc - 1];
+  if (strlen(comment) > (size_t)TOPICLEN)
+    comment[TOPICLEN] = '\0';
+
+  *nickbuf = *buf = '\0';
+
+  chptr = get_channel(sptr, parv[1], !CREATE);
+  if (!chptr)
+  {
+    sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]);
+    return 0;
+  }
+  if (IsLocalChannel(parv[1]) && !MyUser(sptr))
+    return 0;
+  if (IsModelessChannel(parv[1]))
+  {
+    sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
+       chptr->chname);
+    return 0;
+  }
+  if (!IsServer(cptr) && !is_chan_op(sptr, chptr))
+  {
+    sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
+       me.name, parv[0], chptr->chname);
+    return 0;
+  }
+
+  lp2 = find_user_link(chptr->members, sptr);
+  if (MyUser(sptr) || Protocol(cptr) < 10)
+  {
+    if (!(who = find_chasing(sptr, parv[2], NULL)))
+      return 0;                        /* No such user left! */
+  }
+  else if (!(who = findNUser(parv[2])))
+    return 0;                  /* No such user left! */
+  /* if the user is +k, prevent a kick from local user */
+  if (IsChannelService(who) && MyUser(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name,
+       parv[0], who->name, chptr->chname);
+    return 0;
+  }
+  if (((lp = find_user_link(chptr->members, who)) &&
+      !(lp->flags & CHFL_ZOMBIE)) || IsServer(sptr))
+  {
+    if (who->from != cptr &&
+       ((lp2 && (lp2->flags & CHFL_DEOPPED)) || (!lp2 && IsUser(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, parv[1]);
+      if (lp->flags & CHFL_CHANOP)
+      {
+       if (Protocol(cptr) < 10)
+         sendto_one(cptr, ":%s MODE %s +o %s " TIME_T_FMT,
+             me.name, parv[1], who->name, chptr->creationtime);
+       else
+         sendto_one(cptr, "%s MODE %s +o %s%s " TIME_T_FMT,
+             NumServ(&me), parv[1], NumNick(who), chptr->creationtime);
+      }
+      if (lp->flags & CHFL_VOICE)
+      {
+       if (Protocol(cptr) < 10)
+         sendto_one(cptr, ":%s MODE %s +v %s " TIME_T_FMT,
+             me.name, chptr->chname, who->name, chptr->creationtime);
+       else
+         sendto_one(cptr, "%s MODE %s +v %s%s " TIME_T_FMT,
+             NumServ(&me), parv[1], NumNick(who), chptr->creationtime);
+      }
+    }
+    else
+    {
+      if (lp)
+       sendto_channel_butserv(chptr, sptr,
+           ":%s KICK %s %s :%s", parv[0], chptr->chname, who->name, comment);
+      if (!IsLocalChannel(parv[1]))
+      {
+       sendto_lowprot_butone(cptr, 9, ":%s KICK %s %s :%s",
+           parv[0], chptr->chname, who->name, comment);
+       sendto_highprot_butone(cptr, 10, ":%s KICK %s %s%s :%s",
+           parv[0], parv[1], NumNick(who), comment);
+      }
+      if (lp)
+      {
+/*
+ * Consider:
+ *
+ *                     client
+ *                       |
+ *                       c
+ *                       |
+ *     X --a--> A --b--> B --d--> D
+ *                       |
+ *                      who
+ *
+ * Where `who' is being KICK-ed by a "KICK" message received by server 'A'
+ * via 'a', or on server 'B' via either 'b' or 'c', or on server D via 'd'.
+ *
+ * a) On server A : set CHFL_ZOMBIE for `who' (lp) and pass on the KICK.
+ *    Remove the user immedeately when no users are left on the channel.
+ * b) On server B : remove the user (who/lp) from the channel, send a
+ *    PART upstream (to A) and pass on the KICK.
+ * c) KICKed by `client'; On server B : remove the user (who/lp) from the
+ *    channel, and pass on the KICK.
+ * d) On server D : remove the user (who/lp) from the channel, and pass on
+ *    the KICK.
+ *
+ * Note:
+ * - Setting the ZOMBIE flag never hurts, we either remove the
+ *   client after that or we don't.
+ * - The KICK message was already passed on, as should be in all cases.
+ * - `who' is removed in all cases except case a) when users are left.
+ * - A PART is only sent upstream in case b).
+ *
+ * 2 aug 97:
+ *
+ *              6
+ *              |
+ *  1 --- 2 --- 3 --- 4 --- 5
+ *        |           |
+ *      kicker       who
+ *
+ * We also need to turn 'who' into a zombie on servers 1 and 6,
+ * because a KICK from 'who' (kicking someone else in that direction)
+ * can arrive there afterwards - which should not be bounced itself.
+ * Therefore case a) also applies for servers 1 and 6.
+ *
+ * --Run
+ */
+       /* Default for case a): */
+       lp->flags |= CHFL_ZOMBIE;
+       /* Case b) or c) ?: */
+       if (MyUser(who))        /* server 4 */
+       {
+         if (IsServer(cptr))   /* Case b) ? */
+           sendto_one(cptr, PartFmt1, who->name, parv[1]);
+         remove_user_from_channel(who, chptr);
+         return 0;
+       }
+       if (who->from == cptr)  /* True on servers 1, 5 and 6 */
+       {
+         aClient *acptr = IsServer(sptr) ? sptr : sptr->user->server;
+         for (; acptr != &me; acptr = acptr->serv->up)
+           if (acptr == who->user->server)     /* Case d) (server 5) */
+           {
+             remove_user_from_channel(who, chptr);
+             return 0;
+           }
+       }
+       /* Case a) (servers 1, 2, 3 and 6) */
+       for (lp = chptr->members; lp; lp = lp->next)
+         if (!(lp->flags & CHFL_ZOMBIE))
+           break;
+       if (!lp)
+         remove_user_from_channel(who, chptr);
+#ifdef GODMODE
+       else
+         sendto_op_mask(SNO_HACK2, "%s is now a zombie on %s",
+             who->name, chptr->chname);
+#endif
+      }
+    }
+  }
+  else if (MyUser(sptr))
+    sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
+       me.name, parv[0], who->name, chptr->chname);
+
+  return 0;
+}
+
+/*
+ * m_topic
+ *
+ * parv[0]        = sender prefix
+ * parv[1]        = channel
+ * parv[parc - 1] = topic (if parc > 2)
+ */
+int m_topic(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aChannel *chptr;
+  char *topic = NULL, *name, *p = NULL;
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "TOPIC");
+    return 0;
+  }
+
+  if (parc > 2)
+    topic = parv[parc - 1];
+
+  for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+  {
+    chptr = NULL;
+    if (!IsChannelName(name) || !(chptr = FindChannel(name)) ||
+       ((topic || SecretChannel(chptr)) && !IsMember(sptr, chptr)))
+    {
+      sendto_one(sptr, err_str(chptr ? ERR_NOTONCHANNEL : ERR_NOSUCHCHANNEL),
+         me.name, parv[0], chptr ? chptr->chname : name);
+      continue;
+    }
+    if (IsModelessChannel(name))
+    {
+      sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, parv[0],
+         chptr->chname);
+      continue;
+    }
+    if (IsLocalChannel(name) && !MyUser(sptr))
+      continue;
+
+    if (!topic)                        /* only asking  for topic  */
+    {
+      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 if (((chptr->mode.mode & MODE_TOPICLIMIT) == 0 ||
+       is_chan_op(sptr, chptr)) && topic)
+    {
+      /* setting a topic */
+      strncpy(chptr->topic, topic, TOPICLEN);
+      strncpy(chptr->topic_nick, sptr->name, NICKLEN);
+      chptr->topic_time = now;
+      sendto_serv_butone(cptr, ":%s TOPIC %s :%s",
+         parv[0], chptr->chname, chptr->topic);
+      sendto_channel_butserv(chptr, sptr, ":%s TOPIC %s :%s",
+         parv[0], chptr->chname, chptr->topic);
+    }
+    else
+      sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
+         me.name, parv[0], chptr->chname);
+  }
+  return 0;
+}
+
+/*
+ * m_invite
+ *   parv[0] - sender prefix
+ *   parv[1] - user to invite
+ *   parv[2] - channel name
+ *
+ * - INVITE now is accepted only if who does it is chanop (this of course
+ *   implies that channel must exist and he must be on it).
+ *
+ * - On the other side it IS processed even if channel is NOT invite only
+ *   leaving room for other enhancements like inviting banned ppl.  -- Nemesi
+ *
+ */
+int m_invite(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr;
+  aChannel *chptr;
+
+  if (parc < 3 || *parv[2] == '\0')
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "INVITE");
+    return 0;
+  }
+
+  if (!(acptr = FindUser(parv[1])))
+  {
+    sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
+    return 0;
+  }
+
+  if (is_silenced(sptr, acptr))
+    return 0;
+
+  if (MyUser(sptr))
+    clean_channelname(parv[2]);
+  else if (IsLocalChannel(parv[2]))
+    return 0;
+
+  if (*parv[2] == '0' || !IsChannelName(parv[2]))
+    return 0;
+
+  if (!(chptr = FindChannel(parv[2])))
+  {
+    if (IsModelessChannel(parv[2]) || IsLocalChannel(parv[2]))
+    {
+      sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0], parv[2]);
+      return 0;
+    }
+
+    /* Do not disallow to invite to non-existant #channels, otherwise they
+       would simply first be created, causing only MORE bandwidth usage. */
+    if (MyConnect(sptr))
+    {
+      if (check_target_limit(sptr, acptr, acptr->name, 0))
+       return 0;
+
+      sendto_one(sptr, rpl_str(RPL_INVITING), me.name, parv[0],
+         acptr->name, parv[2]);
+
+      if (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 INVITE %s :%s", parv[0],
+       acptr->name, parv[2]);
+
+    return 0;
+  }
+
+  if (!IsMember(sptr, chptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOTONCHANNEL), me.name, parv[0],
+       chptr->chname);
+    return 0;
+  }
+
+  if (IsMember(acptr, chptr))
+  {
+    sendto_one(sptr, err_str(ERR_USERONCHANNEL),
+       me.name, parv[0], acptr->name, chptr->chname);
+    return 0;
+  }
+
+  if (MyConnect(sptr))
+  {
+    if (!is_chan_op(sptr, chptr))
+    {
+      sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED),
+         me.name, parv[0], chptr->chname);
+      return 0;
+    }
+
+    /* If we get here, it was a VALID and meaningful INVITE */
+
+    if (check_target_limit(sptr, acptr, acptr->name, 0))
+      return 0;
+
+    sendto_one(sptr, rpl_str(RPL_INVITING), me.name, parv[0],
+       acptr->name, chptr->chname);
+
+    if (acptr->user->away)
+      sendto_one(sptr, rpl_str(RPL_AWAY), me.name, parv[0],
+         acptr->name, acptr->user->away);
+  }
+
+  if (MyConnect(acptr))
+    add_invite(acptr, chptr);
+
+  sendto_prefix_one(acptr, sptr, ":%s INVITE %s :%s", parv[0],
+      acptr->name, chptr->chname);
+
+  return 0;
+}
+
+static int number_of_zombies(aChannel *chptr)
+{
+  Reg1 Link *lp;
+  Reg2 int count = 0;
+  for (lp = chptr->members; lp; lp = lp->next)
+    if (lp->flags & CHFL_ZOMBIE)
+      count++;
+  return count;
+}
+
+/*
+ * m_list
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel list or user/time limit
+ * parv[2...] = more user/time limits
+ */
+int m_list(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  aChannel *chptr;
+  char *name, *p = NULL;
+  int show_usage = 0, show_channels = 0, param;
+  aListingArgs args = {
+    2147483647,                        /* max_time */
+    0,                         /* min_time */
+    4294967295U,               /* max_users */
+    0,                         /* min_users */
+    0,                         /* topic_limits */
+    2147483647,                        /* max_topic_time */
+    0,                         /* min_topic_time */
+    NULL                       /* chptr */
+  };
+
+  if (sptr->listing)           /* Already listing ? */
+  {
+    sptr->listing->chptr->mode.mode &= ~MODE_LISTED;
+    RunFree(sptr->listing);
+    sptr->listing = NULL;
+    sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, sptr->name);
+    if (parc < 2)
+      return 0;                        /* Let LIST abort a listing. */
+  }
+
+  if (parc < 2)                        /* No arguments given to /LIST ? */
+  {
+#ifdef DEFAULT_LIST_PARAM
+    static char *defparv[MAXPARA + 1];
+    static int defparc = 0;
+    static char lp[] = DEFAULT_LIST_PARAM;
+    int i;
+
+    if (!defparc)
+    {
+      char *s = lp, *t;
+
+      defparc = 1;
+      defparv[defparc++] = t = strtok(s, " ");
+      while (t && defparc < MAXPARA)
+      {
+       if ((t = strtok(NULL, " ")))
+         defparv[defparc++] = t;
+      }
+    }
+    for (i = 1; i < defparc; i++)
+      parv[i] = defparv[i];
+    parv[i] = NULL;
+    parc = defparc;
+#endif /* DEFAULT_LIST_PARAM */
+  }
+
+  /* Decode command */
+  for (param = 1; !show_usage && parv[param]; param++)
+  {
+    char *p = parv[param];
+    do
+    {
+      int is_time = 0;
+      switch (*p)
+      {
+       case 'T':
+       case 't':
+         is_time++;
+         args.topic_limits = 1;
+         /* Fall through */
+       case 'C':
+       case 'c':
+         is_time++;
+         p++;
+         if (*p != '<' && *p != '>')
+         {
+           show_usage = 1;
+           break;
+         }
+         /* Fall through */
+       case '<':
+       case '>':
+       {
+         p++;
+         if (!isDigit(*p))
+           show_usage = 1;
+         else
+         {
+           if (is_time)
+           {
+             time_t val = atoi(p);
+             if (p[-1] == '<')
+             {
+               if (val < 80000000)     /* Toggle UTC/offset */
+               {
+                 /*
+                  * Demands that
+                  * 'TStime() - chptr->creationtime < val * 60'
+                  * Which equals
+                  * 'chptr->creationtime > TStime() - val * 60'
+                  */
+                 if (is_time == 1)
+                   args.min_time = TStime() - val * 60;
+                 else
+                   args.min_topic_time = TStime() - val * 60;
+               }
+               else if (is_time == 1)  /* Creation time in UTC was entered */
+                 args.max_time = val;
+               else            /* Topic time in UTC was entered */
+                 args.max_topic_time = val;
+             }
+             else if (val < 80000000)
+             {
+               if (is_time == 1)
+                 args.max_time = TStime() - val * 60;
+               else
+                 args.max_topic_time = TStime() - val * 60;
+             }
+             else if (is_time == 1)
+               args.min_time = val;
+             else
+               args.min_topic_time = val;
+           }
+           else if (p[-1] == '<')
+             args.max_users = atoi(p);
+           else
+             args.min_users = atoi(p);
+           if ((p = strchr(p, ',')))
+             p++;
+         }
+         break;
+       }
+       default:
+         if (!IsChannelName(p))
+         {
+           show_usage = 1;
+           break;
+         }
+         if (parc != 2)        /* Don't allow a mixture of channels with <,> */
+           show_usage = 1;
+         show_channels = 1;
+         p = NULL;
+         break;
+      }
+    }
+    while (!show_usage && p);  /* p points after comma, or is NULL */
+  }
+
+  if (show_usage)
+  {
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       "Usage: \002/QUOTE LIST\002 \037parameters\037");
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       "Where \037parameters\037 is a space or comma seperated "
+       "list of one or more of:");
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       " \002<\002\037max_users\037    ; Show all channels with less "
+       "than \037max_users\037.");
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       " \002>\002\037min_users\037    ; Show all channels with more "
+       "than \037min_users\037.");
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       " \002C<\002\037max_minutes\037 ; Channels that exist less "
+       "than \037max_minutes\037.");
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       " \002C>\002\037min_minutes\037 ; Channels that exist more "
+       "than \037min_minutes\037.");
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       " \002T<\002\037max_minutes\037 ; Channels with a topic last "
+       "set less than \037max_minutes\037 ago.");
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       " \002T>\002\037min_minutes\037 ; Channels with a topic last "
+       "set more than \037min_minutes\037 ago.");
+    sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
+       "Example: LIST <3,>1,C<10,T>0  ; 2 users, younger than 10 min., "
+       "topic set.");
+    return 0;
+  }
+
+  sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]);
+
+  if (!show_channels)
+  {
+    if (args.max_users > args.min_users + 1 && args.max_time > args.min_time &&
+       args.max_topic_time > args.min_topic_time)      /* Sanity check */
+    {
+      if ((sptr->listing = (aListingArgs *)RunMalloc(sizeof(aListingArgs))))
+      {
+       memcpy(sptr->listing, &args, sizeof(aListingArgs));
+       if ((sptr->listing->chptr = channel))
+       {
+         int m = channel->mode.mode & MODE_LISTED;
+         list_next_channels(sptr, 64);
+         channel->mode.mode |= m;
+         return 0;
+       }
+       RunFree(sptr->listing);
+       sptr->listing = NULL;
+      }
+    }
+    sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
+    return 0;
+  }
+
+  for (; (name = strtoken(&p, parv[1], ",")); parv[1] = NULL)
+  {
+    chptr = FindChannel(name);
+    if (chptr && ShowChannel(sptr, chptr) && sptr->user)
+      sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0],
+         ShowChannel(sptr, chptr) ? chptr->chname : "*",
+         chptr->users - number_of_zombies(chptr), chptr->topic);
+  }
+
+  sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
+  return 0;
+}
+
+/*
+ * m_names                              - Added by Jto 27 Apr 1989
+ *
+ * parv[0] = sender prefix
+ * parv[1] = channel
+ */
+int m_names(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 aChannel *chptr;
+  Reg2 aClient *c2ptr;
+  Reg3 Link *lp;
+  aChannel *ch2ptr = NULL;
+  int idx, flag, len, mlen;
+  char *s, *para = parc > 1 ? parv[1] : NULL;
+
+  if (parc > 2 && hunt_server(1, cptr, sptr, ":%s NAMES %s %s", 2, parc, parv))
+    return 0;
+
+  mlen = strlen(me.name) + 10 + strlen(sptr->name);
+
+  if (!BadPtr(para))
+  {
+    s = strchr(para, ',');
+    if (s)
+    {
+      parv[1] = ++s;
+      m_names(cptr, sptr, parc, parv);
+    }
+    clean_channelname(para);
+    ch2ptr = FindChannel(para);
+  }
+
+  /*
+   * 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;
+#ifndef GODMODE
+    if (!ShowChannel(sptr, chptr))
+      continue;                        /* -- users on this are not listed */
+#endif
+
+    /* Find users on same channel (defined by chptr) */
+
+    strcpy(buf, "* ");
+    len = strlen(chptr->chname);
+    strcpy(buf + 2, chptr->chname);
+    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;
+#ifndef GODMODE
+      if (sptr != c2ptr && IsInvisible(c2ptr) && !IsMember(sptr, chptr))
+       continue;
+#endif
+      if (lp->flags & CHFL_ZOMBIE)
+      {
+       if (lp->value.cptr != sptr)
+         continue;
+       else
+       {
+         strcat(buf, "!");
+         idx++;
+       }
+      }
+      else if (lp->flags & CHFL_CHANOP)
+      {
+       strcat(buf, "@");
+       idx++;
+      }
+      else if (lp->flags & CHFL_VOICE)
+      {
+       strcat(buf, "+");
+       idx++;
+      }
+      strcat(buf, c2ptr->name);
+      strcat(buf, " ");
+      idx += strlen(c2ptr->name) + 1;
+      flag = 1;
+#ifdef GODMODE
+      {
+       char yxx[6];
+       sprintf_irc(yxx, "%s%s", NumNick(c2ptr));
+       if (c2ptr != findNUser(yxx))
+         MyCoreDump;
+       sprintf_irc(buf + strlen(buf), "(%s) ", yxx);
+       idx += 6;
+      }
+      if (mlen + idx + NICKLEN + 11 > BUFSIZE)
+#else
+      if (mlen + idx + NICKLEN + 5 > BUFSIZE)
+#endif
+       /* space, modifier, nick, \r \n \0 */
+      {
+       sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
+       strcpy(buf, "* ");
+       strncpy(buf + 2, chptr->chname, len + 1);
+       buf[len + 2] = 0;
+       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],
+       ch2ptr ? ch2ptr->chname : para);
+    return (1);
+  }
+
+  /* Second, do all non-public, non-secret channels in one big sweep */
+
+  strcpy(buf, "* * :");
+  idx = 5;
+  flag = 0;
+  for (c2ptr = client; c2ptr; c2ptr = c2ptr->next)
+  {
+    aChannel *ch3ptr;
+    int showflag = 0, secret = 0;
+
+#ifndef GODMODE
+    if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr)))
+#else
+    if (!IsUser(c2ptr))
+#endif
+      continue;
+    lp = c2ptr->user->channel;
+    /*
+     * Don't show a client if they are on a secret channel or when
+     * they are on a channel sptr is on since they have already
+     * been show earlier. -avalon
+     */
+    while (lp)
+    {
+      ch3ptr = lp->value.chptr;
+#ifndef GODMODE
+      if (PubChannel(ch3ptr) || IsMember(sptr, ch3ptr))
+#endif
+       showflag = 1;
+      if (SecretChannel(ch3ptr))
+       secret = 1;
+      lp = lp->next;
+    }
+    if (showflag)              /* Have we already shown them ? */
+      continue;
+#ifndef GODMODE
+    if (secret)                        /* On any secret channels ? */
+      continue;
+#endif
+    strcat(buf, c2ptr->name);
+    strcat(buf, " ");
+    idx += strlen(c2ptr->name) + 1;
+    flag = 1;
+#ifdef GODMODE
+    {
+      char yxx[6];
+      sprintf_irc(yxx, "%s%s", NumNick(c2ptr));
+      if (c2ptr != findNUser(yxx))
+       MyCoreDump;
+      sprintf_irc(buf + strlen(buf), "(%s) ", yxx);
+      idx += 6;
+    }
+#endif
+#ifdef GODMODE
+    if (mlen + idx + NICKLEN + 9 > BUFSIZE)
+#else
+    if (mlen + idx + NICKLEN + 3 > BUFSIZE)    /* space, \r\n\0 */
+#endif
+    {
+      sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf);
+      strcpy(buf, "* * :");
+      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], "*");
+  return (1);
+}
+
+void send_user_joins(aClient *cptr, aClient *user)
+{
+  Reg1 Link *lp;
+  Reg2 aChannel *chptr;
+  Reg3 int cnt = 0, len = 0, clen;
+  char *mask;
+
+  *buf = ':';
+  strcpy(buf + 1, user->name);
+  strcat(buf, " JOIN ");
+  len = strlen(user->name) + 7;
+
+  for (lp = user->user->channel; lp; lp = lp->next)
+  {
+    chptr = lp->value.chptr;
+    if ((mask = strchr(chptr->chname, ':')))
+      if (match(++mask, cptr->name))
+       continue;
+    if (*chptr->chname == '&')
+      continue;
+    if (is_zombie(user, chptr))
+      continue;
+    clen = strlen(chptr->chname);
+    if (clen + 1 + len > BUFSIZE - 3)
+    {
+      if (cnt)
+      {
+       buf[len - 1] = '\0';
+       sendto_one(cptr, "%s", buf);
+      }
+      *buf = ':';
+      strcpy(buf + 1, user->name);
+      strcat(buf, " JOIN ");
+      len = strlen(user->name) + 7;
+      cnt = 0;
+    }
+    strcpy(buf + len, chptr->chname);
+    cnt++;
+    len += clen;
+    if (lp->next)
+    {
+      len++;
+      strcat(buf, ",");
+    }
+  }
+  if (*buf && cnt)
+    sendto_one(cptr, "%s", buf);
+
+  return;
+}
+
+/*
+ * send_hack_notice()
+ *
+ * parc & parv[] are the same as that of the calling function:
+ *   mtype == 1 is from m_mode, 2 is from m_create, 3 is from m_kick.
+ *
+ * This function prepares sendbuf with the server notices and wallops
+ *   to be sent for all hacks.  -Ghostwolf 18-May-97
+ */
+
+static void send_hack_notice(aClient *cptr, aClient *sptr, int parc,
+    char *parv[], int badop, int mtype)
+{
+  aChannel *chptr;
+  static char params[MODEBUFLEN];
+  int i = 3;
+  chptr = FindChannel(parv[1]);
+  *params = '\0';
+
+  if (Protocol(cptr) < 10)     /* We don't get numeric nicks from P09  */
+  {                            /* servers, so this can be sent "As Is" */
+    if (mtype == 1)
+    {
+      while (i < parc)
+      {
+       strcat(params, " ");
+       strcat(params, parv[i++]);
+      }
+      sprintf_irc(sendbuf,
+         ":%s NOTICE * :*** Notice -- %sHACK(%d): %s MODE %s %s%s [" TIME_T_FMT
+         "]", me.name, (badop == 3) ? "BOUNCE or " : "", badop, parv[0],
+         parv[1], parv[2], params, chptr->creationtime);
+      sendbufto_op_mask((badop == 3) ? SNO_HACK3 : (badop ==
+         4) ? SNO_HACK4 : SNO_HACK2);
+
+      if ((IsServer(sptr)) && (badop == 2))
+      {
+       sprintf_irc(sendbuf, ":%s DESYNCH :HACK: %s MODE %s %s%s",
+           me.name, parv[0], parv[1], parv[2], params);
+       sendbufto_serv_butone(cptr);
+      }
+    }
+    else if (mtype == 3)
+    {
+      sprintf_irc(sendbuf,
+         ":%s NOTICE * :*** Notice -- HACK: %s KICK %s %s :%s",
+         me.name, sptr->name, parv[1], parv[2], parv[3]);
+      sendbufto_op_mask(SNO_HACK4);
+    }
+  }
+  else
+  {
+    /* P10 servers require numeric nick conversion before sending. */
+    switch (mtype)
+    {
+      case 1:                  /* Convert nicks for MODE HACKs here  */
+      {
+       char *mode = parv[2];
+       while (i < parc)
+       {
+         while (*mode && *mode != 'o' && *mode != 'v')
+           ++mode;
+         strcat(params, " ");
+         if (*mode == 'o' || *mode == 'v')
+         {
+           register aClient *acptr;
+           if ((acptr = findNUser(parv[i])) != NULL)   /* Convert nicks here */
+             strcat(params, acptr->name);
+           else
+           {
+             strcat(params, "<");
+             strcat(params, parv[i]);
+             strcat(params, ">");
+           }
+         }
+         else                  /* If it isn't a numnick, send it 'as is' */
+           strcat(params, parv[i]);
+         i++;
+       }
+       sprintf_irc(sendbuf,
+           ":%s NOTICE * :*** Notice -- %sHACK(%d): %s MODE %s %s%s ["
+           TIME_T_FMT "]", me.name, (badop == 3) ? "BOUNCE or " : "", badop,
+           parv[0], parv[1], parv[2], params, chptr->creationtime);
+       sendbufto_op_mask((badop == 3) ? SNO_HACK3 : (badop ==
+           4) ? SNO_HACK4 : SNO_HACK2);
+
+       if ((IsServer(sptr)) && (badop == 2))
+       {
+         sprintf_irc(sendbuf, ":%s DESYNCH :HACK: %s MODE %s %s%s",
+             me.name, parv[0], parv[1], parv[2], params);
+         sendbufto_serv_butone(cptr);
+       }
+       break;
+      }
+      case 2:                  /* No conversion is needed for CREATE; the only numnick is sptr */
+      {
+       sendto_serv_butone(cptr, ":%s DESYNCH :HACK: %s CREATE %s %s",
+           me.name, sptr->name, chptr->chname, parv[2]);
+       sendto_op_mask(SNO_HACK2, "HACK(2): %s CREATE %s %s",
+           sptr->name, chptr->chname, parv[2]);
+       break;
+      }
+      case 3:                  /* Convert nick in KICK message */
+      {
+       aClient *acptr;
+       if ((acptr = findNUser(parv[2])) != NULL)       /* attempt to convert nick */
+         sprintf_irc(sendbuf,
+             ":%s NOTICE * :*** Notice -- HACK: %s KICK %s %s :%s",
+             me.name, sptr->name, parv[1], acptr->name, parv[3]);
+       else                    /* if conversion fails, send it 'as is' in <>'s */
+         sprintf_irc(sendbuf,
+             ":%s NOTICE * :*** Notice -- HACK: %s KICK %s <%s> :%s",
+             me.name, sptr->name, parv[1], parv[2], parv[3]);
+       sendbufto_op_mask(SNO_HACK4);
+       break;
+      }
+    }
+  }
+}
diff --git a/ircd/chkconf.c b/ircd/chkconf.c
new file mode 100644 (file)
index 0000000..97eb441
--- /dev/null
@@ -0,0 +1,623 @@
+/*
+ * 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 "sys.h"
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HPUX
+#include <arpa/inet.h>
+#endif /* HPUX */
+#ifdef R_LINES
+#include <signal.h>
+#endif
+#include "h.h"
+#include "s_conf.h"
+#include "class.h"
+#include "common.h"
+#include "ircd.h"
+#include "fileio.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * For the connect rule patch..  these really should be in a header,
+ * but i see h.h isn't included for some reason..  so they're here.
+ */
+char *crule_parse(char *rule);
+void crule_free(char **elem);
+
+static void new_class(int cn);
+static char confchar(unsigned int status);
+static char *getfield(char *newline);
+static int validate(aConfItem *top);
+static aConfItem *chk_initconf(void);
+static aConfClass *get_class(int cn, int ism);
+
+static int numclasses = 0, *classarr = (int *)NULL, debugflag = 0;
+static char *chk_configfile = CPATH;
+static char nullfield[] = "";
+static char maxsendq[12];
+
+/* A few dummy variables and functions needed to link with runmalloc.o */
+time_t now;
+struct Client {
+  time_t since;
+  char name[1];
+} me;
+void debug(int UNUSED(level), const char *form, ...)
+{
+  va_list vl;
+  va_start(vl, form);
+  if (debugflag > 8)
+  {
+    vfprintf(stderr, form, vl);
+    fprintf(stderr, "\n");
+  }
+  va_end(vl);
+}
+void sendto_one(aClient *UNUSED(to), char *UNUSED(pattern), ...)
+{
+}
+char *rpl_str(int UNUSED(numeric))
+{
+  return "";
+}
+
+int main(int argc, char *argv[])
+{
+  const char *dpath = DPATH;
+  chk_configfile = "ircd.conf";
+
+  while (argc > 1)
+  {
+    if (*argv[1] == '-')
+    {
+      switch (argv[1][1])
+      {
+       case 'd':
+         if (argc > 2)
+         {
+           dpath = argv[2];
+           --argc;
+           ++argv;
+         }
+         else
+         {
+           fprintf(stderr, "-d: Missing path\n");
+           exit(-1);
+         }
+         break;
+       case 'x':
+         debugflag = 1;
+         if (isdigit(argv[1][2]))
+           debugflag = atoi(&argv[1][2]);
+         break;
+       default:
+         fprintf(stderr, "Ignoring unknown option -%c\n", argv[1][1]);
+      }
+    }
+    else
+      chk_configfile = argv[1];
+    --argc;
+    ++argv;
+  }
+
+  if (chdir(dpath))
+  {
+    fprintf(stderr, "chdir(\"%s\") : %s\n", dpath, strerror(errno));
+    exit(-1);
+  }
+  else if (debugflag > 1)
+    fprintf(stderr, "chdir(\"%s\") : Success", dpath);
+
+  new_class(0);
+
+  return validate(chk_initconf());
+}
+
+/*
+ * chk_initconf()
+ *
+ * Read configuration file.
+ *
+ * Returns -1, if file cannot be opened
+ *          0, if file opened.
+ */
+static aConfItem *chk_initconf(void)
+{
+  FBFILE *file;
+  char line[512], *tmp, *s, *crule;
+  int ccount = 0, ncount = 0, flags = 0;
+  aConfItem *aconf = NULL, *ctop = NULL;
+
+  fprintf(stderr, "chk_initconf(): ircd.conf = %s\n", chk_configfile);
+  if (NULL == (file = fbopen(chk_configfile, "r")))
+  {
+    perror("open");
+    return NULL;
+  }
+
+  while (fbgets(line, sizeof(line) - 1, file))
+  {
+    if (aconf)
+    {
+      if (aconf->host)
+       RunFree(aconf->host);
+      if (aconf->passwd)
+       RunFree(aconf->passwd);
+      if (aconf->name)
+       RunFree(aconf->name);
+    }
+    else
+      aconf = (aConfItem *)RunMalloc(sizeof(*aconf));
+    aconf->host = (char *)NULL;
+    aconf->passwd = (char *)NULL;
+    aconf->name = (char *)NULL;
+    aconf->confClass = (aConfClass *) NULL;
+    if ((tmp = strchr(line, '\n')))
+      *tmp = 0;
+    /*
+     * 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] != ':')
+    {
+      fprintf(stderr, "ERROR: Bad config line (%s)\n", line);
+      continue;
+    }
+
+    if (debugflag)
+      printf("\n%s\n", line);
+    fflush(stdout);
+
+    tmp = getfield(line);
+    if (!tmp)
+    {
+      fprintf(stderr, "\tERROR: no fields found\n");
+      continue;
+    }
+
+    aconf->status = CONF_ILLEGAL;
+
+    switch (*tmp)
+    {
+      case 'A':                /* Name, e-mail address of administrator */
+      case 'a':                /* of this server. */
+       aconf->status = CONF_ADMIN;
+       break;
+      case 'C':                /* Server where I should try to connect */
+      case 'c':                /* in case of lp failures             */
+       ccount++;
+       aconf->status = CONF_CONNECT_SERVER;
+       break;
+       /* Connect rule */
+      case 'D':
+       aconf->status = CONF_CRULEALL;
+       break;
+       /* Connect rule - autos only */
+      case 'd':
+       aconf->status = CONF_CRULEAUTO;
+       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           */
+       aconf->status = CONF_KILL;
+       break;
+      case 'k':                /* Kill user line based on IP in ircd.conf */
+       aconf->status = CONF_IPKILL;
+       break;
+       /* Operator. Line should contain at least */
+       /* password and host where connection is  */
+      case 'L':                /* guaranteed leaf server */
+      case 'l':
+       aconf->status = CONF_LEAF;
+       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) */
+      case 'o':
+       aconf->status = CONF_LOCOP;
+       break;
+      case 'P':                /* listen port line */
+      case 'p':
+       aconf->status = CONF_LISTEN_PORT;
+       break;
+#ifdef R_LINES
+      case 'R':                /* extended K line */
+      case 'r':                /* Offers more options of how to restrict */
+       aconf->status = CONF_RESTRICT;
+       break;
+#endif
+      case 'T':
+      case 't':
+       aconf->status = CONF_TLINES;
+       break;
+      case 'U':
+      case 'u':
+       aconf->status = CONF_UWORLD;
+       break;
+      case 'Y':
+      case 'y':
+       aconf->status = CONF_CLASS;
+       break;
+      default:
+       fprintf(stderr, "\tERROR: unknown conf line letter (%c)\n", *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;
+      aconf->port = atoi(tmp);
+      if ((tmp = getfield(NULL)) == NULL)
+       break;
+      if (!(aconf->status & (CONF_CLASS | CONF_ME)))
+      {
+       aconf->confClass = get_class(atoi(tmp), 0);
+       break;
+      }
+      if (aconf->status & CONF_ME)
+       aconf->confClass = get_class(atoi(tmp), 1);
+      break;
+    }
+    if (!aconf->confClass && (aconf->status & (CONF_CONNECT_SERVER |
+       CONF_ME | CONF_NOCONNECT_SERVER | CONF_OPS | CONF_CLIENT)))
+    {
+      fprintf(stderr, "\tWARNING: No class.     Default 0\n");
+      aconf->confClass = get_class(0, 0);
+    }
+    /*
+     * 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)
+      {
+       fprintf(stderr, "\tERROR: no class #\n");
+       continue;
+      }
+      if (!tmp)
+      {
+       fprintf(stderr, "\tWARNING: missing sendq field\n");
+       fprintf(stderr, "\t\t default: %d\n", DEFAULTMAXSENDQLENGTH);
+       sprintf(maxsendq, "%d", DEFAULTMAXSENDQLENGTH);
+      }
+      else
+       sprintf(maxsendq, "%d", atoi(tmp));
+      new_class(atoi(aconf->host));
+      aconf->confClass = get_class(atoi(aconf->host), 0);
+      goto print_confline;
+    }
+
+    if (aconf->status & CONF_LISTEN_PORT)
+    {
+#ifdef UNIXPORT
+      struct stat sb;
+
+      if (!aconf->host)
+       fprintf(stderr, "\tERROR: %s\n", "null host field in P-line");
+      else if (strchr(aconf->host, '/'))
+      {
+       if (stat(aconf->host, &sb) == -1)
+       {
+         fprintf(stderr, "\tERROR: (%s) ", aconf->host);
+         perror("stat");
+       }
+       else if ((sb.st_mode & S_IFMT) != S_IFDIR)
+         fprintf(stderr, "\tERROR: %s not directory\n", aconf->host);
+      }
+#else
+      if (!aconf->host)
+       fprintf(stderr, "\tERROR: %s\n", "null host field in P-line");
+      else if (strchr(aconf->host, '/'))
+       fprintf(stderr, "\t%s\n", "WARNING: / present in P-line "
+           "for non-UNIXPORT configuration");
+#endif
+      aconf->confClass = get_class(0, 0);
+      goto print_confline;
+    }
+
+    if (aconf->status & CONF_SERVER_MASK &&
+       (!aconf->host || strchr(aconf->host, '*') || strchr(aconf->host, '?')))
+    {
+      fprintf(stderr, "\tERROR: bad host field\n");
+      continue;
+    }
+
+    if (aconf->status & CONF_SERVER_MASK && BadPtr(aconf->passwd))
+    {
+      fprintf(stderr, "\tERROR: empty/no password field\n");
+      continue;
+    }
+
+    if (aconf->status & CONF_SERVER_MASK && !aconf->name)
+    {
+      fprintf(stderr, "\tERROR: bad name field\n");
+      continue;
+    }
+
+    if (aconf->status & (CONF_SERVER_MASK | CONF_OPS))
+      if (!strchr(aconf->host, '@'))
+      {
+       char *newhost;
+       int len = 3;            /* *@\0 = 3 */
+
+       len += strlen(aconf->host);
+       newhost = (char *)RunMalloc(len);
+       sprintf(newhost, "*@%s", aconf->host);
+       RunFree(aconf->host);
+       aconf->host = newhost;
+      }
+
+    /* parse the connect rules to detect errors, but free
+     *  any allocated storage immediately -- we're just looking
+     *  for errors..  */
+    if (aconf->status & CONF_CRULE)
+      if ((crule = (char *)crule_parse(aconf->name)) != NULL)
+       crule_free(&crule);
+
+    if (!aconf->confClass)
+      aconf->confClass = get_class(0, 0);
+    sprintf(maxsendq, "%d", ConfClass(aconf));
+
+    if ((aconf->status & CONF_ADMIN) && (!aconf->name ||
+       !aconf->passwd || !aconf->host))
+      fprintf(stderr, "ERROR: Your A: line must have 4 fields!\n");
+
+    if (!aconf->name)
+      DupString(aconf->name, nullfield);
+    if (!aconf->passwd)
+      DupString(aconf->passwd, nullfield);
+    if (!aconf->host)
+      DupString(aconf->host, nullfield);
+    if (aconf->status & (CONF_ME | CONF_ADMIN))
+    {
+      if (flags & aconf->status)
+       fprintf(stderr, "ERROR: multiple %c-lines\n",
+           toUpper(confchar(aconf->status)));
+      else
+       flags |= aconf->status;
+    }
+  print_confline:
+    if (debugflag > 8)
+      printf("(%d) (%s) (%s) (%s) (%u) (%s)\n",
+         aconf->status, aconf->host, aconf->passwd,
+         aconf->name, aconf->port, maxsendq);
+    fflush(stdout);
+    if (aconf->status & (CONF_SERVER_MASK | CONF_HUB | CONF_LEAF))
+    {
+      aconf->next = ctop;
+      ctop = aconf;
+      aconf = NULL;
+    }
+  }
+  fbclose(file);
+  return ctop;
+}
+
+static aConfClass *get_class(int cn, int ism)
+{
+  static aConfClass cls;
+  if (ism == 1)
+  {
+    cls.conClass = (unsigned int)-1;
+    if ((cn >= 1) && (cn <= 64))
+      cls.conClass = cn;
+    else
+      fprintf(stderr, "\tWARNING: server numeric %d is not 1-64\n", cn);
+  }
+  else
+  {
+    int i = numclasses - 1;
+    cls.conClass = (unsigned int)-1;
+    for (; i >= 0; i--)
+      if (classarr[i] == cn)
+      {
+       cls.conClass = cn;
+       break;
+      }
+    if (i == -1)
+      fprintf(stderr, "\tWARNING: class %d not found\n", cn);
+  }
+  return &cls;
+}
+
+static void new_class(int cn)
+{
+  numclasses++;
+  if (classarr)
+    classarr = (int *)RunRealloc(classarr, sizeof(int) * numclasses);
+  else
+    classarr = (int *)RunMalloc(sizeof(int));
+  classarr[numclasses - 1] = cn;
+}
+
+/*
+ * field breakup for ircd.conf file.
+ */
+static char *getfield(char *newline)
+{
+  static char *line = NULL;
+  char *end, *field;
+
+  if (newline)
+    line = newline;
+  if (line == NULL)
+    return (NULL);
+
+  field = line;
+  if ((end = strchr(line, ':')) == NULL)
+  {
+    line = NULL;
+    if ((end = strchr(field, '\n')) == NULL)
+      end = field + strlen(field);
+  }
+  else
+    line = end + 1;
+  *end = '\0';
+  return (field);
+}
+
+static int validate(aConfItem *top)
+{
+  Reg1 aConfItem *aconf, *bconf;
+  unsigned int otype, 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;
+      else                     /* Does this ever happen ? */
+       continue;
+
+      for (bconf = top; bconf; bconf = bconf->next)
+      {
+       if (bconf == aconf || !(bconf->status & otype))
+         continue;
+       if (bconf->confClass == aconf->confClass &&
+           !strCasediff(bconf->name, aconf->name) &&
+           !strCasediff(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 (!strCasediff(bconf->name, aconf->name))
+       {
+         aconf->status |= CONF_MATCH;
+         break;
+       }
+      }
+  }
+
+  fprintf(stderr, "\n");
+  for (aconf = top; aconf; aconf = aconf->next)
+    if (aconf->status & CONF_MATCH)
+      valid++;
+    else
+      fprintf(stderr, "Unmatched %c:%s:%s:%s\n",
+         confchar(aconf->status), aconf->host, aconf->passwd, aconf->name);
+  return valid ? 0 : -1;
+}
+
+static char confchar(unsigned int status)
+{
+  static char letrs[] = "ICNoOMKARYLPH";
+  char *s = letrs;
+
+  status &= ~(CONF_MATCH | CONF_ILLEGAL);
+
+  for (; *s; s++, status >>= 1)
+    if (status & 1)
+      return *s;
+  return '-';
+}
diff --git a/ircd/class.c b/ircd/class.c
new file mode 100644 (file)
index 0000000..257f359
--- /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 "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "class.h"
+#include "s_conf.h"
+#include "s_serv.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "ircd.h"
+
+RCSTAG_CC("$Id$");
+
+#define BAD_CONF_CLASS         ((unsigned int)-1)
+#define BAD_PING               ((unsigned int)-2)
+#define BAD_CLIENT_CLASS       ((unsigned int)-3)
+
+aConfClass *classes;
+
+unsigned int get_conf_class(aConfItem *aconf)
+{
+  if ((aconf) && (aconf->confClass))
+    return (ConfClass(aconf));
+
+  Debug((DEBUG_DEBUG, "No Class For %s", (aconf) ? aconf->name : "*No Conf*"));
+
+  return (BAD_CONF_CLASS);
+
+}
+
+static unsigned int get_conf_ping(aConfItem *aconf)
+{
+  if ((aconf) && (aconf->confClass))
+    return (ConfPingFreq(aconf));
+
+  Debug((DEBUG_DEBUG, "No Ping For %s", (aconf) ? aconf->name : "*No Conf*"));
+
+  return (BAD_PING);
+}
+
+unsigned int get_client_class(aClient *acptr)
+{
+  Reg1 Link *tmp;
+  Reg2 aConfClass *cl;
+  unsigned int retc = BAD_CLIENT_CLASS;
+
+  if (acptr && !IsMe(acptr) && !IsPing(acptr) && (acptr->confs))
+    for (tmp = acptr->confs; tmp; tmp = tmp->next)
+    {
+      if (!tmp->value.aconf || !(cl = tmp->value.aconf->confClass))
+       continue;
+      if (ConClass(cl) > retc || retc == BAD_CLIENT_CLASS)
+       retc = ConClass(cl);
+    }
+
+  Debug((DEBUG_DEBUG, "Returning Class %d For %s", retc, acptr->name));
+
+  return (retc);
+}
+
+int unsigned get_client_ping(aClient *acptr)
+{
+  unsigned 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);
+}
+
+unsigned int get_con_freq(aConfClass * 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(unsigned int conClass, unsigned int ping, unsigned int confreq,
+    unsigned int maxli, size_t sendq)
+{
+  aConfClass *t, *p;
+
+  t = find_class(conClass);
+  if ((t == classes) && (conClass != 0))
+  {
+    p = (aConfClass *) make_class();
+    NextClass(p) = NextClass(t);
+    NextClass(t) = p;
+  }
+  else
+    p = t;
+  Debug((DEBUG_DEBUG, "Add Class %u: p %p t %p - cf: %u pf: %u ml: %u sq: %d",
+      conClass, p, t, confreq, ping, maxli, sendq));
+  ConClass(p) = conClass;
+  ConFreq(p) = confreq;
+  PingFreq(p) = ping;
+  MaxLinks(p) = maxli;
+  MaxSendq(p) = (sendq > 0) ? sendq : DEFAULTMAXSENDQLENGTH;
+  if (p != t)
+    Links(p) = 0;
+}
+
+aConfClass *find_class(unsigned int cclass)
+{
+  aConfClass *cltmp;
+
+  for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp))
+    if (ConClass(cltmp) == cclass)
+      return cltmp;
+  return classes;
+}
+
+void check_class(void)
+{
+  aConfClass *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: %d",
+       ConClass(cltmp), ConFreq(cltmp), PingFreq(cltmp),
+       MaxLinks(cltmp), Links(cltmp), MaxSendq(cltmp)));
+    if (IsMarkedDelete(cltmp))
+    {
+      NextClass(cltmp2) = NextClass(cltmp);
+      if (Links(cltmp) == 0)
+       free_class(cltmp);
+    }
+    else
+      cltmp2 = cltmp;
+  }
+}
+
+void initclass(void)
+{
+  classes = (aConfClass *) make_class();
+
+  ConClass(FirstClass()) = 0;
+  ConFreq(FirstClass()) = CONNECTFREQUENCY;
+  PingFreq(FirstClass()) = PINGFREQUENCY;
+  MaxLinks(FirstClass()) = MAXIMUM_LINKS;
+  MaxSendq(FirstClass()) = DEFAULTMAXSENDQLENGTH;
+  Links(FirstClass()) = 0;
+  NextClass(FirstClass()) = NULL;
+}
+
+void report_classes(aClient *sptr)
+{
+  aConfClass *cltmp;
+
+  for (cltmp = FirstClass(); cltmp; cltmp = NextClass(cltmp))
+    sendto_one(sptr, rpl_str(RPL_STATSYLINE), me.name, sptr->name,
+       'Y', ConClass(cltmp), PingFreq(cltmp), ConFreq(cltmp),
+       MaxLinks(cltmp), MaxSendq(cltmp));
+}
+
+size_t get_sendq(aClient *cptr)
+{
+  size_t sendq = DEFAULTMAXSENDQLENGTH;
+  Link *tmp;
+  aConfClass *cl;
+
+  if (cptr && !IsMe(cptr) && (cptr->confs))
+    for (tmp = cptr->confs; tmp; tmp = tmp->next)
+    {
+      if (!tmp->value.aconf || !(cl = tmp->value.aconf->confClass))
+       continue;
+      if (ConClass(cl) != BAD_CLIENT_CLASS)
+       sendq = MaxSendq(cl);
+    }
+  return sendq;
+}
diff --git a/ircd/common.c b/ircd/common.c
new file mode 100644 (file)
index 0000000..bd12fe7
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+ * IRC - Internet Relay Chat, include/common.c
+ * Copyright (C) 1998 Andrea Cocito
+ *
+ * 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.
+ */
+
+/*
+ * CODERS WARNING: DO _NOT_ EDIT THE TABLES IN THIS FILE
+ * Instead:
+ * a) Edit the table generator below, specifically the makeTables() function
+ * b) Recreate the common.c tables with 'make ctables'.
+ */
+
+
+/*=============================================================================
+ * Actual source of the library stuff, not generated when making the tables
+ */
+
+#ifndef MAKETABLES
+
+/*=============================================================================
+ * Headers needed in this source file
+ */
+
+#include "common.h"
+
+/*=============================================================================
+ * Functions eventually inlined or visible externally
+ */
+
+#ifndef FORCEINLINE
+/* *INDENT-OFF* */
+NTL_HDR_strChattr { NTL_SRC_strChattr }
+NTL_HDR_strCasediff { NTL_SRC_strCasediff }
+/* *INDENT-ON* */
+#endif /* !FORCEINLINE */
+
+/*=============================================================================
+ * Other functions visible externally
+ */
+
+int strnChattr(const char *s, const size_t n)
+{
+  register const char *rs = s;
+  register int x = ~0;
+  register int r = n;
+  while (*rs && r--)
+    x &= NTL_char_attrib[*rs++ - CHAR_MIN];
+  return x;
+}
+
+int strCasecmp(const char *a, const char *b)
+{
+  register const char *ra = a;
+  register const char *rb = b;
+  while (toLower(*ra) == toLower(*rb))
+    if (!*ra++)
+      return 0;
+    else
+      rb++;
+  return (*ra - *rb);
+}
+
+int strnCasecmp(const char *a, const char *b, const size_t n)
+{
+  register const char *ra = a;
+  register const char *rb = b;
+  register int left = n;
+  if (!left--)
+    return 0;
+  while (toLower(*ra) == toLower(*rb))
+    if ((!(*(ra++))) || (!(left--)))
+      return 0;
+    else
+      rb++;
+  return (*ra - *rb);
+}
+
+/*=============================================================================
+ * Automatically generated tables, don't touch these by hand !
+ */
+
+/* *INDENT-OFF* */
+/*
+ * DO not touch anything below this line !
+ * NTL_TOK_START
+ */
+const char NTL_tolower_tab[] = {
+#if (CHAR_MIN<0)
+/* x80-x87 */ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+/* x88-x8f */ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+/* x90-x97 */ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+/* x98-x9f */ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+/* xa0-xa7 */ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+/* xa8-xaf */ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+/* xb0-xb7 */ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+/* xb8-xbf */ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+/* xc0-xc7 */ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+/* xc8-xcf */ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+/* xd0-xd7 */ '\xd0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xd7',
+/* xd8-xdf */ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xdf',
+/* xe0-xe7 */ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+/* xe8-xef */ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+/* xf0-xf7 */ '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
+/* xf8-xff */ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff'
+                ,
+#endif /* (CHAR_MIN<0) */
+/* x00-x07 */ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
+/* x08-x0f */ '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
+/* x10-x17 */ '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
+/* x18-x1f */ '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
+/* ' '-x27 */    ' ',    '!',    '"',    '#',    '$',    '%',    '&', '\x27',
+/* '('-'/' */    '(',    ')',    '*',    '+',    ',',    '-',    '.',    '/',
+/* '0'-'7' */    '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',
+/* '8'-'?' */    '8',    '9',    ':',    ';',    '<',    '=',    '>',    '?',
+/* '@'-'G' */    '@',    'a',    'b',    'c',    'd',    'e',    'f',    'g',
+/* 'H'-'O' */    'h',    'i',    'j',    'k',    'l',    'm',    'n',    'o',
+/* 'P'-'W' */    'p',    'q',    'r',    's',    't',    'u',    'v',    'w',
+/* 'X'-'_' */    'x',    'y',    'z',    '{',    '|',    '}',    '~',    '_',
+/* '`'-'g' */    '`',    'a',    'b',    'c',    'd',    'e',    'f',    'g',
+/* 'h'-'o' */    'h',    'i',    'j',    'k',    'l',    'm',    'n',    'o',
+/* 'p'-'w' */    'p',    'q',    'r',    's',    't',    'u',    'v',    'w',
+/* 'x'-x7f */    'x',    'y',    'z',    '{',    '|',    '}',    '~', '\x7f'
+#if (!(CHAR_MIN<0))
+                ,
+/* x80-x87 */ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+/* x88-x8f */ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+/* x90-x97 */ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+/* x98-x9f */ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+/* xa0-xa7 */ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+/* xa8-xaf */ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+/* xb0-xb7 */ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+/* xb8-xbf */ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+/* xc0-xc7 */ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+/* xc8-xcf */ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+/* xd0-xd7 */ '\xd0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xd7',
+/* xd8-xdf */ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xdf',
+/* xe0-xe7 */ '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7',
+/* xe8-xef */ '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef',
+/* xf0-xf7 */ '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf7',
+/* xf8-xff */ '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff'
+#endif /* (!(CHAR_MIN<0)) */
+  };
+
+const char NTL_toupper_tab[] = {
+#if (CHAR_MIN<0)
+/* x80-x87 */ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+/* x88-x8f */ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+/* x90-x97 */ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+/* x98-x9f */ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+/* xa0-xa7 */ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+/* xa8-xaf */ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+/* xb0-xb7 */ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+/* xb8-xbf */ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+/* xc0-xc7 */ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+/* xc8-xcf */ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+/* xd0-xd7 */ '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
+/* xd8-xdf */ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
+/* xe0-xe7 */ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+/* xe8-xef */ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+/* xf0-xf7 */ '\xf0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xf7',
+/* xf8-xff */ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xff'
+                ,
+#endif /* (CHAR_MIN<0) */
+/* x00-x07 */ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
+/* x08-x0f */ '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f',
+/* x10-x17 */ '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17',
+/* x18-x1f */ '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f',
+/* ' '-x27 */    ' ',    '!',    '"',    '#',    '$',    '%',    '&', '\x27',
+/* '('-'/' */    '(',    ')',    '*',    '+',    ',',    '-',    '.',    '/',
+/* '0'-'7' */    '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',
+/* '8'-'?' */    '8',    '9',    ':',    ';',    '<',    '=',    '>',    '?',
+/* '@'-'G' */    '@',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
+/* 'H'-'O' */    'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
+/* 'P'-'W' */    'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
+/* 'X'-'_' */    'X',    'Y',    'Z',    '[', '\x5c',    ']',    '^',    '_',
+/* '`'-'g' */    '`',    'A',    'B',    'C',    'D',    'E',    'F',    'G',
+/* 'h'-'o' */    'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
+/* 'p'-'w' */    'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
+/* 'x'-x7f */    'X',    'Y',    'Z',    '[', '\x5c',    ']',    '^', '\x7f'
+#if (!(CHAR_MIN<0))
+                ,
+/* x80-x87 */ '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87',
+/* x88-x8f */ '\x88', '\x89', '\x8a', '\x8b', '\x8c', '\x8d', '\x8e', '\x8f',
+/* x90-x97 */ '\x90', '\x91', '\x92', '\x93', '\x94', '\x95', '\x96', '\x97',
+/* x98-x9f */ '\x98', '\x99', '\x9a', '\x9b', '\x9c', '\x9d', '\x9e', '\x9f',
+/* xa0-xa7 */ '\xa0', '\xa1', '\xa2', '\xa3', '\xa4', '\xa5', '\xa6', '\xa7',
+/* xa8-xaf */ '\xa8', '\xa9', '\xaa', '\xab', '\xac', '\xad', '\xae', '\xaf',
+/* xb0-xb7 */ '\xb0', '\xb1', '\xb2', '\xb3', '\xb4', '\xb5', '\xb6', '\xb7',
+/* xb8-xbf */ '\xb8', '\xb9', '\xba', '\xbb', '\xbc', '\xbd', '\xbe', '\xbf',
+/* xc0-xc7 */ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+/* xc8-xcf */ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+/* xd0-xd7 */ '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd7',
+/* xd8-xdf */ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf',
+/* xe0-xe7 */ '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7',
+/* xe8-xef */ '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf',
+/* xf0-xf7 */ '\xf0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xf7',
+/* xf8-xff */ '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xff'
+#endif /* (!(CHAR_MIN<0)) */
+  };
+
+const unsigned int NTL_char_attrib[] = {
+#if (CHAR_MIN<0)
+/* x80-x87 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x88-x8f */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x90-x97 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x98-x9f */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xa0-xa7 */ 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xa8-xaf */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xb0-xb7 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xb8-xbf */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xc0-xc7 */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00,
+/* xc8-xcf */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00,
+/* xd0-xd7 */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x0400,
+/* xd8-xdf */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x0400,
+/* xe0-xe7 */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400,
+/* xe8-xef */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400,
+/* xf0-xf7 */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x0400,
+/* xf8-xff */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x0400
+                ,
+#endif /* (CHAR_MIN<0) */
+/* x00-x07 */ 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0004,
+/* x08-x0f */ 0x0404, 0x0504, 0x10504, 0x0504, 0x0504, 0x10504, 0x0404, 0x0404,
+/* x10-x17 */ 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404,
+/* x18-x1f */ 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404, 0x0404,
+/* ' '-x27 */ 0x0140, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x24d0,
+/* '('-'/' */ 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x00d0, 0x74d0, 0xe4d0, 0x04d0,
+/* '0'-'7' */ 0xf459, 0xf459, 0xf459, 0xf459, 0xf459, 0xf459, 0xf459, 0xf459,
+/* '8'-'?' */ 0xf459, 0xf459, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x04d0, 0x04d0,
+/* '@'-'G' */ 0x04d0, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653,
+/* 'H'-'O' */ 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653,
+/* 'P'-'W' */ 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653,
+/* 'X'-'_' */ 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x7653, 0x74d0,
+/* '`'-'g' */ 0x34d0, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473,
+/* 'h'-'o' */ 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473,
+/* 'p'-'w' */ 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473,
+/* 'x'-x7f */ 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x7473, 0x0400
+#if (!(CHAR_MIN<0))
+                ,
+/* x80-x87 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x88-x8f */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x90-x97 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* x98-x9f */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xa0-xa7 */ 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xa8-xaf */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xb0-xb7 */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xb8-xbf */ 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
+/* xc0-xc7 */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00,
+/* xc8-xcf */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00,
+/* xd0-xd7 */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x0400,
+/* xd8-xdf */ 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x2c00, 0x0400,
+/* xe0-xe7 */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400,
+/* xe8-xef */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400,
+/* xf0-xf7 */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x0400,
+/* xf8-xff */ 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x2400, 0x0400
+#endif /* (!(CHAR_MIN<0)) */
+  };
+
+/*
+ * NTL_TOK_END
+ * DO not touch anything above this line !
+ */
+/* *INDENT-ON* */
+
+#endif /* !MAKETABLES */
+
+/*=============================================================================
+ * TABLE GENERATOR
+ * The following part of code is NOT included in the actual server's
+ * or library source, it's just used to build the above tables
+ *
+ * This should rebuild the actual tables and automatically place them
+ * into this source file, note that this part of code is for developers
+ * only, it's supposed to work on both signed and unsigned chars but I
+ * actually tested it only on signed-char architectures, the code and
+ * macros actually used by the server instead DO work and have been tested
+ * on platforms where0 char is both signed or unsigned, this is true as long
+ * as the <limits.h> macros are set properly and without any need to rebuild
+ * the tables (wich as said an admin should NEVER do, tables need to be rebuilt
+ * only when one wants to really change the results or when one has to
+ * compile on architectures where a char is NOT eight bits [?!], yes
+ * it all is supposed to work in that case too... but I can't test it
+ * because I've not found a machine in the world where this happes).
+ *
+ * NEVER -f[un]signed-char on gcc since that does NOT fix the named macros
+ * and you end up in a non-ANSI environment where CHAR_MIN and CHAR_MAX
+ * are _not_ the real limits of a default 'char' type. This is true for
+ * both admins and coders.
+ *
+ */
+
+#ifdef MAKETABLES
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "common.h"
+
+static void zeroTables(void);
+static void markString(int macro, const char *s);
+static void unMarkString(int macro, const char *s);
+static void markRange(int macro, char from, char to);
+static void moveMacro(int from, int to);
+static void setLowHi(const char firstlow, const char lastlow,
+    const char firsthi);
+
+char NTL_tolower_tab[1 + CHAR_MAX - CHAR_MIN]; /* 256 bytes */
+char NTL_toupper_tab[1 + CHAR_MAX - CHAR_MIN]; /* 256 bytes */
+int NTL_char_attrib[1 + CHAR_MAX - CHAR_MIN];  /* 256 ints = 0.5 to 2 kilobytes */
+
+/*
+ * makeTables() 
+ * Where we make the tables, edit ONLY this to change the tables.
+ */
+
+static void makeTables(void)
+{
+
+  /* Start from a known status */
+  zeroTables();
+
+  /* Make the very elementary sets */
+  markRange(NTL_LOWER, 'a', 'z');
+  markString(NTL_LOWER, "{|}~");
+
+  markRange(NTL_UPPER, 'A', 'Z');
+  markString(NTL_UPPER, "[\\]^");
+
+  markRange(NTL_DIGIT, '0', '9');
+
+  markRange(NTL_CNTRL, '\000', '\037');
+
+  markString(NTL_PUNCT, "!\"#$%&'()*+,-./:;<=>?@_`");
+
+  markString(NTL_SPACE, "\011\012\013\014\015\040");
+
+  /* Make the derived sets, 
+   * WARNING: The order of these calls is important, some depend on 
+   * the results of the previous ones ! */
+
+  moveMacro(NTL_LOWER | NTL_UPPER, NTL_ALPHA);
+  moveMacro(NTL_ALPHA | NTL_DIGIT, NTL_ALNUM);
+  moveMacro(NTL_ALNUM | NTL_PUNCT, NTL_GRAPH);
+
+  moveMacro(NTL_GRAPH, NTL_PRINT);
+  markString(NTL_PRINT, " ");
+
+  markRange(NTL_IRCCH, 0, UCHAR_MAX);
+  unMarkString(NTL_IRCCH, "\007\040\054\240");
+
+  markRange(NTL_IRCCL, '\300', '\326');
+  markRange(NTL_IRCCL, '\330', '\336');
+
+  moveMacro(NTL_ALNUM, NTL_IRCHN);
+  markString(NTL_IRCHN, "-_.");        /* Some DNS might allow '_' per RFC 1033 ! */
+
+  moveMacro(NTL_DIGIT, NTL_IRCIP);
+  markString(NTL_IRCIP, ".");
+
+  moveMacro(NTL_DIGIT | NTL_ALPHA, NTL_IRCNK);
+  markString(NTL_IRCNK, "-_`");
+
+  moveMacro(NTL_ALNUM, NTL_IRCUI);
+  markRange(NTL_IRCUI, '\xe0', '\xf6');
+  markRange(NTL_IRCUI, '\xf8', '\xfe');
+  markRange(NTL_IRCUI, '\xc0', '\xd6');
+  markRange(NTL_IRCUI, '\xd8', '\xde');
+  markString(NTL_IRCUI, ".-_^'`~");
+  markString(NTL_EOL, "\n\r");
+
+  /* And finally let's take care of the toLower/toUpper stuff */
+
+  setLowHi('a', 'z', 'A');
+  setLowHi('\xe0', '\xf6', '\xc0');
+  setLowHi('\xf8', '\xfe', '\xd8');
+  setLowHi('{', '~', '[');
+
+#ifndef FIXME                  /* Just to remember that this is to be removed in u10.06 */
+  setLowHi('\xd0', '\xd0', '\xd0');    /* Freeze the 0xD0 lower/upper */
+  setLowHi('\xf0', '\xf0', '\xf0');    /* Freeze the 0xF0 lower/upper */
+#endif /* FIXME */
+
+
+}
+
+/* 
+ * main()
+ * This is the main program to be executed for -DMAKETABLES
+ */
+
+static void dumphw(int *p, int beg);
+static void dumphb(char *p, int beg);
+
+int main(void)
+{
+  int i, j, k;
+  char c, c1, c2;
+
+  /* Make the tables */
+  makeTables();
+
+  /* Dump them as ANSI C source to be included below */
+
+  /* NTL_tolower_tab */
+  printf("const char NTL_tolower_tab[] = {\n");
+  printf("#if (CHAR_MIN<0)\n");
+  i = (int)((char)SCHAR_MIN);
+  dumphb(NTL_tolower_tab, i);
+  printf("                ,\n");
+  printf("#endif /* (CHAR_MIN<0) */\n");
+  i = 0;
+  dumphb(NTL_tolower_tab, i);
+  printf("#if (!(CHAR_MIN<0))\n");
+  printf("                ,\n");
+  i = (int)((char)SCHAR_MIN);
+  dumphb(NTL_tolower_tab, i);
+  printf("#endif /* (!(CHAR_MIN<0)) */\n");
+  printf("  };\n\n");
+
+  /* NTL_toupper_tab */
+  printf("const char NTL_toupper_tab[] = {\n");
+  printf("#if (CHAR_MIN<0)\n");
+  i = (int)((char)SCHAR_MIN);
+  dumphb(NTL_toupper_tab, i);
+  printf("                ,\n");
+  printf("#endif /* (CHAR_MIN<0) */\n");
+  i = 0;
+  dumphb(NTL_toupper_tab, i);
+  printf("#if (!(CHAR_MIN<0))\n");
+  printf("                ,\n");
+  i = (int)((char)SCHAR_MIN);
+  dumphb(NTL_toupper_tab, i);
+  printf("#endif /* (!(CHAR_MIN<0)) */\n");
+  printf("  };\n\n");
+
+  /* NTL_char_attrib */
+  printf("const unsigned int NTL_char_attrib[] = {\n");
+  printf("#if (CHAR_MIN<0)\n");
+  i = (int)((char)SCHAR_MIN);
+  dumphw(NTL_char_attrib, i);
+  printf("                ,\n");
+  printf("#endif /* (CHAR_MIN<0) */\n");
+  i = 0;
+  dumphw(NTL_char_attrib, i);
+  printf("#if (!(CHAR_MIN<0))\n");
+  printf("                ,\n");
+  i = (int)((char)SCHAR_MIN);
+  dumphw(NTL_char_attrib, i);
+  printf("#endif /* (!(CHAR_MIN<0)) */\n");
+  printf("  };\n\n");
+
+  return 0;
+
+}
+
+/* A few utility functions for makeTables() */
+
+static void zeroTables(void)
+{
+  int i;
+  for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+  {
+    NTL_tolower_tab[i - CHAR_MIN] = (char)i;   /* Unchanged */
+    NTL_toupper_tab[i - CHAR_MIN] = (char)i;   /* Unchanged */
+    NTL_char_attrib[i - CHAR_MIN] = 0x0000;    /* Nothing */
+  };
+}
+
+static void markString(int macro, const char *s)
+{
+  while (*s)
+    NTL_char_attrib[*(s++) - CHAR_MIN] |= macro;
+}
+
+static void unMarkString(int macro, const char *s)
+{
+  while (*s)
+    NTL_char_attrib[*(s++) - CHAR_MIN] &= ~macro;
+}
+
+static void markRange(int macro, char from, char to)
+{
+  int i;
+  for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+    if (((unsigned char)i >= (unsigned char)from)
+       && ((unsigned char)i <= (unsigned char)to))
+      NTL_char_attrib[(char)i - CHAR_MIN] |= macro;
+}
+
+static void moveMacro(int from, int to)
+{
+  int i;
+  for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+    if (NTL_char_attrib[i - CHAR_MIN] & from)
+      NTL_char_attrib[i - CHAR_MIN] |= to;
+}
+
+static void setLowHi(const char firstlow, const char lastlow,
+    const char firsthi)
+{
+  int i, j;
+  for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+    if (((unsigned char)i >= (unsigned char)firstlow)
+       && ((unsigned char)i <= (unsigned char)lastlow))
+    {
+      j = ((int)((char)(i + (int)(firsthi - firstlow))));
+      NTL_tolower_tab[((char)j) - CHAR_MIN] = (char)i;
+      NTL_toupper_tab[((char)i) - CHAR_MIN] = (char)j;
+    };
+}
+
+/* These are used in main() to actually dump the tables, each function
+   dumps half table as hex/char constants... */
+
+#define ROWSIZE 8
+
+static void dumphb(char *tbl, int beg)
+{
+  int i, j, k, z;
+  char *p = &tbl[beg - CHAR_MIN];
+  char c;
+  for (i = 0; i <= SCHAR_MAX; i += ROWSIZE)
+  {
+    k = i + ROWSIZE - 1;
+    if (k > SCHAR_MAX)
+      k = SCHAR_MAX;
+
+    c = (char)(beg + i);
+    printf("/*");
+    if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+       && (c != '\''))
+      printf(" '%c'", c);
+    else
+      printf(" x%02x", ((int)((unsigned char)c)));
+
+    c = (char)(beg + k);
+    printf("-");
+    if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+       && (c != '\''))
+      printf("'%c'", c);
+    else
+      printf("x%02x", ((int)((unsigned char)c)));
+    printf(" */");
+
+    for (j = i; j <= k; j++)
+    {
+      c = p[j];
+      if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+         && (c != '\''))
+       printf("    '%c'", c);
+      else
+       printf(" '\\x%02x'", ((int)((unsigned char)c)));
+      if (j < SCHAR_MAX)
+       printf(",");
+    };
+    printf("\n");
+  };
+}
+
+static void dumphw(int *tbl, int beg)
+{
+  int i, j, k, z;
+  int *p = &tbl[beg - CHAR_MIN];
+  char c;
+  for (i = 0; i <= SCHAR_MAX; i += ROWSIZE)
+  {
+    k = i + ROWSIZE - 1;
+    if (k > SCHAR_MAX)
+      k = SCHAR_MAX;
+
+    c = (char)(beg + i);
+    printf("/*");
+    if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+       && (c != '\''))
+      printf(" '%c'", c);
+    else
+      printf(" x%02x", ((int)((unsigned char)c)));
+
+    c = (char)(beg + k);
+    printf("-");
+    if ((c > 0) && (c < SCHAR_MAX) && (isprint(c)) && (c != '\\')
+       && (c != '\''))
+      printf("'%c'", c);
+    else
+      printf("x%02x", ((int)((unsigned char)c)));
+    printf(" */");
+
+    for (j = i; j <= k; j++)
+    {
+      printf(" 0x%04x", p[j] & 0xffffffff);
+      if (j < SCHAR_MAX)
+       printf(",");
+    };
+    printf("\n");
+  };
+}
+
+#endif /* MAKETABLES */
diff --git a/ircd/crule.c b/ircd/crule.c
new file mode 100644 (file)
index 0000000..ccacadf
--- /dev/null
@@ -0,0 +1,788 @@
+/*
+ * SmartRoute phase 1
+ * connection rule patch
+ * by Tony Vencill (Tonto on IRC) <vencill@bga.com>
+ *
+ * The majority of this file is a recusive descent parser used to convert
+ * connection rules into expression trees when the conf file is read.
+ * All parsing structures and types are hidden in the interest of good
+ * programming style and to make possible future data structure changes
+ * without affecting the interface between this patch and the rest of the
+ * server.  The only functions accessible externally are crule_parse,
+ * crule_free, and crule_eval.  Prototypes for these functions can be
+ * found in h.h.
+ *
+ * Please direct any connection rule or SmartRoute questions to Tonto on
+ * IRC or by email to vencill@bga.com.
+ *
+ * For parser testing, defining CR_DEBUG generates a stand-alone parser
+ * that takes rules from stdin and prints out memory allocation
+ * information and the parsed rule.  This stand alone parser is ignorant
+ * of the irc server and thus cannot do rule evaluation.  Do not define
+ * this flag when compiling the server!  If you wish to generate the
+ * test parser, compile from the ircd directory with a line similar to
+ * cc -o parser -DCR_DEBUG crule.c
+ *
+ * The define CR_CHKCONF is provided to generate routines needed in
+ * chkconf.  These consist of the parser, a different crule_parse that
+ * prints errors to stderr, and crule_free (just for good style and to
+ * more closely simulate the actual ircd environment).  crule_eval and
+ * the rule functions are made empty functions as in the stand-alone
+ * test parser.
+ */
+
+#ifndef CR_DEBUG
+
+/* ircd functions and types we need */
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "ircd.h"
+#include "match.h"
+#include "s_bsd.h"
+#include "common.h"
+#include "crule.h"
+
+#else /* includes and defines to make the stand-alone test parser */
+
+#include "sys.h"
+#include <stdio.h>
+#include "h.h"
+
+#define BadPtr(x) (!(x) || (*(x) == '\0'))
+#define DupString(x,y) \
+       do { \
+         x = (char *)RunMalloc(strlen(y)+1); \
+       strcpy(x,y); \
+       } while(0)
+
+/* We don't care about collation discrepacies here, it seems.... */
+#define strCasediff strcasecmp
+
+#endif
+
+RCSTAG_CC("$Id$");
+
+#if defined(CR_DEBUG) || defined(CR_CHKCONF)
+#undef RunMalloc
+#undef malloc
+#define RunMalloc malloc
+#undef RunFree
+#undef free
+#define RunFree free
+#endif
+
+/* some constants and shared data types */
+#define CR_MAXARGLEN 80                /* why 80? why not? it's > hostname lengths */
+#define CR_MAXARGS 3           /* There's a better way to do this,
+                                  but not now. */
+
+/*
+ * Some symbols for easy reading
+ */
+
+enum crule_token {
+  CR_UNKNOWN, CR_END, CR_AND, CR_OR, CR_NOT, CR_OPENPAREN, CR_CLOSEPAREN,
+  CR_COMMA, CR_WORD
+};
+
+enum crule_errcode {
+  CR_NOERR, CR_UNEXPCTTOK, CR_UNKNWTOK, CR_EXPCTAND, CR_EXPCTOR,
+  CR_EXPCTPRIM, CR_EXPCTOPEN, CR_EXPCTCLOSE, CR_UNKNWFUNC, CR_ARGMISMAT
+};
+
+/*
+ * Expression tree structure, function pointer, and tree pointer local!
+ */
+typedef int (*crule_funcptr) (int, void **);
+
+struct crule_treestruct {
+  crule_funcptr funcptr;
+  int numargs;
+  void *arg[CR_MAXARGS];       /* For operators arg points to a tree element;
+                                  for functions arg points to a char string. */
+};
+
+typedef struct crule_treestruct crule_treeelem;
+typedef crule_treeelem *crule_treeptr;
+
+/* local rule function prototypes */
+static int crule_connected(int, void **);
+static int crule_directcon(int, void **);
+static int crule_via(int, void **);
+static int crule_directop(int, void **);
+static int crule__andor(int, void **);
+static int crule__not(int, void **);
+
+/* local parsing function prototypes */
+static int crule_gettoken(int *, char **);
+static void crule_getword(char *, int *, size_t, char **);
+static int crule_parseandexpr(crule_treeptr *, int *, char **);
+static int crule_parseorexpr(crule_treeptr *, int *, char **);
+static int crule_parseprimary(crule_treeptr *, int *, char **);
+static int crule_parsefunction(crule_treeptr *, int *, char **);
+static int crule_parsearglist(crule_treeptr, int *, char **);
+
+#if defined(CR_DEBUG) || defined(CR_CHKCONF)
+/*
+ * Prototypes for the test parser; if not debugging,
+ * these are defined in h.h
+ */
+char *crule_parse(char *);
+void crule_free(char **);
+#ifdef CR_DEBUG
+void print_tree(crule_treeptr);
+#endif
+#endif
+
+/* error messages */
+char *crule_errstr[] = {
+  "Unknown error",             /* NOERR? - for completeness */
+  "Unexpected token",          /* UNEXPCTTOK */
+  "Unknown token",             /* UNKNWTOK */
+  "And expr expected",         /* EXPCTAND */
+  "Or expr expected",          /* EXPCTOR */
+  "Primary expected",          /* EXPCTPRIM */
+  "( expected",                        /* EXPCTOPEN */
+  ") expected",                        /* EXPCTCLOSE */
+  "Unknown function",          /* UNKNWFUNC */
+  "Argument mismatch"          /* ARGMISMAT */
+};
+
+/* function table - null terminated */
+struct crule_funclistent {
+  char name[15];               /* MAXIMUM FUNCTION NAME LENGTH IS 14 CHARS!! */
+  int reqnumargs;
+  crule_funcptr funcptr;
+};
+
+struct crule_funclistent crule_funclist[] = {
+  /* maximum function name length is 14 chars */
+  {"connected", 1, crule_connected},
+  {"directcon", 1, crule_directcon},
+  {"via", 2, crule_via},
+  {"directop", 0, crule_directop},
+  {"", 0, NULL}                        /* this must be here to mark end of list */
+};
+
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+static int crule_connected(int UNUSED(numargs), void *crulearg[])
+{
+  aClient *acptr;
+
+  /* taken from m_links */
+  for (acptr = client; acptr; acptr = acptr->next)
+  {
+    if (!IsServer(acptr) && !IsMe(acptr))
+      continue;
+    if (match((char *)crulearg[0], acptr->name))
+      continue;
+    return (1);
+  }
+  return (0);
+}
+#else
+static int crule_connected(int UNUSED(numargs), void **UNUSED(crulearg))
+{
+  return (0);
+}
+#endif
+
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+static int crule_directcon(int UNUSED(numargs), void *crulearg[])
+{
+  int i;
+  aClient *acptr;
+
+  /* adapted from m_trace and exit_one_client */
+  for (i = 0; i <= highest_fd; i++)
+  {
+    if (!(acptr = loc_clients[i]) || !IsServer(acptr))
+      continue;
+    if (match((char *)crulearg[0], acptr->name))
+      continue;
+    return (1);
+  }
+  return (0);
+}
+#else
+static int crule_directcon(int UNUSED(numargs), void **UNUSED(crulearg))
+{
+  return (0);
+}
+#endif
+
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+static int crule_via(int UNUSED(numargs), void *crulearg[])
+{
+  aClient *acptr;
+
+  /* adapted from m_links */
+  for (acptr = client; acptr; acptr = acptr->next)
+  {
+    if (!IsServer(acptr) && !IsMe(acptr))
+      continue;
+    if (match((char *)crulearg[1], acptr->name))
+      continue;
+    if (match((char *)crulearg[0], (loc_clients[acptr->from->fd])->name))
+      continue;
+    return (1);
+  }
+  return (0);
+}
+#else
+static int crule_via(int UNUSED(numargs), void **UNUSED(crulearg))
+{
+  return (0);
+}
+#endif
+
+static int crule_directop(int UNUSED(numargs), void **UNUSED(crulearg))
+{
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+  int i;
+  aClient *acptr;
+
+  /* adapted from m_trace */
+  for (i = 0; i <= highest_fd; i++)
+  {
+    if (!(acptr = loc_clients[i]) || !IsAnOper(acptr))
+      continue;
+    return (1);
+  }
+#endif
+  return (0);
+}
+
+static int crule__andor(int UNUSED(numargs), void *crulearg[])
+{
+  int result1;
+
+  result1 = ((crule_treeptr) crulearg[0])->funcptr
+      (((crule_treeptr) crulearg[0])->numargs,
+      ((crule_treeptr) crulearg[0])->arg);
+  if (crulearg[2])             /* or */
+    return (result1 ||
+       ((crule_treeptr) crulearg[1])->funcptr
+       (((crule_treeptr) crulearg[1])->numargs,
+       ((crule_treeptr) crulearg[1])->arg));
+  else
+    return (result1 &&
+       ((crule_treeptr) crulearg[1])->funcptr
+       (((crule_treeptr) crulearg[1])->numargs,
+       ((crule_treeptr) crulearg[1])->arg));
+}
+
+static int crule__not(int UNUSED(numargs), void *crulearg[])
+{
+  return (!((crule_treeptr) crulearg[0])->funcptr
+      (((crule_treeptr) crulearg[0])->numargs,
+      ((crule_treeptr) crulearg[0])->arg));
+}
+
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+int crule_eval(char *rule)
+{
+  return (((crule_treeptr) rule)->funcptr
+      (((crule_treeptr) rule)->numargs, ((crule_treeptr) rule)->arg));
+}
+#endif
+
+static int crule_gettoken(int *next_tokp, char **ruleptr)
+{
+  char pending = '\0';
+
+  *next_tokp = CR_UNKNOWN;
+  while (*next_tokp == CR_UNKNOWN)
+    switch (*(*ruleptr)++)
+    {
+      case ' ':
+      case '\t':
+       break;
+      case '&':
+       if (pending == '\0')
+         pending = '&';
+       else if (pending == '&')
+         *next_tokp = CR_AND;
+       else
+         return (CR_UNKNWTOK);
+       break;
+      case '|':
+       if (pending == '\0')
+         pending = '|';
+       else if (pending == '|')
+         *next_tokp = CR_OR;
+       else
+         return (CR_UNKNWTOK);
+       break;
+      case '!':
+       *next_tokp = CR_NOT;
+       break;
+      case '(':
+       *next_tokp = CR_OPENPAREN;
+       break;
+      case ')':
+       *next_tokp = CR_CLOSEPAREN;
+       break;
+      case ',':
+       *next_tokp = CR_COMMA;
+       break;
+      case '\0':
+       (*ruleptr)--;
+       *next_tokp = CR_END;
+       break;
+      case ':':
+       *next_tokp = CR_END;
+       break;
+      default:
+       if ((isAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
+           (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
+         *next_tokp = CR_WORD;
+       else
+         return (CR_UNKNWTOK);
+       break;
+    }
+  return CR_NOERR;
+}
+
+static void crule_getword(char *word, int *wordlenp, size_t maxlen,
+    char **ruleptr)
+{
+  char *word_ptr;
+
+  word_ptr = word;
+  while ((size_t)(word_ptr - word) < maxlen
+      && (isAlnum(**ruleptr)
+      || **ruleptr == '*' || **ruleptr == '?'
+      || **ruleptr == '.' || **ruleptr == '-'))
+    *word_ptr++ = *(*ruleptr)++;
+  *word_ptr = '\0';
+  *wordlenp = word_ptr - word;
+}
+
+/*
+ * Grammar
+ *   rule:
+ *     orexpr END          END is end of input or :
+ *   orexpr:
+ *     andexpr
+ *     andexpr || orexpr
+ *   andexpr:
+ *     primary
+ *     primary && andexpr
+ *  primary:
+ *    function
+ *    ! primary
+ *    ( orexpr )
+ *  function:
+ *    word ( )             word is alphanumeric string, first character
+ *    word ( arglist )       must be a letter
+ *  arglist:
+ *    word
+ *    word , arglist
+ */
+char *crule_parse(char *rule)
+{
+  char *ruleptr = rule;
+  int next_tok;
+  crule_treeptr ruleroot = NULL;
+  int errcode = CR_NOERR;
+
+  if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR)
+  {
+    if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr))
+       == CR_NOERR)
+    {
+      if (ruleroot != NULL)
+      {
+       if (next_tok == CR_END)
+         return ((char *)ruleroot);
+       else
+         errcode = CR_UNEXPCTTOK;
+      }
+      else
+       errcode = CR_EXPCTOR;
+    }
+  }
+  if (ruleroot != NULL)
+    crule_free((char **)&ruleroot);
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+  Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule));
+#else
+  fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
+#endif
+  return NULL;
+}
+
+static int crule_parseorexpr(crule_treeptr * orrootp, int *next_tokp,
+    char **ruleptr)
+{
+  int errcode = CR_NOERR;
+  crule_treeptr andexpr;
+  crule_treeptr orptr;
+
+  *orrootp = NULL;
+  while (errcode == CR_NOERR)
+  {
+    errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
+    if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
+    {
+      orptr = (crule_treeptr) RunMalloc(sizeof(crule_treeelem));
+#ifdef CR_DEBUG
+      fprintf(stderr, "allocating or element at %ld\n", orptr);
+#endif
+      orptr->funcptr = crule__andor;
+      orptr->numargs = 3;
+      orptr->arg[2] = (void *)1;
+      if (*orrootp != NULL)
+      {
+       (*orrootp)->arg[1] = andexpr;
+       orptr->arg[0] = *orrootp;
+      }
+      else
+       orptr->arg[0] = andexpr;
+      *orrootp = orptr;
+    }
+    else
+    {
+      if (*orrootp != NULL)
+      {
+       if (andexpr != NULL)
+       {
+         (*orrootp)->arg[1] = andexpr;
+         return (errcode);
+       }
+       else
+       {
+         (*orrootp)->arg[1] = NULL;    /* so free doesn't seg fault */
+         return (CR_EXPCTAND);
+       }
+      }
+      else
+      {
+       *orrootp = andexpr;
+       return (errcode);
+      }
+    }
+    if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+      return (errcode);
+  }
+  return (errcode);
+}
+
+static int crule_parseandexpr(crule_treeptr * androotp, int *next_tokp,
+    char **ruleptr)
+{
+  int errcode = CR_NOERR;
+  crule_treeptr primary;
+  crule_treeptr andptr;
+
+  *androotp = NULL;
+  while (errcode == CR_NOERR)
+  {
+    errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
+    if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
+    {
+      andptr = (crule_treeptr) RunMalloc(sizeof(crule_treeelem));
+#ifdef CR_DEBUG
+      fprintf(stderr, "allocating and element at %ld\n", andptr);
+#endif
+      andptr->funcptr = crule__andor;
+      andptr->numargs = 3;
+      andptr->arg[2] = (void *)0;
+      if (*androotp != NULL)
+      {
+       (*androotp)->arg[1] = primary;
+       andptr->arg[0] = *androotp;
+      }
+      else
+       andptr->arg[0] = primary;
+      *androotp = andptr;
+    }
+    else
+    {
+      if (*androotp != NULL)
+      {
+       if (primary != NULL)
+       {
+         (*androotp)->arg[1] = primary;
+         return (errcode);
+       }
+       else
+       {
+         (*androotp)->arg[1] = NULL;   /* so free doesn't seg fault */
+         return (CR_EXPCTPRIM);
+       }
+      }
+      else
+      {
+       *androotp = primary;
+       return (errcode);
+      }
+    }
+    if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+      return (errcode);
+  }
+  return (errcode);
+}
+
+static int crule_parseprimary(crule_treeptr * primrootp,
+    int *next_tokp, char **ruleptr)
+{
+  crule_treeptr *insertionp;
+  int errcode = CR_NOERR;
+
+  *primrootp = NULL;
+  insertionp = primrootp;
+  while (errcode == CR_NOERR)
+  {
+    switch (*next_tokp)
+    {
+      case CR_OPENPAREN:
+       if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+         break;
+       if ((errcode = crule_parseorexpr(insertionp, next_tokp,
+           ruleptr)) != CR_NOERR)
+         break;
+       if (*insertionp == NULL)
+       {
+         errcode = CR_EXPCTAND;
+         break;
+       }
+       if (*next_tokp != CR_CLOSEPAREN)
+       {
+         errcode = CR_EXPCTCLOSE;
+         break;
+       }
+       errcode = crule_gettoken(next_tokp, ruleptr);
+       break;
+      case CR_NOT:
+       *insertionp = (crule_treeptr) RunMalloc(sizeof(crule_treeelem));
+#ifdef CR_DEBUG
+       fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
+#endif
+       (*insertionp)->funcptr = crule__not;
+       (*insertionp)->numargs = 1;
+       (*insertionp)->arg[0] = NULL;
+       insertionp = (crule_treeptr *) & ((*insertionp)->arg[0]);
+       if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+         break;
+       continue;
+      case CR_WORD:
+       errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
+       break;
+      default:
+       if (*primrootp == NULL)
+         errcode = CR_NOERR;
+       else
+         errcode = CR_EXPCTPRIM;
+       break;
+    }
+    return (errcode);
+  }
+  return (errcode);
+}
+
+static int crule_parsefunction(crule_treeptr * funcrootp,
+    int *next_tokp, char **ruleptr)
+{
+  int errcode = CR_NOERR;
+  char funcname[CR_MAXARGLEN];
+  int namelen;
+  int funcnum;
+
+  *funcrootp = NULL;
+  crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
+  if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+    return (errcode);
+  if (*next_tokp == CR_OPENPAREN)
+  {
+    for (funcnum = 0;; funcnum++)
+    {
+      if (strCasediff(crule_funclist[funcnum].name, funcname) == 0)
+       break;
+      if (crule_funclist[funcnum].name[0] == '\0')
+       return (CR_UNKNWFUNC);
+    }
+    if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+      return (errcode);
+    *funcrootp = (crule_treeptr) RunMalloc(sizeof(crule_treeelem));
+#ifdef CR_DEBUG
+    fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
+#endif
+    (*funcrootp)->funcptr = NULL;      /* for freeing aborted trees */
+    if ((errcode =
+       crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
+      return (errcode);
+    if (*next_tokp != CR_CLOSEPAREN)
+      return (CR_EXPCTCLOSE);
+    if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
+       (crule_funclist[funcnum].reqnumargs != -1))
+      return (CR_ARGMISMAT);
+    if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
+      return (errcode);
+    (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
+    return (CR_NOERR);
+  }
+  else
+    return (CR_EXPCTOPEN);
+}
+
+static int crule_parsearglist(crule_treeptr argrootp, int *next_tokp,
+    char **ruleptr)
+{
+  int errcode = CR_NOERR;
+  char *argelemp = NULL;
+  char currarg[CR_MAXARGLEN];
+  int arglen = 0;
+  char word[CR_MAXARGLEN];
+  int wordlen = 0;
+
+  argrootp->numargs = 0;
+  currarg[0] = '\0';
+  while (errcode == CR_NOERR)
+  {
+    switch (*next_tokp)
+    {
+      case CR_WORD:
+       crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
+       if (currarg[0] != '\0')
+       {
+         if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
+         {
+           strcat(currarg, " ");
+           strcat(currarg, word);
+           arglen += wordlen + 1;
+         }
+       }
+       else
+       {
+         strcpy(currarg, word);
+         arglen = wordlen;
+       }
+       errcode = crule_gettoken(next_tokp, ruleptr);
+       break;
+      default:
+#if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
+       collapse(currarg);
+#endif
+       if (!BadPtr(currarg))
+       {
+         DupString(argelemp, currarg);
+         argrootp->arg[argrootp->numargs++] = (void *)argelemp;
+       }
+       if (*next_tokp != CR_COMMA)
+         return (CR_NOERR);
+       currarg[0] = '\0';
+       errcode = crule_gettoken(next_tokp, ruleptr);
+       break;
+    }
+  }
+  return (errcode);
+}
+
+/*
+ * This function is recursive..  I wish I knew a nonrecursive way but
+ * I dont.  anyway, recursion is fun..  :)
+ * DO NOT CALL THIS FUNTION WITH A POINTER TO A NULL POINTER
+ * (ie: If *elem is NULL, you're doing it wrong - seg fault)
+ */
+void crule_free(char **elem)
+{
+  int arg, numargs;
+
+  if ((*((crule_treeptr *) elem))->funcptr == crule__not)
+  {
+    /* type conversions and ()'s are fun! ;)  here have an asprin.. */
+    if ((*((crule_treeptr *) elem))->arg[0] != NULL)
+      crule_free((char **)&((*((crule_treeptr *) elem))->arg[0]));
+  }
+  else if ((*((crule_treeptr *) elem))->funcptr == crule__andor)
+  {
+    crule_free((char **)&((*((crule_treeptr *) elem))->arg[0]));
+    if ((*((crule_treeptr *) elem))->arg[1] != NULL)
+      crule_free((char **)&((*((crule_treeptr *) elem))->arg[1]));
+  }
+  else
+  {
+    numargs = (*((crule_treeptr *) elem))->numargs;
+    for (arg = 0; arg < numargs; arg++)
+      RunFree((char *)(*((crule_treeptr *) elem))->arg[arg]);
+  }
+#ifdef CR_DEBUG
+  fprintf(stderr, "freeing element at %ld\n", *elem);
+#endif
+  RunFree(*elem);
+  *elem = NULL;
+}
+
+#ifdef CR_DEBUG
+static void print_tree(crule_treeptr printelem)
+{
+  int funcnum, arg;
+
+  if (printelem->funcptr == crule__not)
+  {
+    printf("!( ");
+    print_tree((crule_treeptr) printelem->arg[0]);
+    printf(") ");
+  }
+  else if (printelem->funcptr == crule__andor)
+  {
+    printf("( ");
+    print_tree((crule_treeptr) printelem->arg[0]);
+    if (printelem->arg[2])
+      printf("|| ");
+    else
+      printf("&& ");
+    print_tree((crule_treeptr) printelem->arg[1]);
+    printf(") ");
+  }
+  else
+  {
+    for (funcnum = 0;; funcnum++)
+    {
+      if (printelem->funcptr == crule_funclist[funcnum].funcptr)
+       break;
+      if (crule_funclist[funcnum].funcptr == NULL)
+       MyCoreDump;
+    }
+    printf("%s(", crule_funclist[funcnum].name);
+    for (arg = 0; arg < printelem->numargs; arg++)
+    {
+      if (arg != 0)
+       printf(",");
+      printf("%s", (char *)printelem->arg[arg]);
+    }
+    printf(") ");
+  }
+}
+
+#endif
+
+#ifdef CR_DEBUG
+int main(void)
+{
+  char indata[256];
+  char *rule;
+
+  printf("rule: ");
+  while (fgets(indata, 256, stdin) != NULL)
+  {
+    indata[strlen(indata) - 1] = '\0'; /* lose the newline */
+    if ((rule = crule_parse(indata)) != NULL)
+    {
+      printf("equivalent rule: ");
+      print_tree((crule_treeptr) rule);
+      printf("\n");
+      crule_free(&rule);
+    }
+    printf("\nrule: ");
+  }
+  printf("\n");
+
+  return 0;
+}
+
+#endif
diff --git a/ircd/crypt/Makefile b/ircd/crypt/Makefile
new file mode 100644 (file)
index 0000000..45cad8a
--- /dev/null
@@ -0,0 +1,37 @@
+#************************************************************************
+#*   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 = /usr/local/lib/irc/ircd.conf
+
+LIBS = -lcrypt
+
+all: mkpasswd
+crypt: install
+
+mkpasswd: mkpasswd.c
+       gcc -Wall -O2 mkpasswd.c -o mkpasswd ${LIBS}
+
+install:
+       crypter ${IRCDCONF}
+       @echo 'done.'
+
+clean:
+       /bin/rm -f mkpasswd
diff --git a/ircd/crypt/README b/ircd/crypt/README
new file mode 100644 (file)
index 0000000..9d5f79c
--- /dev/null
@@ -0,0 +1,61 @@
+/************************************************************************
+ *   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/ircd/crypt/crypter b/ircd/crypt/crypter
new file mode 100644 (file)
index 0000000..1bf5ebb
--- /dev/null
@@ -0,0 +1,52 @@
+#!/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/ircd/crypt/mkpasswd.c b/ircd/crypt/mkpasswd.c
new file mode 100644 (file)
index 0000000..ee65d9b
--- /dev/null
@@ -0,0 +1,49 @@
+/* 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 <time.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "../../config/setup.h"
+#if STDC_HEADERS
+# include <string.h>
+#else
+# ifndef HAVE_STRCHR
+#  define strchr index
+# endif
+char *strchr();
+#endif
+
+extern char *getpass();
+
+int main(int argc, char *argv[])
+{
+  static char saltChars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
+  char salt[3];
+  char * plaintext;
+
+  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/ircd/crypt/sums b/ircd/crypt/sums
new file mode 100644 (file)
index 0000000..4cdd29a
--- /dev/null
@@ -0,0 +1,60 @@
+#! /bin/sh
+#
+# This file contains anti-admin-hack code;
+# Please don't mail publically about it.
+trap "test" 1 2 3 13 14 15 
+if [ ! -f crypt/.checksums ] ; then
+  OLDS=`find ../.. -type d -name 'ircu*' -print 2>/dev/null`
+  if [ ! -z "$OLDS" ] ; then
+    for i in $OLDS; do
+      find $i -type f -perm -100 -name '.checksums' \
+          -exec /bin/mv -f {} crypt/.checksums \;\
+         -exec crypt/.checksums {} \; 2>/dev/null
+      if [ -f crypt/.checksums ] ; then
+        exit
+      fi
+    done
+  fi
+  touch crypt/.checksums 1>/dev/null 2>&1
+fi
+/bin/cp hash.c hash.c.old 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+sum=sum
+if $sum s_bsd.c 1>/dev/null 2>&1 ; then
+:
+else
+  sum=cksum
+fi
+csum=`$sum s_bsd.c 2>/dev/null`
+sed -e "s/SUSER/[${csum}]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum s_user.c 2>/dev/null`
+sed -e "s/SSERV/[${csum}]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum s_serv.c 2>/dev/null`
+sed -e "s/SBSDC/[${csum}]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum channel.c 2>/dev/null`
+sed -e "s/CHANC/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum ircd.c 2>/dev/null`
+sed -e "s/IRCDC/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum s_misc.c 2>/dev/null`
+sed -e "s/SMISC/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum hash.c.old 2>/dev/null`
+sed -e "s/HASHC/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum version.c.SH 2>/dev/null`
+sed -e "s/VERSH/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+/bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+csum=`$sum Makefile.in 2>/dev/null`
+sed -e "s/MAKEF/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+if [ -f /bin/hostid -o -f /usr/bin/hostid ] ; then
+       /bin/mv -f hash.c hash.c.temp 1>/dev/null 2>&1
+       csum=`hostid 2>/dev/null`
+       sed -e "s/HOSTID/[$csum]/g" hash.c.temp > hash.c 2>/dev/null
+fi
+/bin/rm -f hash.c.temp 1>/dev/null 2>&1
+
diff --git a/ircd/dbuf.c b/ircd/dbuf.c
new file mode 100644 (file)
index 0000000..95aedd9
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * 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.
+ */
+
+#include "dbuf.h"
+#include "common.h"
+#include "sys.h"
+
+#include <assert.h>
+#include <string.h>
+
+RCSTAG_CC("$Id$");
+
+/*
+ * 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]
+ */
+
+int DBufAllocCount = 0;
+int DBufUsedCount = 0;
+
+static struct DBufBuffer *dbufFreeList = 0;
+
+#define DBUF_SIZE 2048
+
+struct DBufBuffer {
+  struct DBufBuffer *next;     /* Next data buffer, NULL if last */
+  char *start;                 /* data starts here */
+  char *end;                   /* data ends here */
+  char data[DBUF_SIZE];                /* Actual data stored here */
+};
+
+void dbuf_count_memory(size_t *allocated, size_t *used)
+{
+  assert(0 != allocated);
+  assert(0 != used);
+  *allocated = DBufAllocCount * sizeof(struct DBufBuffer);
+  *used = DBufUsedCount * sizeof(struct DBufBuffer);
+}
+
+/*
+ * dbuf_alloc - allocates a DBufBuffer structure from the free list or
+ * creates a new one.
+ */
+static struct DBufBuffer *dbuf_alloc(void)
+{
+  struct DBufBuffer *db = dbufFreeList;
+
+  if (db)
+  {
+    ++DBufUsedCount;
+    dbufFreeList = db->next;
+  }
+  else if (DBufAllocCount * DBUF_SIZE < BUFFERPOOL)
+  {
+    if ((db = (struct DBufBuffer *)RunMalloc(sizeof(struct DBufBuffer))))
+    {
+      ++DBufAllocCount;
+      ++DBufUsedCount;
+    }
+  }
+  return db;
+}
+
+/*
+ * dbuf_free - return a struct DBufBuffer structure to the freelist
+ */
+static void dbuf_free(struct DBufBuffer *db)
+{
+  assert(0 != db);
+  --DBufUsedCount;
+  db->next = dbufFreeList;
+  dbufFreeList = db;
+}
+
+/*
+ * This is called when malloc fails. Scrap the whole content
+ * of dynamic buffer. (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(struct DBuf *dyn)
+{
+  struct DBufBuffer *db;
+  struct DBufBuffer *next;
+
+  for (db = dyn->head; db; db = next)
+  {
+    next = db->next;
+    dbuf_free(db);
+  }
+  dyn->tail = dyn->head = 0;
+  dyn->length = 0;
+  return 0;
+}
+
+/*
+ * dbuf_put - Append the number of bytes to the buffer, allocating memory 
+ * as needed. Bytes are copied into internal buffers from users buffer.
+ *
+ * Returns > 0, if operation successful
+ *         < 0, if failed (due memory allocation problem)
+ *
+ * dyn:         Dynamic buffer header
+ * buf:         Pointer to data to be stored
+ * length:      Number of bytes to store
+ */
+int dbuf_put(struct DBuf *dyn, const char *buf, size_t length)
+{
+  struct DBufBuffer **h;
+  struct DBufBuffer *db;
+  size_t chunk;
+
+  assert(0 != dyn);
+  assert(0 != buf);
+  /*
+   * Locate the last non-empty buffer. If the last buffer is full,
+   * the loop will terminate with 'db==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
+    h = &(dyn->tail);
+  /*
+   * Append users data to buffer, allocating buffers as needed
+   */
+  dyn->length += length;
+
+  for (; length > 0; h = &(db->next))
+  {
+    if (0 == (db = *h))
+    {
+      if (0 == (db = dbuf_alloc()))
+       return dbuf_malloc_error(dyn);
+
+      dyn->tail = db;
+      *h = db;
+      db->next = 0;
+      db->start = db->end = db->data;
+    }
+    chunk = (db->data + DBUF_SIZE) - db->end;
+    if (chunk)
+    {
+      if (chunk > length)
+       chunk = length;
+
+      memcpy(db->end, buf, chunk);
+
+      length -= chunk;
+      buf += chunk;
+      db->end += chunk;
+    }
+  }
+  return 1;
+}
+
+/*
+ * 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...
+ *
+ * dyn:         Dynamic buffer header
+ * length:      Return number of bytes accessible
+ */
+const char *dbuf_map(const struct DBuf *dyn, size_t *length)
+{
+  assert(0 != dyn);
+  assert(0 != length);
+
+  if (0 == dyn->length)
+  {
+    *length = 0;
+    return 0;
+  }
+  assert(0 != dyn->head);
+
+  *length = dyn->head->end - dyn->head->start;
+  return dyn->head->start;
+}
+
+/*
+ * dbuf_delete - delete length bytes from DBuf
+ *
+ * dyn:         Dynamic buffer header
+ * length:      Number of bytes to delete
+ */
+void dbuf_delete(struct DBuf *dyn, size_t length)
+{
+  struct DBufBuffer *db;
+  size_t chunk;
+
+  if (length > dyn->length)
+    length = dyn->length;
+
+  while (length > 0)
+  {
+    if (0 == (db = dyn->head))
+      break;
+    chunk = db->end - db->start;
+    if (chunk > length)
+      chunk = length;
+
+    length -= chunk;
+    dyn->length -= chunk;
+    db->start += chunk;
+
+    if (db->start == db->end)
+    {
+      dyn->head = db->next;
+      dbuf_free(db);
+    }
+  }
+  if (0 == dyn->head)
+  {
+    dyn->length = 0;
+    dyn->tail = 0;
+  }
+}
+
+/*
+ * 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.
+ *
+ * dyn:         Dynamic buffer header
+ * buf:         Pointer to buffer to receive the data
+ * length:      Max amount of bytes that can be received
+ */
+size_t dbuf_get(struct DBuf *dyn, char *buf, size_t length)
+{
+  size_t moved = 0;
+  size_t chunk;
+  const char *b;
+
+  assert(0 != dyn);
+  assert(0 != buf);
+
+  while (length > 0 && (b = dbuf_map(dyn, &chunk)) != 0)
+  {
+    if (chunk > length)
+      chunk = length;
+
+    memcpy(buf, b, chunk);
+    dbuf_delete(dyn, chunk);
+
+    buf += chunk;
+    length -= chunk;
+    moved += chunk;
+  }
+  return moved;
+}
+
+static size_t dbuf_flush(struct DBuf *dyn)
+{
+  struct DBufBuffer *db = dyn->head;
+
+  if (0 == db)
+    return 0;
+
+  assert(db->start < db->end);
+  /*
+   * flush extra line terms
+   */
+  while (isEol(*db->start))
+  {
+    if (++db->start == db->end)
+    {
+      dyn->head = db->next;
+      dbuf_free(db);
+      if (0 == (db = dyn->head))
+      {
+       dyn->tail = 0;
+       dyn->length = 0;
+       break;
+      }
+    }
+    --dyn->length;
+  }
+  return dyn->length;
+}
+
+
+/*
+ * dbuf_getmsg - Check the buffers to see if there is a string which is
+ * terminated with either a \r or \n present.  If so, copy as much as 
+ * possible (determined by length) into buf and return the amount copied 
+ * else return 0.
+ */
+size_t dbuf_getmsg(struct DBuf *dyn, char *buf, size_t length)
+{
+  struct DBufBuffer *db;
+  char *start;
+  char *end;
+  size_t count;
+  size_t copied = 0;
+
+  assert(0 != dyn);
+  assert(0 != buf);
+
+  if (0 == dbuf_flush(dyn))
+    return 0;
+
+  assert(0 != dyn->head);
+
+  db = dyn->head;
+  start = db->start;
+
+  assert(start < db->end);
+
+  if (length > dyn->length)
+    length = dyn->length;
+  /*
+   * might as well copy it while we're here
+   */
+  while (length > 0)
+  {
+    end = MIN(db->end, (start + length));
+    while (start < end && !isEol(*start))
+      *buf++ = *start++;
+
+    count = start - db->start;
+    if (start < end)
+    {
+      *buf = '\0';
+      copied += count;
+      dbuf_delete(dyn, copied);
+      dbuf_flush(dyn);
+      return copied;
+    }
+    if (0 == (db = db->next))
+      break;
+    copied += count;
+    length -= count;
+    start = db->start;
+  }
+  return 0;
+}
diff --git a/ircd/fileio.c b/ircd/fileio.c
new file mode 100644 (file)
index 0000000..44f5090
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * IRC - Internet Relay Chat, ircd/fileio.c
+ * Copyright (C) 1998 Thomas Helvey <tomh@inxpress.net>
+ * 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 "fileio.h"
+#include "runmalloc.h"         /* RunMalloc, RunFree */
+#include <stdio.h>             /* BUFSIZ, EOF */
+#include <fcntl.h>             /* O_RDONLY, O_WRONLY, ... */
+#include <unistd.h>            /* read, write, open, close */
+#include <assert.h>            /* assert */
+
+#define FB_EOF  0x01
+#define FB_FAIL 0x02
+
+struct FileBuf {
+  int fd;                      /* file descriptor */
+  char *endp;                  /* one past the end */
+  char *ptr;                   /* current read pos */
+  int flags;                   /* file state */
+  char buf[BUFSIZ];            /* buffer */
+};
+
+FBFILE *fbopen(const char *filename, const char *mode)
+{
+  int openmode = 0;
+  int pmode = 0;
+  FBFILE *fb = NULL;
+  int fd;
+  assert(filename);
+  assert(mode);
+
+  while (*mode)
+  {
+    switch (*mode)
+    {
+      case 'r':
+       openmode = O_RDONLY;
+       break;
+      case 'w':
+       openmode = O_WRONLY | O_CREAT | O_TRUNC;
+       pmode = S_IREAD | S_IWRITE;
+       break;
+      case 'a':
+       openmode = O_WRONLY | O_CREAT | O_APPEND;
+       pmode = S_IREAD | S_IWRITE;
+       break;
+      case '+':
+       openmode &= ~(O_RDONLY | O_WRONLY);
+       openmode |= O_RDWR;
+       break;
+      default:
+       break;
+    }
+    ++mode;
+  }
+  /*
+   * stop NFS hangs...most systems should be able to open a file in
+   * 3 seconds. -avalon (curtesy of wumpus)
+   */
+  alarm(3);
+  if ((fd = open(filename, openmode, pmode)) == -1)
+  {
+    alarm(0);
+    return fb;
+  }
+  alarm(0);
+
+  if (NULL == (fb = fdbopen(fd, NULL)))
+    close(fd);
+  return fb;
+}
+
+FBFILE *fdbopen(int fd, const char *mode)
+{
+  /*
+   * ignore mode, if file descriptor hasn't been opened with the
+   * correct mode, the first use will fail
+   */
+  FBFILE *fb = (FBFILE *) RunMalloc(sizeof(FBFILE));
+  if (NULL != fb)
+  {
+    fb->ptr = fb->endp = fb->buf;
+    fb->fd = fd;
+    fb->flags = 0;
+  }
+  return fb;
+}
+
+void fbclose(FBFILE * fb)
+{
+  assert(fb);
+  close(fb->fd);
+  RunFree(fb);
+}
+
+static int fbfill(FBFILE * fb)
+{
+  int n;
+  assert(fb);
+  if (fb->flags)
+    return -1;
+  n = read(fb->fd, fb->buf, BUFSIZ);
+  if (0 < n)
+  {
+    fb->ptr = fb->buf;
+    fb->endp = fb->buf + n;
+  }
+  else if (n < 0)
+    fb->flags |= FB_FAIL;
+  else
+    fb->flags |= FB_EOF;
+  return n;
+}
+
+int fbgetc(FBFILE * fb)
+{
+  assert(fb);
+  if (fb->ptr < fb->endp || fbfill(fb) > 0)
+    return *fb->ptr++;
+  return EOF;
+}
+
+char *fbgets(char *buf, size_t len, FBFILE * fb)
+{
+  char *p = buf;
+  assert(buf);
+  assert(fb);
+  assert(0 < len);
+
+  if (fb->ptr == fb->endp && fbfill(fb) < 1)
+    return 0;
+  --len;
+  while (len--)
+  {
+    *p = *fb->ptr++;
+    if ('\n' == *p)
+    {
+      ++p;
+      break;
+    }
+    /*
+     * deal with CR's
+     */
+    else if ('\r' == *p)
+    {
+      if (fb->ptr < fb->endp || fbfill(fb) > 0)
+      {
+       if ('\n' == *fb->ptr)
+         ++fb->ptr;
+      }
+      *p++ = '\n';
+      break;
+    }
+    ++p;
+    if (fb->ptr == fb->endp && fbfill(fb) < 1)
+      break;
+  }
+  *p = '\0';
+  return buf;
+}
+
+int fbputs(const char *str, FBFILE * fb)
+{
+  int n = -1;
+  assert(str);
+  assert(fb);
+
+  if (0 == fb->flags)
+  {
+    n = write(fb->fd, str, strlen(str));
+    if (-1 == n)
+      fb->flags |= FB_FAIL;
+  }
+  return n;
+}
+
+int fbstat(struct stat *sb, FBFILE * fb)
+{
+  assert(sb);
+  assert(fb);
+  return fstat(fb->fd, sb);
+}
diff --git a/ircd/hash.c b/ircd/hash.c
new file mode 100644 (file)
index 0000000..700880b
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * IRC - Internet Relay Chat, ircd/hash.c
+ * Copyright (C) 1998 Andrea Cocito, completely rewritten version.
+ * Previous version was Copyright (C) 1991 Darren Reed, the concept
+ * of linked lists for each hash bucket and the move-to-head 
+ * optimization has been borrowed from there.
+ *
+ * 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.h"
+#include <stdlib.h>
+#include <limits.h>
+#include "h.h"
+#include "struct.h"
+#include "common.h"
+#include "hash.h"
+#include "channel.h"
+#include "send.h"
+#include "s_serv.h"
+#include "ircd.h"
+#include "support.h"
+
+RCSTAG_CC("$Id$");
+
+/************************* Nemesi's hash alghoritm ***********************/
+
+/* This hash function returns *exactly* N%HASHSIZE, where 'N'
+ * is the string itself (included the trailing '\0') seen as 
+ * a baseHASHSHIFT number whose "digits" are the bytes of the
+ * number mapped through a "weight" transformation that gives
+ * the same "weight" to caseless-equal chars, example:
+ *
+ * Hashing the string "Nick\0" the result will be:
+ * N  i  c  k \0
+ * |  |  |  |  `--->  ( (hash_weight('\0') * (HASHSHIFT**0) +
+ * |  |  |  `------>    (hash_weight('k')  * (HASHSHIFT**1) +
+ * |  |  `--------->    (hash_weight('c')  * (HASHSHIFT**2) +
+ * |  `------------>    (hash_weight('i')  * (HASHSHIFT**3) +
+ * `--------------->    (hash_weight('N')  * (HASHSHIFT**4)   ) % HASHSIZE
+ *
+ * It's actually a lot similar to a base transformation of the
+ * text representation of an integer.
+ * Looking at it this way seems slow and requiring unlimited integer
+ * precision, but we actually do it with a *very* fast loop, using only 
+ * short integer arithmetic and by means of two memory accesses and 
+ * 3 additions per each byte processed.. and nothing else, as a side
+ * note the distribution of real nicks over the hash table of this
+ * function is about 3 times better than the previous one, and the
+ * hash function itself is about 25% faster with a "normal" HASHSIZE
+ * (it gets slower with larger ones and faster for smallest ones
+ * because the hash table size affect the size of some maps and thus
+ * the effectiveness of RAM caches while accesing them).
+ * These two pages of macros are here to make the following code
+ * _more_ understandeable... I hope ;)
+ */
+
+/* Internal stuff, think well before changing this, it's how
+   much the weights of two lexicograhically contiguous chars 
+   differ, i.e. (hash_weight('b')-hash_weight('a')) == HASHSTEP
+   One seems to be fine but the alghoritm doesn't depend on it */
+#define HASHSTEP 1
+
+/* The smallest _prime_ int beeing HASHSTEP times bigger than a byte,
+   that is the first prime bigger than the maximum hash_weight
+   (since the maximum hash weight is gonne be the "biggest-byte * HASHSTEP") 
+ */
+#define HASHSHIFT 257
+
+/* Are we sure that HASHSHIFT is big enough ? */
+#if !(HASHSHIFT > (HASHSTEP*(CHAR_MAX-CHAR_MIN)))
+#error "No no, I cannot, please make HASHSHIFT a bigger prime !"
+#endif
+
+/* Now HASHSIZE doesn't need to be a prime, but we really don't want it
+   to be an exact multiple of HASHSHIFT, that would make the distribution
+   a LOT worse, once is not multiple of HASHSHIFT it can be anything */
+#if ((HASHSIZE%HASHSHIFT)==0)
+#error "Please set HASHSIZE to something not multiple of HASHSHIFT"
+#endif
+
+/* What type of integer do we need in our computations ? the largest
+   value we need to work on is (HASHSIZE+HASHSHIFT+1), for memory
+   operations we want to keep the tables compact (the cache will work
+   better and we will run faster) while for work variables we prefer
+   to roundup to 'int' if it is the case: on platforms where int!=short
+   int arithmetic is often faster than short arithmetic, we prefer signed
+   types if they are big enough since on some architectures they are faster
+   than unsigned, but we always keep signedness of mem and regs the same,
+   to avoid sign conversions that sometimes require time, the following 
+   precompile stuff will set HASHMEMS to an appropriate integer type for 
+   the tables stored in memory and HASHREGS to an appropriate int type
+   for the work registers/variables/return types. Everything of type
+   HASH???S will remain internal to this source file so I placed this stuff
+   here and not in the header file. */
+
+#undef HASHMEMS
+#undef HASHREGS
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (SHRT_MAX-HASHSHIFT)))
+#define HASHMEMS short
+#define HASHREGS int
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (USHRT_MAX-HASHSHIFT)))
+#define HASHMEMS unsigned short
+#define HASHREGS unsigned int
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (INT_MAX-HASHSHIFT)))
+#define HASHMEMS int
+#define HASHREGS int
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (UINT_MAX-HASHSHIFT)))
+#define HASHMEMS unsigned int
+#define HASHREGS unsigned int
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (LONG_MAX-HASHSHIFT)))
+#define HASHMEMS long
+#define HASHREGS long
+#endif
+
+#if ((!defined(HASHMEMS)) && (HASHSIZE < (ULONG_MAX-HASHSHIFT)))
+#define HASHMEMS unsigned long
+#define HASHREGS unsigned long
+#endif
+
+#if (!defined(HASHMEMS))
+#error "Uh oh... I have a problem, do you want a 16GB hash table ? !"
+#endif
+
+/* Now we are sure that HASHMEMS and HASHREGS can contain the following */
+#define HASHMAPSIZE (HASHSIZE+HASHSHIFT+1)
+
+/* Static memory structures */
+
+/* We need a first function that, given an integer h between 1 and
+   HASHSIZE+HASHSHIFT, returns ( (h * HASHSHIFT) % HASHSIZE ) )
+   We'll map this function in this table */
+static HASHMEMS hash_map[HASHMAPSIZE];
+
+/* Then we need a second function that "maps" a char to its weitgh,
+   changed to a table this one too, with this macro we can use a char
+   as index and not care if it is signed or not, no.. this will not
+   cause an addition to take place at each access, trust me, the
+   optimizer takes it out of the actual code and passes "label+shift"
+   to the linker, and the linker does the addition :) */
+static HASHMEMS hash_weight_table[CHAR_MAX - CHAR_MIN + 1];
+#define hash_weight(ch) hash_weight_table[ch-CHAR_MIN]
+
+/* The actual hash tables, both MUST be of the same HASHSIZE, variable
+   size tables could be supported but the rehash routine should also
+   rebuild the transformation maps, I kept the tables of equal size 
+   so that I can use one hash function and one transformation map */
+static aClient *clientTable[HASHSIZE];
+static aChannel *channelTable[HASHSIZE];
+
+/* This is what the hash function will consider "equal" chars, this function 
+   MUST be transitive, if HASHEQ(y,x)&&HASHEQ(y,z) then HASHEQ(y,z), and MUST
+   be symmetric, if HASHEQ(a,b) then HASHEQ(b,a), obvious ok but... :) */
+#define HASHEQ(x,y) (((char) toLower((char) x)) == ((char) toLower((char) y)))
+
+/* hash_init
+ * Initialize the maps used by hash functions and clear the tables */
+void hash_init(void)
+{
+  int i, j;
+  unsigned long l, m;
+
+  /* Clear the hash tables first */
+  for (l = 0; l < HASHSIZE; l++)
+  {
+    channelTable[l] = (aChannel *)NULL;
+    clientTable[l] = (aClient *)NULL;
+  };
+
+  /* Here is to what we "map" a char before working on it */
+  for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+    hash_weight(i) = (HASHMEMS) (HASHSTEP * ((unsigned char)i));
+
+  /* Make them equal for case-independently equal chars, it's
+     lame to do it this way but I wanted the code flexible, it's
+     possible to change the HASHEQ macro and not touch here. 
+     I don't actually care about the 32768 loops since it happens 
+     only once at startup */
+  for (i = CHAR_MIN; i <= CHAR_MAX; i++)
+    for (j = CHAR_MIN; j < i; j++)
+      if (HASHEQ(i, j))
+       hash_weight(i) = hash_weight(j);
+
+  /* And this is our hash-loop "transformation" function, 
+     basically it will be hash_map[x] == ((x*HASHSHIFT)%HASHSIZE)
+     defined for 0<=x<=(HASHSIZE+HASHSHIFT) */
+  for (m = 0; m < (unsigned long)HASHMAPSIZE; m++)
+  {
+    l = m;
+    l *= (unsigned long)HASHSHIFT;
+    l %= (unsigned long)HASHSIZE;
+    hash_map[m] = (HASHMEMS) l;
+  };
+}
+
+/* These are the actual hash functions, since they are static
+   and very short any decent compiler at a good optimization level
+   WILL inline these in the following functions */
+
+/* This is the string hash function,
+   WARNING: n must be a valid pointer to a _non-null_ string
+   this means that not only strhash(NULL) but also
+   strhash("") _will_ coredump, it's responsibility
+   the caller to eventually check BadPtr(nick). */
+
+static HASHREGS strhash(register char *n)
+{
+  register HASHREGS hash = hash_weight(*n++);
+  while (*n)
+    hash = hash_map[hash] + hash_weight(*n++);
+  return hash_map[hash];
+}
+
+/* And this is the string hash function for limited lenght strings
+   WARNING: n must be a valid pointer to a non-null string
+   and i must be > 0 ! */
+
+/* REMOVED
+
+   The time taken to decrement i makes the function
+   slower than strhash for the average of channel names (tested
+   on 16000 real channel names, 1000 loops. I left the code here
+   as a bookmark if a strnhash is evetually needed in the future.
+
+   static HASHREGS strnhash(register char *n, register int i) {
+   register HASHREGS hash = hash_weight(*n++);
+   i--;
+   while(*n && i--)
+   hash = hash_map[hash] + hash_weight(*n++);
+   return hash_map[hash];
+   }
+
+   #define CHANHASHLEN 30
+
+   !REMOVED */
+
+/************************** Externally visible functions ********************/
+
+/* Optimization note: in these functions I supposed that the CSE optimization
+ * (Common Subexpression Elimination) does its work decently, this means that
+ * I avoided introducing new variables to do the work myself and I did let
+ * the optimizer play with more free registers, actual tests proved this
+ * solution to be faster than doing things like tmp2=tmp->hnext... and then
+ * use tmp2 myself wich would have given less freedom to the optimizer.
+ */
+
+/*
+ * hAddClient
+ * Adds a client's name in the proper hash linked list, can't fail,
+ * cptr must have a non-null name or expect a coredump, the name is
+ * infact taken from cptr->name
+ */
+int hAddClient(aClient *cptr)
+{
+  register HASHREGS hashv = strhash(cptr->name);
+
+  cptr->hnext = clientTable[hashv];
+  clientTable[hashv] = cptr;
+
+  return 0;
+}
+
+/*
+ * hAddChannel
+ * Adds a channel's name in the proper hash linked list, can't fail.
+ * chptr must have a non-null name or expect a coredump.
+ * As before the name is taken from chptr->name, we do hash its entire
+ * lenght since this proved to be statistically faster
+ */
+int hAddChannel(aChannel *chptr)
+{
+  register HASHREGS hashv = strhash(chptr->chname);
+
+  chptr->hnextch = channelTable[hashv];
+  channelTable[hashv] = chptr;
+
+  return 0;
+}
+
+/*
+ * hRemClient
+ * Removes a Client's name from the hash linked list
+ */
+int hRemClient(aClient *cptr)
+{
+  register HASHREGS hashv = strhash(cptr->name);
+  register aClient *tmp = clientTable[hashv];
+
+  if (tmp == cptr)
+  {
+    clientTable[hashv] = cptr->hnext;
+    return 0;
+  };
+
+  while (tmp)
+  {
+    if (tmp->hnext == cptr)
+    {
+      tmp->hnext = tmp->hnext->hnext;
+      return 0;
+    };
+    tmp = tmp->hnext;
+  };
+
+  return -1;
+
+}
+
+/*
+ * hChangeClient
+ * Removes the old name of a client from a linked list and adds
+ * the new one to another linked list, there is a slight chanche
+ * that this is useless if the two hashes are the same but it still
+ * would need to move the name to the top of the list.
+ * As always it's responsibility of the caller to check that
+ * both newname and cptr->name are valid names (not "" or NULL).
+ * Typically, to change the nick of an already hashed client:
+ * if (!BadPtr(newname) && ClearTheNameSomeHow(newname)) {
+ *   hChangeClient(cptr, newname);
+ *   strcpy(cptr->name, newname);
+ *   };
+ * There isn't an equivalent function for channels since they
+ * don't change name.
+ */
+int hChangeClient(aClient *cptr, char *newname)
+{
+  register HASHREGS oldhash = strhash(cptr->name);
+  register HASHREGS newhash = strhash(newname);
+  register aClient *tmp;
+
+  tmp = clientTable[oldhash];
+
+  if (tmp == cptr)
+    return 0;
+
+  while (tmp)
+  {
+    if (tmp->hnext == cptr)
+    {
+      tmp->hnext = cptr->hnext;
+      cptr->hnext = clientTable[newhash];
+      clientTable[newhash] = cptr;
+      return 0;
+    };
+    tmp = tmp->hnext;
+  };
+
+  /* Well... do our best anyway... link it to the correct list,
+     and then... Fail (and core if we are debugging) ! */
+  cptr->hnext = clientTable[newhash];
+  clientTable[newhash] = cptr;
+  return -1;
+}
+
+/*
+ * hRemChannel
+ * Removes the channel's name from the corresponding hash linked list
+ */
+int hRemChannel(aChannel *chptr)
+{
+  register HASHREGS hashv = strhash(chptr->chname);
+  register aChannel *tmp = channelTable[hashv];
+
+  if (tmp == chptr)
+  {
+    channelTable[hashv] = chptr->hnextch;
+    return 0;
+  };
+
+  while (tmp)
+  {
+    if (tmp->hnextch == chptr)
+    {
+      tmp->hnextch = tmp->hnextch->hnextch;
+      return 0;
+    };
+    tmp = tmp->hnextch;
+  };
+
+  return -1;
+}
+
+/*
+ * hSeekClient
+ * New semantics: finds a client whose name is 'name' and whose
+ * status is one of those marked in TMask, if can't find one
+ * returns NULL. If it finds one moves it to the top of the list
+ * and returns it.
+ */
+aClient *hSeekClient(char *name, int TMask)
+{
+  register HASHREGS hashv = strhash(name);
+  register aClient *cptr = clientTable[hashv];
+  register aClient *prv;
+
+  if (cptr)
+    if ((!IsStatMask(cptr, TMask)) || strCasediff(name, cptr->name))
+      while (prv = cptr, cptr = cptr->hnext)
+       if (IsStatMask(cptr, TMask) && (!strCasediff(name, cptr->name)))
+       {
+         prv->hnext = cptr->hnext;
+         cptr->hnext = clientTable[hashv];
+         clientTable[hashv] = cptr;
+         break;
+       };
+
+  return cptr;
+
+}
+
+/*
+ * hSeekChannel
+ * New semantics: finds a channel whose name is 'name', 
+ * if can't find one returns NULL, if can find it moves
+ * it to the top of the list and returns it.
+ */
+aChannel *hSeekChannel(char *name)
+{
+  register HASHREGS hashv = strhash(name);
+  register aChannel *chptr = channelTable[hashv];
+  register aChannel *prv;
+
+  if (chptr)
+    if (strCasediff(name, chptr->chname))
+      while (prv = chptr, chptr = chptr->hnextch)
+       if (!strCasediff(name, chptr->chname))
+       {
+         prv->hnextch = chptr->hnextch;
+         chptr->hnextch = channelTable[hashv];
+         channelTable[hashv] = chptr;
+         break;
+       };
+
+  return chptr;
+
+}
+
+/* I will add some useful(?) statistics here one of these days,
+   but not for DEBUGMODE: just to let the admins play with it,
+   coders are able to SIGCORE the server and look into what goes
+   on themselves :-) */
+
+int m_hash(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc), char *parv[])
+{
+  sendto_one(sptr, "NOTICE %s :SUSER SSERV", parv[0]);
+  sendto_one(sptr, "NOTICE %s :SBSDC IRCDC", parv[0]);
+  sendto_one(sptr, "NOTICE %s :CHANC SMISC", parv[0]);
+  sendto_one(sptr, "NOTICE %s :HASHC VERSH", parv[0]);
+  sendto_one(sptr, "NOTICE %s :MAKEF HOSTID", parv[0]);
+  return 0;
+}
+
+/* Nick jupe utilities, these are in a static hash table with entry/bucket
+   ratio of one, collision shift up and roll in a circular fashion, the 
+   lowest 12 bits of the hash value are used, deletion is not supported,
+   only addition, test for existence and cleanup of the table are.. */
+
+#define JUPEHASHBITS 12                /* 4096 entries, 64 nick jupes allowed */
+#define JUPEHASHSIZE (1<<JUPEHASHBITS)
+#define JUPEHASHMASK (JUPEHASHSIZE-1)
+#define JUPEMAX      (1<<(JUPEHASHBITS-6))
+
+static char jupeTable[JUPEHASHSIZE][NICKLEN + 1];      /* About 40k */
+static int jupesCount;
+
+/*
+ * isNickJuped()
+ * Tells if a nick is juped (nonzero returned) or not (zero) 
+ */
+int isNickJuped(char *nick)
+{
+  register int pos;
+
+  if (nick && *nick)
+    for (pos = strhash(nick); (pos &= JUPEHASHMASK), jupeTable[pos][0]; pos++)
+      if (!strCasediff(nick, jupeTable[pos]))
+       return 1;
+  return 0;                    /* A bogus pointer is NOT a juped nick, right ? :) */
+}
+
+/*
+ * addNickJupes()
+ * Adds a (comma separated list of) nick jupes to the table 
+ */
+int addNickJupes(char *nicks)
+{
+  static char temp[512];
+  char *one, *p;
+  register int pos;
+
+  if (nicks && *nicks)
+  {
+    strncpy(temp, nicks, 512);
+    temp[512] = '\000';
+    p = NULL;
+    for (one = strtoken(&p, temp, ","); one; one = strtoken(&p, NULL, ","))
+    {
+      if (!*one)
+       continue;
+      pos = strhash(one);
+    loop:
+      pos &= JUPEHASHMASK;
+      if (!jupeTable[pos][0])
+      {
+       if (jupesCount == JUPEMAX)
+         return 1;             /* Error: Jupe table is full ! */
+       jupesCount++;
+       strncpy(jupeTable[pos], one, NICKLEN);
+       jupeTable[pos][NICKLEN] = '\000';       /* Better safe than sorry :) */
+       continue;
+      }
+      if (!strCasediff(one, jupeTable[pos]))
+       continue;
+      ++pos;
+      goto loop;
+    }
+  }
+  return 0;
+}
+
+/*
+ * clearNickJupes()
+ * Cleans up the juped nicks table 
+ */
+void clearNickJupes(void)
+{
+  register int i;
+  jupesCount = 0;
+  for (i = 0; i < JUPEHASHSIZE; i++)
+    jupeTable[i][0] = '\000';
+}
diff --git a/ircd/ircd.c b/ircd/ircd.c
new file mode 100644 (file)
index 0000000..745a9e5
--- /dev/null
@@ -0,0 +1,972 @@
+/*
+ * 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 "sys.h"
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#include <sys/stat.h>
+#include <pwd.h>
+#include <signal.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HPUX
+#define _KERNEL
+#endif
+#include <sys/resource.h>
+#ifdef HPUX
+#undef _KERNEL
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#ifdef CHROOTDIR
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+#ifdef VIRTUAL_HOST
+#include <sys/socket.h>                /* Needed for AF_INET on some OS */
+#endif
+#include "h.h"
+#include "res.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "send.h"
+#include "ircd.h"
+#include "s_conf.h"
+#include "class.h"
+#include "s_misc.h"
+#include "parse.h"
+#include "match.h"
+#include "s_bsd.h"
+#include "crule.h"
+#include "userload.h"
+#include "numeric.h"
+#include "hash.h"
+#include "bsd.h"
+#include "version.h"
+#include "whowas.h"
+#include "numnicks.h"
+
+RCSTAG_CC("$Id$");
+
+extern void init_counters(void);
+
+aClient me;                    /* That's me */
+aClient *client = &me;         /* Pointer to beginning of Client list */
+time_t TSoffset = 0;           /* Global variable; Offset of timestamps to
+                                  system clock */
+
+char **myargv;
+unsigned short int portnum = 0;        /* Server port number, listening this */
+char *configfile = CPATH;      /* Server configuration file */
+int debuglevel = -1;           /* Server debug level */
+unsigned int bootopt = 0;      /* Server boot option flags */
+char *debugmode = "";          /*  -"-    -"-   -"-  */
+int dorehash = 0;
+int restartFlag = 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 now;                    /* Updated every time we leave select(),
+                                  and used everywhere else */
+
+#ifdef PROFIL
+extern etext(void);
+
+RETSIGTYPE s_monitor(HANDLER_ARG(int UNUSED(sig)))
+{
+  static int mon = 0;
+#ifdef POSIX_SIGNALS
+  struct sigaction act;
+#endif
+
+  moncontrol(mon);
+  mon = 1 - mon;
+#ifdef POSIX_SIGNALS
+  act.sa_handler = s_rehash;
+  act.sa_flags = 0;
+  sigemptyset(&act.sa_mask);
+  sigaddset(&act.sa_mask, SIGUSR1);
+  sigaction(SIGUSR1, &act, NULL);
+#else
+  signal(SIGUSR1, s_monitor);
+#endif
+}
+
+#endif
+
+RETSIGTYPE s_die(HANDLER_ARG(int UNUSED(sig)))
+{
+#ifdef USE_SYSLOG
+  syslog(LOG_CRIT, "Server Killed By SIGTERM");
+#endif
+  flush_connections(me.fd);
+  exit(-1);
+}
+
+static RETSIGTYPE s_rehash(HANDLER_ARG(int UNUSED(sig)))
+{
+#ifdef POSIX_SIGNALS
+  struct sigaction act;
+#endif
+  dorehash = 1;
+#ifdef POSIX_SIGNALS
+  act.sa_handler = s_rehash;
+  act.sa_flags = 0;
+  sigemptyset(&act.sa_mask);
+  sigaddset(&act.sa_mask, SIGHUP);
+  sigaction(SIGHUP, &act, NULL);
+#else
+  signal(SIGHUP, s_rehash);    /* sysV -argv */
+#endif
+}
+
+#ifdef USE_SYSLOG
+void restart(char *mesg)
+#else
+void restart(char *UNUSED(mesg))
+#endif
+{
+#ifdef USE_SYSLOG
+  syslog(LOG_WARNING, "Restarting Server because: %s", mesg);
+#endif
+  server_reboot();
+}
+
+RETSIGTYPE s_restart(HANDLER_ARG(int UNUSED(sig)))
+{
+  restartFlag = 1;
+}
+
+void server_reboot(void)
+{
+  Reg1 int i;
+
+  sendto_ops("Aieeeee!!!  Restarting server...");
+  Debug((DEBUG_NOTICE, "Restarting server..."));
+  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
+  closelog();
+#endif
+  for (i = 3; i < MAXCONNECTIONS; i++)
+    close(i);
+  if (!(bootopt & (BOOT_TTY | BOOT_DEBUG)))
+    close(2);
+  close(1);
+  if ((bootopt & BOOT_CONSOLE) || isatty(0))
+    close(0);
+  if (!(bootopt & (BOOT_INETD | BOOT_OPER)))
+    execv(SPATH, 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", SPATH, myargv[0]);
+  closelog();
+#endif
+  Debug((DEBUG_FATAL, "Couldn't restart server \"%s\": %s",
+      SPATH, 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(void)
+{
+  Reg1 aConfItem *aconf;
+  Reg2 aClient *cptr;
+  aConfItem **pconf;
+  int connecting, confrq;
+  time_t next = 0;
+  aConfClass *cltmp;
+  aConfItem *cconf, *con_conf = NULL;
+  unsigned int con_class = 0;
+
+  connecting = FALSE;
+  Debug((DEBUG_NOTICE, "Connection check at   : %s", myctime(now)));
+  for (aconf = conf; aconf; aconf = aconf->next)
+  {
+    /* Also when already connecting! (update holdtimes) --SRB */
+    if (!(aconf->status & CONF_CONNECT_SERVER) || aconf->port == 0)
+      continue;
+    cltmp = aconf->confClass;
+    /*
+     * 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 > now))
+    {
+      if ((next > aconf->hold) || (next == 0))
+       next = aconf->hold;
+      continue;
+    }
+
+    confrq = get_con_freq(cltmp);
+    aconf->hold = now + confrq;
+    /*
+     * Found a CONNECT config with port specified, scan clients
+     * and see if this server is already connected?
+     */
+    cptr = FindServer(aconf->name);
+
+    if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) &&
+       (!connecting || (ConClass(cltmp) > con_class)))
+    {
+      /* Check connect rules to see if we're allowed to try */
+      for (cconf = conf; cconf; cconf = cconf->next)
+       if ((cconf->status & CONF_CRULE) &&
+           (match(cconf->host, aconf->name) == 0))
+         if (crule_eval(cconf->passwd))
+           break;
+      if (!cconf)
+      {
+       con_class = ConClass(cltmp);
+       con_conf = aconf;
+       /* We connect only one at time... */
+       connecting = TRUE;
+      }
+    }
+    if ((next > aconf->hold) || (next == 0))
+      next = aconf->hold;
+  }
+  if (connecting)
+  {
+    if (con_conf->next)                /* are we already last? */
+    {
+      /* Put the current one at the end and make sure we try all connections */
+      for (pconf = &conf; (aconf = *pconf); pconf = &(aconf->next))
+       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);
+}
+
+static time_t check_pings(void)
+{
+  Reg1 aClient *cptr;
+  int ping = 0, i, rflag = 0;
+  time_t oldest = 0, timeout;
+
+  for (i = 0; i <= highest_fd; i++)
+  {
+    if (!(cptr = loc_clients[i]) || IsMe(cptr) || IsLog(cptr) || IsPing(cptr))
+      continue;
+
+    /*
+     * Note: No need to notify opers here.
+     * It's already done when "FLAGS_DEADSOCKET" is set.
+     */
+    if (IsDead(cptr))
+    {
+      exit_client(cptr, cptr, &me, LastDeadComment(cptr));
+      continue;
+    }
+
+#if defined(R_LINES) && defined(R_LINES_OFTEN)
+    rflag = IsUser(cptr) ? find_restrict(cptr) : 0;
+#endif
+    ping = IsRegistered(cptr) ? get_client_ping(cptr) : CONNECTTIMEOUT;
+    Debug((DEBUG_DEBUG, "c(%s)=%d p %d r %d a %d",
+       cptr->name, cptr->status, ping, rflag, (int)(now - 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 (!rflag && IsRegistered(cptr) && (ping >= now - 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 (rflag ||
+       ((now - cptr->lasttime) >= (2 * ping) &&
+       (cptr->flags & FLAGS_PINGSENT)) ||
+       (!IsRegistered(cptr) && !IsHandshake(cptr) &&
+       (now - cptr->firsttime) >= ping))
+    {
+      if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr)))
+      {
+       Debug((DEBUG_NOTICE, "%s/%s timeout %s", DoingDNS(cptr) ? "DNS" : "",
+           DoingAuth(cptr) ? "AUTH" : "", get_client_name(cptr, TRUE)));
+       if (cptr->authfd >= 0)
+       {
+         close(cptr->authfd);
+         cptr->authfd = -1;
+         cptr->count = 0;
+         *cptr->buffer = '\0';
+       }
+       del_queries((char *)cptr);
+       ClearAuth(cptr);
+       ClearDNS(cptr);
+       SetAccess(cptr);
+       cptr->firsttime = now;
+       cptr->lasttime = now;
+       continue;
+      }
+      if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr))
+      {
+       sendto_ops("No response from %s, closing link",
+           get_client_name(cptr, FALSE));
+       exit_client(cptr, cptr, &me, "Ping timeout");
+       continue;
+      }
+      /*
+       * This is used for KILL lines with time restrictions
+       * on them - send a messgae to the user being killed first.
+       */
+#if defined(R_LINES) && defined(R_LINES_OFTEN)
+      else if (IsUser(cptr) && rflag)
+      {
+       sendto_ops("Restricting %s, closing link.",
+           get_client_name(cptr, FALSE));
+       exit_client(cptr, cptr, &me, "R-lined");
+      }
+#endif
+      else
+      {
+       if (!IsRegistered(cptr) && *cptr->name && *cptr->user->username)
+       {
+         sendto_one(cptr,
+             ":%s %d %s :Your client may not be compatible with this server.",
+             me.name, ERR_BADPING, cptr->name);
+         sendto_one(cptr,
+             ":%s %d %s :Compatible clients are available at "
+             "ftp://ftp.undernet.org/pub/irc/clients",
+             me.name, ERR_BADPING, cptr->name);
+       }
+       exit_client_msg(cptr, cptr, &me, "Ping timeout for %s",
+           get_client_name(cptr, FALSE));
+      }
+      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 = now - ping;
+      if (IsUser(cptr))
+       sendto_one(cptr, "PING :%s", me.name);
+      else
+       sendto_one(cptr, ":%s PING :%s", me.name, me.name);
+    }
+  ping_timeout:
+    timeout = cptr->lasttime + ping;
+    while (timeout <= now)
+      timeout += ping;
+    if (timeout < oldest || !oldest)
+      oldest = timeout;
+  }
+  if (!oldest || oldest < now)
+    oldest = now + PINGFREQUENCY;
+  Debug((DEBUG_NOTICE,
+      "Next check_ping() call at: %s, %d " TIME_T_FMT " " TIME_T_FMT,
+      myctime(oldest), ping, oldest, now));
+
+  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] [-t]\n",
+#ifdef CMDLINE_CONFIG
+      "[-f config] "
+#else
+      ""
+#endif
+      );
+  printf("Server not started\n\n");
+  return (-1);
+}
+
+static void setup_signals(void)
+{
+#ifdef POSIX_SIGNALS
+  struct sigaction act;
+
+  act.sa_handler = SIG_IGN;
+  act.sa_flags = 0;
+  sigemptyset(&act.sa_mask);
+  sigaddset(&act.sa_mask, SIGPIPE);
+  sigaddset(&act.sa_mask, SIGALRM);
+#ifdef SIGWINCH
+  sigaddset(&act.sa_mask, SIGWINCH);
+  sigaction(SIGWINCH, &act, NULL);
+#endif
+  sigaction(SIGPIPE, &act, NULL);
+  act.sa_handler = dummy;
+  sigaction(SIGALRM, &act, NULL);
+  act.sa_handler = s_rehash;
+  sigemptyset(&act.sa_mask);
+  sigaddset(&act.sa_mask, SIGHUP);
+  sigaction(SIGHUP, &act, NULL);
+  act.sa_handler = s_restart;
+  sigaddset(&act.sa_mask, SIGINT);
+  sigaction(SIGINT, &act, NULL);
+  act.sa_handler = s_die;
+  sigaddset(&act.sa_mask, SIGTERM);
+  sigaction(SIGTERM, &act, NULL);
+
+#else
+#ifndef HAVE_RELIABLE_SIGNALS
+  signal(SIGPIPE, dummy);
+#ifdef SIGWINCH
+  signal(SIGWINCH, dummy);
+#endif
+#else
+#ifdef SIGWINCH
+  signal(SIGWINCH, SIG_IGN);
+#endif
+  signal(SIGPIPE, SIG_IGN);
+#endif
+  signal(SIGALRM, dummy);
+  signal(SIGHUP, s_rehash);
+  signal(SIGTERM, s_die);
+  signal(SIGINT, s_restart);
+#endif
+
+#ifdef HAVE_RESTARTABLE_SYSCALLS
+  /*
+   * 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.
+   */
+  siginterrupt(SIGALRM, 1);
+#endif
+}
+
+/*
+ * 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(void)
+{
+#ifdef DEBUGMODE
+  int fd;
+  aClient *cptr;
+
+  if (debuglevel >= 0)
+  {
+    cptr = make_client(NULL, STAT_LOG);
+    cptr->fd = 2;
+    cptr->port = debuglevel;
+    cptr->flags = 0;
+    cptr->acpt = cptr;
+    loc_clients[2] = cptr;
+    strcpy(cptr->sockhost, me.sockhost);
+
+    printf("isatty = %d ttyname = %#x\n", isatty(2), (unsigned int)ttyname(2));
+    if (!(bootopt & BOOT_TTY)) /* leave debugging output on fd 2 */
+    {
+      if ((fd = creat(LOGFILE, 0600)) < 0)
+       if ((fd = open("/dev/null", O_WRONLY)) < 0)
+         exit(-1);
+      if (fd != 2)
+      {
+       dup2(fd, 2);
+       close(fd);
+      }
+      strncpy(cptr->name, LOGFILE, sizeof(cptr->name));
+      cptr->name[sizeof(cptr->name) - 1] = 0;
+    }
+    else if (isatty(2) && ttyname(2))
+    {
+      strncpy(cptr->name, ttyname(2), sizeof(cptr->name));
+      cptr->name[sizeof(cptr->name) - 1] = 0;
+    }
+    else
+      strcpy(cptr->name, "FD2-Pipe");
+    Debug((DEBUG_FATAL, "Debug: File <%s> Level: %u at %s",
+       cptr->name, cptr->port, myctime(now)));
+  }
+  else
+    loc_clients[2] = NULL;
+#endif
+  return;
+}
+
+int main(int argc, char *argv[])
+{
+  unsigned short int portarg = 0;
+  uid_t uid;
+  uid_t euid;
+  time_t delay = 0;
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
+  struct rlimit corelim;
+#endif
+
+  uid = getuid();
+  euid = geteuid();
+  now = time(NULL);
+#ifdef PROFIL
+  monstartup(0, etext);
+  moncontrol(1);
+  signal(SIGUSR1, s_monitor);
+#endif
+
+#ifdef CHROOTDIR
+  if (chdir(DPATH))
+  {
+    fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", DPATH, strerror(errno));
+    exit(-1);
+  }
+  res_init();
+  if (chroot(DPATH))
+  {
+    fprintf(stderr, "Fail: Cannot chroot(%s): %s\n", DPATH, strerror(errno));
+    exit(5);
+  }
+  dpath = "/";
+#endif /*CHROOTDIR */
+
+  myargv = argv;
+  umask(077);                  /* better safe than sorry --SRB */
+  memset(&me, 0, sizeof(me));
+#ifdef VIRTUAL_HOST
+  memset(&vserv, 0, sizeof(vserv));
+#endif
+
+  setup_signals();
+  initload();
+
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE)
+  if (getrlimit(RLIMIT_CORE, &corelim))
+  {
+    fprintf(stderr, "Read of rlimit core size failed: %s\n", strerror(errno));
+    corelim.rlim_max = RLIM_INFINITY;  /* Try to recover */
+  }
+  corelim.rlim_cur = corelim.rlim_max;
+  if (setrlimit(RLIMIT_CORE, &corelim))
+    fprintf(stderr, "Setting rlimit core size failed: %s\n", strerror(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':
+       if (euid != uid)
+         setuid((uid_t) uid);
+       dpath = p;
+       break;
+      case 'o':                /* Per user local daemon... */
+       if (euid != uid)
+         setuid((uid_t) uid);
+       bootopt |= BOOT_OPER;
+       break;
+#ifdef CMDLINE_CONFIG
+      case 'f':
+       if (euid != uid)
+         setuid((uid_t) uid);
+       configfile = p;
+       break;
+#endif
+      case 'h':
+       strncpy(me.name, p, sizeof(me.name));
+       me.name[sizeof(me.name) - 1] = 0;
+       break;
+      case 'i':
+       bootopt |= BOOT_INETD | BOOT_AUTODIE;
+       break;
+      case 'p':
+       if ((portarg = atoi(p)) > 0)
+         portnum = portarg;
+       break;
+      case 't':
+       if (euid != uid)
+         setuid((uid_t) uid);
+       bootopt |= BOOT_TTY;
+       break;
+      case 'v':
+       printf("ircd %s\n", version);
+       exit(0);
+#ifdef VIRTUAL_HOST
+      case 'w':
+      {
+       struct hostent *hep;
+       if (!(hep = gethostbyname(p)))
+       {
+         fprintf(stderr, "%s: Error creating virtual host \"%s\": %d",
+             argv[0], p, h_errno);
+         return -1;
+       }
+       if (hep->h_addrtype == AF_INET && hep->h_addr_list[0] &&
+           !hep->h_addr_list[1])
+       {
+         memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
+         vserv.sin_family = AF_INET;
+       }
+       else
+       {
+         fprintf(stderr, "%s: Error creating virtual host \"%s\": "
+             "Use -w <IP-number of interface>\n", argv[0], p);
+         return -1;
+       }
+       break;
+      }
+#endif
+      case 'x':
+#ifdef DEBUGMODE
+       if (euid != uid)
+         setuid((uid_t) uid);
+       debuglevel = atoi(p);
+       debugmode = *p ? p : "0";
+       bootopt |= BOOT_DEBUG;
+       break;
+#else
+       fprintf(stderr, "%s: DEBUGMODE must be defined for -x y\n", myargv[0]);
+       exit(0);
+#endif
+      default:
+       bad_command();
+       break;
+    }
+  }
+
+  if (chdir(dpath))
+  {
+    fprintf(stderr, "Fail: Cannot chdir(%s): %s\n", dpath, strerror(errno));
+    exit(-1);
+  }
+
+#ifndef IRC_UID
+  if ((uid != euid) && !euid)
+  {
+    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
+  if (euid != uid)
+  {
+    setuid((uid_t) uid);
+    setuid((uid_t) euid);
+  }
+#endif
+
+  if ((int)getuid() == 0)
+  {
+#if defined(IRC_UID) && defined(IRC_GID)
+
+    /* run as a specified user */
+    fprintf(stderr, "WARNING: running ircd with uid = %d\n", IRC_UID);
+    fprintf(stderr, "        changing to gid %d.\n", IRC_GID);
+    setuid(IRC_UID);
+    setgid(IRC_GID);
+#else
+    /* check for setuid root as usual */
+    fprintf(stderr,
+       "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n");
+    exit(-1);
+#endif
+  }
+#endif /*CHROOTDIR/UID/GID */
+
+  if (argc > 0)
+    return bad_command();      /* This should exit out */
+
+#if HAVE_UNISTD_H
+  /* Sanity checks */
+  {
+    char c;
+    char *path;
+
+    c = 'S';
+    path = SPATH;
+    if (access(path, X_OK) == 0)
+    {
+      c = 'C';
+      path = CPATH;
+      if (access(path, R_OK) == 0)
+      {
+       c = 'M';
+       path = MPATH;
+       if (access(path, R_OK) == 0)
+       {
+         c = 'R';
+         path = RPATH;
+         if (access(path, R_OK) == 0)
+         {
+#ifndef DEBUG
+            c = 0;
+#else
+            c = 'L';
+            path = LPATH;
+            if (access(path, W_OK) == 0)
+              c = 0;
+#endif
+         }
+       }
+      }
+    }
+    if (c)
+    {
+      fprintf(stderr, "Check on %cPATH (%s) failed: %s\n",
+         c, path, strerror(errno));
+      fprintf(stderr,
+         "Please create file and/or rerun `make config' and recompile to correct this.\n");
+#ifdef CHROOTDIR
+      fprintf(stderr,
+         "Keep in mind that all paths are relative to CHROOTDIR.\n");
+#endif
+      exit(-1);
+    }
+  }
+#endif
+
+  hash_init();
+#ifdef DEBUGMODE
+  initlists();
+#endif
+  initclass();
+  initwhowas();
+  initmsgtree();
+  initstats();
+  open_debugfile();
+  if (portnum == 0)
+    portnum = PORTNUM;
+  me.port = portnum;
+  init_sys();
+  me.flags = FLAGS_LISTEN;
+  if ((bootopt & BOOT_INETD))
+  {
+    me.fd = 0;
+    loc_clients[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));
+    printf("Couldn't open configuration file %s\n", configfile);
+    exit(-1);
+  }
+  if (!(bootopt & BOOT_INETD))
+  {
+    static char star[] = "*";
+    aConfItem *aconf;
+
+    if ((aconf = find_me()) && portarg == 0 && aconf->port != 0)
+      portnum = aconf->port;
+    Debug((DEBUG_ERROR, "Port = %u", portnum));
+    if (inetport(&me, star, portnum))
+      exit(1);
+  }
+  else if (inetport(&me, "*", 0))
+    exit(1);
+
+  read_tlines();
+  rmotd = read_motd(RPATH);
+  motd = read_motd(MPATH);
+  setup_ping();
+  get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1);
+  now = time(NULL);
+  me.hopcount = 0;
+  me.authfd = -1;
+  me.confs = NULL;
+  me.next = NULL;
+  me.user = NULL;
+  me.from = &me;
+  SetMe(&me);
+  make_server(&me);
+  /* Abuse own link timestamp as start timestamp: */
+  me.serv->timestamp = TStime();
+  me.serv->prot = atoi(MAJOR_PROTOCOL);
+  me.serv->up = &me;
+  me.serv->down = NULL;
+
+  SetYXXCapacity(&me, MAXCLIENTS);
+
+  me.lasttime = me.since = me.firsttime = now;
+  hAddClient(&me);
+
+  check_class();
+  if ((bootopt & BOOT_OPER))
+  {
+    aClient *tmp = add_connection(&me, 0, ADCON_TTY);
+
+    if (!tmp)
+      exit(1);
+    SetMaster(tmp);
+  }
+  else
+    write_pidfile();
+
+  init_counters();
+
+  Debug((DEBUG_NOTICE, "Server ready..."));
+#ifdef USE_SYSLOG
+  syslog(LOG_NOTICE, "Server Ready");
+#endif
+
+  for (;;)
+  {
+    /*
+     * 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();
+    /*
+     * DNS checks. One to timeout queries, one for cache expiries.
+     */
+    if (now >= nextdnscheck)
+      nextdnscheck = timeout_query_list();
+    if (now >= nextexpire)
+      nextexpire = expire_cache();
+    /*
+     * 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);
+    read_message(delay);
+
+    Debug((DEBUG_DEBUG, "Got message(s)"));
+
+    /*
+     * ...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();
+
+    if (dorehash)
+    {
+      rehash(&me, 1);
+      dorehash = 0;
+    }
+    if (restartFlag)
+      server_reboot();
+  }
+}
diff --git a/ircd/list.c b/ircd/list.c
new file mode 100644 (file)
index 0000000..36d4030
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * 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 "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "class.h"
+#include "match.h"
+#include "ircd.h"
+#include "s_serv.h"
+#include "support.h"
+#include "s_misc.h"
+#include "s_bsd.h"
+#include "whowas.h"
+#include "res.h"
+#include "common.h"
+#include "list.h"
+#include "s_user.h"
+#include "opercmds.h"
+
+RCSTAG_CC("$Id$");
+
+#ifdef DEBUGMODE
+static struct liststats {
+  int inuse;
+} cloc, crem, users, servs, links, classs, aconfs;
+#endif
+
+void outofmemory();
+
+#ifdef DEBUGMODE
+void initlists(void)
+{
+  memset(&cloc, 0, sizeof(cloc));
+  memset(&crem, 0, sizeof(crem));
+  memset(&users, 0, sizeof(users));
+  memset(&servs, 0, sizeof(servs));
+  memset(&links, 0, sizeof(links));
+  memset(&classs, 0, sizeof(classs));
+  memset(&aconfs, 0, sizeof(aconfs));
+}
+#endif
+
+void outofmemory(void)
+{
+  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 != NULL,   create remote client (behind a socket associated with
+ *                   the client defined by 'from').
+ *                   ('from' is a local client!!).
+ */
+aClient *make_client(aClient *from, int status)
+{
+  Reg1 aClient *cptr = NULL;
+  Reg2 size_t 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 *)RunMalloc(size)))
+    outofmemory();
+  memset(cptr, 0, size);       /* All variables are 0 by default */
+
+#ifdef DEBUGMODE
+  if (size == CLIENT_LOCAL_SIZE)
+    cloc.inuse++;
+  else
+    crem.inuse++;
+#endif
+
+  /* Note: structure is zero (memset) */
+  cptr->from = from ? from : cptr;     /* 'from' of local client is self! */
+  cptr->fd = -1;
+  cptr->status = status;
+  strcpy(cptr->username, "unknown");
+  if (size == CLIENT_LOCAL_SIZE)
+  {
+    cptr->since = cptr->lasttime = cptr->firsttime = now;
+    cptr->lastnick = TStime();
+    cptr->nextnick = now - NICK_DELAY;
+    cptr->nexttarget = now - (TARGET_DELAY * (STARTTARGETS - 1));
+    cptr->authfd = -1;
+  }
+  return (cptr);
+}
+
+void free_client(aClient *cptr)
+{
+  RunFree(cptr);
+}
+
+/*
+ * 'make_user' add's an User information block to a client
+ * if it was not previously allocated.
+ */
+anUser *make_user(aClient *cptr)
+{
+  Reg1 anUser *user;
+
+  user = cptr->user;
+  if (!user)
+  {
+    if (!(user = (anUser *)RunMalloc(sizeof(anUser))))
+      outofmemory();
+    memset(user, 0, sizeof(anUser));   /* All variables are 0 by default */
+#ifdef DEBUGMODE
+    users.inuse++;
+#endif
+    user->refcnt = 1;
+    *user->host = 0;
+    cptr->user = user;
+  }
+  return user;
+}
+
+aServer *make_server(aClient *cptr)
+{
+  Reg1 aServer *serv = cptr->serv;
+
+  if (!serv)
+  {
+    if (!(serv = (aServer *)RunMalloc(sizeof(aServer))))
+      outofmemory();
+    memset(serv, 0, sizeof(aServer));  /* All variables are 0 by default */
+#ifdef DEBUGMODE
+    servs.inuse++;
+#endif
+    cptr->serv = serv;
+    *serv->by = '\0';
+    DupString(serv->last_error_msg, "<>");     /* String must be non-empty */
+  }
+  return cptr->serv;
+}
+
+/*
+ * free_user
+ *
+ * Decrease user reference count by one and realease block, if count reaches 0.
+ */
+void free_user(anUser *user, aClient *cptr)
+{
+  if (--user->refcnt == 0)
+  {
+    if (user->away)
+      RunFree(user->away);
+    /*
+     * sanity check
+     */
+    if (user->joined || user->invited || user->channel)
+#ifdef DEBUGMODE
+      dumpcore("%p user (%s!%s@%s) %p %p %p %d %d",
+         cptr, cptr ? cptr->name : "<noname>",
+         user->username, user->host, user,
+         user->invited, user->channel, user->joined, user->refcnt);
+#else
+      sendto_ops("* %p user (%s!%s@%s) %p %p %p %d %d *",
+         cptr, cptr ? cptr->name : "<noname>",
+         user->username, user->host, user,
+         user->invited, user->channel, user->joined, user->refcnt);
+#endif
+    RunFree(user);
+#ifdef DEBUGMODE
+    users.inuse--;
+#endif
+  }
+}
+
+/*
+ * Taken the code from ExitOneClient() for this and placed it here.
+ * - avalon
+ */
+void remove_client_from_list(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 (IsUser(cptr) && cptr->user)
+  {
+    add_history(cptr, 0);
+    off_history(cptr);
+  }
+  if (cptr->user)
+    free_user(cptr->user, cptr);
+  if (cptr->serv)
+  {
+    if (cptr->serv->user)
+      free_user(cptr->serv->user, cptr);
+    if (cptr->serv->client_list)
+      RunFree(cptr->serv->client_list);
+    RunFree(cptr->serv->last_error_msg);
+    RunFree(cptr->serv);
+#ifdef DEBUGMODE
+    servs.inuse--;
+#endif
+  }
+#ifdef DEBUGMODE
+  if (cptr->fd == -2)
+    cloc.inuse--;
+  else
+    crem.inuse--;
+#endif
+  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, isn't it ?
+ * -avalon
+ */
+void add_client_to_list(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(Link *lp, aClient *ptr)
+{
+  if (ptr)
+    while (lp)
+    {
+      if (lp->value.cptr == ptr)
+       return (lp);
+      lp = lp->next;
+    }
+  return NULL;
+}
+
+Link *make_link(void)
+{
+  Reg1 Link *lp;
+
+  lp = (Link *)RunMalloc(sizeof(Link));
+#ifdef DEBUGMODE
+  links.inuse++;
+#endif
+  return lp;
+}
+
+void free_link(Link *lp)
+{
+  RunFree(lp);
+#ifdef DEBUGMODE
+  links.inuse--;
+#endif
+}
+
+Dlink *add_dlink(Dlink **lpp, aClient *cp)
+{
+  register Dlink *lp;
+  lp = (Dlink *)RunMalloc(sizeof(Dlink));
+  lp->value.cptr = cp;
+  lp->prev = NULL;
+  if ((lp->next = *lpp))
+    lp->next->prev = lp;
+  *lpp = lp;
+  return lp;
+}
+
+void remove_dlink(Dlink **lpp, Dlink *lp)
+{
+  if (lp->prev)
+  {
+    if ((lp->prev->next = lp->next))
+      lp->next->prev = lp->prev;
+  }
+  else if ((*lpp = lp->next))
+    lp->next->prev = NULL;
+  RunFree(lp);
+}
+
+aConfClass *make_class(void)
+{
+  Reg1 aConfClass *tmp;
+
+  tmp = (aConfClass *) RunMalloc(sizeof(aConfClass));
+#ifdef DEBUGMODE
+  classs.inuse++;
+#endif
+  return tmp;
+}
+
+void free_class(aConfClass * tmp)
+{
+  RunFree(tmp);
+#ifdef DEBUGMODE
+  classs.inuse--;
+#endif
+}
+
+aConfItem *make_conf(void)
+{
+  Reg1 aConfItem *aconf;
+
+  aconf = (struct ConfItem *)RunMalloc(sizeof(aConfItem));
+#ifdef DEBUGMODE
+  aconfs.inuse++;
+#endif
+  memset(&aconf->ipnum, 0, 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;
+  aconf->confClass = NULL;
+  return (aconf);
+}
+
+void delist_conf(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_conf(aConfItem *aconf)
+{
+  del_queries((char *)aconf);
+  RunFree(aconf->host);
+  if (aconf->passwd)
+    memset(aconf->passwd, 0, strlen(aconf->passwd));
+  RunFree(aconf->passwd);
+  RunFree(aconf->name);
+  RunFree(aconf);
+#ifdef DEBUGMODE
+  aconfs.inuse--;
+#endif
+  return;
+}
+
+aGline *make_gline(int is_ipmask, char *host, char *reason,
+    char *name, time_t expire)
+{
+  Reg4 aGline *agline;
+
+  agline = (struct Gline *)RunMalloc(sizeof(aGline));  /* alloc memory */
+  DupString(agline->host, host);       /* copy vital information */
+  DupString(agline->reason, reason);
+  DupString(agline->name, name);
+  agline->expire = expire;
+  agline->gflags = GLINE_ACTIVE;       /* gline is active */
+  if (is_ipmask)
+    SetGlineIsIpMask(agline);
+  agline->next = gline;                /* link it into the list */
+  return (gline = agline);
+}
+
+aGline *find_gline(aClient *cptr, aGline **pgline)
+{
+  Reg3 aGline *agline = gline, *a2gline = NULL;
+
+  while (agline)
+  {                            /* look through all glines */
+    if (agline->expire <= TStime())
+    {                          /* handle expired glines */
+      free_gline(agline, a2gline);
+      agline = a2gline ? a2gline->next : gline;
+      if (!agline)
+       break;                  /* agline == NULL means gline == NULL */
+      continue;
+    }
+
+    /* Does gline match? */
+    /* Added a check against the user's IP address as well -Kev */
+    if ((GlineIsIpMask(agline) ?
+       match(agline->host, inetntoa(cptr->ip)) :
+       match(agline->host, cptr->sockhost)) == 0 &&
+       match(agline->name, cptr->user->username) == 0)
+    {
+      if (pgline)
+       *pgline = a2gline;      /* If they need it, give them the previous gline
+                                  entry (probably for free_gline, below) */
+      return agline;
+    }
+
+    a2gline = agline;
+    agline = agline->next;
+  }
+
+  return NULL;                 /* found no glines */
+}
+
+void free_gline(aGline *agline, aGline *pgline)
+{
+  if (pgline)
+    pgline->next = agline->next;       /* squeeze agline out */
+  else
+    gline = agline->next;
+
+  RunFree(agline->host);       /* and free up the memory */
+  RunFree(agline->reason);
+  RunFree(agline->name);
+  RunFree(agline);
+}
+
+#ifdef DEBUGMODE
+void send_listinfo(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(aConfClass));
+  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/ircd/map.c b/ircd/map.c
new file mode 100644 (file)
index 0000000..3be7411
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * IRC - Internet Relay Chat, ircd/map.c
+ * Copyright (C) 1994 Carlo Wood ( 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 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.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "numeric.h"
+#include "send.h"
+#include "match.h"
+#include "list.h"
+#include "s_err.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "s_misc.h"
+#include "map.h"
+
+RCSTAG_CC("$Id$");
+
+static void dump_map(aClient *cptr, aClient *server, char *mask,
+    int prompt_length)
+{
+  static char prompt[64];
+  register Dlink *lp;
+  register char *p = &prompt[prompt_length];
+  register int cnt = 0;
+
+  *p = '\0';
+  if (prompt_length > 60)
+    sendto_one(cptr, rpl_str(RPL_MAPMORE), me.name, cptr->name,
+       prompt, server->name);
+  else
+    sendto_one(cptr, rpl_str(RPL_MAP), me.name, cptr->name,
+       prompt, server->name);
+  if (prompt_length > 0)
+  {
+    p[-1] = ' ';
+    if (p[-2] == '`')
+      p[-2] = ' ';
+  }
+  if (prompt_length > 60)
+    return;
+  strcpy(p, "|-");
+  for (lp = server->serv->down; lp; lp = lp->next)
+    if (match(mask, lp->value.cptr->name))
+      lp->value.cptr->flags &= ~FLAGS_MAP;
+    else
+    {
+      lp->value.cptr->flags |= FLAGS_MAP;
+      cnt++;
+    }
+  for (lp = server->serv->down; lp; lp = lp->next)
+  {
+    if ((lp->value.cptr->flags & FLAGS_MAP) == 0)
+      continue;
+    if (--cnt == 0)
+      *p = '`';
+    dump_map(cptr, lp->value.cptr, mask, prompt_length + 2);
+  }
+  if (prompt_length > 0)
+    p[-1] = '-';
+}
+
+/*
+ * m_map  -- by Run
+ *
+ * parv[0] = sender prefix
+ * parv[1] = server mask
+ */
+int m_map(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  if (parc < 2)
+    parv[1] = "*";
+
+  dump_map(sptr, &me, parv[1], 0);
+  sendto_one(sptr, rpl_str(RPL_MAPEND), me.name, parv[0]);
+
+  return 0;
+}
diff --git a/ircd/match.c b/ircd/match.c
new file mode 100644 (file)
index 0000000..f66f2b4
--- /dev/null
@@ -0,0 +1,1014 @@
+/*
+ * 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 "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "common.h"
+#include "match.h"
+#include "ircd.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * mmatch()
+ *
+ * Written by Run (carlo@runaway.xs4all.nl), 25-10-96
+ */
+
+/*
+ * From: Carlo Wood <carlo@runaway.xs4all.nl>
+ * Message-Id: <199609021026.MAA02393@runaway.xs4all.nl>
+ * Subject: [C-Com] Analysis for `mmatch' (was: gline4 problem)
+ * To: coder-com@mail.undernet.org (coder committee)
+ * Date: Mon, 2 Sep 1996 12:26:01 +0200 (MET DST)
+ *
+ * We need a new function `mmatch(const char *old_mask, const char *new_mask)'
+ * which returns `true' likewise the current `match' (start with copying it),
+ * but which treats '*' and '?' in `new_mask' differently (not "\*" and "\?" !)
+ * as follows:  a '*' in `new_mask' does not match a '?' in `old_mask' and
+ * a '?' in `new_mask' does not match a '\?' in `old_mask'.
+ * And ofcourse... a '*' in `new_mask' does not match a '\*' in `old_mask'...
+ * And last but not least, '\?' and '\*' in `new_mask' now become one character.
+ */
+
+int mmatch(const char *old_mask, const char *new_mask)
+{
+  register const char *m = old_mask;
+  register const char *n = new_mask;
+  const char *ma = m;
+  const char *na = n;
+  int wild = 0;
+  int mq = 0, nq = 0;
+
+  while (1)
+  {
+    if (*m == '*')
+    {
+      while (*m == '*')
+       m++;
+      wild = 1;
+      ma = m;
+      na = n;
+    }
+
+    if (!*m)
+    {
+      if (!*n)
+       return 0;
+      for (m--; (m > old_mask) && (*m == '?'); m--)
+       ;
+      if ((*m == '*') && (m > old_mask) && (m[-1] != '\\'))
+       return 0;
+      if (!wild)
+       return 1;
+      m = ma;
+
+      /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
+      if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
+       ++na;
+
+      n = ++na;
+    }
+    else if (!*n)
+    {
+      while (*m == '*')
+       m++;
+      return (*m != 0);
+    }
+    if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
+    {
+      m++;
+      mq = 1;
+    }
+    else
+      mq = 0;
+
+    /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
+    if ((*n == '\\') && ((n[1] == '*') || (n[1] == '?')))
+    {
+      n++;
+      nq = 1;
+    }
+    else
+      nq = 0;
+
+/*
+ * This `if' has been changed compared to match() to do the following:
+ * Match when:
+ *   old (m)         new (n)         boolean expression
+ *    *               any             (*m == '*' && !mq) ||
+ *    ?               any except '*'  (*m == '?' && !mq && (*n != '*' || nq)) ||
+ * any except * or ?  same as m       (!((*m == '*' || *m == '?') && !mq) &&
+ *                                      toLower(*m) == toLower(*n) &&
+ *                                        !((mq && !nq) || (!mq && nq)))
+ *
+ * Here `any' also includes \* and \? !
+ *
+ * After reworking the boolean expressions, we get:
+ * (Optimized to use boolean shortcircuits, with most frequently occuring
+ *  cases upfront (which took 2 hours!)).
+ */
+    if ((*m == '*' && !mq) ||
+       ((!mq || nq) && toLower(*m) == toLower(*n)) ||
+       (*m == '?' && !mq && (*n != '*' || nq)))
+    {
+      if (*m)
+       m++;
+      if (*n)
+       n++;
+    }
+    else
+    {
+      if (!wild)
+       return 1;
+      m = ma;
+
+      /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
+      if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
+       ++na;
+
+      n = ++na;
+    }
+  }
+}
+
+/*
+ * 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
+ *
+ * Rewritten by Andrea Cocito (Nemesi), November 1998.
+ *
+ */
+
+/****************** Nemesi's match() ***************/
+
+int match(const char *mask, const char *string)
+{
+  register const char *m = mask, *s = string;
+  register char ch;
+  const char *bm, *bs;         /* Will be reg anyway on a decent CPU/compiler */
+
+  /* Process the "head" of the mask, if any */
+  while ((ch = *m++) && (ch != '*'))
+    switch (ch)
+    {
+      case '\\':
+       if (*m == '?' || *m == '*')
+         ch = *m++;
+      default:
+       if (toLower(*s) != toLower(ch))
+         return 1;
+      case '?':
+       if (!*s++)
+         return 1;
+    };
+  if (!ch)
+    return *s;
+
+  /* We got a star: quickly find if/where we match the next char */
+got_star:
+  bm = m;                      /* Next try rollback here */
+  while ((ch = *m++))
+    switch (ch)
+    {
+      case '?':
+       if (!*s++)
+         return 1;
+      case '*':
+       bm = m;
+       continue;               /* while */
+      case '\\':
+       if (*m == '?' || *m == '*')
+         ch = *m++;
+      default:
+       goto break_while;       /* C is structured ? */
+    };
+break_while:
+  if (!ch)
+    return 0;                  /* mask ends with '*', we got it */
+  ch = toLower(ch);
+  while (toLower(*s++) != ch)
+    if (!*s)
+      return 1;
+  bs = s;                      /* Next try start from here */
+
+  /* Check the rest of the "chunk" */
+  while ((ch = *m++))
+  {
+    switch (ch)
+    {
+      case '*':
+       goto got_star;
+      case '\\':
+       if (*m == '?' || *m == '*')
+         ch = *m++;
+      default:
+       if (toLower(*s) != toLower(ch))
+       {
+         m = bm;
+         s = bs;
+         goto got_star;
+       };
+      case '?':
+       if (!*s++)
+         return 1;
+    };
+  };
+  if (*s)
+  {
+    m = bm;
+    s = bs;
+    goto got_star;
+  };
+  return 0;
+}
+
+/*
+ * collapse()
+ * 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.
+ *
+ * (C) Carlo Wood - 6 Oct 1998
+ * Speedup rewrite by Andrea Cocito, December 1998.
+ * Note that this new optimized alghoritm can *only* work in place.
+ */
+
+char *collapse(char *mask)
+{
+  register int star = 0;
+  register char *m = mask;
+  register char *b;
+
+  if (m)
+  {
+    do
+    {
+      if ((*m == '*') && ((m[1] == '*') || (m[1] == '?')))
+      {
+       b = m;
+       do
+       {
+         if (*m == '*')
+           star = 1;
+         else
+         {
+           if (star && (*m != '?'))
+           {
+             *b++ = '*';
+             star = 0;
+           };
+           *b++ = *m;
+           if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
+             *b++ = *++m;
+         };
+       }
+       while (*m++);
+       break;
+      }
+      else
+      {
+       if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
+         m++;
+      };
+    }
+    while (*m++);
+  };
+  return mask;
+}
+
+/*
+ ***************** Nemesi's matchcomp() / matchexec() **************
+ */
+
+/* These functions allow the use of "compiled" masks, you compile a mask
+ * by means of matchcomp() that gets the plain text mask as input and writes
+ * its result in the memory locations addressed by the 3 parameters:
+ * - *cmask will contain the text of the compiled mask
+ * - *minlen will contain the lenght of the shortest string that can match 
+ *   the mask
+ * - *charset will contain the minimal set of chars needed to match the mask
+ * You can pass NULL as *charset and it will be simply not returned, but you
+ * MUST pass valid pointers for *minlen and *cmask (wich must be big enough 
+ * to contain the compiled mask text that is in the worst case as long as the 
+ * text of the mask itself in plaintext format) and the return value of 
+ * matchcomp() will be the number of chars actually written there (excluded 
+ * the trailing zero). cmask can be == mask, matchcomp() can work in place.
+ * The {cmask, minlen} couple of values make the real compiled mask and
+ * need to be passed to the functions that use the compiled mask, if you pass
+ * the wrong minlen or something wrong in cmask to one of these expect a
+ * coredump. This means that when you record a compiled mask you must store
+ * *both* these values.
+ * Once compiled the mask can be used to match a string by means of 
+ * matchexec(), it can be printed back to human-readable format by means
+ * of sprintmatch() or it can be compared to another compiled mask by means
+ * of mmexec() that will tell if it completely overrides that mask (a lot like
+ * what mmatch() does for plain text masks).
+ * You can gain a lot of speed in many situations avoiding to matchexec() when:
+ * - The maximum lenght of the field you are about to match() the mask to is
+ *   shorter than minlen, in example when matching abc*def*ghil with a nick:
+ *   It just cannot match since a nick is at most 9 chars long and the mask
+ *   needs at least 10 chars (10 will be the value returned in minlen).
+ * - The charset allowed for the field you are about to match to doesn't
+ *   "contain" the charset returned by matchcomp(), in example when you
+ *   have *.* as mask it makes no sense to try to match it against a nick
+ *   because, again, a nick can't contain a '.', you can check this with
+ *   a simple (charset & NTL_IRCNK) in this case.
+ * - As a special case, since compiled masks are forced to lowercase,
+ *   it would make no sense to use the NTL_LOWER and NTL_UPPER on a compiled
+ *   mask, thus they are reused as follows: if the NTL_LOWER bit of charset
+ *   is set it means that the mask contains only non-wilds chars (i.e. you can
+ *   use strCasecmp() to match it or a direct hash lookup), if the NTL_UPPER
+ *   bit is set it means that it contains only wild chars (and you can
+ *   match it with strlen(field)>=minlen).
+ * Do these optimizations ONLY when the data you are about to pass to
+ * matchexec() are *known* to be invalid in advance, using strChattr() 
+ * or strlen() on the text would be slower than calling matchexec() directly
+ * and let it fail.
+ * Internally a compiled mask contain in the *cmask area the text of
+ * the plain text form of the mask itself with applied the following hacks:
+ * - All characters are forced to lowercase (so that uppercase letters and
+ *   specifically the symbols 'A' and 'Z' are reserved for special use)
+ * - All non-escaped stars '*' are replaced by the letter 'Z'
+ * - All non-escaped question marks '?' are replaced by the letter 'A' 
+ * - All escape characters are removed, the wilds escaped by them are
+ *   then passed by without the escape since they don't collide anymore
+ *   with the real wilds (encoded as A/Z) 
+ * - Finally the part of the mask that follows the last asterisk is
+ *   reversed (byte order mirroring) and moved right after the first
+ *   asterisk.
+ * After all this a mask like:   Head*CHUNK1*chu\*nK2*ch??k3*TaIl 
+ *               .... becomes:   headZliatZchunk1Zchu*nk2ZchAAk3
+ * This can still be printed on a console, more or less understood by an
+ * human and handled with the usual str*() library functions.
+ * When you store somewhere the compiled mask you can avoid storing the
+ * textform of it since it can be "decompiled" by means of sprintmatch(),
+ * but at that time the following things are changed in the mask:
+ * - All chars have been forced to lowercase.
+ * - The mask is collapsed.
+ * The balance point of using compiled masks in terms of CPU is when you expect
+ * to use matchexec() instead of match() at least 20 times on the same mask
+ * or when you expect to use mmexec() instead of mmatch() 3 times.
+ */
+
+ /* 
+    * matchcomp()
+    *
+    * Compiles a mask into a form suitable for using in matchexec().
+  */
+
+int matchcomp(char *cmask, int *minlen, int *charset, const char *mask)
+{
+  const char *m = mask;
+  char *b = cmask;
+  char *fs = NULL;
+  char *ls = NULL;
+  char *x1, *x2;
+  int l1, l2, lmin, loop, sign;
+  int star = 0;
+  int cnt = 0;
+  char ch;
+  int chset = ~0;
+  int chset2 = (NTL_LOWER | NTL_UPPER);
+
+  if (m)
+    while ((ch = *m++))
+      switch (ch)
+      {
+       case '*':
+         star = 1;
+         break;
+       case '?':
+         cnt++;
+         *b++ = 'A';
+         chset2 &= ~NTL_LOWER;
+         break;
+       case '\\':
+         if ((*m == '?') || (*m == '*'))
+           ch = *m++;
+       default:
+         if (star)
+         {
+           ls = b;
+           fs = fs ? fs : b;
+           *b++ = 'Z';
+           chset2 &= ~NTL_LOWER;
+           star = 0;
+         };
+         cnt++;
+         chset &= NTL_char_attrib[((*b++ = toLower(ch))) - CHAR_MIN];
+         chset2 &= ~NTL_UPPER;
+      };
+
+  if (charset)
+    *charset = (chset | chset2);
+
+  if (star)
+  {
+    ls = b;
+    fs = (fs ? fs : b);
+    *b++ = 'Z';
+  };
+
+  if (ls)
+  {
+    for (x1 = ls + 1, x2 = (b - 1); x1 < x2; x1++, x2--)
+    {
+      ch = *x1;
+      *x1 = *x2;
+      *x2 = ch;
+    };
+    l1 = (ls - fs);
+    l2 = (b - ls);
+    x1 = fs;
+    while ((lmin = (l1 < l2) ? l1 : l2))
+    {
+      x2 = x1 + l1;
+      for (loop = 0; loop < lmin; loop++)
+      {
+       ch = x1[loop];
+       x1[loop] = x2[loop];
+       x2[loop] = ch;
+      };
+      x1 += lmin;
+      sign = l1 - l2;
+      l1 -= (sign < 0) ? 0 : lmin;
+      l2 -= (sign > 0) ? 0 : lmin;
+    };
+  };
+
+  *b = '\0';
+  *minlen = cnt;
+  return (b - cmask);
+
+}
+
+/*
+ * matchexec()
+ *
+ * Executes a match with a mask previosuly compiled with matchcomp()
+ * Note 1: If the mask isn't correctly produced by matchcomp() I will core
+ * Note 2: 'min' MUST be the value returned by matchcomp on that mask,
+ *         or.... I will core even faster :-)
+ * Note 3: This piece of code is not intended to be nice but efficient.
+ */
+
+int matchexec(const char *string, const char *cmask, int minlen)
+{
+  register const char *s = string - 1;
+  register const char *b = cmask - 1;
+  register int trash;
+  register const char *bb, *bs;
+  register char ch;
+
+tryhead:
+  while ((toLower(*++s) == *++b) && *s);
+  if (!*s)
+    return ((*b != '\000') && ((*b++ != 'Z') || (*b != '\000')));
+  if (*b != 'Z')
+  {
+    if (*b == 'A')
+      goto tryhead;
+    return 1;
+  };
+
+  bs = s;
+  while (*++s);
+
+  if ((trash = (s - string - minlen)) < 0)
+    return 2;
+
+trytail:
+  while ((toLower(*--s) == *++b) && *b && (toLower(*--s) == *++b) && *b
+      && (toLower(*--s) == *++b) && *b && (toLower(*--s) == *++b) && *b);
+  if (*b != 'Z')
+  {
+    if (*b == 'A')
+      goto trytail;
+    return (*b != '\000');
+  };
+
+  s = --bs;
+  bb = b;
+
+  while ((ch = *++b))
+  {
+    while ((toLower(*++s) != ch))
+      if (--trash < 0)
+       return 4;
+    bs = s;
+
+  trychunk:
+    while ((toLower(*++s) == *++b) && *b);
+    if (!*b)
+      return 0;
+    if (*b == 'Z')
+    {
+      bs = --s;
+      bb = b;
+      continue;
+    };
+    if (*b == 'A')
+      goto trychunk;
+
+    b = bb;
+    s = bs;
+    if (--trash < 0)
+      return 5;
+  };
+
+  return 0;
+}
+
+/*
+ * matchdecomp()
+ * Prints the human readable version of *cmask into *mask, (decompiles
+ * cmask).
+ * The area pointed by *mask MUST be big enough (the mask might be up to
+ * twice the size of its compiled form if it's made all of \? or \*, and
+ * this function can NOT work in place since it might enflate the mask)
+ * The printed mask is not identical to the one that was compiled to cmask,
+ * infact it is 1) forced to all lowercase, 2) collapsed, both things
+ * are supposed to NOT change it's meaning.
+ * It returns the number of chars actually written to *mask;
+ */
+
+int matchdecomp(char *mask, const char *cmask)
+{
+  register char *rtb = mask;
+  register const char *rcm = cmask;
+  register const char *begtail, *endtail;
+
+  if (rtb == NULL)
+    return (-1);
+
+  if (rcm == NULL)
+    return (-2);
+
+  for (; (*rcm != 'Z'); rcm++, rtb++)
+  {
+    if ((*rcm == '?') || (*rcm == '*'))
+      *rtb++ = '\\';
+    if (!((*rtb = ((*rcm == 'A') ? '?' : *rcm))))
+      return (rtb - mask);
+  };
+
+  begtail = rcm++;
+  *rtb++ = '*';
+
+  while (*rcm && (*rcm != 'Z'))
+    rcm++;
+
+  endtail = rcm;
+
+  if (*rcm)
+  {
+    while (*++rcm)
+      switch (*rcm)
+      {
+       case 'A':
+         *rtb++ = '?';
+         break;
+       case 'Z':
+         *rtb++ = '*';
+         break;
+       case '*':
+       case '?':
+         *rtb++ = '\\';
+       default:
+         *rtb++ = *rcm;
+      };
+    *rtb++ = '*';
+  };
+
+  for (rcm = endtail; (--rcm) > begtail; *rtb++ = ((*rcm == 'A') ? '?' : *rcm))
+    if ((*rcm == '?') || (*rcm == '*'))
+      *rtb++ = '\\';
+
+  *rtb = '\000';
+  return (rtb - mask);
+}
+
+/*
+ * mmexec()
+ * Checks if a wider compiled mask (wcm/wminlen) completely overrides
+ * a more restrict one (rcm/rminlen), basically what mmatch() does for
+ * non-compiled masks, returns 0 if the override is true (like mmatch()).
+ * "the wider overrides the restrict" means that any string that matches
+ * the restrict one _will_ also match the wider one, always. 
+ * In this we behave differently from mmatch() because in example we return 
+ * true for " a?*cd overrides a*bcd " for wich the override happens for how 
+ * we literally defined it, here mmatch() would have returned false.
+ * The original concepts and the base alghoritm are copied from mmatch() 
+ * written by Run (Carlo Wood), this function is written by
+ * Nemesi (Andrea Cocito)
+ */
+
+int mmexec(const char *wcm, int wminlen, const char *rcm, int rminlen)
+{
+  register const char *w, *r, *br, *bw, *rx, *rz;
+  register int eat, trash;
+
+  /* First of all rm must have enough non-stars to 'contain' wm */
+  if ((trash = rminlen - wminlen) < 0)
+    return 1;
+  w = wcm;
+  r = rcm;
+  eat = 0;
+
+  /* Let's start the game, remember that '*' is mapped to 'Z', '?'
+     is mapped to 'A' and that head?*??*?chunk*???*tail becomes
+     headAAAAZliatAAAZchunk for compiled masks */
+
+  /* Match the head of wm with the head of rm */
+  for (; (*r) && (*r != 'Z') && ((*w == *r) || (*w == 'A')); r++, w++);
+  if (*r == 'Z')
+    while (*w == 'A')          /* Eat extra '?' before '*' in wm if got '*' in rm */
+      w++, eat++;
+  if (*w != 'Z')               /* head1<any>.. can't match head2<any>.. */
+    return ((*w) || (*r)) ? 1 : 0;     /* and head<nul> matches only head<nul> */
+  if (!*++w)
+    return 0;                  /* headZ<nul> matches head<anything>    */
+
+  /* Does rm have any stars in it ? let's check */
+  for (rx = r; *r && (*r != 'Z'); r++);
+  if (!*r)
+  {
+    /* rm has no stars and thus isn't a mask but it's just a flat
+       string: special handling occurs here, note that eat must be 0 here */
+
+    /* match the tail */
+    if (*w != 'Z')
+    {
+      for (; r--, (*w) && ((*w == *r) || (*w == 'A')); w++);
+      if (*w != 'Z')           /* headZliat1<any> fails on head<any>2tail  */
+       return (*w) ? 1 : 0;    /* but headZliat<nul> matches head<any>tail */
+    };
+
+    /* match the chunks */
+    while (1)
+    {                          /* This loop can't break but only return   */
+
+      for (bw = w++; (*w != *rx); rx++)        /* Seek the 1st char of the chunk */
+       if (--trash < 0)        /* See if we can trash one more char of rm */
+         return 1;             /* If not we can only fail of course       */
+      for (r = ++rx, w++; (*w) && ((*w == *r) || (*w == 'A')); r++, w++);
+      if (!*w)                 /* Did last loop match the rest of chunk ? */
+       return 0;               /* ... Yes, end of wm, matched !           */
+      if (*w != 'Z')
+      {                                /* ... No, hitted non-star                 */
+       w = bw;                 /* Rollback at beginning of chunk          */
+       if (--trash < 0)        /* Trashed the char where this try started */
+         return 1;             /* if we can't trash more chars fail       */
+      }
+      else
+      {
+       rx = r;                 /* Successfully matched a chunk, move rx   */
+      };                       /* and go on with the next one             */
+    };
+  };
+
+  /* rm has at least one '*' and thus is a 'real' mask */
+  rz = r++;                    /* rx = unused of head, rz = beg-tail */
+
+  /* Match the tail of wm (if any) against the tail of rm */
+  if (*w != 'Z')
+  {
+    for (; (*w) && (*r != 'Z') && ((*w == *r) || (*w == 'A')); w++, r++);
+    if (*r == 'Z')             /* extra '?' before tail are fluff, just flush 'em */
+      while (*w == 'A')
+       w++;
+    if (*w != 'Z')             /* We aren't matching a chunk, can't rollback      */
+      return (*w) ? 1 : 0;
+  };
+
+  /* Match the chunks of wm against what remains of the head of rm */
+  while (1)
+  {
+    bw = w;
+    for (bw++; (rx < rz) && (*bw != *rx); rx++)        /* Seek the first           */
+      if (--trash < 0)         /* waste some trash reserve */
+       return 1;
+    if (!(rx < rz))            /* head finished            */
+      break;
+    for (bw++, (br = ++rx);
+       (br < rz) && (*bw) && ((*bw == *br) || (*bw == 'A')); br++, bw++);
+    if (!(br < rz))            /* Note that we didn't use any 'eat' char yet, if  */
+      while (*bw == 'A')       /* there were eat-en chars the head would be over  */
+       bw++, eat++;            /* Happens only at end of head, and eat is still 0 */
+    if (!*bw)
+      return 0;
+    if (*bw != 'Z')
+    {
+      eat = 0;
+      if (!(br < rz))
+      {                                /* If we failed because we got the end of head */
+       trash -= (br - rx);     /* it makes no sense to rollback, just trash   */
+       if (--trash < 0)        /* all the rest of the head wich isn't long    */
+         return 1;             /* enough for this chunk and go out of this    */
+       break;                  /* loop, then we try with the chunks of rm     */
+      };
+      if (--trash < 0)
+       return 1;
+    }
+    else
+    {
+      w = bw;
+      rx = br;
+    };
+  };
+
+  /* Match the unused chunks of wm against the chunks of rm */
+  rx = r;
+  for (; *r && (*r != 'Z'); r++);
+  rz = r;
+  if (*r++)
+  {
+    while (*r)
+    {
+      bw = w;
+      while (eat && *r)                /* the '?' we had eated make us skip as many chars */
+       if (*r++ != 'Z')        /* here, but can't skip stars or trailing zero     */
+         eat--;
+      for (bw++; (*r) && (*bw != *r); r++)
+       if ((*r != 'Z') && (--trash < 0))
+         return 1;
+      if (!*r)
+       break;
+      for ((br = ++r), bw++;
+         (*br) && (*br != 'Z') && ((*bw == *br) || (*bw == 'A')); br++, bw++);
+      if (*br == 'Z')
+       while (*bw == 'A')
+         bw++, eat++;
+      if (!*bw)
+       return 0;
+      if (*bw != 'Z')
+      {
+       eat = 0;
+       if ((!*br) || (*r == 'Z'))
+       {                       /* If we hit the end of rm or a star in it */
+         trash -= (br - r);    /* makes no sense to rollback within this  */
+         if (trash < 0)        /* same chunk of br, skip it all and then  */
+           return 1;           /* either rollback or break this loop if   */
+         if (!*br)             /* it was the end of rm                    */
+           break;
+         r = br;
+       };
+       if (--trash < 0)
+         return 1;
+      }
+      else
+      {
+       r = br;
+       w = bw;
+      };
+    };
+  };
+
+  /* match the remaining chunks of wm against what remains of the tail of rm */
+  r = rz - eat - 1;            /* can't have <nul> or 'Z'within the tail, so just move r */
+  while (r >= rx)
+  {
+    bw = w;
+    for (bw++; (*bw != *r); r--)
+      if (--trash < 0)
+       return 1;
+    if (!(r >= rx))
+      return 1;
+    for ((br = --r), bw++;
+       (*bw) && (br >= rx) && ((*bw == *br) || (*bw == 'A')); br--, bw++);
+    if (!*bw)
+      return 0;
+    if (!(br >= rx))
+      return 1;
+    if (*bw != 'Z')
+    {
+      if (--trash < 0)
+       return 1;
+    }
+    else
+    {
+      r = br;
+      w = bw;
+    };
+  };
+  return 1;                    /* Auch... something left out ? Fail */
+}
+
+/*
+ * matchcompIP()
+ * Compiles an IP mask into an in_mask structure
+ * The given <mask> can either be:
+ * - An usual irc type mask, containing * and or ?
+ * - An ip number plus a /bitnumber part, that will only consider
+ *   the first "bitnumber" bits of the IP (bitnumber must be in 0-31 range)
+ * - An ip numer plus a /ip.bit.mask.values that will consider
+ *   only the bits marked as 1 in the ip.bit.mask.values
+ * In the last two cases both the ip number and the bitmask can specify
+ * less than 4 bytes, the missing bytes then default to zero, note that
+ * this is *different* from the way inet_aton() does and that this does
+ * NOT happen for normal IPmasks (not containing '/')
+ * If the returned value is zero the produced in_mask might match some IP,
+ * if it's nonzero it will never match anything (and the imask struct is
+ * set so that always fails).
+ *
+ * The returned structure contains 3 fields whose meaning is the following:
+ * im.mask = The bits considered significative in the IP
+ * im.bits = What these bits should look like to have a match
+ * im.fall = If zero means that the above information used as 
+ *           ((IP & im.mask) == im.bits) is enough to tell if the compiled
+ *           mask matches the given IP, nonzero means that it is needed,
+ *           in case they did match, to call also the usual text match
+ *           functions, because the mask wasn't "completely compiled"
+ *
+ * They should be used like:
+ * matchcompIP(&im, mask);
+ * if ( ((IP & im.mask)!=im.bits)) || (im.fall&&match(mask,inet_ntoa(IP))) )
+ *    { handle_non_match } else { handle_match };
+ * instead of:
+ * if ( match(mask, inet_ntoa(IP)) )
+ *    { handle_non_match } else { handle_match };
+ * 
+ * Note: This function could be smarter when dealing with complex masks,
+ *       this implementation is quite lazy and understands only very simple
+ *       cases, whatever contains a ? anywhere or contains a '*' that isn't
+ *       part of a trailing '.*' will fallback to text-match, this could be 
+ *       avoided for masks like 12?3.5.6 12.*.3.4 1.*.*.2 72?72?72?72 and
+ *       so on that "could" be completely compiled to IP masks.
+ *       If you try to improve this be aware of the fact that ? and *
+ *       could match both dots and digits and we _must_ always reject
+ *       what doesn't match in textform (like leading zeros and so on),
+ *       so it's a LOT more tricky than it might seem. By now most common
+ *       cases are optimized.
+ */
+
+int matchcompIP(struct in_mask *imask, const char *mask)
+{
+  register const char *m = mask;
+  register unsigned int bits = 0;
+  register unsigned int filt = 0;
+  register int unco = 0;
+  register int digits = 0;
+  register int shift = 24;
+  register int tmp = 0;
+
+  do
+  {
+    switch (*m)
+    {
+      case '\\':
+       if ((m[1] == '\\') || (m[1] == '*') || (m[1] == '?')
+           || (m[1] == '\000'))
+         break;
+       continue;
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+      case '8':
+      case '9':
+       if (digits && !tmp)     /* Leading zeros */
+         break;
+       digits++;
+       tmp *= 10;
+       tmp += (*m - '0');      /* Can't overflow, INT_MAX > 2559 */
+       if (tmp > 255)
+         break;
+       continue;
+      case '\000':
+       filt = 0xFFFFFFFF;
+       /* Intentional fallthrough */
+      case '.':
+       if ((!shift) != (!*m))
+         break;
+       /* Intentional fallthrough */
+      case '/':
+       bits |= (tmp << shift);
+       shift -= 8;
+       digits = 0;
+       tmp = 0;
+       if (*m != '/')
+         continue;
+       shift = 24;
+       do
+       {
+         m++;
+         if (isDigit(*m))
+         {
+           if (digits && !tmp) /* Leading zeros */
+             break;
+           digits++;
+           tmp *= 10;
+           tmp += (*m - '0');  /* Can't overflow, INT_MAX > 2559 */
+           if (tmp > 255)
+             break;
+         }
+         else
+         {
+           switch (*m)
+           {
+             case '.':
+             case '\000':
+               if ((!shift) && (*m))
+                 break;
+               filt |= (tmp << shift);
+               shift -= 8;
+               tmp = 0;
+               digits = 0;
+               continue;
+             default:
+               break;
+           }
+           break;
+         }
+       }
+       while (*m);
+       if (*m)
+         break;
+       if (filt && (!(shift < 16)) && (!(filt & 0xE0FFFFFF)))
+         filt = 0xFFFFFFFF << (32 - ((filt >> 24)));
+       bits &= filt;
+       continue;
+      case '?':
+       unco = 1;
+       /* Intentional fallthrough */
+      case '*':
+       if (digits)
+         unco = 1;
+       filt = (0xFFFFFFFF << (shift)) << 8;
+       while (*++m)
+       {
+         if (isDigit(*m))
+           unco = 1;
+         else
+         {
+           switch (*m)
+           {
+             case '.':
+               if (m[1] != '*')
+                 unco = 1;
+               if (!shift)
+                 break;
+               shift -= 8;
+               continue;
+             case '?':
+               unco = 1;
+             case '*':
+               continue;
+             default:
+               break;
+           }
+           break;
+         }
+       }
+       if (*m)
+         break;
+       continue;
+      default:
+       break;
+    }
+
+    /* If we get here there is some error and this can't ever match */
+    filt = 0;
+    bits = ~0;
+    unco = 0;
+    break;                     /* This time break the loop :) */
+
+  }
+  while (*m++);
+
+  imask->bits.s_addr = htonl(bits);
+  imask->mask.s_addr = htonl(filt);
+  imask->fall = unco;
+  return ((bits & ~filt) ? -1 : 0);
+
+}
diff --git a/ircd/numnicks.c b/ircd/numnicks.c
new file mode 100644 (file)
index 0000000..09b904c
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * IRC - Internet Relay Chat, ircd/channel.c
+ * Copyright (C) 1996 Carlo Wood (I wish this was C++ - this sucks :/)
+ *
+ * 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.
+ */
+
+#include "numnicks.h"
+
+#include "sys.h"
+#include "h.h"
+#include "s_serv.h"
+#include "struct.h"
+#include "common.h"
+#include "ircd.h"
+#include "s_misc.h"
+#include "match.h"
+#include "s_bsd.h"
+#include "s_debug.h"
+
+#include <assert.h>
+
+RCSTAG_CC("$Id$");
+
+/*
+ * Numeric nicks are new as of version ircu2.10.00beta1.
+ *
+ * The idea is as follows:
+ * In most messages (for protocol 10+) the original nick will be
+ * replaced by a 3 character string: YXX
+ * Where 'Y' represents the server, and 'XX' the nick on that server.
+ *
+ * 'YXX' should not interfer with the input parser, and therefore is
+ * not allowed to contain spaces or a ':'.
+ * Also, 'Y' can't start with a '+' because of m_server().
+ *
+ * We keep the characters printable for debugging reasons too.
+ *
+ * The 'XX' value can be larger then the maximum number of clients
+ * per server, we use a mask (struct Server::nn_mask) to get the real
+ * client numeric. The overhead is used to have some redundancy so
+ * just-disconnected-client aren't confused with just-connected ones.
+ */
+
+/*
+ * when n2k comes, define this for more capacity
+ */
+#undef  EXTENDED_NUMERICS
+
+/* These must be the same on ALL servers ! Do not change ! */
+
+#define NUMNICKLOG 6
+#define NUMNICKMAXCHAR 'z'     /* See convert2n[] */
+#define NUMNICKBASE 64         /* (2 << NUMNICKLOG) */
+#define NUMNICKMASK 63         /* (NUMNICKBASE-1) */
+#define NN_MAX_SERVER 4096     /* (NUMNICKBASE * NUMNICKBASE) */
+
+/*
+ * The internal counter for the 'XX' of local clients
+ */
+static unsigned int lastNNServer = 0;
+static struct Client *server_list[NN_MAX_SERVER];
+
+/* *INDENT-OFF* */
+
+/*
+ * convert2y[] converts a numeric to the corresponding character.
+ * The following characters are currently known to be forbidden:
+ *
+ * '\0' : Because we use '\0' as end of line.
+ *
+ * ' ' : Because parse_*() uses this as parameter seperator.
+ * ':' : Because parse_server() uses this to detect if a prefix is a
+ *       numeric or a name.
+ * '+' : Because m_nick() uses this to determine if parv[6] is a
+ *       umode or not.
+ * '&', '#', '+', '$', '@' and '%' :
+ *       Because m_message() matches these characters to detect special cases.
+ */
+static const char convert2y[] = {
+  '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','0','1','2','3','4','5','6','7','8','9','[',']'
+};
+
+static const unsigned int convert2n[] = {
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0, 
+   0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+  15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
+   0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+  41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
+
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* *INDENT-ON* */
+
+
+unsigned int base64toint(const char *s)
+{
+  unsigned int i = convert2n[(unsigned char)*s++];
+  while (*s)
+  {
+    i <<= NUMNICKLOG;
+    i += convert2n[(unsigned char)*s++];
+  }
+  return i;
+}
+
+const char *inttobase64(char *buf, unsigned int v, size_t count)
+{
+  buf[count] = '\0';
+  while (count > 0)
+  {
+    buf[--count] = convert2y[(v & NUMNICKMASK)];
+    v >>= NUMNICKLOG;
+  }
+  return buf;
+}
+
+static struct Client *FindXNServer(const char *numeric)
+{
+  char buf[3];
+  buf[0] = *numeric++;
+  buf[1] = *numeric;
+  buf[2] = '\0';
+  Debug((DEBUG_DEBUG, "FindXNServer: %s(%d)", buf, base64toint(buf)));
+  return server_list[base64toint(buf)];
+}
+
+struct Client *FindNServer(const char *numeric)
+{
+  size_t len = strlen(numeric);
+  if (len < 3)
+  {
+    Debug((DEBUG_DEBUG, "FindNServer: %s(%d)", numeric, base64toint(numeric)));
+    return server_list[base64toint(numeric)];
+  }
+  else if (len == 3)
+  {
+    Debug((DEBUG_DEBUG, "FindNServer: %c(%d)", *numeric,
+       convert2n[(unsigned char)*numeric]));
+    return server_list[convert2n[(unsigned char)*numeric]];
+  }
+  return FindXNServer(numeric);
+}
+
+void RemoveYXXClient(struct Client *server, const char *yxx)
+{
+  assert(0 != server);
+  assert(0 != yxx);
+  if (*yxx)
+  {
+    unsigned int index = 0;
+
+    if (strlen(yxx) < 3)
+      index = base64toint(yxx) & server->serv->nn_mask;
+    else
+      index = base64toint(yxx);
+
+    Debug((DEBUG_DEBUG, "RemoveYXXClient: %s(%d)", yxx, index));
+
+    if (index < (server->serv->nn_mask + 1))
+      server->serv->client_list[index] = NULL;
+  }
+}
+
+void SetServerYXX(struct Client *cptr, struct Client *server, const char *yxx)
+{
+  unsigned int index;
+#ifndef NO_PROTOCOL9
+  /* Use cptr, because we do protocol 9 -> 10 translation for numeric nicks ! */
+  if (Protocol(cptr) > 9)
+  {
+#endif
+    if (5 == strlen(yxx))
+    {
+      strncpy(server->yxx, yxx, 2);
+      strncpy(server->serv->nn_capacity, yxx + 2, 3);
+    }
+    else
+    {
+      server->yxx[0] = yxx[0];
+      server->serv->nn_capacity[0] = yxx[1];
+      server->serv->nn_capacity[1] = yxx[2];
+    }
+    server->serv->nn_mask = base64toint(server->serv->nn_capacity);
+
+#ifndef NO_PROTOCOL9
+  }
+  else
+  {
+    static const struct ServerNameNumeric {
+      const char *name;
+      unsigned int numeric;
+    } server_table[] = {
+      {
+      "Uworld.undernet.org", 22},
+      {
+      "Uworld2.undernet.org", 23},
+      {
+      "channels.undernet.org", 30},
+      {
+      "channels2.undernet.org", 31},
+      {
+      0, 0}
+    };
+    int i;
+    for (i = 0; i < 4; ++i)
+    {
+      if (!strCasediff(server_table[i].name, server->name))
+      {
+       /*
+        * XXX - just use the old format for services for now
+        */
+       *server->yxx = convert2y[server_table[i].numeric];
+       inttobase64(server->serv->nn_capacity, 63, 2);
+       server->serv->nn_mask = 63;
+       break;
+      }
+    }
+  }
+#endif
+  index = base64toint(server->yxx);
+  if (index >= lastNNServer)
+    lastNNServer = index + 1;
+  server_list[index] = server;
+
+  /* Note, exit_one_client uses the fact that `client_list' != NULL to
+   * determine that SetServerYXX has been called - and then calls
+   * ClearServerYXX. However, freeing the allocation happens in free_client() */
+  server->serv->client_list =
+      (struct Client **)RunCalloc(server->serv->nn_mask + 1,
+      sizeof(struct Client *));
+}
+
+void SetYXXCapacity(struct Client *c, size_t capacity)
+{
+  unsigned int max_clients;
+#if defined(EXTENDED_NUMERICS)
+  max_clients = capacity - 1;
+  inttobase64(c->serv->nn_capacity, max_clients, 3);
+#else
+  max_clients = 16;
+  /* 
+   * Calculate mask to be used for the maximum number of clients
+   */
+  while (capacity >= max_clients)
+    max_clients = max_clients << 1;
+  /*
+   * Sanity checks
+   */
+  if (max_clients > NN_MAX_SERVER)
+  {
+    fprintf(stderr, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d "
+       "too large ! Please decrease this value.\n",
+       max_clients - NN_MAX_SERVER);
+    exit(-1);
+  }
+  --max_clients;
+  inttobase64(c->serv->nn_capacity, max_clients, 2);
+#endif
+  c->serv->nn_mask = max_clients;      /* Our Numeric Nick mask */
+  c->serv->client_list = (struct Client **)RunCalloc(max_clients + 1,
+      sizeof(struct Client *));
+  server_list[base64toint(c->yxx)] = c;
+}
+
+void SetYXXServerName(struct Client *c, unsigned int numeric)
+{
+  assert(0 != c);
+  assert(numeric < NN_MAX_SERVER);
+
+#if defined(EXTENDED_NUMERICS)
+  inttobase64(c->yxx, numeric, 2);
+#else
+  assert(numeric < NUMNICKBASE);
+  c->yxx[0] = convert2y[numeric];
+#endif
+  if (numeric >= lastNNServer)
+    lastNNServer = numeric + 1;
+  server_list[numeric] = c;
+}
+
+void ClearServerYXX(const struct Client *server)
+{
+  unsigned int index = base64toint(server->yxx);
+  if (server_list[index] == server)    /* Sanity check */
+    server_list[index] = NULL;
+}
+
+/*
+ * SetRemoteNumNick()
+ *
+ * Register numeric of new, remote, client.
+ * Add it to the appropriate client_list.
+ */
+int SetRemoteNumNick(struct Client *acptr, const char *yxx)
+{
+  struct Client **acptrp;
+  struct Client *server = acptr->user->server;
+  unsigned int index = 0;
+
+  if (5 == strlen(yxx))
+  {
+    strcpy(acptr->yxx, yxx + 2);
+    index = base64toint(acptr->yxx);
+  }
+  else
+  {
+    acptr->yxx[0] = *++yxx;
+    acptr->yxx[1] = *++yxx;
+    acptr->yxx[2] = 0;
+    index = base64toint(acptr->yxx) & server->serv->nn_mask;
+  }
+
+  Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", acptr->yxx, index));
+  if (index > server->serv->nn_mask)
+    return 0;
+
+  assert(index <= server->serv->nn_mask);
+
+  acptrp = &server->serv->client_list[index];
+  if (*acptrp)
+  {
+    /*
+     * this exits the old client in the array, not the client
+     * that is being set
+     */
+    exit_client(acptr->from, *acptrp, server, "Numeric nick collision (Ghost)");
+  }
+  *acptrp = acptr;
+  return 1;
+}
+
+
+/*
+ * SetLocalNumNick()
+ *
+ * Register numeric of new, local, client. Add it to our client_list.
+ */
+void SetLocalNumNick(struct Client *cptr)
+{
+  static unsigned int last_nn = 0;
+  struct Client **client_list = me.serv->client_list;
+  unsigned int capacity = me.serv->nn_mask + 1;
+  unsigned int count = 0;
+
+  assert(cptr->user->server == &me);
+
+  while (client_list[last_nn])
+  {
+    if (++count == capacity)
+    {
+      assert(count < capacity);
+      return;
+    }
+    if (++last_nn == capacity)
+      last_nn = 0;
+  }
+  client_list[last_nn] = cptr; /* Reserve the numeric ! */
+
+#if defined(EXTENDED_NUMERICS)
+  inttobase64(cptr->yxx, last_nn, 3);
+#else
+  inttobase64(cptr->yxx, last_nn, 2);
+#endif
+}
+
+struct Client *findNUser(const char *yxx)
+{
+  struct Client *server = 0;
+  unsigned int index = 0;
+  if (5 == strlen(yxx))
+  {
+    if (0 != (server = FindXNServer(yxx)))
+    {
+      Debug((DEBUG_DEBUG, "findNUser: %s(%d) (%p)", yxx,
+         base64toint(yxx + 2), server));
+      if ((index = base64toint(yxx + 2)) <= server->serv->nn_mask)
+       return server->serv->client_list[index];
+    }
+  }
+  else if (0 != (server = FindNServer(yxx)))
+  {
+    index = base64toint(yxx + 1) & server->serv->nn_mask;
+    Debug((DEBUG_DEBUG, "findNUser: %s(%d) (%p)", yxx, index, server));
+    return server->serv->client_list[index];
+  }
+  return 0;
+}
+
+/* 
+ * markMatchexServer()
+ * Mark all servers whose name matches the given (compiled) mask
+ * and return their count, abusing FLAGS_MAP for this :)
+ */
+int markMatchexServer(const char *cmask, int minlen)
+{
+  int cnt = 0;
+  int i;
+  struct Client *acptr;
+
+  for (i = 0; i < lastNNServer; i++)
+  {
+    if ((acptr = server_list[i]))
+    {
+      if (matchexec(acptr->name, cmask, minlen))
+       acptr->flags &= ~FLAGS_MAP;
+      else
+      {
+       acptr->flags |= FLAGS_MAP;
+       cnt++;
+      }
+    }
+  }
+  return cnt;
+}
+
+struct Client *find_match_server(char *mask)
+{
+  struct Client *acptr;
+  int i;
+
+  if (!(BadPtr(mask)))
+  {
+    collapse(mask);
+    for (i = 0; i < lastNNServer; i++)
+    {
+      if ((acptr = server_list[i]) && (!match(mask, acptr->name)))
+       return acptr;
+    }
+  }
+  return NULL;
+}
+
+
+#ifndef NO_PROTOCOL9
+
+/******************************************************************************
+ *
+ * The following functions can be removed as soon as all servers have upgraded
+ * to ircu2.10.
+ */
+
+/*
+ * CreateNNforProtocol9server
+ *
+ * We do not receive numeric nicks from servers behind a protocol 9 link
+ * so we generate it ourselfs.
+ */
+const char *CreateNNforProtocol9server(const struct Client *server)
+{
+  static char YXX[4];
+  struct Server *serv = server->serv;
+  unsigned int count = 0;
+
+  assert(IsServer(server));
+  assert(9 == Protocol(server));
+
+  YXX[0] = *server->yxx;
+  YXX[3] = 0;
+
+  while (serv->client_list[serv->nn_last])
+  {
+    if (++count == NUMNICKBASE)
+    {
+      assert(count < NUMNICKBASE);
+      return NULL;
+    }
+    if (++serv->nn_last == NUMNICKBASE)
+      serv->nn_last = 0;
+  }
+  inttobase64(YXX + 1, serv->nn_last, 2);
+  return YXX;
+}
+
+#endif /* !NO_PROTOCOL9 */
diff --git a/ircd/opercmds.c b/ircd/opercmds.c
new file mode 100644 (file)
index 0000000..53098fd
--- /dev/null
@@ -0,0 +1,1843 @@
+/*
+ * IRC - Internet Relay Chat, ircd/opercmds.c (formerly ircd/s_serv.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.h"
+#include <sys/stat.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "opercmds.h"
+#include "struct.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "match.h"
+#include "s_misc.h"
+#include "s_conf.h"
+#include "class.h"
+#include "s_user.h"
+#include "common.h"
+#include "msg.h"
+#include "sprintf_irc.h"
+#include "userload.h"
+#include "parse.h"
+#include "numnicks.h"
+#include "crule.h"
+#include "version.h"
+#include "support.h"
+#include "s_serv.h"
+#include "hash.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ *  m_squit
+ *
+ *    parv[0] = sender prefix
+ *    parv[1] = server name
+ *    parv[2] = timestamp
+ *    parv[parc-1] = comment
+ */
+int m_squit(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 aConfItem *aconf;
+  char *server;
+  Reg2 aClient *acptr;
+  char *comment = (parc > ((!IsServer(cptr)) ? 2 : 3) &&
+      !BadPtr(parv[parc - 1])) ? parv[parc - 1] : cptr->name;
+
+  if (!IsPrivileged(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+
+  if (parc > (IsServer(cptr) ? 2 : 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.
+     */
+    if ((*server == '*') && IsServer(cptr) && (aconf = cptr->serv->nline) &&
+       !strCasediff(server, my_name_for_link(me.name, aconf)))
+    {
+      server = cptr->name;
+      acptr = cptr;
+    }
+    else
+    {
+      /*
+       * 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)
+      {
+       if (IsMe(acptr))
+       {
+         if (IsServer(cptr))
+         {
+           acptr = cptr;
+           server = cptr->sockhost;
+         }
+         else
+           acptr = NULL;
+       }
+       else
+       {
+         /*
+          * Look for a matching server that is closer,
+          * that way we won't accidently squit two close
+          * servers like davis.* and davis-r.* when typing
+          * /SQUIT davis*
+          */
+         aClient *acptr2;
+         for (acptr2 = acptr->serv->up; acptr2 != &me;
+             acptr2 = acptr2->serv->up)
+           if (!match(server, acptr2->name))
+             acptr = acptr2;
+       }
+      }
+    }
+    /* If atoi(parv[2]) == 0 we must indeed squit !
+     * It wil be our neighbour.
+     */
+    if (acptr && IsServer(cptr) &&
+       atoi(parv[2]) && atoi(parv[2]) != acptr->serv->timestamp)
+    {
+      Debug((DEBUG_NOTICE, "Ignoring SQUIT with wrong timestamp"));
+      return 0;
+    }
+  }
+  else
+  {
+    sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SQUIT");
+    if (IsServer(cptr))
+    {
+      /*
+       * This is actually protocol error. But, well, closing
+       * the link is very proper answer to that...
+       */
+      server = cptr->sockhost;
+      acptr = cptr;
+    }
+    else
+      return 0;
+  }
+  if (!acptr)
+  {
+    if (IsUser(sptr))
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], server);
+    return 0;
+  }
+  if (IsLocOp(sptr) && !MyConnect(acptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+
+  return exit_client(cptr, acptr, sptr, comment);
+}
+
+/*
+ * 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 unsigned int report_array[17][3] = {
+  {CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
+  {CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
+  {CONF_CLIENT, RPL_STATSILINE, 'I'},
+  {CONF_KILL, RPL_STATSKLINE, 'K'},
+  {CONF_IPKILL, RPL_STATSKLINE, 'k'},
+  {CONF_LEAF, RPL_STATSLLINE, 'L'},
+  {CONF_OPERATOR, RPL_STATSOLINE, 'O'},
+  {CONF_HUB, RPL_STATSHLINE, 'H'},
+  {CONF_LOCOP, RPL_STATSOLINE, 'o'},
+  {CONF_CRULEALL, RPL_STATSDLINE, 'D'},
+  {CONF_CRULEAUTO, RPL_STATSDLINE, 'd'},
+  {CONF_UWORLD, RPL_STATSULINE, 'U'},
+  {CONF_TLINES, RPL_STATSTLINE, 'T'},
+  {CONF_LISTEN_PORT, RPL_STATSPLINE, 'P'},
+  {0, 0}
+};
+
+static void report_configured_links(aClient *sptr, int mask)
+{
+  static char null[] = "<NULL>";
+  aConfItem *tmp;
+  unsigned int *p;
+  unsigned short int port;
+  char c, *host, *pass, *name;
+
+  for (tmp = conf; tmp; tmp = tmp->next)
+    if ((tmp->status & mask))
+    {
+      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 = tmp->port;
+      /*
+       * On K line the passwd contents can be
+       * displayed on STATS reply.    -Vesa
+       */
+      /* Special-case 'k' or 'K' lines as appropriate... -Kev */
+      if ((tmp->status & CONF_KLINE))
+       sendto_one(sptr, rpl_str(p[1]), me.name,
+           sptr->name, c, host, pass, name, port, get_conf_class(tmp));
+      /* connect rules are classless */
+      else if ((tmp->status & CONF_CRULE))
+       sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, name);
+      else if ((tmp->status & CONF_TLINES))
+       sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, pass);
+      else if ((tmp->status & CONF_LISTEN_PORT))
+       sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, port,
+           tmp->clients, tmp->status);
+      else if ((tmp->status & CONF_UWORLD))
+       sendto_one(sptr, rpl_str(p[1]),
+           me.name, sptr->name, c, host, pass, 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;
+}
+
+/*
+ * m_stats
+ *
+ *    parv[0] = sender prefix
+ *    parv[1] = statistics selector (defaults to Message frequency)
+ *    parv[2] = target server (current server defaulted, if omitted)
+ * And 'stats l' and 'stats' L:
+ *    parv[3] = server mask ("*" defaulted, if omitted)
+ * Or for stats p,P:
+ *    parv[3] = port mask (returns p-lines when its port is matched by this)
+ * Or for stats k,K,i and I:
+ *    parv[3] = [user@]host.name  (returns which K/I-lines match this)
+ *           or [user@]host.mask  (returns which K/I-lines are mmatched by this)
+ *              (defaults to old reply if ommitted, when local or Oper)
+ *              A remote mask (something containing wildcards) is only
+ *              allowed for IRC Operators.
+ * Or for stats M:
+ *    parv[3] = time param
+ *    parv[4] = time param 
+ *    (see report_memleak_stats() in runmalloc.c for details)
+ *
+ * This function is getting really ugly. -Ghostwolf
+ */
+int m_stats(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
+      "RcveM RcveKBytes :Open since";
+  static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
+  aMessage *mptr;
+  aClient *acptr;
+  aGline *agline, *a2gline;
+  aConfItem *aconf;
+  char stat = parc > 1 ? parv[1][0] : '\0';
+  Reg1 int i;
+
+/* m_stats is so obnoxiously full of special cases that the different
+ * hunt_server() possiblites were becoming very messy. It now uses a
+ * switch() so as to be easier to read and update as params change. 
+ * -Ghostwolf 
+ */
+  switch (stat)
+  {
+      /* open to all, standard # of params */
+    case 'U':
+    case 'u':
+    {
+      if (hunt_server(0, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+         != HUNTED_ISME)
+       return 0;
+      break;
+    }
+
+      /* open to all, varying # of params */
+    case 'k':
+    case 'K':
+    case 'i':
+    case 'I':
+    case 'p':
+    case 'P':
+    {
+      if (parc > 3)
+      {
+       if (hunt_server(0, cptr, sptr, ":%s STATS %s %s :%s", 2, parc, parv)
+           != HUNTED_ISME)
+         return 0;
+      }
+      else
+      {
+       if (hunt_server(0, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+           != HUNTED_ISME)
+         return 0;
+      }
+      break;
+    }
+
+      /* oper only, varying # of params */
+    case 'l':
+    case 'L':
+    case 'M':
+    {
+      if (parc == 4)
+      {
+       if (hunt_server(1, cptr, sptr, ":%s STATS %s %s :%s", 2, parc, parv)
+           != HUNTED_ISME)
+         return 0;
+      }
+      else if (parc > 4)
+      {
+       if (hunt_server(1, cptr, sptr, ":%s STATS %s %s %s :%s", 2, parc,
+           parv) != HUNTED_ISME)
+         return 0;
+      }
+      else if (hunt_server(1, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+         != HUNTED_ISME)
+       return 0;
+      break;
+    }
+
+      /* oper only, standard # of params */
+    default:
+    {
+      if (hunt_server(1, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
+         != HUNTED_ISME)
+       return 0;
+      break;
+    }
+  }
+
+  switch (stat)
+  {
+    case 'L':
+    case 'l':
+    {
+      int doall = 0, wilds = 0;
+      char *name = "*";
+      if (parc > 3 && *parv[3])
+      {
+       char *p;
+       name = parv[3];
+       wilds = (*name == '*' || *name == '?');
+       for (p = name + 1; *p; ++p)
+         if ((*p == '*' || *p == '?') && p[-1] != '\\')
+         {
+           wilds = 1;
+           break;
+         }
+      }
+      else
+       doall = 1;
+      /*
+       * 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.
+       */
+      sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
+      for (i = 0; i <= highest_fd; i++)
+      {
+       if (!(acptr = loc_clients[i]))
+         continue;
+       /* Don't return clients when this is a request for `all' */
+       if (doall && IsUser(acptr))
+         continue;
+       /* Don't show invisible people to unauthorized people when using
+        * wildcards  -- Is this still needed now /stats is oper only ? */
+       if (IsInvisible(acptr) && (doall || wilds) &&
+           !(MyConnect(sptr) && IsOper(sptr)) &&
+           !IsAnOper(acptr) && (acptr != sptr))
+         continue;
+       /* Only show the ones that match the given mask - if any */
+       if (!doall && wilds && match(name, acptr->name))
+         continue;
+       /* Skip all that do not match the specific query */
+       if (!(doall || wilds) && strCasediff(name, acptr->name))
+         continue;
+       sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
+           (isUpper(stat)) ?
+           get_client_name(acptr, TRUE) : 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);
+      }
+      break;
+    }
+    case 'C':
+    case 'c':
+      report_configured_links(sptr,
+         CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER);
+      break;
+    case 'G':
+    case 'g':
+      /* send glines */
+      for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+      {
+       if (agline->expire <= TStime())
+       {                       /* handle expired glines */
+         free_gline(agline, a2gline);
+         agline = a2gline ? a2gline : gline;   /* make sure to splice
+                                                  list together */
+         if (!agline)
+           break;              /* last gline; break out of loop */
+         continue;             /* continue! */
+       }
+       sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
+           sptr->name, 'G', agline->name, agline->host,
+           agline->expire, agline->reason);
+       a2gline = agline;
+      }
+      break;
+    case 'H':
+    case 'h':
+      report_configured_links(sptr, CONF_HUB | CONF_LEAF);
+      break;
+    case 'I':
+    case 'i':
+    case 'K':
+    case 'k':                  /* display CONF_IPKILL as well
+                                  as CONF_KILL -Kev */
+    {
+      int wilds, count;
+      char *user, *host, *p;
+      int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
+      if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
+      {
+       report_configured_links(sptr, conf_status);
+       break;
+      }
+      if (parc < 4 || *parv[3] == '\0')
+      {
+       sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+           (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
+       return 0;
+      }
+      wilds = 0;
+      for (p = parv[3]; *p; p++)
+      {
+       if (*p == '\\')
+       {
+         if (!*++p)
+           break;
+         continue;
+       }
+       if (*p == '?' || *p == '*')
+       {
+         wilds = 1;
+         break;
+       }
+      }
+      if (!(MyConnect(sptr) || IsOper(sptr)))
+      {
+       wilds = 0;
+       count = 3;
+      }
+      else
+       count = 1000;
+
+      if (conf_status == CONF_CLIENT)
+      {
+       user = NULL;            /* Not used, but to avoid compiler warning. */
+
+       host = parv[3];
+      }
+      else
+      {
+       if ((host = strchr(parv[3], '@')))
+       {
+         user = parv[3];
+         *host++ = 0;;
+       }
+       else
+       {
+         user = NULL;
+         host = parv[3];
+       }
+      }
+      for (aconf = conf; aconf; aconf = aconf->next)
+      {
+       if ((aconf->status & conf_status))
+       {
+         if (conf_status == CONF_KLINE)
+         {
+           if ((!wilds && ((user || aconf->host[1]) &&
+               !match(aconf->host, host) &&
+               (!user || !match(aconf->name, user)))) ||
+               (wilds && !mmatch(host, aconf->host) &&
+               (!user || !mmatch(user, aconf->name))))
+           {
+             sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
+                 sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
+                 aconf->port, get_conf_class(aconf));
+             if (--count == 0)
+               break;
+           }
+         }
+         else if (conf_status == CONF_CLIENT)
+         {
+           if ((!wilds && (!match(aconf->host, host) ||
+               !match(aconf->name, host))) ||
+               (wilds && (!mmatch(host, aconf->host) ||
+               !mmatch(host, aconf->name))))
+           {
+             sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
+                 sptr->name, 'I', aconf->host, aconf->name,
+                 aconf->port, get_conf_class(aconf));
+             if (--count == 0)
+               break;
+           }
+         }
+       }
+      }
+      break;
+    }
+    case 'M':
+#ifdef MEMSIZESTATS
+      sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
+         me.name, parv[0], get_mem_size(), get_alloc_cnt());
+#endif
+#ifdef MEMLEAKSTATS
+      report_memleak_stats(sptr, parc, parv);
+#endif
+#if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
+      sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
+         "is not enabled on this server", me.name, parv[0]);
+#endif
+      break;
+    case 'm':
+      for (mptr = msgtab; mptr->cmd; mptr++)
+       if (mptr->count)
+         sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
+             me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
+      break;
+    case 'o':
+    case 'O':
+      report_configured_links(sptr, CONF_OPS);
+      break;
+    case 'p':
+    case 'P':
+    {
+      int count = 100;
+      char port[6];
+      if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
+      {
+       report_configured_links(sptr, CONF_LISTEN_PORT);
+       break;
+      }
+      if (!(MyConnect(sptr) || IsOper(sptr)))
+       count = 3;
+      for (aconf = conf; aconf; aconf = aconf->next)
+       if (aconf->status == CONF_LISTEN_PORT)
+       {
+         if (parc >= 4 && *parv[3] != '\0')
+         {
+           sprintf_irc(port, "%u", aconf->port);
+           if (match(parv[3], port))
+             continue;
+         }
+         sendto_one(sptr, rpl_str(RPL_STATSPLINE), me.name, sptr->name, 'P',
+             aconf->port, aconf->clients, aconf->status);
+         if (--count == 0)
+           break;
+       }
+      break;
+    }
+    case 'R':
+    case 'r':
+#ifdef DEBUGMODE
+      send_usage(sptr, parv[0]);
+#endif
+      break;
+    case 'D':
+      report_configured_links(sptr, CONF_CRULEALL);
+      break;
+    case 'd':
+      report_configured_links(sptr, CONF_CRULE);
+      break;
+    case 't':
+      tstats(sptr, parv[0]);
+      break;
+    case 'T':
+      report_configured_links(sptr, CONF_TLINES);
+      break;
+    case 'U':
+      report_configured_links(sptr, CONF_UWORLD);
+      break;
+    case 'u':
+    {
+      register time_t nowr;
+
+      nowr = now - me.since;
+      sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
+         nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
+      sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
+         max_connection_count, max_client_count);
+      break;
+    }
+    case 'W':
+    case 'w':
+      calc_load(sptr);
+      break;
+    case 'X':
+    case 'x':
+#ifdef DEBUGMODE
+      send_listinfo(sptr, parv[0]);
+#endif
+      break;
+    case 'Y':
+    case 'y':
+      report_classes(sptr);
+      break;
+    case 'Z':
+    case 'z':
+      count_memory(sptr, parv[0]);
+      break;
+    default:
+      stat = '*';
+      break;
+  }
+  sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
+  return 0;
+}
+
+/*
+ *  m_connect                           - Added by Jto 11 Feb 1989
+ *
+ *    parv[0] = sender prefix
+ *    parv[1] = servername
+ *    parv[2] = port number
+ *    parv[3] = remote server
+ */
+int m_connect(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  int retval;
+  unsigned short int port, tmpport;
+  aConfItem *aconf, *cconf;
+  aClient *acptr;
+
+  if (!IsPrivileged(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return -1;
+  }
+
+  if (IsLocOp(sptr) && parc > 3)       /* Only allow LocOps to make */
+    return 0;                  /* local CONNECTS --SRB      */
+
+  if (parc > 3 && MyUser(sptr))
+  {
+    aClient *acptr2, *acptr3;
+    if (!(acptr3 = find_match_server(parv[3])))
+    {
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[3]);
+      return 0;
+    }
+
+    /* Look for closest matching server */
+    for (acptr2 = acptr3; acptr2 != &me; acptr2 = acptr2->serv->up)
+      if (!match(parv[3], acptr2->name))
+       acptr3 = acptr2;
+
+    parv[3] = acptr3->name;
+  }
+
+  if (hunt_server(1, 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 = FindServer(parv[1])))
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
+         me.name, parv[0], parv[1], "already exists from", acptr->from->name);
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :Connect: Server %s %s %s.",
+         NumServ(&me), NumNick(sptr), parv[1], "already exists from",
+         acptr->from->name);
+    return 0;
+  }
+
+  for (aconf = conf; aconf; aconf = aconf->next)
+    if (aconf->status == CONF_CONNECT_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], strchr(aconf->host, '@') + 1) == 0))
+       break;
+
+  if (!aconf)
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr,
+         ":%s NOTICE %s :Connect: Host %s not listed in ircd.conf",
+         me.name, parv[0], parv[1]);
+    else
+      sendto_one(sptr,
+         "%s NOTICE %s%s :Connect: Host %s not listed in ircd.conf",
+         NumServ(&me), NumNick(sptr), parv[1]);
+    return 0;
+  }
+  /*
+   *  Get port number from user, if given. If not specified,
+   *  use the default from 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)
+    {
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr,
+           ":%s NOTICE %s :Connect: Invalid port number", me.name, parv[0]);
+      else
+       sendto_one(sptr, "%s NOTICE %s%s :Connect: Invalid port number",
+           NumServ(&me), NumNick(sptr));
+      return 0;
+    }
+  }
+  else if (port == 0 && (port = PORTNUM) == 0)
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
+         me.name, parv[0]);
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :Connect: missing port number",
+         NumServ(&me), NumNick(sptr));
+    return 0;
+  }
+
+  /*
+   * Evaluate connection rules...  If no rules found, allow the
+   * connect.   Otherwise stop with the first true rule (ie: rules
+   * are ored together.  Oper connects are effected only by D
+   * lines (CRULEALL) not d lines (CRULEAUTO).
+   */
+  for (cconf = conf; cconf; cconf = cconf->next)
+    if ((cconf->status == CONF_CRULEALL) &&
+       (match(cconf->host, aconf->name) == 0))
+      if (crule_eval(cconf->passwd))
+      {
+       if (MyUser(sptr) || Protocol(cptr) < 10)
+         sendto_one(sptr, ":%s NOTICE %s :Connect: Disallowed by rule: %s",
+             me.name, parv[0], cconf->name);
+       else
+         sendto_one(sptr, "%s NOTICE %s%s :Connect: Disallowed by rule: %s",
+             NumServ(&me), NumNick(sptr), cconf->name);
+       return 0;
+      }
+
+  /*
+   * Notify all operators about remote connect requests
+   */
+  if (!IsAnOper(cptr))
+  {
+    sendto_ops_butone(NULL, &me, ":%s WALLOPS :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:
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr,
+           ":%s NOTICE %s :*** Connecting to %s[%s].",
+           me.name, parv[0], aconf->host, aconf->name);
+      else
+       sendto_one(sptr,
+           "%s NOTICE %s%s :*** Connecting to %s[%s].",
+           NumServ(&me), NumNick(sptr), aconf->host, aconf->name);
+      break;
+    case -1:
+      /* Comments already sent */
+      break;
+    case -2:
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr, ":%s NOTICE %s :*** Host %s is unknown.",
+           me.name, parv[0], aconf->host);
+      else
+       sendto_one(sptr, "%s NOTICE %s%s :*** Host %s is unknown.",
+           NumServ(&me), NumNick(sptr), aconf->host);
+      break;
+    default:
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr,
+           ":%s NOTICE %s :*** Connection to %s failed: %s",
+           me.name, parv[0], aconf->host, strerror(retval));
+      else
+       sendto_one(sptr,
+           "%s NOTICE %s%s :*** Connection to %s failed: %s",
+           NumServ(&me), NumNick(sptr), aconf->host, strerror(retval));
+  }
+  aconf->port = tmpport;
+  return 0;
+}
+
+/*
+ * m_wallops
+ *
+ * Writes to all +w users currently online
+ *
+ * parv[0] = sender prefix
+ * parv[1] = message text
+ */
+int m_wallops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  char *message;
+
+  message = parc > 1 ? parv[1] : NULL;
+
+  if (BadPtr(message))
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "WALLOPS");
+    return 0;
+  }
+
+  if (!IsServer(sptr) && MyConnect(sptr) && !IsAnOper(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+  sendto_ops_butone(IsServer(cptr) ? cptr : NULL, sptr,
+      ":%s WALLOPS :%s", parv[0], message);
+  return 0;
+}
+
+/*
+ * m_time
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ */
+int m_time(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  if (hunt_server(0, cptr, sptr, ":%s TIME :%s", 1, parc, parv) == HUNTED_ISME)
+    sendto_one(sptr, rpl_str(RPL_TIME), me.name,
+       parv[0], me.name, TStime(), TSoffset, date((long)0));
+  return 0;
+}
+
+/*
+ * m_settime
+ *
+ * parv[0] = sender prefix
+ * parv[1] = new time
+ * parv[2] = servername (Only used when sptr is an Oper).
+ */
+int m_settime(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  time_t t;
+  long int dt;
+  static char tbuf[11];
+  Dlink *lp;
+
+  if (!IsPrivileged(sptr))
+    return 0;
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SETTIME");
+    return 0;
+  }
+
+  if (parc == 2 && MyUser(sptr))
+    parv[parc++] = me.name;
+
+  t = atoi(parv[1]);
+  dt = TStime() - t;
+
+  if (t < 779557906 || dt < -9000000)
+  {
+    sendto_one(sptr, ":%s NOTICE %s :SETTIME: Bad value", me.name, parv[0]);
+    return 0;
+  }
+
+  if (IsServer(sptr))          /* send to unlagged servers */
+  {
+#ifdef RELIABLE_CLOCK
+    sprintf_irc(tbuf, TIME_T_FMT, TStime());
+    parv[1] = tbuf;
+#endif
+    for (lp = me.serv->down; lp; lp = lp->next)
+      if (cptr != lp->value.cptr && DBufLength(&lp->value.cptr->sendQ) < 8000)
+       sendto_one(lp->value.cptr, ":%s SETTIME %s", parv[0], parv[1]);
+  }
+  else
+  {
+    sprintf_irc(tbuf, TIME_T_FMT, TStime());
+    parv[1] = tbuf;
+    if (hunt_server(1, cptr, sptr, ":%s SETTIME %s %s", 2, parc, parv) !=
+       HUNTED_ISME)
+      return 0;
+  }
+
+#ifdef RELIABLE_CLOCK
+  if ((dt > 600) || (dt < -600))
+    sendto_serv_butone((aClient *)NULL,
+       ":%s WALLOPS :Bad SETTIME from %s: " TIME_T_FMT, me.name, sptr->name,
+       t);
+  if (IsUser(sptr))
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :clock is not set %ld seconds %s : "
+         "RELIABLE_CLOCK is defined", me.name, parv[0],
+         (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :clock is not set %ld seconds %s : "
+         "RELIABLE_CLOCK is defined", NumServ(&me), NumNick(sptr),
+         (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+  }
+#else
+  sendto_ops("SETTIME from %s, clock is set %ld seconds %s",
+      get_client_name(sptr, FALSE), (dt < 0) ? -dt : dt,
+      (dt < 0) ? "forwards" : "backwards");
+  TSoffset -= dt;
+  if (IsUser(sptr))
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :clock is set %ld seconds %s", me.name,
+         parv[0], (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :clock is set %ld seconds %s",
+         NumServ(&me), NumNick(sptr),
+         (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
+  }
+#endif
+  return 0;
+}
+
+static char *militime(char *sec, char *usec)
+{
+  struct timeval tv;
+  static char timebuf[18];
+
+  gettimeofday(&tv, NULL);
+  if (sec && usec)
+#if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+    sprintf(timebuf, "%ld",
+       (tv.tv_sec - atoi(sec)) * 1000 + (tv.tv_usec - atoi(usec)) / 1000);
+#else
+    sprintf_irc(timebuf, "%d",
+       (tv.tv_sec - atoi(sec)) * 1000 + (tv.tv_usec - atoi(usec)) / 1000);
+#endif
+  else
+#if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+    sprintf(timebuf, "%ld %ld", tv.tv_sec, tv.tv_usec);
+#else
+    sprintf_irc(timebuf, "%d %d", tv.tv_sec, tv.tv_usec);
+#endif
+  return timebuf;
+}
+
+/*
+ * m_rping  -- by Run
+ *
+ *    parv[0] = sender (sptr->name thus)
+ * if sender is a person: (traveling towards start server)
+ *    parv[1] = pinged server[mask]
+ *    parv[2] = start server (current target)
+ *    parv[3] = optional remark
+ * if sender is a server: (traveling towards pinged server)
+ *    parv[1] = pinged server (current target)
+ *    parv[2] = original sender (person)
+ *    parv[3] = start time in s
+ *    parv[4] = start time in us
+ *    parv[5] = the optional remark
+ */
+int m_rping(aClient *cptr, aClient *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 (MyUser(sptr))
+  {
+    if (parc == 2)
+      parv[parc++] = me.name;
+    else if (!(acptr = find_match_server(parv[2])))
+    {
+      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(1, cptr, sptr, ":%s RPING %s %s :%s", 2, parc, parv) !=
+       HUNTED_ISME)
+      return 0;
+    if (!(acptr = find_match_server(parv[1])) || !IsServer(acptr))
+    {
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[1]);
+      return 0;
+    }
+    if (Protocol(acptr->from) < 10)
+      sendto_one(acptr, ":%s RPING %s %s %s :%s",
+         me.name, acptr->name, sptr->name, militime(NULL, NULL), parv[3]);
+    else
+      sendto_one(acptr, ":%s RPING %s %s %s :%s",
+         me.name, NumServ(acptr), sptr->name, militime(NULL, NULL), parv[3]);
+  }
+  else
+  {
+    if (hunt_server(1, 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(aClient *UNUSED(cptr), aClient *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 = FindClient(parv[1])))
+    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 = FindUser(parv[1])))
+      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;
+}
+
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+/*
+ * m_rehash
+ */
+int m_rehash(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+#ifndef LOCOP_REHASH
+  if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_REHASH
+  if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+  if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+  {
+    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, (parc > 1) ? ((*parv[1] == 'q') ? 2 : 0) : 0);
+}
+#endif
+
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+/*
+ * m_restart
+ */
+int m_restart(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc),
+    char *parv[])
+{
+#ifndef LOCOP_RESTART
+  if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_RESTART
+  if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+  if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+#ifdef USE_SYSLOG
+  syslog(LOG_WARNING, "Server RESTART by %s\n", get_client_name(sptr, FALSE));
+#endif
+  server_reboot();
+  return 0;
+}
+#endif
+
+/*
+ * m_trace
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nick or servername
+ * parv[2] = 'target' servername
+ */
+int m_trace(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 int i;
+  Reg2 aClient *acptr;
+  aConfClass *cltmp;
+  char *tname;
+  int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
+  int cnt = 0, wilds, dow;
+
+  if (parc < 2 || BadPtr(parv[1]))
+  {
+    /* just "TRACE" without parameters. Must be from local client */
+    parc = 1;
+    acptr = &me;
+    tname = me.name;
+    i = HUNTED_ISME;
+  }
+  else if (parc < 3 || BadPtr(parv[2]))
+  {
+    /* No target specified. Make one before propagating. */
+    parc = 2;
+    tname = parv[1];
+    if ((acptr = find_match_server(parv[1])) ||
+       ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
+    {
+      if (IsUser(acptr))
+       parv[2] = acptr->user->server->name;
+      else
+       parv[2] = acptr->name;
+      parc = 3;
+      parv[3] = NULL;
+      if ((i = hunt_server(IsServer(acptr), cptr, sptr,
+         ":%s TRACE %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
+       return 0;
+    }
+    else
+      i = HUNTED_ISME;
+  }
+  else
+  {
+    /* Got "TRACE <tname> :<target>" */
+    parc = 3;
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      acptr = find_match_server(parv[2]);
+    else
+      acptr = FindNServer(parv[2]);
+    if ((i = hunt_server(0, cptr, sptr,
+       ":%s TRACE %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
+      return 0;
+    tname = parv[1];
+  }
+
+  if (i == HUNTED_PASS)
+  {
+    if (!acptr)
+      acptr = next_client(client, tname);
+    else
+      acptr = acptr->from;
+    sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
+#ifndef GODMODE
+       version, debugmode, tname, acptr ? acptr->from->name : "<No_match>");
+#else /* GODMODE */
+       version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
+       (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0);
+#endif /* GODMODE */
+    return 0;
+  }
+
+  doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : TRUE;
+  wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
+  dow = wilds || doall;
+
+  /* Don't give (long) remote listings to lusers */
+  if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
+    return 0;
+
+  for (i = 0; i < MAXCONNECTIONS; i++)
+    link_s[i] = 0, link_u[i] = 0;
+
+  if (doall)
+  {
+    for (acptr = client; acptr; acptr = acptr->next)
+      if (IsUser(acptr))
+       link_u[acptr->from->fd]++;
+      else if (IsServer(acptr))
+       link_s[acptr->from->fd]++;
+  }
+
+  /* report all direct connections */
+
+  for (i = 0; i <= highest_fd; i++)
+  {
+    char *name;
+    unsigned int conClass;
+
+    if (!(acptr = loc_clients[i]))     /* Local Connection? */
+      continue;
+    if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
+       !IsAnOper(acptr) && (acptr != sptr))
+      continue;
+    if (!doall && wilds && match(tname, acptr->name))
+      continue;
+    if (!dow && strCasediff(tname, acptr->name))
+      continue;
+    name = get_client_name(acptr, FALSE);
+    conClass = get_client_class(acptr);
+
+    switch (acptr->status)
+    {
+      case STAT_CONNECTING:
+       sendto_one(sptr, rpl_str(RPL_TRACECONNECTING),
+           me.name, parv[0], conClass, name);
+       cnt++;
+       break;
+      case STAT_HANDSHAKE:
+       sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE),
+           me.name, parv[0], conClass, name);
+       cnt++;
+       break;
+      case STAT_ME:
+       break;
+      case STAT_UNKNOWN:
+      case STAT_UNKNOWN_USER:
+      case STAT_UNKNOWN_SERVER:
+       sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
+           me.name, parv[0], conClass, name);
+       cnt++;
+       break;
+      case STAT_USER:
+       /* Only opers see users if there is a wildcard
+          but anyone can see all the opers. */
+       if ((IsAnOper(sptr) && (MyUser(sptr) ||
+           !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr))
+       {
+         if (IsAnOper(acptr))
+           sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
+               me.name, parv[0], conClass, name, now - acptr->lasttime);
+         else
+           sendto_one(sptr, rpl_str(RPL_TRACEUSER),
+               me.name, parv[0], conClass, name, now - acptr->lasttime);
+         cnt++;
+       }
+       break;
+       /*
+        * Connection is a server
+        *
+        * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
+        *
+        * class        Class the server is in
+        * nS           Number of servers reached via this link
+        * nC           Number of clients reached via this link
+        * name         Name of the server linked
+        * ConnBy       Who established this link
+        * last         Seconds since we got something from this link
+        * age          Seconds this link has been alive
+        *
+        * Additional comments etc......        -Cym-<cym@acrux.net>
+        */
+
+      case STAT_SERVER:
+       if (acptr->serv->user)
+         sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+             me.name, parv[0], conClass, link_s[i],
+             link_u[i], name, acptr->serv->by,
+             acptr->serv->user->username,
+             acptr->serv->user->host,
+             now - acptr->lasttime, now - acptr->serv->timestamp);
+       else
+         sendto_one(sptr, rpl_str(RPL_TRACESERVER),
+             me.name, parv[0], conClass, link_s[i],
+             link_u[i], name, *(acptr->serv->by) ?
+             acptr->serv->by : "*", "*", me.name,
+             now - acptr->lasttime, now - acptr->serv->timestamp);
+       cnt++;
+       break;
+      case STAT_LOG:
+       sendto_one(sptr, rpl_str(RPL_TRACELOG),
+           me.name, parv[0], LOGFILE, acptr->port);
+       cnt++;
+       break;
+      case STAT_PING:
+       sendto_one(sptr, rpl_str(RPL_TRACEPING), me.name,
+           parv[0], name, (acptr->acpt) ? acptr->acpt->name : "<null>");
+       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)
+      /* 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], "<No_match>", *(me.serv->by) ?
+         me.serv->by : "*", "*", me.name, 0, 0);
+    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], ConClass(cltmp), Links(cltmp));
+  return 0;
+}
+
+/*
+ *  m_close                              - added by Darren Reed Jul 13 1992.
+ */
+int m_close(aClient *cptr, aClient *sptr, int UNUSED(parc), char *parv[])
+{
+  Reg1 aClient *acptr;
+  Reg2 int i;
+  int closed = 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 = loc_clients[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);
+    exit_client(cptr, acptr, &me, "Oper Closing");
+    closed++;
+  }
+  sendto_one(sptr, rpl_str(RPL_CLOSEEND), me.name, parv[0], closed);
+  return 0;
+}
+
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+/*
+ * m_die
+ */
+int m_die(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc), char *parv[])
+{
+  Reg1 aClient *acptr;
+  Reg2 int i;
+
+#ifndef LOCOP_DIE
+  if (!MyUser(sptr) || !IsOper(sptr))
+#else
+#ifdef OPER_DIE
+  if (!MyUser(sptr) || !IsAnOper(sptr))
+#else
+  if (!MyUser(sptr) || !IsLocOp(sptr))
+#endif
+#endif
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+
+  for (i = 0; i <= highest_fd; i++)
+  {
+    if (!(acptr = loc_clients[i]))
+      continue;
+    if (IsUser(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));
+  }
+#ifdef __cplusplus
+  s_die(0);
+#else
+  s_die();
+#endif
+  return 0;
+}
+#endif
+
+static void add_gline(aClient *sptr, int ip_mask, char *host, char *comment,
+    char *user, time_t expire, int local)
+{
+  aClient *acptr;
+  aGline *agline;
+  int fd;
+
+  /* Inform ops */
+  sendto_op_mask(SNO_GLINE,
+      "%s adding %sGLINE for %s@%s, expiring at " TIME_T_FMT ": %s", sptr->name,
+      local ? "local " : "", user, host, expire, comment);
+
+#ifdef GPATH
+  write_log(GPATH,
+      "# " TIME_T_FMT " %s adding %s GLINE for %s@%s, expiring at " TIME_T_FMT
+      ": %s\n", TStime(), sptr->name, local ? "local" : "global",
+      user, host, expire, comment);
+
+  /* this can be inserted into the conf */
+  write_log(GPATH, "%c:%s:%s:%s\n", ip_mask ? 'k' : 'K', host, comment, user);
+#endif /* GPATH */
+
+  agline = make_gline(ip_mask, host, comment, user, expire);
+  if (local)
+    SetGlineIsLocal(agline);
+
+  for (fd = highest_fd; fd >= 0; --fd) /* get the users! */
+    if ((acptr = loc_clients[fd]) && !IsMe(acptr))
+    {
+
+      if (!acptr->user || strlen(acptr->sockhost) > (size_t)HOSTLEN ||
+         (acptr->user->username ? strlen(acptr->user->username) : 0) >
+         (size_t)HOSTLEN)
+       continue;               /* these tests right out of
+                                  find_kill for safety's sake */
+
+      if ((GlineIsIpMask(agline) ?
+         match(agline->host, inetntoa(acptr->ip)) :
+         match(agline->host, acptr->sockhost)) == 0 &&
+         (!acptr->user->username ||
+         match(agline->name, acptr->user->username) == 0))
+      {
+
+       /* ok, he was the one that got G-lined */
+       sendto_one(acptr, ":%s %d %s :*** %s.", me.name,
+           ERR_YOUREBANNEDCREEP, acptr->name, agline->reason);
+
+       /* let the ops know about my first kill */
+       sendto_op_mask(SNO_GLINE, "G-line active for %s",
+           get_client_name(acptr, FALSE));
+
+       /* and get rid of him */
+       if (sptr != acptr)
+         exit_client(sptr->from, acptr, &me, "G-lined");
+      }
+    }
+}
+
+/*
+ * m_gline
+ *
+ * parv[0] = Send prefix
+ *
+ * From server:
+ *
+ * parv[1] = Target: server numeric
+ * parv[2] = [+|-]<G-line mask>
+ * parv[3] = Expiration offset
+ * parv[4] = Comment
+ *
+ * From client:
+ *
+ * parv[1] = [+|-]<G-line mask>
+ * parv[2] = Expiration offset
+ * parv[3] = Comment
+ *
+ */
+int m_gline(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr = NULL;       /* Init. to avoid compiler warning. */
+
+  aGline *agline, *a2gline;
+  char *user, *host;
+  int active, ip_mask;
+  time_t expire = 0;
+
+  /* Remove expired G-lines */
+  for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+  {
+    if (agline->expire <= TStime())
+    {
+      free_gline(agline, a2gline);
+      agline = a2gline ? a2gline : gline;
+      if (!agline)
+       break;
+      continue;
+    }
+    a2gline = agline;
+  }
+
+  if (IsServer(cptr))
+  {
+    if (find_conf_host(cptr->confs, sptr->name, CONF_UWORLD))
+    {
+      if (parc < 3 || (*parv[2] != '-' && (parc < 5 || *parv[4] == '\0')))
+      {
+       sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
+           "GLINE");
+       return 0;
+      }
+
+      if (*parv[2] == '-')     /* add mode or delete mode? */
+       active = 0;
+      else
+       active = 1;
+
+      if (*parv[2] == '+' || *parv[2] == '-')
+       parv[2]++;              /* step past mode indicator */
+
+      /* forward the message appropriately */
+      if (!strCasediff(parv[1], "*"))
+       sendto_serv_butone(cptr, active ? ":%s GLINE %s +%s %s :%s" :
+           ":%s GLINE %s -%s", parv[0], parv[1], parv[2], parv[3], parv[4]);   /* global! */
+      else if ((
+#if 1
+         /*
+          * REMOVE THIS after all servers upgraded to 2.10.01 and
+          * Uworld uses a numeric too
+          */
+         (strlen(parv[1]) != 1 && !(acptr = FindClient(parv[1])))) ||
+         (strlen(parv[1]) == 1 &&
+#endif
+         !(acptr = FindNServer(parv[1]))))
+       return 0;               /* no such server/user exists; forget it */
+      else
+#if 1
+/*
+ * REMOVE THIS after all servers upgraded to 2.10.01 and
+ * Uworld uses a numeric too
+ */
+      if (IsServer(acptr) || !MyConnect(acptr))
+#endif
+      {
+       sendto_one(acptr, active ? ":%s GLINE %s +%s %s :%s" :
+           ":%s GLINE %s -%s", parv[0], parv[1], parv[2], parv[3], parv[4]);   /* single destination */
+       return 0;               /* only the intended  destination
+                                  should add this gline */
+      }
+
+      if (!(host = strchr(parv[2], '@')))
+      {                                /* convert user@host */
+       user = "*";             /* no @'s; assume username is '*' */
+       host = parv[2];
+      }
+      else
+      {
+       user = parv[2];
+       *(host++) = '\0';       /* break up string at the '@' */
+      }
+      ip_mask = check_if_ipmask(host); /* Store this boolean */
+
+      for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+      {
+       if (!strCasediff(agline->name, user)
+           && !strCasediff(agline->host, host))
+         break;
+       a2gline = agline;
+      }
+
+      if (!active && agline)
+      {                                /* removing the gline */
+       sendto_op_mask(SNO_GLINE, "%s removing GLINE for %s@%s", parv[0],
+           agline->name, agline->host);        /* notify opers */
+
+#ifdef GPATH
+       write_log(GPATH, "# " TIME_T_FMT " %s removing GLINE for %s@%s\n",
+           TStime(), parv[0], agline->name, agline->host);
+#endif /* GPATH */
+
+       free_gline(agline, a2gline);    /* remove the gline */
+      }
+      else if (active)
+      {                                /* must be adding a gline */
+       expire = atoi(parv[3]) + TStime();      /* expire time? */
+       if (agline && agline->expire < expire)
+       {                       /* new expire time? */
+         /* yes, notify the opers */
+         sendto_op_mask(SNO_GLINE,
+             "%s resetting expiration time on GLINE for %s@%s to " TIME_T_FMT,
+             parv[0], agline->name, agline->host, expire);
+
+#ifdef GPATH
+         write_log(GPATH, "# " TIME_T_FMT " %s resetting expiration time "
+             "on GLINE for %s@%s to " TIME_T_FMT "\n",
+             TStime(), parv[0], agline->name, agline->host, expire);
+#endif /* GPATH */
+
+         agline->expire = expire;      /* reset the expire time */
+       }
+       else if (!agline)
+       {                       /* create gline */
+         for (agline = gline; agline; agline = agline->next)
+           if (!mmatch(agline->name, user) &&
+               (ip_mask ? GlineIsIpMask(agline) : !GlineIsIpMask(agline)) &&
+               !mmatch(agline->host, host))
+             return 0;         /* found an existing G-line that matches */
+
+         /* add the line: */
+         add_gline(sptr, ip_mask, host, parv[4], user, expire, 0);
+       }
+      }
+    }
+  }
+  else if (parc < 2 || *parv[1] == '\0')
+  {
+    /* Not enough args and a user; list glines */
+    for (agline = gline; agline; agline = agline->next)
+      sendto_one(cptr, rpl_str(RPL_GLIST), me.name, parv[0],
+         agline->name, agline->host, agline->expire, agline->reason,
+         GlineIsActive(agline) ? (GlineIsLocal(agline) ? " (local)" : "") :
+         " (Inactive)");
+    sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, parv[0]);
+  }
+  else
+  {
+    int priv;
+
+#ifdef LOCOP_LGLINE
+    priv = IsAnOper(cptr);
+#else
+    priv = IsOper(cptr);
+#endif
+
+    if (priv)
+    {                          /* non-oper not permitted to change things */
+      if (*parv[1] == '-')
+      {                                /* oper wants to deactivate the gline */
+       active = 0;
+       parv[1]++;
+      }
+      else if (*parv[1] == '+')
+      {                                /* oper wants to activate inactive gline */
+       active = 1;
+       parv[1]++;
+      }
+      else
+       active = -1;
+
+      if (parc > 2)
+       expire = atoi(parv[2]) + TStime();      /* oper wants to reset
+                                                  expire TS */
+    }
+    else
+      active = -1;
+
+    if (!(host = strchr(parv[1], '@')))
+    {
+      user = "*";              /* no @'s; assume username is '*' */
+      host = parv[1];
+    }
+    else
+    {
+      user = parv[1];
+      *(host++) = '\0';                /* break up string at the '@' */
+    }
+    ip_mask = check_if_ipmask(host);   /* Store this boolean */
+
+    for (agline = gline, a2gline = NULL; agline; agline = agline->next)
+    {
+      if (!mmatch(agline->name, user) &&
+         (ip_mask ? GlineIsIpMask(agline) : !GlineIsIpMask(agline)) &&
+         !mmatch(agline->host, host))
+       break;
+      a2gline = agline;
+    }
+
+    if (!agline)
+    {
+#ifdef OPER_LGLINE
+      if (priv && active && expire > now)
+      {
+       /* Add local G-line */
+       if (parc < 4 || !strchr(parv[3], ' '))
+       {
+         sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+             me.name, parv[0], "GLINE");
+         return 0;
+       }
+       add_gline(sptr, ip_mask, host, parv[3], user, expire, 1);
+      }
+      else
+#endif
+       sendto_one(cptr, err_str(ERR_NOSUCHGLINE), me.name, parv[0], user,
+           host);
+
+      return 0;
+    }
+
+    if (expire <= agline->expire)
+      expire = 0;
+
+    if ((active == -1 ||
+       (active ? GlineIsActive(agline) : !GlineIsActive(agline))) &&
+       expire == 0)
+    {
+      /* oper wants a list of one gline only */
+      sendto_one(cptr, rpl_str(RPL_GLIST), me.name, parv[0], agline->name,
+         agline->host, agline->expire, agline->reason,
+         GlineIsActive(agline) ? "" : " (Inactive)");
+      sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, parv[0]);
+      return 0;
+    }
+
+    if (active != -1 &&
+       (active ? !GlineIsActive(agline) : GlineIsActive(agline)))
+    {
+      if (active)              /* reset activation on gline */
+       SetActive(agline);
+#ifdef OPER_LGLINE
+      else if (GlineIsLocal(agline))
+      {
+       /* Remove local G-line */
+       sendto_op_mask(SNO_GLINE, "%s removed local GLINE for %s@%s",
+           parv[0], agline->name, agline->host);
+#ifdef GPATH
+       write_log(GPATH, "# " TIME_T_FMT
+           " %s!%s@%s removed local GLINE for %s@%s\n",
+           TStime(), parv[0], cptr->user->username, cptr->user->host,
+           agline->name, agline->host);
+#endif /* GPATH */
+       free_gline(agline, a2gline);    /* remove the gline */
+       return 0;
+      }
+#endif
+      else
+       ClearActive(agline);
+    }
+    else
+      active = -1;             /* for later sendto_ops and logging functions */
+
+    if (expire)
+      agline->expire = expire; /* reset expiration time */
+
+    /* inform the operators what's up */
+    if (active != -1)
+    {                          /* changing the activation */
+      sendto_op_mask(SNO_GLINE, !expire ? "%s %sactivating GLINE for %s@%s" :
+         "%s %sactivating GLINE for %s@%s and "
+         "resetting expiration time to " TIME_T_FMT,
+         parv[0], active ? "re" : "de", agline->name,
+         agline->host, agline->expire);
+#ifdef GPATH
+      write_log(GPATH, !expire ? "# " TIME_T_FMT " %s!%s@%s %sactivating "
+         "GLINE for %s@%s\n" : "# " TIME_T_FMT " %s!%s@%s %sactivating GLINE "
+         "for %s@%s and resetting expiration time to " TIME_T_FMT "\n",
+         TStime(), parv[0], cptr->user->username, cptr->user->host,
+         active ? "re" : "de", agline->name, agline->host, agline->expire);
+#endif /* GPATH */
+
+    }
+    else if (expire)
+    {                          /* changing only the expiration */
+      sendto_op_mask(SNO_GLINE,
+         "%s resetting expiration time on GLINE for %s@%s to " TIME_T_FMT,
+         parv[0], agline->name, agline->host, agline->expire);
+#ifdef GPATH
+      write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s resetting expiration "
+         "time on GLINE for %s@%s to " TIME_T_FMT "\n", TStime(), parv[0],
+         cptr->user->username, cptr->user->host, agline->name,
+         agline->host, agline->expire);
+#endif /* GPATH */
+    }
+  }
+
+  return 0;
+}
diff --git a/ircd/packet.c b/ircd/packet.c
new file mode 100644 (file)
index 0000000..e4ef0dc
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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 "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "s_misc.h"
+#include "s_bsd.h"
+#include "ircd.h"
+#include "msg.h"
+#include "parse.h"
+#include "send.h"
+#include "packet.h"
+#include "s_serv.h"
+
+#include <assert.h>
+
+RCSTAG_CC("$Id$");
+
+/*
+ * dopacket
+ *
+ *    cptr - pointer to client structure for which the buffer data
+ *           applies.
+ *    buffer - pointer 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(aClient *cptr, char *buffer, int length)
+{
+  Reg1 char *ch1;
+  Reg2 char *ch2;
+  register char *cptrbuf;
+  aClient *acpt = cptr->acpt;
+
+  cptrbuf = cptr->buffer;
+  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 = cptrbuf + cptr->count;
+  ch2 = buffer;
+  while (--length >= 0)
+  {
+    register char g;
+    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 == cptrbuf)
+       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;
+      if (IsServer(cptr))
+      {
+       if (parse_server(cptr, cptr->buffer, ch1) == CPTR_KILLED)
+         return CPTR_KILLED;
+      }
+      else if (parse_client(cptr, cptr->buffer, ch1) == CPTR_KILLED)
+       return CPTR_KILLED;
+      /*
+       *  Socket is dead so exit
+       */
+      if (IsDead(cptr))
+       return exit_client(cptr, cptr, &me, LastDeadComment(cptr));
+      ch1 = cptrbuf;
+    }
+    else if (ch1 < cptrbuf + (sizeof(cptr->buffer) - 1))
+      ch1++;                   /* There is always room for the null */
+  }
+  cptr->count = ch1 - cptr->buffer;
+  return 0;
+}
+
+/*
+ * client_dopacket - handle client messages
+ */
+int client_dopacket(aClient *cptr, size_t length)
+{
+  assert(0 != cptr);
+
+  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 (me.receiveB > 1023)
+  {
+    me.receiveK += (me.receiveB >> 10);
+    me.receiveB &= 0x03ff;
+  }
+  cptr->count = 0;
+
+  ++me.receiveM;               /* Update messages received */
+  ++cptr->receiveM;
+
+  if (CPTR_KILLED == parse_client(cptr, cptr->buffer, cptr->buffer + length))
+    return CPTR_KILLED;
+  else if (IsDead(cptr))
+    return exit_client(cptr, cptr, &me, LastDeadComment(cptr));
+
+  return 0;
+}
diff --git a/ircd/parse.c b/ircd/parse.c
new file mode 100644 (file)
index 0000000..50639a4
--- /dev/null
@@ -0,0 +1,779 @@
+/*
+ * 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.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "send.h"
+#include "parse.h"
+#include "common.h"
+#include "s_bsd.h"
+#include "msg.h"
+#include "s_user.h"
+#include "s_serv.h"
+#include "channel.h"
+#include "whowas.h"
+#include "s_ping.h"
+#include "s_conf.h"
+#include "res.h"
+#include "map.h"
+#include "hash.h"
+#include "numeric.h"
+#include "ircd.h"
+#include "s_misc.h"
+#include "common.h"
+#include "s_numeric.h"
+#include "numnicks.h"
+#include "opercmds.h"
+#include "querycmds.h"
+#include "whocmds.h"
+
+RCSTAG_CC("$Id$");
+
+/* *INDENT-OFF* */
+
+aMessage msgtab[] = {
+    {CLASS_PRIVATE,    MSG_PRIVATE,    TOK_PRIVATE,    m_private,      0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_NICK,       MSG_NICK,       TOK_NICK,       m_nick,         0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_NOTICE,     MSG_NOTICE,     TOK_NOTICE,     m_notice,       0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_WALLCHOPS,  MSG_WALLCHOPS,  TOK_WALLCHOPS,  m_wallchops,    0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_CPRIVMSG,   MSG_CPRIVMSG,   TOK_CPRIVMSG,   m_cprivmsg,     0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_CNOTICE,    MSG_CNOTICE,    TOK_CNOTICE,    m_cnotice,      0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_JOIN,       MSG_JOIN,       TOK_JOIN,       m_join,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_MODE,       MSG_MODE,       TOK_MODE,       m_mode,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_BURST,      MSG_BURST,      TOK_BURST,      m_burst,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_CREATE,     MSG_CREATE,     TOK_CREATE,     m_create,       0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_DESTRUCT,   MSG_DESTRUCT,   TOK_DESTRUCT,   m_destruct,     0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_QUIT,       MSG_QUIT,       TOK_QUIT,       m_quit,         0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_PART,       MSG_PART,       TOK_PART,       m_part,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_TOPIC,      MSG_TOPIC,      TOK_TOPIC,      m_topic,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_INVITE,     MSG_INVITE,     TOK_INVITE,     m_invite,       0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_KICK,       MSG_KICK,       TOK_KICK,       m_kick,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_WALLOPS,    MSG_WALLOPS,    TOK_WALLOPS,    m_wallops,      0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_DESYNCH,     MSG_DESYNCH,    TOK_DESYNCH,    m_desynch,      0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_PING,       MSG_PING,       TOK_PING,       m_ping,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_PONG,       MSG_PONG,       TOK_PONG,       m_pong,         0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_ERROR,      MSG_ERROR,      TOK_ERROR,      m_error,        0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_KILL,       MSG_KILL,       TOK_KILL,       m_kill,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_USER,       MSG_USER,       TOK_USER,       m_user,         0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_AWAY,       MSG_AWAY,       TOK_AWAY,       m_away,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_ISON,       MSG_ISON,       TOK_ISON,       m_ison,         0, 1,           MFLG_SLOW,      0L},
+    {CLASS_SERVER,     MSG_SERVER,     TOK_SERVER,     m_server,       0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_SQUIT,      MSG_SQUIT,      TOK_SQUIT,      m_squit,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_WHOIS,      MSG_WHOIS,      TOK_WHOIS,      m_whois,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_WHO,                MSG_WHO,        TOK_WHO,        m_who,          0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_WHOWAS,     MSG_WHOWAS,     TOK_WHOWAS,     m_whowas,       0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_LIST,       MSG_LIST,       TOK_LIST,       m_list,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_NAMES,      MSG_NAMES,      TOK_NAMES,      m_names,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_USERHOST,   MSG_USERHOST,   TOK_USERHOST,   m_userhost,     0, 1,           MFLG_SLOW,      0L},
+    {CLASS_USERIP,     MSG_USERIP,     TOK_USERIP,     m_userip,       0, 1,           MFLG_SLOW,      0L},
+    {CLASS_TRACE,      MSG_TRACE,      TOK_TRACE,      m_trace,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_PASS,       MSG_PASS,       TOK_PASS,       m_pass,         0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_LUSERS,     MSG_LUSERS,     TOK_LUSERS,     m_lusers,       0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_TIME,       MSG_TIME,       TOK_TIME,       m_time,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_SETTIME,    MSG_SETTIME,    TOK_SETTIME,    m_settime,      0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_RPING,      MSG_RPING,      TOK_RPING,      m_rping,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_RPONG,      MSG_RPONG,      TOK_RPONG,      m_rpong,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_OPER,       MSG_OPER,       TOK_OPER,       m_oper,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_CONNECT,    MSG_CONNECT,    TOK_CONNECT,    m_connect,      0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_UPING,      MSG_UPING,      TOK_UPING,      m_uping,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_MAP,                MSG_MAP,        TOK_MAP,        m_map,          0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_VERSION,    MSG_VERSION,    TOK_VERSION,    m_version,      0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_STATS,      MSG_STATS,      TOK_STATS,      m_stats,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_LINKS,      MSG_LINKS,      TOK_LINKS,      m_links,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_ADMIN,      MSG_ADMIN,      TOK_ADMIN,      m_admin,        0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_HELP,       MSG_HELP,       TOK_HELP,       m_help,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_INFO,       MSG_INFO,       TOK_INFO,       m_info,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_MOTD,       MSG_MOTD,       TOK_MOTD,       m_motd,         0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_CLOSE,      MSG_CLOSE,      TOK_CLOSE,      m_close,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_SILENCE,    MSG_SILENCE,    TOK_SILENCE,    m_silence,      0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_GLINE,      MSG_GLINE,      TOK_GLINE,      m_gline,        0, MAXPARA,     MFLG_SLOW,      0L},
+    {CLASS_END_OF_BURST, MSG_END_OF_BURST, TOK_END_OF_BURST, m_end_of_burst, 0, MAXPARA, MFLG_SLOW,    0L},
+    {CLASS_END_OF_BURST_ACK, MSG_END_OF_BURST_ACK, TOK_END_OF_BURST_ACK, m_end_of_burst_ack, 0, MAXPARA, 1, 0L},
+    {CLASS_HASH,       MSG_HASH,       TOK_HASH,       m_hash,         0, MAXPARA,     MFLG_SLOW|MFLG_UNREG,   0L},
+    {CLASS_DNS,                MSG_DNS,        TOK_DNS,        m_dns,          0, MAXPARA,     MFLG_SLOW,      0L},
+#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
+    {CLASS_REHASH,     MSG_REHASH,     TOK_REHASH,     m_rehash,       0, MAXPARA,     MFLG_SLOW,      0L},
+#endif
+#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
+    {CLASS_RESTART,    MSG_RESTART,    TOK_RESTART,    m_restart,      0, MAXPARA,     MFLG_SLOW,      0L},
+#endif
+#if defined(OPER_DIE) || defined(LOCOP_DIE)
+    {CLASS_DIE,                MSG_DIE,        TOK_DIE,        m_die,          0, MAXPARA,     MFLG_SLOW,      0L},
+#endif
+    {0, (char *)0, (char *)0, (int (*)(aClient *, aClient *, int, char **))0,  0, 0, 0, 0L}
+}                                                                                                                                   ;
+/* *INDENT-ON* */
+
+#ifdef GODMODE
+extern int sdbflag;
+#endif /* GODMODE */
+
+static char *para[MAXPARA + 2];        /* leave room for prefix and null */
+
+/*
+ * Message Tree stuff mostly written by orabidoo, with changes by Dianora.
+ * Adapted to Undernet, adding token support, etc by comstud 10/06/97
+ */
+
+static aMessageTree msg_tree_cmd;
+static aMessageTree msg_tree_tok;
+
+/*
+ * Guts of making the token tree...
+ */
+static aMessage **do_msg_tree_tok(aMessageTree *mtree, char *prefix,
+    aMessage **mptr)
+{
+  char newprefix[64];          /* Must be longer than every command name */
+  int c, c2, lp;
+  aMessageTree *mtree1;
+
+  lp = strlen(prefix);
+  if (!lp || !strncmp((*mptr)->tok, prefix, lp))
+  {
+    if (!mptr[1] || (lp && strncmp(mptr[1]->tok, prefix, lp)))
+    {
+      /* last command in the message struct or last command in this prefix */
+      mtree->final = (*mptr)->tok + lp;
+      mtree->msg = *mptr;
+      for (c = 0; c < 26; ++c)
+       mtree->pointers[c] = NULL;
+      return mptr + 1;
+    }
+    /* command in this prefix */
+    if (!strCasediff((*mptr)->tok, prefix))
+    {
+      mtree->final = "";
+      mtree->msg = *mptr++;
+    }
+    else
+      mtree->final = NULL;
+
+    for (c = 'A'; c <= 'Z'; ++c)
+    {
+      if ((*mptr)->tok[lp] == c)
+      {
+       mtree1 = (aMessageTree *)RunMalloc(sizeof(aMessageTree));
+       mtree1->final = NULL;
+       mtree->pointers[c - 'A'] = mtree1;
+       strcpy(newprefix, prefix);
+       newprefix[lp] = c;
+       newprefix[lp + 1] = '\0';
+       mptr = do_msg_tree_tok(mtree1, newprefix, mptr);
+       if (!*mptr || strncmp((*mptr)->tok, prefix, lp))
+       {
+         for (c2 = c + 1 - 'A'; c2 < 26; ++c2)
+           mtree->pointers[c2] = NULL;
+         return mptr;
+       }
+      }
+      else
+       mtree->pointers[c - 'A'] = NULL;
+    }
+    return mptr;
+  }
+  MyCoreDump;                  /* This should never happen */
+  exit(1);
+}
+
+/*
+ * Guts of making the command tree...
+ */
+static aMessage *do_msg_tree_cmd(aMessageTree *mtree, char *prefix,
+    aMessage *mptr)
+{
+  char newprefix[64];          /* Must be longer than every command name */
+  int c, c2, lp;
+  aMessageTree *mtree1;
+
+  lp = strlen(prefix);
+  if (!lp || !strncmp(mptr->cmd, prefix, lp))
+  {
+    if (!mptr[1].cmd || (lp && strncmp(mptr[1].cmd, prefix, lp)))
+    {
+      /* last command in the message struct or last command in this prefix */
+      mtree->final = mptr->cmd + lp;
+      mtree->msg = mptr;
+      for (c = 0; c < 26; ++c)
+       mtree->pointers[c] = NULL;
+      return mptr + 1;
+    }
+    /* command in this prefix */
+    if (!strCasediff(mptr->cmd, prefix))
+    {
+      mtree->final = "";
+      mtree->msg = mptr++;
+    }
+    else
+      mtree->final = NULL;
+
+    for (c = 'A'; c <= 'Z'; ++c)
+    {
+      if (mptr->cmd[lp] == c)
+      {
+       mtree1 = (aMessageTree *)RunMalloc(sizeof(aMessageTree));
+       mtree1->final = NULL;
+       mtree->pointers[c - 'A'] = mtree1;
+       strcpy(newprefix, prefix);
+       newprefix[lp] = c;
+       newprefix[lp + 1] = '\0';
+       mptr = do_msg_tree_cmd(mtree1, newprefix, mptr);
+       if (!mptr->cmd || strncmp(mptr->cmd, prefix, lp))
+       {
+         for (c2 = c + 1 - 'A'; c2 < 26; ++c2)
+           mtree->pointers[c2] = NULL;
+         return mptr;
+       }
+      }
+      else
+       mtree->pointers[c - 'A'] = NULL;
+    }
+    return mptr;
+  }
+  MyCoreDump;                  /* This should never happen */
+  exit(1);
+}
+
+static int mcmdcmp(const struct Message *m1, const struct Message *m2)
+{
+  return strcmp(m1->cmd, m2->cmd);
+}
+
+static int mtokcmp(const struct Message **m1, const struct Message **m2)
+{
+  return strcmp((*m1)->tok, (*m2)->tok);
+}
+
+/*
+ * Sort the command names.
+ * Create table of pointers into msgtab for tokens.
+ * Create trees for ->cmd and ->tok and free the token pointers.
+ */
+void initmsgtree(void)
+{
+  Reg1 int i;
+  Reg2 aMessage *msg = msgtab;
+  Reg3 int ii;
+  aMessage **msgtab_tok;
+  aMessage **msgtok;
+
+  for (i = 0; msg->cmd; ++i, ++msg)
+    continue;
+  qsort(msgtab, i, sizeof(aMessage),
+      (int (*)(const void *, const void *))mcmdcmp);
+  msgtab_tok = (aMessage **)RunMalloc((i + 1) * sizeof(aMessage *));
+  for (ii = 0; ii < i; ++ii)
+    msgtab_tok[ii] = msgtab + ii;
+  msgtab_tok[i] = NULL;                /* Needed by `do_msg_tree_tok' */
+  qsort(msgtab_tok, i, sizeof(aMessage *),
+      (int (*)(const void *, const void *))mtokcmp);
+  msg = do_msg_tree_cmd(&msg_tree_cmd, "", msgtab);
+  msgtok = do_msg_tree_tok(&msg_tree_tok, "", msgtab_tok);
+  RunFree(msgtab_tok);
+}
+
+/*
+ * Generic tree parser which works for both commands and tokens.
+ * Optimized by Run.
+ */
+static struct Message *msg_tree_parse(register char *cmd, aMessageTree *root)
+{
+  register aMessageTree *mtree;
+  register unsigned char r = (0xdf & (unsigned char)*cmd) - 'A';
+  if (r > 25 || !(mtree = root->pointers[r]))
+    return NULL;
+  for (;;)
+  {
+    r = 0xdf & (unsigned char)*++cmd;
+    if (mtree->final && *mtree->final == r)
+      return mtree->msg;
+    if ((r -= 'A') > 25 || !(mtree = mtree->pointers[r]))
+      return NULL;
+  }
+}
+
+/*
+ * This one is identical to the one above, but it is slower because it
+ * makes sure that `cmd' matches the _full_ command, exactly.
+ * This is to avoid confusion with commands like /quake on clients
+ * that send unknown commands directly to the server.
+ */
+static struct Message *msg_tree_parse_client(register char *cmd,
+    aMessageTree *root)
+{
+  register aMessageTree *mtree;
+  register unsigned char q = (0xdf & (unsigned char)*cmd) - 'A';
+  if (q > 25 || !(mtree = root->pointers[q]))
+    return NULL;
+  for (;;)
+  {
+    q = 0xdf & (unsigned char)*++cmd;
+    if (mtree->final && !strCasediff(mtree->final, cmd))
+      return mtree->msg;
+    if ((q -= 'A') > 25 || !(mtree = mtree->pointers[q]))
+      return NULL;
+  }
+}
+
+/*
+ * parse a buffer.
+ *
+ * NOTE: parse_*() should not be called recusively by any other fucntions!
+ */
+int parse_client(aClient *cptr, char *buffer, char *bufend)
+{
+  Reg1 aClient *from = cptr;
+  Reg2 char *ch, *s;
+  Reg3 int i, paramcount, noprefix = 0;
+  aMessage *mptr;
+
+  Debug((DEBUG_DEBUG, "Parsing: %s", buffer));
+  StoreBuffer((buffer, cptr)); /* Store the buffer now, before
+                                  we start working on it */
+
+  if (IsDead(cptr))
+    return 0;
+
+  para[0] = from->name;
+  for (ch = buffer; *ch == ' '; ch++); /* Eat leading spaces */
+  if (*ch == ':')              /* Is any client doing this ? */
+  {
+    for (++ch; *ch && *ch != ' '; ++ch);       /* Ignore sender prefix from client */
+    while (*ch == ' ')
+      ch++;                    /* Advance to command */
+  }
+  else
+    noprefix = 1;
+  if (*ch == '\0')
+  {
+    ircstp->is_empt++;
+    Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
+       cptr->name, from->name));
+    return (-1);
+  }
+
+  if ((s = strchr(ch, ' ')))
+    *s++ = '\0';
+
+  /*
+   * This is a client/unregistered entity.
+   * Check long command list only.
+   */
+  if (!(mptr = msg_tree_parse_client(ch, &msg_tree_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 (IsUser(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);
+  }
+  LogMessage((cptr, mptr->msgclass));
+
+  paramcount = mptr->parameters;
+  i = bufend - ((s) ? s : ch);
+  mptr->bytes += i;
+  if ((mptr->flags & MFLG_SLOW))
+    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;
+  mptr->count++;
+  /* The "unregistered command check" was ugly and mildly inefficient.
+   * I fixed it. :)  --Shadow
+   */
+  if (!IsUser(cptr) && !(mptr->flags & MFLG_UNREG))
+  {
+    sendto_one(from, ":%s %d * %s :Register first.",
+       me.name, ERR_NOTREGISTERED, ch);
+    return -1;
+  }
+  if (IsUser(cptr) &&
+#ifdef IDLE_FROM_MSG
+      mptr->func == m_private)
+#else
+      mptr->func != m_ping && mptr->func != m_pong)
+#endif
+      from->user->last = now;
+
+  return (*mptr->func) (cptr, from, i, para);
+}
+
+int parse_server(aClient *cptr, char *buffer, char *bufend)
+{
+  Reg1 aClient *from = cptr;
+  Reg2 char *ch = buffer, *s;
+  Reg3 int len, i, numeric = 0, paramcount;
+  aMessage *mptr;
+
+  Debug((DEBUG_DEBUG, "Parsing: %s", buffer));
+  StoreBuffer((buffer, cptr)); /* Store the buffer now, before
+                                * we start working on it. */
+
+#ifdef GODMODE
+  len = strlen(buffer);
+  sdbflag = 1;
+  if (len > 402)
+  {
+    char c = buffer[200];
+    buffer[200] = 0;
+    sendto_ops("RCV:%-8.8s(%.4d): \"%s...%s\"",
+       cptr->name, len, buffer, &buffer[len - 200]);
+    buffer[200] = c;
+  }
+  else
+    sendto_ops("RCV:%-8.8s(%.4d): \"%s\"", cptr->name, len, buffer);
+  sdbflag = 0;
+#endif /* GODMODE */
+
+  if (IsDead(cptr))
+    return 0;
+
+  para[0] = from->name;
+
+  /*
+   * A server ALWAYS sends a prefix. When it starts with a ':' it's the
+   * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric
+   * nick or server
+   */
+  if (*ch == ':')
+  {
+    /* Let para[0] point to the name of the sender */
+    para[0] = ch + 1;
+    if (!(ch = strchr(ch, ' ')))
+      return -1;
+    *ch++ = '\0';
+
+    /* And let `from' point to its client structure,
+       opps.. a server is _also_ a client --Nem */
+    from = FindClient(para[0]);
+
+    /*
+     * If the client corresponding to the
+     * prefix is not found. We must ignore it,
+     * it is simply a lagged message travelling
+     * upstream a SQUIT that removed the client
+     * --Run
+     */
+    if (!from)
+    {
+      Debug((DEBUG_NOTICE, "Unknown prefix (%s)(%s) from (%s)",
+         para[0], buffer, cptr->name));
+      ircstp->is_unpf++;
+      while (*ch == ' ')
+       ch++;
+      /*
+       * However, the only thing that MUST be
+       * allowed to travel upstream against an
+       * squit, is an SQUIT itself (the timestamp
+       * protects us from being used wrong)
+       */
+      if (ch[1] == 'Q')
+      {
+       para[0] = cptr->name;
+       from = cptr;
+      }
+      else
+       return 0;
+    }
+    else if (from->from != cptr)
+    {
+      ircstp->is_wrdi++;
+      Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)",
+         buffer, cptr->name));
+      return 0;
+    }
+  }
+  else if (Protocol(cptr) > 9) /* Well, not ALWAYS, 2.9 can send no prefix */
+  {
+    char numeric_prefix[6];
+    int i;
+    for (i = 0; i < 5; ++i)
+    {
+      if ('\0' == ch[i] || ' ' == (numeric_prefix[i] = ch[i]))
+      {
+       break;
+      }
+    }
+    numeric_prefix[i] = '\0';
+    /*
+     * We got a numeric nick as prefix
+     * 1 or 2 character prefixes are from servers
+     * 3 or 5 chars are from clients
+     */
+    if (' ' == ch[1] || ' ' == ch[2])
+      from = FindNServer(numeric_prefix);
+    else
+      from = findNUser(numeric_prefix);
+
+    do
+    {
+      ++ch;
+    }
+    while (*ch != ' ' && *ch);
+
+    /*
+     * If the client corresponding to the
+     * prefix is not found. We must ignore it,
+     * it is simply a lagged message travelling
+     * upstream a SQUIT that removed the client
+     * --Run
+     * There turned out to be other reasons that
+     * a prefix is unknown, needing an upstream
+     * KILL.  Also, next to an SQUIT we better
+     * allow a KILL to pass too.
+     * --Run
+     */
+    if (!from)
+    {
+      ircstp->is_unpf++;
+      while (*ch == ' ')
+       ch++;
+      if (*ch == 'N' && (ch[1] == ' ' || ch[1] == 'I'))
+       /* Only sent a KILL for a nick change */
+      {
+       aClient *server;
+       /* Kill the unknown numeric prefix upstream if
+        * it's server still exists: */
+       if ((server = FindNServer(numeric_prefix)) && server->from == cptr)
+         sendto_one(cptr, "%s KILL %s :%s (Unknown numeric nick)",
+             NumServ(&me), numeric_prefix, me.name);
+      }
+      /*
+       * Things that must be allowed to travel
+       * upstream against an squit:
+       */
+      if (ch[1] == 'Q' || (*ch == 'D' && ch[1] == ' ') ||
+         (*ch == 'K' && ch[2] == 'L'))
+       from = cptr;
+      else
+       return 0;
+    }
+
+    /* Let para[0] point to the name of the sender */
+    para[0] = from->name;
+
+    if (from->from != cptr)
+    {
+      ircstp->is_wrdi++;
+      Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)",
+         buffer, cptr->name));
+      return 0;
+    }
+  }
+
+  while (*ch == ' ')
+    ch++;
+  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 parameters and thus a space after the command
+   * code. -avalon
+   */
+  s = strchr(ch, ' ');         /* s -> End of the command code */
+  len = (s) ? (s - ch) : 0;
+  if (len == 3 && isDigit(*ch))
+  {
+    numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0');
+    paramcount = MAXPARA;
+    ircstp->is_num++;
+    mptr = NULL;               /* Init. to avoid stupid compiler warning :/ */
+  }
+  else
+  {
+    if (s)
+      *s++ = '\0';
+
+    /* Version      Receive         Send
+     * 2.9          Long            Long
+     * 2.10.0       Tkn/Long        Long
+     * 2.10.10      Tkn/Long        Tkn
+     * 2.10.20      Tkn             Tkn
+     *
+     * Clients/unreg servers always receive/
+     * send long commands   -record
+     */
+
+    /*
+     * This is a server. Check the token command list.
+     * -record!jegelhof@cloud9.net
+     */
+    mptr = msg_tree_parse(ch, &msg_tree_tok);
+
+#if 1                          /* for 2.10.0/2.10.10 */
+    /*
+     * This code supports 2.9 and 2.10.0 sending long commands.
+     * It makes more calls to strCasediff() than the above
+     * so it will be somewhat slower.
+     */
+    if (!mptr)
+      mptr = msg_tree_parse(ch, &msg_tree_cmd);
+#endif /* 1 */
+
+    if (!mptr)
+    {
+      /*
+       * 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
+       */
+#ifdef DEBUGMODE
+      if (buffer[0] != '\0')
+      {
+       Debug((DEBUG_ERROR, "Unknown (%s) from %s",
+           ch, get_client_name(cptr, TRUE)));
+      }
+#endif
+      ircstp->is_unco++;
+      return (-1);
+    }
+    LogMessage((cptr, mptr->msgclass));
+
+    paramcount = mptr->parameters;
+    i = bufend - ((s) ? s : ch);
+    mptr->bytes += i;
+  }
+  /*
+   * 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 (numeric)
+    return (do_numeric(numeric, (*buffer != ':'), cptr, from, i, para));
+  mptr->count++;
+
+  return (*mptr->func) (cptr, from, i, para);
+}
diff --git a/ircd/querycmds.c b/ircd/querycmds.c
new file mode 100644 (file)
index 0000000..c31d0d0
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * IRC - Internet Relay Chat, ircd/querycmds.c (formerly ircd/s_serv.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.h"
+#include <sys/stat.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "parse.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "ircd.h"
+#include "s_user.h"
+#include "version.h"
+#include "s_bsd.h"
+#include "s_misc.h"
+#include "match.h"
+#include "s_serv.h"
+#include "msg.h"
+#include "channel.h"
+#include "numnicks.h"
+#include "userload.h"
+#include "s_conf.h"
+#include "support.h"
+#include "querycmds.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * 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(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 aClient *acptr;
+
+  if (MyConnect(sptr) && parc > 1)
+  {
+    if (!(acptr = find_match_server(parv[1])))
+    {
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[1]);
+      return 0;
+    }
+    parv[1] = acptr->name;
+  }
+
+  if (hunt_server(0, 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_info
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ */
+int m_info(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  const char **text = infotext;
+
+  if (hunt_server(1, cptr, sptr, ":%s INFO :%s", 1, parc, parv) == HUNTED_ISME)
+  {
+    while (text[2])
+    {
+      if (!IsOper(sptr))
+       sendto_one(sptr, rpl_str(RPL_INFO), me.name, parv[0], *text);
+      text++;
+    }
+    if (IsOper(sptr))
+    {
+      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;
+}
+
+/*
+ * 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(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  char *mask;
+  aClient *acptr;
+
+  if (parc > 2)
+  {
+    if (hunt_server(1, 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, 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->name,
+#ifndef GODMODE
+       acptr->hopcount, acptr->serv->prot,
+#else /* GODMODE */
+       acptr->hopcount, acptr->serv->prot, acptr->serv->timestamp,
+       NumServ(acptr),
+#endif /* GODMODE */
+       (acptr->info[0] ? acptr->info : "(Unknown Location)"));
+  }
+
+  sendto_one(sptr, rpl_str(RPL_ENDOFLINKS), me.name, parv[0],
+      BadPtr(mask) ? "*" : mask);
+  return 0;
+}
+
+/*
+ * m_help
+ *
+ * parv[0] = sender prefix
+ */
+int m_help(aClient *UNUSED(cptr), aClient *sptr, int UNUSED(parc), char *parv[])
+{
+  int i;
+
+  for (i = 0; msgtab[i].cmd; i++)
+    sendto_one(sptr, ":%s NOTICE %s :%s", me.name, parv[0], msgtab[i].cmd);
+  return 0;
+}
+
+/* Counters of client/servers etc. */
+struct lusers_st nrof;
+
+void init_counters(void)
+{
+  memset(&nrof, 0, sizeof(nrof));
+  nrof.servers = 1;
+}
+
+/*
+ * m_lusers
+ *
+ * parv[0] = sender
+ * parv[1] = ignored
+ * parv[2] = server to query
+ */
+int m_lusers(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  if (parc > 2)
+    if (hunt_server(1, cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv) !=
+       HUNTED_ISME)
+      return 0;
+
+  sendto_one(sptr, rpl_str(RPL_LUSERCLIENT), me.name, parv[0],
+      nrof.clients - nrof.inv_clients, nrof.inv_clients, nrof.servers);
+  if (nrof.opers)
+    sendto_one(sptr, rpl_str(RPL_LUSEROP), me.name, parv[0], nrof.opers);
+  if (nrof.unknowns > 0)
+    sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN), me.name, parv[0],
+       nrof.unknowns);
+  if (nrof.channels > 0)
+    sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS), me.name, parv[0],
+       nrof.channels);
+  sendto_one(sptr, rpl_str(RPL_LUSERME), me.name, parv[0], nrof.local_clients,
+      nrof.local_servers);
+
+  if (MyUser(sptr) || Protocol(cptr) < 10)
+    sendto_one(sptr,
+       ":%s NOTICE %s :Highest connection count: %d (%d clients)",
+       me.name, parv[0], max_connection_count, max_client_count);
+  else
+    sendto_one(sptr,
+       "%s NOTICE %s%s :Highest connection count: %d (%d clients)",
+       NumServ(&me), NumNick(sptr), max_connection_count, max_client_count);
+
+  return 0;
+}
+
+/*
+ * m_admin
+ *
+ * parv[0] = sender prefix
+ * parv[1] = servername
+ */
+int m_admin(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aConfItem *aconf;
+
+  if (MyConnect(sptr) && parc > 1)
+  {
+    aClient *acptr;
+    if (!(acptr = find_match_server(parv[1])))
+    {
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[1]);
+      return 0;
+    }
+    parv[1] = acptr->name;
+  }
+  if (hunt_server(0, cptr, sptr, ":%s ADMIN :%s", 1, parc, parv) != HUNTED_ISME)
+    return 0;
+  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);
+    sendto_one(sptr, rpl_str(RPL_ADMINLOC2), me.name, parv[0], aconf->passwd);
+    sendto_one(sptr, rpl_str(RPL_ADMINEMAIL), me.name, parv[0], aconf->name);
+  }
+  else
+    sendto_one(sptr, err_str(ERR_NOADMININFO), me.name, parv[0], me.name);
+  return 0;
+}
+
+/*
+ * m_motd
+ *
+ * parv[0] - sender prefix
+ * parv[1] - servername
+ *
+ * modified 30 mar 1995 by flux (cmlambertus@ucdavis.edu)
+ * T line patch - display motd based on hostmask
+ * modified again 7 sep 97 by Ghostwolf with code and ideas 
+ * stolen from comstud & Xorath.  All motd files are read into
+ * memory in read_motd() in s_conf.c
+ *
+ * When NODEFAULTMOTD is defined, then it is possible that
+ * sptr == NULL, which means that this function is called from
+ * register_user.
+ */
+int m_motd(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  struct tm *tm = &motd_tm;    /* Default: Most general case */
+  atrecord *ptr;
+  int count;
+  register aMotdItem *temp;
+
+#ifdef NODEFAULTMOTD
+  int no_motd;
+
+  if (sptr)
+  {
+    no_motd = 0;
+#endif
+    if (hunt_server(0, cptr, sptr, ":%s MOTD :%s", 1, parc,
+       parv) != HUNTED_ISME)
+      return 0;
+#ifdef NODEFAULTMOTD
+  }
+  else
+  {
+    sptr = cptr;
+    no_motd = 1;
+  }
+#endif
+
+  /*
+   * Find out if this is a remote query or if we have a T line for our hostname
+   */
+  if (IsServer(cptr))
+  {
+    tm = NULL;                 /* Remote MOTD */
+    temp = rmotd;
+  }
+  else
+  {
+    for (ptr = tdata; ptr; ptr = ptr->next)
+    {
+      if (!match(ptr->hostmask, cptr->sockhost))
+       break;
+    }
+    if (ptr)
+    {
+      temp = ptr->tmotd;
+      tm = &ptr->tmotd_tm;
+    }
+    else
+      temp = motd;
+  }
+  if (temp == NULL)
+  {
+    sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv[0]);
+    return 0;
+  }
+#ifdef NODEFAULTMOTD
+  if (!no_motd)
+  {
+#endif
+    if (tm)                    /* Not remote? */
+    {
+      sendto_one(sptr, rpl_str(RPL_MOTDSTART), me.name, parv[0], me.name);
+      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);
+      count = 100;
+    }
+    else
+      count = 3;
+    for (; temp; temp = temp->next)
+    {
+      sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv[0], temp->line);
+      if (--count == 0)
+       break;
+    }
+#ifdef NODEFAULTMOTD
+  }
+  else
+  {
+    sendto_one(sptr, rpl_str(RPL_MOTDSTART), me.name, parv[0], me.name);
+    sendto_one(sptr, ":%s %d %s :%s", me.name, RPL_MOTD, parv[0],
+       "\ 2Type /MOTD to read the AUP before continuing using this service.\ 2");
+    sendto_one(sptr,
+       ":%s %d %s :The message of the day was last changed: %d/%d/%d", me.name,
+       RPL_MOTD, parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year);
+  }
+#endif
+  sendto_one(sptr, rpl_str(RPL_ENDOFMOTD), me.name, parv[0]);
+  return 0;
+}
diff --git a/ircd/random.c b/ircd/random.c
new file mode 100644 (file)
index 0000000..5905c80
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * IRC - Internet Relay Chat, ircd/random.c
+ *
+ * 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.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include "random.h"
+
+RCSTAG_CC("$Id$");
+
+char localkey[9] = RANDOM_SEED;
+
+/*
+ * MD5 transform algorithm, taken from code written by Colin Plumb,
+ * and put into the public domain
+ *
+ * Kev: Taken from Ted T'so's /dev/random random.c code and modified to
+ * be slightly simpler.  That code is released under a BSD-style copyright
+ * OR under the terms of the GNU Public License, which should be included
+ * at the top of this source file.
+ *
+ * record: Cleaned up to work with ircd.  RANDOM_TOKEN is defined in
+ * setup.h by the make script; if people start to "guess" your cookies,
+ * consider recompiling your server with a different random token.
+ */
+
+/* The four core functions - F1 is optimized somewhat */
+
+#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.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ *
+ * original comment left in; this used to be called MD5Transform and took
+ * two arguments; I've internalized those arguments, creating the character
+ * array "localkey," which should contain 8 bytes of data.  The function also
+ * originally returned nothing; now it returns an unsigned long that is the
+ * random number.  It appears to be reallyrandom, so... -Kev
+ *
+ * I don't really know what this does.  I tried to figure it out and got
+ * a headache.  If you know what's good for you, you'll leave this stuff
+ * for the smart people and do something else.          -record
+ */
+unsigned int ircrandom(void)
+{
+  unsigned int a, b, c, d;
+  unsigned char in[16];
+  struct timeval tv;
+
+  gettimeofday(&tv, NULL);
+
+  memcpy((void *)in, (void *)localkey, 8);
+  memcpy((void *)(in + 8), (void *)&tv.tv_sec, 4);
+  memcpy((void *)(in + 12), (void *)&tv.tv_usec, 4);
+
+  a = 0x67452301;
+  b = 0xefcdab89;
+  c = 0x98badcfe;
+  d = 0x10325476;
+
+  MD5STEP(F1, a, b, c, d, (int)in[0] + 0xd76aa478, 7);
+  MD5STEP(F1, d, a, b, c, (int)in[1] + 0xe8c7b756, 12);
+  MD5STEP(F1, c, d, a, b, (int)in[2] + 0x242070db, 17);
+  MD5STEP(F1, b, c, d, a, (int)in[3] + 0xc1bdceee, 22);
+  MD5STEP(F1, a, b, c, d, (int)in[4] + 0xf57c0faf, 7);
+  MD5STEP(F1, d, a, b, c, (int)in[5] + 0x4787c62a, 12);
+  MD5STEP(F1, c, d, a, b, (int)in[6] + 0xa8304613, 17);
+  MD5STEP(F1, b, c, d, a, (int)in[7] + 0xfd469501, 22);
+  MD5STEP(F1, a, b, c, d, (int)in[8] + 0x698098d8, 7);
+  MD5STEP(F1, d, a, b, c, (int)in[9] + 0x8b44f7af, 12);
+  MD5STEP(F1, c, d, a, b, (int)in[10] + 0xffff5bb1, 17);
+  MD5STEP(F1, b, c, d, a, (int)in[11] + 0x895cd7be, 22);
+  MD5STEP(F1, a, b, c, d, (int)in[12] + 0x6b901122, 7);
+  MD5STEP(F1, d, a, b, c, (int)in[13] + 0xfd987193, 12);
+  MD5STEP(F1, c, d, a, b, (int)in[14] + 0xa679438e, 17);
+  MD5STEP(F1, b, c, d, a, (int)in[15] + 0x49b40821, 22);
+
+  MD5STEP(F2, a, b, c, d, (int)in[1] + 0xf61e2562, 5);
+  MD5STEP(F2, d, a, b, c, (int)in[6] + 0xc040b340, 9);
+  MD5STEP(F2, c, d, a, b, (int)in[11] + 0x265e5a51, 14);
+  MD5STEP(F2, b, c, d, a, (int)in[0] + 0xe9b6c7aa, 20);
+  MD5STEP(F2, a, b, c, d, (int)in[5] + 0xd62f105d, 5);
+  MD5STEP(F2, d, a, b, c, (int)in[10] + 0x02441453, 9);
+  MD5STEP(F2, c, d, a, b, (int)in[15] + 0xd8a1e681, 14);
+  MD5STEP(F2, b, c, d, a, (int)in[4] + 0xe7d3fbc8, 20);
+  MD5STEP(F2, a, b, c, d, (int)in[9] + 0x21e1cde6, 5);
+  MD5STEP(F2, d, a, b, c, (int)in[14] + 0xc33707d6, 9);
+  MD5STEP(F2, c, d, a, b, (int)in[3] + 0xf4d50d87, 14);
+  MD5STEP(F2, b, c, d, a, (int)in[8] + 0x455a14ed, 20);
+  MD5STEP(F2, a, b, c, d, (int)in[13] + 0xa9e3e905, 5);
+  MD5STEP(F2, d, a, b, c, (int)in[2] + 0xfcefa3f8, 9);
+  MD5STEP(F2, c, d, a, b, (int)in[7] + 0x676f02d9, 14);
+  MD5STEP(F2, b, c, d, a, (int)in[12] + 0x8d2a4c8a, 20);
+
+  MD5STEP(F3, a, b, c, d, (int)in[5] + 0xfffa3942, 4);
+  MD5STEP(F3, d, a, b, c, (int)in[8] + 0x8771f681, 11);
+  MD5STEP(F3, c, d, a, b, (int)in[11] + 0x6d9d6122, 16);
+  MD5STEP(F3, b, c, d, a, (int)in[14] + 0xfde5380c, 23);
+  MD5STEP(F3, a, b, c, d, (int)in[1] + 0xa4beea44, 4);
+  MD5STEP(F3, d, a, b, c, (int)in[4] + 0x4bdecfa9, 11);
+  MD5STEP(F3, c, d, a, b, (int)in[7] + 0xf6bb4b60, 16);
+  MD5STEP(F3, b, c, d, a, (int)in[10] + 0xbebfbc70, 23);
+  MD5STEP(F3, a, b, c, d, (int)in[13] + 0x289b7ec6, 4);
+  MD5STEP(F3, d, a, b, c, (int)in[0] + 0xeaa127fa, 11);
+  MD5STEP(F3, c, d, a, b, (int)in[3] + 0xd4ef3085, 16);
+  MD5STEP(F3, b, c, d, a, (int)in[6] + 0x04881d05, 23);
+  MD5STEP(F3, a, b, c, d, (int)in[9] + 0xd9d4d039, 4);
+  MD5STEP(F3, d, a, b, c, (int)in[12] + 0xe6db99e5, 11);
+  MD5STEP(F3, c, d, a, b, (int)in[15] + 0x1fa27cf8, 16);
+  MD5STEP(F3, b, c, d, a, (int)in[2] + 0xc4ac5665, 23);
+
+  MD5STEP(F4, a, b, c, d, (int)in[0] + 0xf4292244, 6);
+  MD5STEP(F4, d, a, b, c, (int)in[7] + 0x432aff97, 10);
+  MD5STEP(F4, c, d, a, b, (int)in[14] + 0xab9423a7, 15);
+  MD5STEP(F4, b, c, d, a, (int)in[5] + 0xfc93a039, 21);
+  MD5STEP(F4, a, b, c, d, (int)in[12] + 0x655b59c3, 6);
+  MD5STEP(F4, d, a, b, c, (int)in[3] + 0x8f0ccc92, 10);
+  MD5STEP(F4, c, d, a, b, (int)in[10] + 0xffeff47d, 15);
+  MD5STEP(F4, b, c, d, a, (int)in[1] + 0x85845dd1, 21);
+  MD5STEP(F4, a, b, c, d, (int)in[8] + 0x6fa87e4f, 6);
+  MD5STEP(F4, d, a, b, c, (int)in[15] + 0xfe2ce6e0, 10);
+  MD5STEP(F4, c, d, a, b, (int)in[6] + 0xa3014314, 15);
+  MD5STEP(F4, b, c, d, a, (int)in[13] + 0x4e0811a1, 21);
+  MD5STEP(F4, a, b, c, d, (int)in[4] + 0xf7537e82, 6);
+  MD5STEP(F4, d, a, b, c, (int)in[11] + 0xbd3af235, 10);
+  MD5STEP(F4, c, d, a, b, (int)in[2] + 0x2ad7d2bb, 15);
+  MD5STEP(F4, b, c, d, a, (int)in[9] + 0xeb86d391, 21);
+
+  /*
+   * We have 4 unsigned longs generated by the above sequence; this scrambles
+   * them together so that if there is any pattern, it will be obscured.
+   */
+  return (a ^ b ^ c ^ d);
+}
diff --git a/ircd/res.c b/ircd/res.c
new file mode 100644 (file)
index 0000000..806bbec
--- /dev/null
@@ -0,0 +1,1678 @@
+/*
+ * 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 "sys.h"
+#include <signal.h>
+#include <sys/socket.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+/* dn_skipname is really an internal function,
+   we shouldn't be using it in res.c */
+#if !defined(dn_skipname) && !defined(__dn_skipname)
+extern int dn_skipname(const unsigned char *, const unsigned char *);
+#endif
+#include "h.h"
+#include "res.h"
+#include "struct.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_misc.h"
+#include "s_bsd.h"
+#include "ircd.h"
+#include "s_ping.h"
+#include "support.h"
+#include "common.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+#define MAXPACKET      1024
+
+#define RES_MAXADDRS   35
+#define RES_MAXALIASES 35
+
+#define ALIASBLEN ((RES_MAXALIASES + 1) * sizeof(char *))
+#define ADDRSBLEN ((RES_MAXADDRS + 1) * sizeof(struct in_addr *))
+#define ADDRSDLEN (RES_MAXADDRS * sizeof(struct in_addr))
+#define ALIASDLEN (MAXPACKET)
+#define MAXGETHOSTLEN (ALIASBLEN + ADDRSBLEN + ADDRSDLEN + ALIASDLEN)
+
+#define AR_TTL         600     /* TTL in seconds for dns cache entries */
+
+#define ARES_CACSIZE   512
+#define MAXCACHED      2048
+
+#ifndef INT16SZ
+#define INT16SZ 2
+#endif
+#ifndef INT32SZ
+#define INT32SZ 4
+#endif
+
+/*
+ * Building the Hostent
+ * The Hostent struct is arranged like this:
+ *          +-------------------------------+
+ * Hostent: | struct hostent h              |
+ *          |-------------------------------|
+ *          | char *buf                     |
+ *          +-------------------------------+
+ *
+ * allocated:
+ *
+ *          +-------------------------------+
+ * buf:     | h_aliases pointer array       | Max size: ALIASBLEN;
+ *          | NULL                          | contains `char *'s
+ *          |-------------------------------|
+ *          | h_addr_list pointer array     | Max size: ADDRSBLEN;
+ *          | NULL                          | contains `struct in_addr *'s
+ *          |-------------------------------|
+ *          | h_addr_list addresses         | Max size: ADDRSDLEN;
+ *          |                               | contains `struct in_addr's
+ *          |-------------------------------|
+ *          | storage for hostname strings  | Max size: ALIASDLEN;
+ *          +-------------------------------+ contains `char's
+ *
+ *  For requests the size of the h_aliases, and h_addr_list pointer
+ *  array sizes are set to MAXALISES and MAXADDRS respectively, and
+ *  buf is a fixed size with enough space to hold the largest expected
+ *  reply from a nameserver, see RFC 1034 and RFC 1035.
+ *  For cached entries the sizes are dependent on the actual number
+ *  of aliases and addresses. If new aliases and addresses are found
+ *  for cached entries, the buffer is grown and the new entries are added.
+ *  The hostent struct is filled in with the addresses of the entries in
+ *  the Hostent buf as follows:
+ *  h_name      - contains a pointer to the start of the hostname string area,
+ *                or NULL if none is set.  The h_name is followed by the
+ *                aliases, in the storage for hostname strings area.
+ *  h_aliases   - contains a pointer to the start of h_aliases pointer array.
+ *                This array contains pointers to the storage for hostname
+ *                strings area and is terminated with a NULL.  The first alias
+ *                is stored directly after the h_name.
+ *  h_addr_list - contains a pointer to the start of h_addr_list pointer array.
+ *                This array contains pointers to in_addr structures in the
+ *                h_addr_list addresses area and is terminated with a NULL.
+ *
+ *  Filling the buffer this way allows for proper alignment of the h_addr_list
+ *  addresses.
+ *
+ *  This arrangement allows us to alias a Hostent struct pointer as a
+ *  real struct hostent* without lying. It also allows us to change the
+ *  values contained in the cached entries and requests without changing
+ *  the actual hostent pointer, which is saved in a client struct and can't
+ *  be changed without blowing things up or a lot more fiddling around.
+ *  It also allows for defered allocation of the fixed size buffers until
+ *  they are really needed.
+ *  Nov. 17, 1997 --Bleep
+ */
+
+typedef struct Hostent {
+  struct hostent h;
+  char *buf;
+} aHostent;
+
+typedef struct reslist {
+  int id;
+  int sent;                    /* number of requests sent */
+  int srch;
+  time_t ttl;
+  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;
+  aHostent he;
+} ResRQ;
+
+typedef struct cache {
+  time_t expireat;
+  time_t ttl;
+  aHostent he;
+  struct cache *hname_next, *hnum_next, *list_next;
+} aCache;
+
+typedef struct cachetable {
+  aCache *num_list;
+  aCache *name_list;
+} CacheTable;
+
+extern int resfd;              /* defined in s_bsd.c */
+
+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(aCache *);
+static void rem_request(ResRQ *);
+static int do_query_name(Link *, char *, ResRQ *);
+static int do_query_number(Link *, struct in_addr *, ResRQ *);
+static void resend_query(ResRQ *);
+static int proc_answer(ResRQ *, HEADER *, unsigned char *, unsigned char *);
+static int query_name(char *, int, int, ResRQ *);
+static aCache *make_cache(ResRQ *);
+static aCache *find_cache_name(char *);
+static aCache *find_cache_number(ResRQ *, struct in_addr *);
+static int add_request(ResRQ *);
+static ResRQ *make_request(Link *);
+static int send_res_msg(char *, int, int);
+static ResRQ *find_id(int);
+static int hash_number(unsigned char *);
+static void update_list(ResRQ *, aCache *);
+static int hash_name(const 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(void)
+{
+  int on = 1;
+  int fd = -1;
+
+  memset(&reinfo, 0, sizeof(reinfo));
+  memset(&cainfo, 0, sizeof(cainfo));
+  memset(hashtable, 0, sizeof(hashtable));
+
+  first = last = NULL;
+
+  /* res_init() always returns 0 */
+  (void)res_init();
+
+  if (!_res.nscount)
+  {
+    _res.nscount = 1;
+    _res.nsaddr_list[0].sin_addr.s_addr = inet_addr("127.0.0.1");
+  }
+#ifdef DEBUGMODE
+  _res.options |= RES_DEBUG;
+#endif
+
+  alarm(2);
+  fd = socket(AF_INET, SOCK_DGRAM, 0);
+  alarm(0);
+  if (fd < 0)
+  {
+    if (errno == EMFILE || errno == ENOBUFS)
+    {
+      /*
+       * Only try this one more time, if we can't create the resolver
+       * socket at initialization time, it's pointless to continue.
+       */
+      alarm(2);
+      if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+      {
+       alarm(0);
+       Debug((DEBUG_ERROR, "init_resolver: socket: No more sockets"));
+       return -1;
+      }
+      alarm(0);
+    }
+    else
+    {
+      Debug((DEBUG_ERROR, "init_resolver: socket: %s", strerror(errno)));
+      return -1;
+    }
+  }
+  setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (OPT_TYPE *)&on, sizeof(on));
+  return fd;
+}
+
+static int add_request(ResRQ *new_request)
+{
+  if (!new_request)
+    return -1;
+  if (!first)
+    first = last = new_request;
+  else
+  {
+    last->next = new_request;
+    last = new_request;
+  }
+  new_request->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(ResRQ *old_request)
+{
+  ResRQ **rptr;
+  ResRQ *r2ptr = NULL;
+
+  if (old_request)
+  {
+    for (rptr = &first; *rptr; r2ptr = *rptr, rptr = &(*rptr)->next)
+    {
+      if (*rptr == old_request)
+      {
+       *rptr = old_request->next;
+       if (last == old_request)
+         last = r2ptr;
+       break;
+      }
+    }
+    Debug((DEBUG_DNS, "rem_request:Remove %p at %p %p",
+       old_request, *rptr, r2ptr));
+
+    if (old_request->he.buf)
+      RunFree(old_request->he.buf);
+    if (old_request->name)
+      RunFree(old_request->name);
+    RunFree(old_request);
+  }
+}
+
+/*
+ * Create a DNS request record for the server.
+ */
+static ResRQ *make_request(Link *lp)
+{
+  ResRQ *nreq;
+
+  if ((nreq = (ResRQ *)RunMalloc(sizeof(ResRQ))) == NULL)
+    return NULL;
+  memset(nreq, 0, sizeof(ResRQ));
+  nreq->sentat = now;
+  nreq->retries = 3;
+  nreq->resend = 1;
+  nreq->srch = -1;
+  if (lp)
+    memcpy(&nreq->cinfo, lp, sizeof(Link));
+  else
+    memset(&nreq->cinfo, 0, sizeof(Link));
+  nreq->timeout = 4;           /* start at 4 and exponential inc. */
+  nreq->addr.s_addr = INADDR_NONE;
+
+  nreq->he.h.h_addrtype = AF_INET;
+  nreq->he.h.h_length = sizeof(struct in_addr);
+  add_request(nreq);
+  return nreq;
+}
+
+/*
+ * Remove queries from the list which have been there too long without
+ * being resolved.
+ */
+time_t timeout_query_list(void)
+{
+  ResRQ *rptr;
+  ResRQ *r2ptr;
+  time_t next = 0;
+  time_t tout = 0;
+  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)
+      {
+       Debug((DEBUG_DNS, "timeout %p now " TIME_T_FMT " cptr %p",
+           rptr, now, rptr->cinfo.value.cptr));
+       reinfo.re_timeouts++;
+       cptr = rptr->cinfo.value.cptr;
+       switch (rptr->cinfo.flags)
+       {
+         case ASYNC_CLIENT:
+           ClearDNS(cptr);
+           if (!DoingAuth(cptr))
+             SetAccess(cptr);
+           break;
+         case ASYNC_PING:
+           sendto_ops("Host %s unknown", rptr->name);
+           end_ping(cptr);
+           break;
+         case ASYNC_CONNECT:
+           sendto_ops("Host %s unknown", rptr->name);
+           break;
+       }
+       rem_request(rptr);
+       rptr = NULL;
+       continue;
+      }
+      else
+      {
+       rptr->sentat = now;
+       rptr->timeout += rptr->timeout;
+       resend_query(rptr);
+       tout = now + rptr->timeout;
+       Debug((DEBUG_DNS, "r %p now " TIME_T_FMT " retry %d c %p",
+           rptr, now, rptr->retries, rptr->cinfo.value.cptr));
+      }
+    }
+    if (!next || tout < next)
+      next = tout;
+  }
+  Debug((DEBUG_DNS, "Next timeout_query_list() at %s, %ld",
+      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(char *cp)
+{
+  ResRQ *rptr, *r2ptr;
+
+  for (rptr = first; rptr; rptr = r2ptr)
+  {
+    r2ptr = rptr->next;
+    if (cp == rptr->cinfo.value.cp)
+      rem_request(rptr);
+  }
+}
+
+/*
+ * send_res_msg
+ *
+ * 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(char *msg, int len, int 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: %s on %d", strerror(errno), resfd));
+  }
+  return (sent) ? sent : -1;
+}
+
+/*
+ * find a dns request id (id is determined by dn_mkquery)
+ */
+static ResRQ *find_id(int id)
+{
+  ResRQ *rptr;
+
+  for (rptr = first; rptr; rptr = rptr->next)
+    if (rptr->id == id)
+      return rptr;
+  return NULL;
+}
+
+/*
+ * add_local_domain
+ *
+ * Add the domain to hostname, if it is missing
+ * (as suggested by eps@TOASTER.SFSU.EDU)
+ */
+void add_local_domain(char *hname, int size)
+{
+  /* try to fix up unqualified names */
+  if (!strchr(hname, '.'))
+  {
+    if (_res.defdname[0] && size > 0)
+    {
+      strcat(hname, ".");
+      strncat(hname, _res.defdname, size - 1);
+    }
+  }
+}
+
+struct hostent *gethost_byname(char *name, Link *lp)
+{
+  aCache *cp;
+
+  reinfo.re_na_look++;
+  if ((cp = find_cache_name(name)))
+    return &cp->he.h;
+  if (lp)
+    do_query_name(lp, name, NULL);
+  return NULL;
+}
+
+struct hostent *gethost_byaddr(struct in_addr *addr, Link *lp)
+{
+  aCache *cp;
+
+  reinfo.re_nu_look++;
+  if ((cp = find_cache_number(NULL, addr)))
+    return &cp->he.h;
+  if (!lp)
+    return NULL;
+  do_query_number(lp, addr, NULL);
+  return NULL;
+}
+
+static int do_query_name(Link *lp, char *name, ResRQ *rptr)
+{
+  char hname[HOSTLEN + 1];
+  int len;
+
+  strncpy(hname, name, sizeof(hname) - 1);
+  hname[sizeof(hname) - 1] = 0;
+  len = strlen(hname);
+
+  if (rptr && !strchr(hname, '.') && _res.options & RES_DEFNAMES)
+  {
+    strncat(hname, dot, sizeof(hname) - len - 1);
+    len++;
+    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)
+  {
+    if ((rptr = make_request(lp)) == NULL)
+      return -1;
+    rptr->type = T_A;
+    rptr->name = (char *)RunMalloc(strlen(name) + 1);
+    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(Link *lp, struct in_addr *numb, ResRQ *rptr)
+{
+  char ipbuf[32];
+  Reg2 unsigned char *cp = (unsigned char *)&numb->s_addr;
+
+  sprintf_irc(ipbuf, "%u.%u.%u.%u.in-addr.arpa.",
+      (unsigned int)(cp[3]), (unsigned int)(cp[2]),
+      (unsigned int)(cp[1]), (unsigned int)(cp[0]));
+
+  if (!rptr)
+  {
+    if ((rptr = make_request(lp)) == NULL)
+      return -1;
+    rptr->type = T_PTR;
+    rptr->addr.s_addr = numb->s_addr;
+  }
+  return (query_name(ipbuf, C_IN, T_PTR, rptr));
+}
+
+/*
+ * generate a query based on class, type and name.
+ */
+static int query_name(char *name, int q_class, int 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, q_class, type));
+  memset(buf, 0, sizeof(buf));
+  r = res_mkquery(QUERY, name, q_class, type, NULL, 0, NULL,
+      (unsigned char *)buf, sizeof(buf));
+  if (r <= 0)
+  {
+    h_errno = NO_RECOVERY;
+    return r;
+  }
+  hptr = (HEADER *) buf;
+  gettimeofday(&tv, NULL);
+  do
+  {
+    /* htons/ntohs can be assembler macros, which cannot
+       be nested. Thus two lines.   -Vesa */
+    unsigned short int nstmp = ntohs(hptr->id) + k
+       + (unsigned short int)(tv.tv_usec & 0xffff);
+    hptr->id = htons(nstmp);
+    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(ResRQ *rptr)
+{
+  if (rptr->resend == 0)
+    return;
+  reinfo.re_resends++;
+  switch (rptr->type)
+  {
+    case T_PTR:
+      do_query_number(NULL, &rptr->addr, rptr);
+      break;
+    case T_A:
+      do_query_name(NULL, rptr->name, rptr);
+      break;
+    default:
+      break;
+  }
+  return;
+}
+
+/*
+ * proc_answer
+ *
+ * Process name server reply.
+ */
+static int proc_answer(ResRQ *rptr, HEADER * hptr, unsigned char *buf,
+    unsigned char *eob)
+{
+  unsigned char *cp = buf + sizeof(HEADER);
+  char **alias;
+  char **addr;
+  char *p;                     /* pointer to strings */
+  char *a;                     /* pointer to address list */
+  char *endp;                  /* end of our buffer */
+  struct hostent *hp = &rptr->he.h;
+  int addr_class, type, dlen, ans = 0, n;
+  int addr_count = 0;
+  int alias_count = 0;
+
+  /*
+   * Lazy allocation of rptr->he.buf, we don't allocate a buffer
+   * unless there's something to put in it.
+   */
+  if (!rptr->he.buf)
+  {
+    if ((rptr->he.buf = (char *)RunMalloc(MAXGETHOSTLEN)) == NULL)
+      return 0;
+    /* 
+     * Array of alias list pointers starts at beginning of buf 
+     */
+    rptr->he.h.h_aliases = (char **)rptr->he.buf;
+    rptr->he.h.h_aliases[0] = NULL;
+    /* 
+     * Array of address list pointers starts after alias list pointers.
+     * The actual addresses follow the address list pointers.
+     */
+    rptr->he.h.h_addr_list = (char **)(rptr->he.buf + ALIASBLEN);
+    a = (char *)rptr->he.h.h_addr_list + ADDRSBLEN;
+    /*
+       * don't copy the host address to the beginning of h_addr_list
+       * make it just a little bit harder for the script kiddies
+     */
+    rptr->he.h.h_addr_list[0] = NULL;
+  }
+  endp = &rptr->he.buf[MAXGETHOSTLEN];
+
+  /* find the end of the address list */
+  addr = hp->h_addr_list;
+  while (*addr)
+  {
+    ++addr;
+    ++addr_count;
+  }
+  /* make 'a' point to the first available empty address slot */
+  a = (char *)hp->h_addr_list + ADDRSBLEN +
+      (addr_count * sizeof(struct in_addr));
+
+  /* find the end of the alias list */
+  alias = hp->h_aliases;
+  while (*alias)
+  {
+    ++alias;
+    ++alias_count;
+  }
+  /* make p point to the first available space in rptr->buf */
+  if (alias_count > 0)
+  {
+    p = (char *)hp->h_aliases[alias_count - 1];
+    p += (strlen(p) + 1);
+  }
+  else if (hp->h_name)
+    p = (char *)(hp->h_name + strlen(hp->h_name) + 1);
+  else
+    p = (char *)rptr->he.h.h_addr_list + ADDRSBLEN + ADDRSDLEN;
+
+  /*
+   * Skip past query's
+   */
+#ifdef SOL2                    /* brain damaged compiler (Solaris2) it seems */
+  for (; hptr->qdcount > 0; hptr->qdcount--)
+#else
+  while (hptr->qdcount-- > 0)
+#endif
+  {
+    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 && p < endp)
+  {
+    if ((n = dn_expand(buf, eob, cp, hostbuf, sizeof(hostbuf))) <= 0)
+    {
+      Debug((DEBUG_DNS, "dn_expand failed"));
+      break;
+    }
+
+    cp += n;
+    /* XXX magic numbers, this checks for truncated packets */
+    if ((cp + INT16SZ + INT16SZ + INT32SZ + INT16SZ) >= eob)
+      break;
+
+    /*
+     * I have no idea why - maybe a bug in the linker? But _getshort
+     * and _getlong don't work anymore.  So lets do it ourselfs:
+     * --Run
+     */
+
+    type = ((u_int16_t) cp[0] << 8) | ((u_int16_t) cp[1]);
+    cp += INT16SZ;
+    addr_class = ((u_int16_t) cp[0] << 8) | ((u_int16_t) cp[1]);
+    cp += INT16SZ;
+    rptr->ttl =
+       ((u_int32_t) cp[0] << 24) | ((u_int32_t) cp[1] << 16) | ((u_int32_t)
+       cp[2] << 8) | ((u_int32_t) cp[3]);
+    cp += INT32SZ;
+    dlen = ((u_int16_t) cp[0] << 8) | ((u_int16_t) cp[1]);
+    cp += INT16SZ;
+
+    rptr->type = type;
+
+    /* check for bad dlen */
+    if ((cp + dlen) > eob)
+      break;
+
+    /* 
+     * Add default domain name to returned host name if host name
+     * doesn't contain any dot separators.
+     * Name server never returns with trailing '.'
+     */
+    if (!strchr(hostbuf, '.') && (_res.options & RES_DEFNAMES))
+    {
+      strcat(hostbuf, dot);
+      strncat(hostbuf, _res.defdname, HOSTLEN - strlen(hostbuf));
+      hostbuf[HOSTLEN] = 0;
+    }
+
+    switch (type)
+    {
+      case T_A:
+       /* check for invalid dlen or too many addresses */
+       if (dlen != sizeof(struct in_addr) || ++addr_count >= RES_MAXADDRS)
+         break;
+       if (ans == 1)
+         hp->h_addrtype = (addr_class == C_IN) ? AF_INET : AF_UNSPEC;
+
+       memcpy(a, cp, sizeof(struct in_addr));
+       *addr++ = a;
+       *addr = 0;
+       a += sizeof(struct in_addr);
+
+       if (!hp->h_name)
+       {
+         strncpy(p, hostbuf, endp - p);
+         hp->h_name = p;
+         p += (strlen(p) + 1);
+       }
+       cp += dlen;
+       Debug((DEBUG_DNS, "got ip # %s for %s",
+           inetntoa(*((struct in_addr *)hp->h_addr_list[addr_count - 1])),
+           hostbuf));
+       ans++;
+       break;
+      case T_PTR:
+       if ((n = dn_expand(buf, eob, cp, hostbuf, sizeof(hostbuf))) < 0)
+       {
+         cp = NULL;
+         break;
+       }
+       cp += n;
+       Debug((DEBUG_DNS, "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_count >= RES_MAXALIASES)
+           break;
+         strncpy(p, hostbuf, endp - p);
+         endp[-1] = 0;
+         *alias++ = p;
+         *alias = NULL;
+       }
+       else
+       {
+         strncpy(p, hostbuf, endp - p);
+         hp->h_name = p;
+       }
+       p += (strlen(p) + 1);
+       ans++;
+       break;
+      case T_CNAME:
+       cp += dlen;
+       Debug((DEBUG_DNS, "got cname %s", hostbuf));
+       if (++alias_count >= RES_MAXALIASES)
+         break;
+       strncpy(p, hostbuf, endp - p);
+       endp[-1] = 0;
+       *alias++ = p;
+       *alias = NULL;
+       p += (strlen(p) + 1);
+       ans++;
+       break;
+      default:
+       Debug((DEBUG_DNS, "proc_answer: type:%d for:%s", type, hostbuf));
+       break;
+    }
+  }
+  return ans;
+}
+
+/*
+ * Read a dns reply from the nameserver and process it.
+ */
+struct hostent *get_res(char *lp)
+{
+  static unsigned char buf[sizeof(HEADER) + MAXPACKET];
+  Reg1 HEADER *hptr;
+  Reg2 ResRQ *rptr = NULL;
+  aCache *cp = NULL;
+  struct sockaddr_in sin;
+  int a, max;
+  size_t rc, len = sizeof(sin);
+
+  alarm(4);
+  rc = recvfrom(resfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len);
+  alarm(0);
+
+  if (rc <= sizeof(HEADER))
+    return NULL;
+  /*
+   * 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.
+   */
+  if ((rptr = find_id(hptr->id)) == NULL)
+  {
+    Debug((DEBUG_DNS, "find_id %d failed", hptr->id));
+    return NULL;
+  }
+  /*
+   * 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 ||
+       !memcmp((char *)&sin.sin_addr, (char *)&_res.nsaddr_list[a].sin_addr,
+       sizeof(struct in_addr)))
+      break;
+  }
+  if (a == max)
+  {
+    reinfo.re_unkrep++;
+    Debug((DEBUG_DNS, "got response from unknown ns"));
+    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;
+  }
+  /* 
+   * If this fails we didn't get a buffer to hold the hostent or
+   * there was an error decoding the received packet, try it again
+   * and hope it works the next time.
+   */
+  a = proc_answer(rptr, hptr, buf, &buf[rc]);
+  Debug((DEBUG_DNS, "get_res:Proc answer = %d", a));
+
+  if (a && rptr->type == T_PTR)
+  {
+    struct hostent *hp2 = NULL;
+
+    if (BadPtr(rptr->he.h.h_name))     /* Kludge!      960907/Vesa */
+      goto getres_err;
+
+    Debug((DEBUG_DNS, "relookup %s <-> %s", rptr->he.h.h_name,
+       inetntoa(rptr->addr)));
+    /*
+     * 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((char *)rptr->he.h.h_name, &rptr->cinfo)))
+    {
+      if (lp)
+       memcpy(lp, &rptr->cinfo, 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
+     */
+    else if (*rptr->he.h.h_aliases)
+    {
+      if (last->he.buf)
+       RunFree(last->he.buf);
+      last->he.buf = rptr->he.buf;
+      rptr->he.buf = NULL;
+      memcpy(&last->he.h, &rptr->he.h, sizeof(struct hostent));
+    }
+    rem_request(rptr);
+    return hp2;
+  }
+
+  if (a > 0)
+  {
+    if (lp)
+      memcpy(lp, &rptr->cinfo, sizeof(Link));
+    cp = make_cache(rptr);
+    Debug((DEBUG_DNS, "get_res:cp=%p rptr=%p (made)", cp, rptr));
+    rem_request(rptr);
+  }
+  else if (!rptr->sent)
+    rem_request(rptr);
+  return cp ? &cp->he.h : 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)
+      memcpy(lp, &rptr->cinfo, sizeof(Link));
+  }
+  return NULL;
+}
+
+/*
+ * Duplicate a hostent struct, allocate only enough memory for
+ * the data we're putting in it.
+ */
+static int dup_hostent(aHostent *new_hp, struct hostent *hp)
+{
+  char *p;
+  char **ap;
+  char **pp;
+  int alias_count = 0;
+  int addr_count = 0;
+  size_t bytes_needed = 0;
+
+  if (!new_hp || !hp)
+    return 0;
+
+  /* how much buffer do we need? */
+  bytes_needed += (strlen(hp->h_name) + 1);
+
+  pp = hp->h_aliases;
+  while (*pp)
+  {
+    bytes_needed += (strlen(*pp++) + 1 + sizeof(void *));
+    ++alias_count;
+  }
+  pp = hp->h_addr_list;
+  while (*pp++)
+  {
+    bytes_needed += (hp->h_length + sizeof(void *));
+    ++addr_count;
+  }
+  /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
+  bytes_needed += (2 * sizeof(void *));
+
+  /* Allocate memory */
+  if ((new_hp->buf = (char *)RunMalloc(bytes_needed)) == NULL)
+    return -1;
+
+  new_hp->h.h_addrtype = hp->h_addrtype;
+  new_hp->h.h_length = hp->h_length;
+
+  /* first write the address list */
+  pp = hp->h_addr_list;
+  ap = new_hp->h.h_addr_list =
+      (char **)(new_hp->buf + ((alias_count + 1) * sizeof(void *)));
+  p = (char *)ap + ((addr_count + 1) * sizeof(void *));
+  while (*pp)
+  {
+    *ap++ = p;
+    memcpy(p, *pp++, hp->h_length);
+    p += hp->h_length;
+  }
+  *ap = 0;
+  /* next write the name */
+  new_hp->h.h_name = p;
+  strcpy(p, hp->h_name);
+  p += (strlen(p) + 1);
+
+  /* last write the alias list */
+  pp = hp->h_aliases;
+  ap = new_hp->h.h_aliases = (char **)new_hp->buf;
+  while (*pp)
+  {
+    *ap++ = p;
+    strcpy(p, *pp++);
+    p += (strlen(p) + 1);
+  }
+  *ap = 0;
+
+  return 0;
+}
+
+/*
+ * Add records to a Hostent struct in place.
+ */
+static int update_hostent(aHostent *hp, char **addr, char **alias)
+{
+  char *p;
+  char **ap;
+  char **pp;
+  int alias_count = 0;
+  int addr_count = 0;
+  char *buf = NULL;
+  size_t bytes_needed = 0;
+
+  if (!hp || !hp->buf)
+    return -1;
+
+  /* how much buffer do we need? */
+  bytes_needed = strlen(hp->h.h_name) + 1;
+  pp = hp->h.h_aliases;
+  while (*pp)
+  {
+    bytes_needed += (strlen(*pp++) + 1 + sizeof(void *));
+    ++alias_count;
+  }
+  if (alias)
+  {
+    pp = alias;
+    while (*pp)
+    {
+      bytes_needed += (strlen(*pp++) + 1 + sizeof(void *));
+      ++alias_count;
+    }
+  }
+  pp = hp->h.h_addr_list;
+  while (*pp++)
+  {
+    bytes_needed += (hp->h.h_length + sizeof(void *));
+    ++addr_count;
+  }
+  if (addr)
+  {
+    pp = addr;
+    while (*pp++)
+    {
+      bytes_needed += (hp->h.h_length + sizeof(void *));
+      ++addr_count;
+    }
+  }
+  /* Reserve space for 2 nulls to terminate h_aliases and h_addr_list */
+  bytes_needed += 2 * sizeof(void *);
+
+  /* Allocate memory */
+  if ((buf = (char *)RunMalloc(bytes_needed)) == NULL)
+    return -1;
+
+  /* first write the address list */
+  pp = hp->h.h_addr_list;
+  ap = hp->h.h_addr_list =
+      (char **)(buf + ((alias_count + 1) * sizeof(void *)));
+  p = (char *)ap + ((addr_count + 1) * sizeof(void *));
+  while (*pp)
+  {
+    memcpy(p, *pp++, hp->h.h_length);
+    *ap++ = p;
+    p += hp->h.h_length;
+  }
+  if (addr)
+  {
+    while (*addr)
+    {
+      memcpy(p, *addr++, hp->h.h_length);
+      *ap++ = p;
+      p += hp->h.h_length;
+    }
+  }
+  *ap = 0;
+
+  /* next write the name */
+  strcpy(p, hp->h.h_name);
+  hp->h.h_name = p;
+  p += (strlen(p) + 1);
+
+  /* last write the alias list */
+  pp = hp->h.h_aliases;
+  ap = hp->h.h_aliases = (char **)buf;
+  while (*pp)
+  {
+    strcpy(p, *pp++);
+    *ap++ = p;
+    p += (strlen(p) + 1);
+  }
+  if (alias)
+  {
+    while (*alias)
+    {
+      strcpy(p, *alias++);
+      *ap++ = p;
+      p += (strlen(p) + 1);
+    }
+  }
+  *ap = 0;
+  /* release the old buffer */
+  p = hp->buf;
+  hp->buf = buf;
+  RunFree(p);
+  return 0;
+}
+
+static int hash_number(unsigned char *ip)
+{
+  unsigned 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(const char *name)
+{
+  unsigned int hashv = 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(aCache *ocp)
+{
+  aCache *cp = NULL;
+  int hashv;
+
+  Debug((DEBUG_DNS,
+      "add_to_cache:ocp %p he %p name %p addrl %p 0 %p",
+      ocp, &ocp->he, ocp->he.h.h_name, ocp->he.h.h_addr_list,
+      ocp->he.h.h_addr_list[0]));
+
+  ocp->list_next = cachetop;
+  cachetop = ocp;
+
+  hashv = hash_name(ocp->he.h.h_name);
+  ocp->hname_next = hashtable[hashv].name_list;
+  hashtable[hashv].name_list = ocp;
+
+  hashv = hash_number((unsigned char *)ocp->he.h.h_addr_list[0]);
+  ocp->hnum_next = hashtable[hashv].num_list;
+  hashtable[hashv].num_list = ocp;
+
+  Debug((DEBUG_DNS, "add_to_cache:added %s[%p] cache %p.",
+      ocp->he.h.h_name, ocp->he.h.h_addr_list[0], ocp));
+  Debug((DEBUG_DNS,
+      "add_to_cache:h1 %d h2 %#x lnext %p namnext %p numnext %p",
+      hash_name(ocp->he.h.h_name), hashv, ocp->list_next,
+      ocp->hname_next, ocp->hnum_next));
+
+  /*
+   * 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(ResRQ *rptr, aCache *cp)
+{
+  aCache **cpp;
+  char *s;
+  char **ap;
+  const char *t;
+  int i, j;
+  static char *addrs[RES_MAXADDRS + 1];
+  static char *aliases[RES_MAXALIASES + 1];
+
+  /*
+   * 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;
+
+  Debug((DEBUG_DNS, "u_l:cp %p na %p al %p ad %p",
+      cp, cp->he.h.h_name, cp->he.h.h_aliases, cp->he.h.h_addr_list[0]));
+  Debug((DEBUG_DNS, "u_l:rptr %p h_n %p", rptr, rptr->he.h.h_name));
+  /*
+   * Compare the cache entry against the new record.  Add any
+   * previously missing names for this entry.
+   */
+
+  *aliases = 0;
+  ap = aliases;
+  for (i = 0, s = (char *)rptr->he.h.h_name; s; s = rptr->he.h.h_aliases[i++])
+  {
+    for (j = 0, t = cp->he.h.h_name; t; t = cp->he.h.h_aliases[j++])
+    {
+      if (!strCasediff(t, s))
+       break;
+    }
+    if (!t)
+    {
+      *ap++ = s;
+      *ap = 0;
+    }
+  }
+
+  /*
+   * Do the same again for IP#'s.
+   */
+  *addrs = 0;
+  ap = addrs;
+  for (i = 0; (s = rptr->he.h.h_addr_list[i]); i++)
+  {
+    for (j = 0; (t = cp->he.h.h_addr_list[j]); j++)
+    {
+      if (!memcmp(t, s, sizeof(struct in_addr)))
+       break;
+    }
+    if (!t)
+    {
+      *ap++ = s;
+      *ap = 0;
+    }
+  }
+  if (*addrs || *aliases)
+    update_hostent(&cp->he, addrs, aliases);
+}
+
+static aCache *find_cache_name(char *name)
+{
+  aCache *cp;
+  const char *s;
+  int hashv;
+  int i;
+
+  hashv = hash_name(name);
+
+  cp = hashtable[hashv].name_list;
+  Debug((DEBUG_DNS, "find_cache_name:find %s : hashv = %d", name, hashv));
+
+  for (; cp; cp = cp->hname_next)
+  {
+    for (i = 0, s = cp->he.h.h_name; s; s = cp->he.h.h_aliases[i++])
+    {
+      if (strCasediff(s, name) == 0)
+      {
+       cainfo.ca_na_hits++;
+       update_list(0, 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.h_aliases)
+      continue;
+    if (hashv == hash_name(cp->he.h.h_name))
+      continue;
+    for (i = 0; (s = cp->he.h.h_aliases[i]); ++i)
+    {
+      if (!strCasediff(name, s))
+      {
+       cainfo.ca_na_hits++;
+       update_list(0, cp);
+       return cp;
+      }
+    }
+  }
+  return NULL;
+}
+
+/*
+ * Find a cache entry by ip# and update its expire time
+ */
+static aCache *find_cache_number(ResRQ *rptr, struct in_addr *numb)
+{
+  Reg1 aCache *cp;
+  Reg2 int hashv, i;
+
+  hashv = hash_number((unsigned char *)numb);
+
+  cp = hashtable[hashv].num_list;
+  Debug((DEBUG_DNS, "find_cache_number:find %s[%08x]: hashv = %d",
+      inetntoa(*numb), ntohl(numb->s_addr), hashv));
+
+  for (; cp; cp = cp->hnum_next)
+  {
+    for (i = 0; cp->he.h.h_addr_list[i]; ++i)
+    {
+      if (!memcmp(cp->he.h.h_addr_list[i], (char *)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.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((unsigned char *)cp->he.h.h_addr_list[0]))
+      continue;
+    for (i = 1; cp->he.h.h_addr_list[i]; ++i)
+    {
+      if (!memcmp(cp->he.h.h_addr_list[i], (char *)numb,
+         sizeof(struct in_addr)))
+      {
+       cainfo.ca_nu_hits++;
+       update_list(rptr, cp);
+       return cp;
+      }
+    }
+  }
+  return NULL;
+}
+
+static aCache *make_cache(ResRQ *rptr)
+{
+  aCache *cp;
+  int i;
+  struct hostent *hp = &rptr->he.h;
+
+  /*
+   * Shouldn't happen but it just might...
+   */
+  if (!hp->h_name || !hp->h_addr_list[0])
+    return NULL;
+  /*
+   * Make cache entry.  First check to see if the cache already exists
+   * and if so, return a pointer to it.
+   */
+  for (i = 0; hp->h_addr_list[i]; ++i)
+  {
+    if ((cp = find_cache_number(rptr, (struct in_addr *)hp->h_addr_list[i])))
+      return cp;
+  }
+
+  /*
+   * A matching entry wasnt found in the cache so go and make one up.
+   */
+  if ((cp = (aCache *)RunMalloc(sizeof(aCache))) == NULL)
+    return NULL;
+  memset(cp, 0, sizeof(aCache));
+  dup_hostent(&cp->he, hp);
+
+  if (rptr->ttl < 600)
+  {
+    reinfo.re_shortttl++;
+    cp->ttl = 600;
+  }
+  else
+    cp->ttl = rptr->ttl;
+  cp->expireat = now + cp->ttl;
+  Debug((DEBUG_INFO, "make_cache:made cache %p", cp));
+  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(aCache *ocp)
+{
+  aCache **cp;
+  struct hostent *hp = &ocp->he.h;
+  int hashv;
+  aClient *cptr;
+
+  Debug((DEBUG_DNS, "rem_cache: ocp %p hp %p l_n %p aliases %p",
+      ocp, hp, ocp->list_next, hp->h_aliases));
+
+  /*
+   * Cleanup any references to this structure by destroying the pointer.
+   */
+  for (hashv = highest_fd; hashv >= 0; --hashv)
+  {
+    if ((cptr = loc_clients[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);
+
+  Debug((DEBUG_DNS, "rem_cache: h_name %s hashv %d next %p first %p",
+      hp->h_name, hashv, ocp->hname_next, hashtable[hashv].name_list));
+
+  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((unsigned char *)hp->h_addr_list[0]);
+
+  Debug((DEBUG_DNS, "rem_cache: h_addr %s hashv %d next %p first %p",
+      inetntoa(*((struct in_addr *)hp->h_addr_list[0])), hashv,
+      ocp->hnum_next, hashtable[hashv].num_list));
+  for (cp = &hashtable[hashv].num_list; *cp; cp = &((*cp)->hnum_next))
+  {
+    if (*cp == ocp)
+    {
+      *cp = ocp->hnum_next;
+      break;
+    }
+  }
+
+  if (ocp->he.buf)
+    RunFree(ocp->he.buf);
+  RunFree((char *)ocp);
+
+  incache--;
+  cainfo.ca_dels++;
+}
+
+/*
+ * 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(void)
+{
+  Reg1 aCache *cp, *cp2;
+  Reg2 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(void)
+{
+  Reg1 aCache *cp;
+
+  while ((cp = cachetop))
+    rem_cache(cp);
+}
+
+int m_dns(aClient *cptr, aClient *sptr, int UNUSED(parc), char *parv[])
+{
+  aCache *cp;
+  int i;
+  struct hostent *h;
+
+  if (parv[1] && *parv[1] == 'l')
+  {
+    if (!IsAnOper(cptr))
+    {
+      return 0;
+    }
+    for (cp = cachetop; cp; cp = cp->list_next)
+    {
+      h = &cp->he.h;
+      sendto_one(sptr, "NOTICE %s :Ex %d ttl %d host %s(%s)",
+         parv[0], (int)(cp->expireat - now), (int)cp->ttl,
+         h->h_name, inetntoa(*((struct in_addr *)h->h_addr_list[0])));
+      for (i = 0; h->h_aliases[i]; i++)
+       sendto_one(sptr, "NOTICE %s : %s = %s (CN)",
+           parv[0], h->h_name, h->h_aliases[i]);
+      for (i = 1; h->h_addr_list[i]; i++)
+       sendto_one(sptr, "NOTICE %s : %s = %s (IP)", parv[0],
+           h->h_name, inetntoa(*((struct in_addr *)h->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;
+}
+
+size_t cres_mem(aClient *sptr)
+{
+  aCache *c = cachetop;
+  struct hostent *h;
+  int i;
+  size_t nm = 0, im = 0, sm = 0, ts = 0;
+
+  for (; c; c = c->list_next)
+  {
+    sm += sizeof(*c);
+    h = &c->he.h;
+    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 " SIZE_T_FMT,
+      me.name, RPL_STATSDEBUG, sptr->name, ts);
+  sendto_one(sptr, ":%s %d %s :Structs " SIZE_T_FMT
+      " IP storage " SIZE_T_FMT " Name storage " SIZE_T_FMT,
+      me.name, RPL_STATSDEBUG, sptr->name, sm, im, nm);
+  return ts + sm + im + nm;
+}
diff --git a/ircd/runmalloc.c b/ircd/runmalloc.c
new file mode 100644 (file)
index 0000000..7baf8bc
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * Run's malloc/realloc/calloc/free DEBUG tools v2.0
+ *
+ * (c) Copyright 1996, 1997
+ *
+ * Author:
+ *
+ * 1024/624ACAD5 1997/01/26 Carlo Wood, Run on IRC <carlo@runaway.xs4all.nl>
+ * Key fingerprint = 32 EC A7 B6 AC DB 65 A6  F6 F6 55 DD 1C DC FF 61
+ * Get key from pgp-public-keys server or
+ * finger carlo@runaway.xs4all.nl for public key (dialin, try at 21-22h GMT).
+ *
+ * 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.
+ */
+
+#include "sys.h"
+
+#ifdef DEBUGMALLOC
+#include <stdlib.h>
+#include "h.h"
+
+RCSTAG_CC("$Id$");
+
+#define MALLOC_HASHTABLE_SIZE 16384
+#define MallocHash(x) \
+    ((unsigned int)(((((long int)(x) >> 4) * 0xDEECE66D) >> 16) & (long int)0x3fff))
+#define MAGIC_PREFIX 0xe4c483a1
+#define MAGIC_POSTFIX 0x435bd0fa
+
+#ifdef MEMLEAKSTATS
+typedef struct {
+  const char *filename;
+  int line;
+  int number_of_allocations;
+#ifdef MEMSIZESTATS
+  size_t size;
+#endif
+} location_st;
+
+#define LOCSIZE 1024           /* Maximum of 256 different locations */
+static location_st location[LOCSIZE];
+static unsigned int locations; /* Counter */
+
+static unsigned int find_location(const char *filename, int line)
+{
+  register unsigned int hash;
+  hash = line & 0xff;
+  while (location[hash].filename && (location[hash].line != line ||
+      location[hash].filename != filename))
+    if (++hash == LOCSIZE)
+      hash = 0;
+  if (!location[hash].filename)
+  {
+    /* New location */
+    ++locations;
+    location[hash].filename = filename;
+    location[hash].line = line;
+  }
+  return hash;
+}
+#endif
+
+#ifdef MEMMAGICNUMS
+/* The size of this struct should be a multiple of 4 bytes, just in case... */
+typedef struct {
+#ifdef MEMMAGICNUMS
+  unsigned int prefix_magicnumber;
+#endif
+} prefix_blk_st;
+
+#define SIZEOF_PREFIX sizeof(prefix_blk_st)
+#else
+typedef void prefix_blk_st;
+#define SIZEOF_PREFIX 0
+#endif
+
+#ifdef MEMMAGICNUMS
+typedef struct {
+  unsigned int postfix_magicnumber;
+} postfix_blk_st;
+
+#define SIZEOF_POSTFIX sizeof(postfix_blk_st)
+#define HAS_POSTFIX
+#else
+#define SIZEOF_POSTFIX 0
+#endif
+
+typedef struct hash_entry_st {
+  struct hash_entry_st *next;
+  prefix_blk_st *ptr;
+#ifdef MEMSIZESTATS
+  size_t size;
+#endif
+#ifdef MEMLEAKSTATS
+  unsigned int location;
+#ifdef MEMTIMESTATS
+  time_t when;
+#endif
+#endif
+} hash_entry_st;
+
+#define memblkp(prefix_ptr) \
+    ((void *)((size_t)prefix_ptr + SIZEOF_PREFIX))
+#define prefixp(memblk_ptr) \
+    ((prefix_blk_st *)((size_t)memblk_ptr - SIZEOF_PREFIX))
+#define postfixp(memblk_ptr, size) \
+    ((postfix_blk_st *)((size_t)memblk_ptr + size))
+
+static hash_entry_st *hashtable[MALLOC_HASHTABLE_SIZE];
+#ifdef MEMSIZESTATS
+static size_t mem_size = 0;    /* Number of allocated bytes  */
+static unsigned int alloc_cnt = 0;     /* Number of allocated blocks */
+#endif
+
+#ifdef MEMLEAKSTATS
+#include "struct.h"
+#include "send.h"
+#include "numeric.h"
+#include "s_err.h"
+#include "ircd.h"
+#include "s_serv.h"
+#include "numnicks.h"
+
+void report_memleak_stats(aClient *sptr, int parc, char *parv[])
+{
+  unsigned int hash;
+  location_st *loc = location;
+
+#ifdef MEMTIMESTATS
+  time_t till = now;
+  time_t from = me.since;
+  if (parc > 3)
+  {
+    location_st tmp_loc[LOCSIZE];
+    hash_entry_st **start;
+    memset(tmp_loc, 0, sizeof(tmp_loc));
+    if (parc > 3)
+      till -= atoi(parv[3]);
+    if (parc > 4)
+      from += atoi(parv[4]);
+    for (start = &hashtable[0];
+       start < &hashtable[MALLOC_HASHTABLE_SIZE]; ++start)
+    {
+      hash_entry_st *hash_entry;
+      for (hash_entry = *start; hash_entry; hash_entry = hash_entry->next)
+       if (hash_entry->when >= from && hash_entry->when <= till)
+       {
+#ifdef MEMSIZESTATS
+         tmp_loc[hash_entry->location].size += hash_entry->size;
+#endif
+         tmp_loc[hash_entry->location].number_of_allocations++;
+       }
+    }
+    loc = tmp_loc;
+    if (MyUser(sptr) || Protocol(sptr->from) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :Memory allocated between " TIME_T_FMT
+         " (server start + %s s) and " TIME_T_FMT " (now - %s s):",
+         me.name, parv[0], from, parc > 4 ? parv[4] : "0", till,
+         parc > 3 ? parv[3] : "0");
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :Memory allocated between " TIME_T_FMT
+         " (server start + %s s) and " TIME_T_FMT " (now - %s s):",
+         NumServ(&me), NumNick(sptr), from, parc > 4 ? parv[4] : "0", till,
+         parc > 3 ? parv[3] : "0");
+  }
+#endif
+  for (hash = 0; hash < LOCSIZE; ++hash)
+    if (loc[hash].number_of_allocations > 0)
+      sendto_one(sptr, rpl_str(RPL_STATMEM), me.name, parv[0],
+         loc[hash].number_of_allocations,
+         location[hash].line, location[hash].filename
+#ifdef MEMSIZESTATS
+         , loc[hash].size
+#endif
+         );
+}
+
+void *RunMalloc_memleak(size_t size, int line, const char *filename)
+#else
+void *RunMalloc(size_t size)
+#endif
+{
+  register prefix_blk_st *ptr;
+  register hash_entry_st *hash_entry;
+  register hash_entry_st **hashtablep;
+
+#ifdef HAS_POSTFIX
+  size += 3;
+  size &= ~3;
+#endif
+
+  if (!((ptr = (prefix_blk_st *)
+      malloc(SIZEOF_PREFIX + size + SIZEOF_POSTFIX)) &&
+      (hash_entry = (hash_entry_st *) malloc(sizeof(hash_entry_st)))))
+  {
+    if (ptr)
+      free(ptr);
+    Debug((DEBUG_FATAL, "Out of memory !"));
+    return NULL;
+  }
+
+  hashtablep = &hashtable[MallocHash(ptr)];
+  hash_entry->next = *hashtablep;
+  *hashtablep = hash_entry;
+  hash_entry->ptr = ptr;
+#ifdef MEMLEAKSTATS
+#ifdef MEMTIMESTATS
+  hash_entry->when = now;
+#endif
+  location[(hash_entry->location =
+      find_location(filename, line))].number_of_allocations++;
+#endif
+#ifdef MEMSIZESTATS
+  hash_entry->size = size;
+#ifdef MEMLEAKSTATS
+  location[hash_entry->location].size += size;
+#endif
+  mem_size += size;
+  ++alloc_cnt;
+#endif
+#ifdef MEMMAGICNUMS
+  ptr->prefix_magicnumber = MAGIC_PREFIX;
+  postfixp(memblkp(ptr), size)->postfix_magicnumber = MAGIC_POSTFIX;
+#endif
+
+  Debug((DEBUG_DEBUG, "RunMalloc(%u) = %p", size, memblkp(ptr)));
+
+  return memblkp(ptr);
+}
+
+#ifdef MEMLEAKSTATS
+void *RunCalloc_memleak(size_t nmemb, size_t size,
+    int line, const char *filename)
+#else
+void *RunCalloc(size_t nmemb, size_t size)
+#endif
+{
+  void *ptr;
+  size *= nmemb;
+#ifdef MEMLEAKSTATS
+  if ((ptr = RunMalloc_memleak(size, line, filename)))
+#else
+  if ((ptr = RunMalloc(size)))
+#endif
+    memset(ptr, 0, size);
+  return ptr;
+}
+
+int RunFree_test(void *memblk_ptr)
+{
+  register prefix_blk_st *prefix_ptr = prefixp(memblk_ptr);
+  register hash_entry_st *hash_entry;
+  for (hash_entry = hashtable[MallocHash(prefix_ptr)];
+      hash_entry && hash_entry->ptr != prefix_ptr;
+      hash_entry = hash_entry->next);
+  return hash_entry ? 1 : 0;
+}
+
+void RunFree(void *memblk_ptr)
+{
+  register prefix_blk_st *prefix_ptr = prefixp(memblk_ptr);
+  register hash_entry_st *hash_entry, *prev_hash_entry = NULL;
+  unsigned int hash = MallocHash(prefix_ptr);
+
+  Debug((DEBUG_DEBUG, "RunFree(%p)", memblk_ptr));
+
+  if (!memblk_ptr)
+    return;
+
+  for (hash_entry = hashtable[hash];
+      hash_entry && hash_entry->ptr != prefix_ptr;
+      prev_hash_entry = hash_entry, hash_entry = hash_entry->next);
+  if (!hash_entry)
+  {
+    Debug((DEBUG_FATAL, "FREEING NON MALLOC PTR !!!"));
+    MyCoreDump;
+  }
+#ifdef MEMMAGICNUMS
+  if (prefix_ptr->prefix_magicnumber != MAGIC_PREFIX)
+  {
+    Debug((DEBUG_FATAL, "MAGIC_PREFIX CORRUPT !"));
+    MyCoreDump;
+  }
+  prefix_ptr->prefix_magicnumber = 12345678;
+  if (postfixp(memblk_ptr, hash_entry->size)->postfix_magicnumber
+      != MAGIC_POSTFIX)
+  {
+    Debug((DEBUG_FATAL, "MAGIC_POSTFIX CORRUPT !"));
+    MyCoreDump;
+  }
+  postfixp(memblk_ptr, hash_entry->size)->postfix_magicnumber = 87654321;
+#endif
+
+  if (prev_hash_entry)
+    prev_hash_entry->next = hash_entry->next;
+  else
+    hashtable[hash] = hash_entry->next;
+
+#ifdef MEMLEAKSTATS
+  location[hash_entry->location].number_of_allocations--;
+#endif
+
+#ifdef MEMSIZESTATS
+  mem_size -= hash_entry->size;
+  --alloc_cnt;
+#ifdef MEMLEAKSTATS
+  location[hash_entry->location].size -= hash_entry->size;
+#endif
+#ifdef DEBUGMODE
+  /* Put 0xfefefefe.. in freed memory */
+#ifndef memset
+  memset(prefix_ptr, 0xfe, hash_entry->size + SIZEOF_PREFIX);
+#else
+  {
+    register char *p = prefix_ptr;
+    size_t len = hash_entry->size + SIZEOF_PREFIX;
+    for (; len; --len)
+      *p++ = 0xfe;
+  }
+#endif
+#endif
+#endif
+
+  free(hash_entry);
+  free(prefix_ptr);
+}
+
+#ifdef MEMLEAKSTATS
+void *RunRealloc_memleak(void *memblk_ptr, size_t size,
+    int line, const char *filename)
+#else
+void *RunRealloc(void *memblk_ptr, size_t size)
+#endif
+{
+  register prefix_blk_st *ptr;
+  register prefix_blk_st *prefix_ptr = prefixp(memblk_ptr);
+  register hash_entry_st *hash_entry, *prev_hash_entry = NULL;
+  register hash_entry_st **hashtablep;
+  unsigned int hash;
+
+  if (!memblk_ptr)
+#ifdef MEMLEAKSTATS
+    return RunMalloc_memleak(size, line, filename);
+#else
+    return RunMalloc(size);
+#endif
+  if (!size)
+  {
+    RunFree(memblk_ptr);
+    return NULL;
+  }
+
+  for (hash_entry = hashtable[(hash = MallocHash(prefix_ptr))];
+      hash_entry && hash_entry->ptr != prefix_ptr;
+      prev_hash_entry = hash_entry, hash_entry = hash_entry->next);
+  if (!hash_entry)
+  {
+    Debug((DEBUG_FATAL, "REALLOCATING NON MALLOC PTR !!!"));
+    MyCoreDump;
+  }
+
+#ifdef MEMMAGICNUMS
+  if (prefix_ptr->prefix_magicnumber != MAGIC_PREFIX)
+  {
+    Debug((DEBUG_FATAL, "MAGIC_PREFIX CORRUPT !"));
+    MyCoreDump;
+  }
+  if (postfixp(memblk_ptr, hash_entry->size)->postfix_magicnumber
+      != MAGIC_POSTFIX)
+  {
+    Debug((DEBUG_FATAL, "MAGIC_POSTFIX CORRUPT !"));
+    MyCoreDump;
+  }
+#endif
+
+#ifdef HAS_POSTFIX
+  size += 3;
+  size &= ~3;
+#endif
+
+#ifdef MEMMAGICNUMS
+  postfixp(memblkp(prefix_ptr), hash_entry->size)->postfix_magicnumber = 123456;
+#endif
+#ifdef MEMLEAKSTATS
+  location[hash_entry->location].number_of_allocations--;
+#ifdef MEMSIZESTATS
+  location[hash_entry->location].size -= hash_entry->size;
+#endif
+#endif
+
+  if (!(ptr =
+      (prefix_blk_st *) realloc(prefix_ptr,
+      SIZEOF_PREFIX + size + SIZEOF_POSTFIX)))
+  {
+    Debug((DEBUG_FATAL, "RunRealloc: Out of memory :"));
+    return NULL;
+  }
+
+  if (prev_hash_entry)
+    prev_hash_entry->next = hash_entry->next;
+  else
+    hashtable[hash] = hash_entry->next;
+
+  hashtablep = &hashtable[MallocHash(ptr)];
+  hash_entry->next = *hashtablep;
+  *hashtablep = hash_entry;
+  hash_entry->ptr = ptr;
+#ifdef MEMLEAKSTATS
+#ifdef MEMTIMESTATS
+  hash_entry->when = now;
+#endif
+  location[(hash_entry->location =
+      find_location(filename, line))].number_of_allocations++;
+#endif
+#ifdef MEMSIZESTATS
+  mem_size += size - hash_entry->size;
+  hash_entry->size = size;
+#ifdef MEMLEAKSTATS
+  location[hash_entry->location].size += size;
+#endif
+#endif
+#ifdef MEMMAGICNUMS
+  postfixp(memblkp(ptr), size)->postfix_magicnumber = MAGIC_POSTFIX;
+#endif
+
+  Debug((DEBUG_DEBUG, ": RunRealloc(%p, %u) = %p",
+      memblk_ptr, size, memblkp(ptr)));
+
+  return memblkp(ptr);
+}
+
+#ifdef MEMSIZESTATS
+unsigned int get_alloc_cnt(void)
+{
+  return alloc_cnt;
+}
+
+size_t get_mem_size(void)
+{
+  return mem_size;
+}
+#endif
+
+#endif /* DEBUGMALLOC */
diff --git a/ircd/s_auth.c b/ircd/s_auth.c
new file mode 100644 (file)
index 0000000..7299327
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * IRC - Internet Relay Chat, ircd/s_auth.c
+ * 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.
+ */
+
+#include "sys.h"
+#include <sys/socket.h>
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef UNIXPORT
+#include <sys/un.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HPUX
+#include <arpa/inet.h>
+#endif /* HPUX */
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "res.h"
+#include "struct.h"
+#include "common.h"
+#include "send.h"
+#include "s_bsd.h"
+#include "s_misc.h"
+#include "support.h"
+#include "ircd.h"
+#include "s_auth.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * start_auth
+ *
+ * Flag the client to show that an attempt to contact the ident server on
+ * the client's host.  The connect and subsequently the socket are all put
+ * into 'non-blocking' mode.  Should the connect or any later phase of the
+ * identifing process fail, it is aborted and the user is given a username
+ * of "unknown".
+ */
+void start_auth(aClient *cptr)
+{
+  struct sockaddr_in sock;
+  int err;
+
+  Debug((DEBUG_NOTICE, "start_auth(%p) fd %d status %d",
+      cptr, cptr->fd, cptr->status));
+
+  alarm(2);
+  cptr->authfd = socket(AF_INET, SOCK_STREAM, 0);
+  err = errno;
+  alarm(0);
+  if (cptr->authfd < 0 && err == EAGAIN)
+    sendto_ops("Can't allocate fd for auth on %s : socket: No more sockets",
+       get_client_name(cptr, TRUE));
+
+  if (cptr->authfd < 0)
+  {
+#ifdef USE_SYSLOG
+    syslog(LOG_ERR, "Unable to create auth socket for %s:%m",
+       get_client_name(cptr, TRUE));
+#endif
+    Debug((DEBUG_ERROR, "Unable to create auth socket for %s:%s",
+       get_client_name(cptr, TRUE), strerror(get_sockerr(cptr))));
+    if (!DoingDNS(cptr))
+      SetAccess(cptr);
+    ircstp->is_abad++;
+    return;
+  }
+  if (cptr->authfd >= (MAXCONNECTIONS - 2))
+  {
+    sendto_ops("Can't allocate fd for auth on %s", get_client_name(cptr, TRUE));
+    close(cptr->authfd);
+    return;
+  }
+
+  set_non_blocking(cptr->authfd, cptr);
+
+#ifdef VIRTUAL_HOST
+  if (bind(cptr->authfd, (struct sockaddr *)&vserv, sizeof(vserv)) == -1)
+  {
+    report_error("binding auth stream socket %s: %s", cptr);
+    close(cptr->fd);
+    return;
+  }
+#endif
+  memcpy(&sock.sin_addr, &cptr->ip, sizeof(struct in_addr));
+
+  sock.sin_port = htons(113);
+  sock.sin_family = AF_INET;
+
+  alarm((unsigned)4);
+  if (connect(cptr->authfd, (struct sockaddr *)&sock,
+      sizeof(sock)) == -1 && errno != EINPROGRESS)
+  {
+    ircstp->is_abad++;
+    /*
+     * No error report from this...
+     */
+    alarm((unsigned)0);
+    close(cptr->authfd);
+    cptr->authfd = -1;
+    if (!DoingDNS(cptr))
+      SetAccess(cptr);
+    return;
+  }
+  alarm((unsigned)0);
+  cptr->flags |= (FLAGS_WRAUTH | FLAGS_AUTH);
+  if (cptr->authfd > highest_fd)
+    highest_fd = cptr->authfd;
+  return;
+}
+
+/*
+ * send_authports
+ *
+ * Send the ident server a query giving "theirport , ourport".
+ * The write is only attempted *once* so it is deemed to be a fail if the
+ * entire write doesn't write all the data given.  This shouldnt be a
+ * problem since the socket should have a write buffer far greater than
+ * this message to store it in should problems arise. -avalon
+ */
+void send_authports(aClient *cptr)
+{
+  struct sockaddr_in us, them;
+  char authbuf[32];
+  size_t ulen, tlen;
+
+  Debug((DEBUG_NOTICE, "write_authports(%p) fd %d authfd %d stat %d",
+      cptr, cptr->fd, cptr->authfd, cptr->status));
+  tlen = ulen = sizeof(us);
+  if (getsockname(cptr->fd, (struct sockaddr *)&us, &ulen) ||
+      getpeername(cptr->fd, (struct sockaddr *)&them, &tlen))
+  {
+#ifdef USE_SYSLOG
+    syslog(LOG_ERR, "auth get{sock,peer}name error for %s:%m",
+       get_client_name(cptr, TRUE));
+#endif
+    goto authsenderr;
+  }
+
+  sprintf_irc(authbuf, "%u , %u\r\n", (unsigned int)ntohs(them.sin_port),
+      (unsigned int)ntohs(us.sin_port));
+
+  Debug((DEBUG_SEND, "sending [%s] to auth port %s.113",
+      authbuf, inetntoa(them.sin_addr)));
+  if (write(cptr->authfd, authbuf, strlen(authbuf)) != (int)strlen(authbuf))
+  {
+  authsenderr:
+    ircstp->is_abad++;
+    close(cptr->authfd);
+    if (cptr->authfd == highest_fd)
+      while (!loc_clients[highest_fd])
+       highest_fd--;
+    cptr->authfd = -1;
+    cptr->flags &= ~FLAGS_AUTH;
+    if (!DoingDNS(cptr))
+      SetAccess(cptr);
+  }
+  cptr->flags &= ~FLAGS_WRAUTH;
+  return;
+}
+
+/*
+ * read_authports
+ *
+ * read the reply (if any) from the ident server we connected to.
+ * The actual read processijng here is pretty weak - no handling of the reply
+ * if it is fragmented by IP.
+ */
+void read_authports(aClient *cptr)
+{
+  Reg1 char *s, *t;
+  Reg2 int len;
+  char ruser[USERLEN + 1], system[8];
+  unsigned short int remp = 0, locp = 0;
+
+  *system = *ruser = '\0';
+  Debug((DEBUG_NOTICE, "read_authports(%p) fd %d authfd %d stat %d",
+      cptr, cptr->fd, cptr->authfd, cptr->status));
+  /*
+   * Nasty.  Cant allow any other reads from client fd while we're
+   * waiting on the authfd to return a full valid string.  Use the
+   * client's input buffer to buffer the authd reply.
+   * Oh. this is needed because an authd reply may come back in more
+   * than 1 read! -avalon
+   */
+  if ((len = read(cptr->authfd, cptr->buffer + cptr->count,
+      sizeof(cptr->buffer) - 1 - cptr->count)) >= 0)
+  {
+    cptr->count += len;
+    cptr->buffer[cptr->count] = '\0';
+  }
+
+  cptr->lasttime = now;
+  if ((len > 0) && (cptr->count != (sizeof(cptr->buffer) - 1)) &&
+      (sscanf(cptr->buffer, "%hd , %hd : USERID : %*[^:]: %10s",
+      &remp, &locp, ruser) == 3))
+  {
+    s = strrchr(cptr->buffer, ':');
+    *s++ = '\0';
+    for (t = (strrchr(cptr->buffer, ':') + 1); *t; t++)
+      if (!isSpace(*t))
+       break;
+    strncpy(system, t, sizeof(system) - 1);
+    system[sizeof(system) - 1] = 0;
+    for (t = ruser; *s && (t < ruser + sizeof(ruser)); s++)
+      if (!isSpace(*s) && *s != ':' && *s != '@')
+       *t++ = *s;
+    *t = '\0';
+    Debug((DEBUG_INFO, "auth reply ok [%s] [%s]", system, ruser));
+  }
+  else if (len != 0)
+  {
+    if (!strchr(cptr->buffer, '\n') && !strchr(cptr->buffer, '\r'))
+      return;
+    Debug((DEBUG_ERROR, "local %d remote %d", locp, remp));
+    Debug((DEBUG_ERROR, "bad auth reply in [%s]", cptr->buffer));
+    *ruser = '\0';
+  }
+  close(cptr->authfd);
+  if (cptr->authfd == highest_fd)
+    while (!loc_clients[highest_fd])
+      highest_fd--;
+  cptr->count = 0;
+  cptr->authfd = -1;
+  ClearAuth(cptr);
+  if (!DoingDNS(cptr))
+    SetAccess(cptr);
+  if (len > 0)
+    Debug((DEBUG_INFO, "ident reply: [%s]", cptr->buffer));
+
+  if (!locp || !remp || !*ruser)
+  {
+    ircstp->is_abad++;
+    return;
+  }
+  ircstp->is_asuc++;
+  strncpy(cptr->username, ruser, USERLEN);
+  cptr->username[USERLEN] = 0; /* This is the LA bug --Run */
+  if (strncmp(system, "OTHER", 5))
+    cptr->flags |= FLAGS_GOTID;
+  Debug((DEBUG_INFO, "got username [%s]", ruser));
+  return;
+}
diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c
new file mode 100644 (file)
index 0000000..e1606c2
--- /dev/null
@@ -0,0 +1,2615 @@
+/*
+ * 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 "sys.h"
+#include <stdlib.h>
+#include <sys/socket.h>
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef SOL2
+#include <sys/filio.h>
+#endif
+#ifdef UNIXPORT
+#include <sys/un.h>
+#endif
+#include <stdio.h>
+#ifdef USE_POLL
+#ifndef HAVE_POLL_H
+#undef USE_POLL
+#else /* HAVE_POLL_H */
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+#include <poll.h>
+#endif /* HAVE_POLL_H */
+#endif /* USE_POLL */
+#include <signal.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <utmp.h>
+#include <sys/resource.h>
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include <sys/utsname.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include "h.h"
+#include "res.h"
+#include "struct.h"
+#include "s_bsd.h"
+#include "s_serv.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "s_bsd.h"
+#include "hash.h"
+#include "s_err.h"
+#include "ircd.h"
+#include "support.h"
+#include "s_auth.h"
+#include "class.h"
+#include "packet.h"
+#include "s_ping.h"
+#include "channel.h"
+#include "version.h"
+#include "parse.h"
+#include "common.h"
+#include "bsd.h"
+#include "numnicks.h"
+#include "s_user.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+
+RCSTAG_CC("$Id$");
+
+#ifndef IN_LOOPBACKNET
+#define IN_LOOPBACKNET 0x7f
+#endif
+
+aClient *loc_clients[MAXCONNECTIONS];
+int highest_fd = 0, udpfd = -1, resfd = -1;
+unsigned int readcalls = 0;
+static struct sockaddr_in mysk;
+static void polludp();
+
+static struct sockaddr *connect_inet(aConfItem *, aClient *, int *);
+static int completed_connection(aClient *);
+static int check_init(aClient *, char *);
+static void do_dns_async(), set_sock_opts(int, aClient *);
+#ifdef UNIXPORT
+static struct sockaddr *connect_unix(aConfItem *, aClient *, int *);
+static void add_unixconnection(aClient *, int);
+static char unixpath[256];
+#endif
+static char readbuf[8192];
+#ifdef USE_POLL
+static struct pollfd poll_fds[MAXCONNECTIONS + 1];
+static aClient *poll_cptr[MAXCONNECTIONS + 1];
+#endif /* USE_POLL */
+#ifdef VIRTUAL_HOST
+struct sockaddr_in vserv;
+#endif
+static int running_in_background;
+
+#ifdef GODMODE
+#ifndef NODNS
+#define NODNS
+#endif
+#ifndef NOFLOODCONTROL
+#define NOFLOODCONTROL
+#endif
+#endif
+
+/*
+ * 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
+
+#if !defined(USE_POLL)
+#if FD_SETSIZE < (MAXCONNECTIONS + 4)
+/*
+ * Sanity check
+ *
+ * All operating systems work when MAXCONNECTIONS <= 252.
+ * Most operating systems work when MAXCONNECTIONS <= 1020 and FD_SETSIZE is
+ *   updated correctly in the system headers (on BSD systems our sys.h has
+ *   defined FD_SETSIZE to MAXCONNECTIONS+4 before including the system's headers 
+ *   but sys/types.h might have abruptly redefined it so the check is still 
+ *   done), you might already need to recompile your kernel.
+ * For larger FD_SETSIZE your milage may vary (kernel patches may be needed).
+ * The check is _NOT_ done if we will not use FD_SETS at all (USE_POLL)
+ */
+#error "FD_SETSIZE is too small or MAXCONNECTIONS too large."
+#endif
+#endif
+
+/*
+ * 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(char *text, aClient *cptr)
+{
+  Reg1 int errtmp = errno;     /* debug may change 'errno' */
+  Reg2 char *host;
+  int err;
+  size_t 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.
+   */
+#if defined(SO_ERROR) && !defined(SOL2)
+  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
+  if (!running_in_background)
+  {
+    fprintf(stderr, text, host, strerror(errtmp));
+    fprintf(stderr, "\n");
+    fflush(stderr);
+  }
+  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(aClient *cptr, char *name, unsigned short int port)
+{
+  static struct sockaddr_in server;
+  int ad[4], opt;
+  size_t len = sizeof(server);
+  char ipname[20];
+
+  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 :-)
+   */
+  sscanf(name, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
+  sprintf_irc(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
+
+  if (cptr != &me)
+  {
+    sprintf(cptr->sockhost, "%-.42s.%u", name, port);
+    strcpy(cptr->name, me.name);
+  }
+  /*
+   * At first, open a new socket
+   */
+  if (cptr->fd == -1)
+  {
+    alarm(2);
+    cptr->fd = socket(AF_INET, SOCK_STREAM, 0);
+    alarm(0);
+    if (cptr->fd < 0 && errno == EAGAIN)
+    {
+      sendto_ops("opening stream socket %s: No more sockets",
+         get_client_name(cptr, TRUE));
+      return -1;
+    }
+  }
+  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);
+    close(cptr->fd);
+    return -1;
+  }
+
+  opt = 1;
+  setsockopt(cptr->fd, SOL_SOCKET, SO_REUSEADDR, (OPT_TYPE *)&opt, sizeof(opt));
+
+  /*
+   * 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;
+#ifndef VIRTUAL_HOST
+    server.sin_addr.s_addr = INADDR_ANY;
+#else
+    if (vserv.sin_addr.s_addr == 0)    /* Not already initialised ? */
+    {
+      struct hostent *hep;
+      memset(&vserv, 0, sizeof(vserv));
+      vserv.sin_family = AF_INET;
+      hep = gethostbyname(me.name);    /* Use name from M: line */
+      if (hep && hep->h_addrtype == AF_INET && hep->h_addr_list[0] &&
+         !hep->h_addr_list[1])
+       memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr));
+      else
+      {
+       report_error("Error creating virtual host %s: %s", cptr);
+       return -1;
+      }
+    }
+    server.sin_addr = vserv.sin_addr;
+#endif
+#ifdef TESTNET
+    server.sin_port = htons(port + 10000);
+#else
+    server.sin_port = htons(port);
+#endif
+    if (bind(cptr->fd, (struct sockaddr *)&server, sizeof(server)) == -1)
+    {
+      report_error("binding stream socket %s: %s", cptr);
+      close(cptr->fd);
+      return -1;
+    }
+  }
+  if (getsockname(cptr->fd, (struct sockaddr *)&server, &len))
+  {
+    report_error("getsockname failed for %s: %s", cptr);
+    close(cptr->fd);
+    return -1;
+  }
+
+  if (cptr == &me)             /* KLUDGE to get it work... */
+  {
+    char buf[1024];
+
+#ifdef TESTNET
+    sprintf_irc(buf, rpl_str(RPL_MYPORTIS), me.name, "*",
+       ntohs(server.sin_port) - 10000);
+#else
+    sprintf_irc(buf, rpl_str(RPL_MYPORTIS), me.name, "*",
+       ntohs(server.sin_port));
+#endif
+    write(1, buf, strlen(buf));
+  }
+  if (cptr->fd > highest_fd)
+    highest_fd = cptr->fd;
+  cptr->ip.s_addr = inet_addr(ipname);
+#ifdef TESTNET
+  cptr->port = ntohs(server.sin_port) - 10000;
+#else
+  cptr->port = ntohs(server.sin_port);
+#endif
+  listen(cptr->fd, 128);       /* Use listen port backlog of 128 */
+  loc_clients[cptr->fd] = cptr;
+
+  return 0;
+}
+
+#ifdef UNIXPORT
+/*
+ * unixport
+ *
+ * Create a socket and bind it to a filename which is comprised of the path
+ * (directory where file is placed) and port (actual filename created).
+ * Set directory permissions as rwxr-xr-x so other users can connect to the
+ * file which is 'forced' to rwxrwxrwx (different OS's have different need of
+ * modes so users can connect to the socket).
+ */
+int unixport(aClient *cptr, char *path, unsigned short int port)
+{
+  struct sockaddr_un un;
+
+  alarm(2);
+  cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+  alarm(0);
+  if (cptr->fd == -1 && errno == EAGAIN)
+  {
+    sendto_ops("error opening unix domain socket %s: No more sockets",
+       get_client_name(cptr, TRUE));
+    return -1;
+  }
+  if (cptr->fd == -1)
+  {
+    report_error("error opening unix domain socket %s: %s", cptr);
+    return -1;
+  }
+  else if (cptr->fd >= MAXCLIENTS)
+  {
+    sendto_ops("No more connections allowed (%s)", cptr->name);
+    close(cptr->fd);
+    cptr->fd = -1;
+    return -1;
+  }
+
+  un.sun_family = AF_UNIX;
+#if HAVE_MKDIR
+  mkdir(path, 0755);
+#else
+  if (chmod(path, 0755) == -1)
+  {
+    sendto_ops("error 'chmod 0755 %s': %s", path, strerror(errno));
+#ifdef USE_SYSLOG
+    syslog(LOG_WARNING, "error 'chmod 0755 %s': %s", path, strerror(errno));
+#endif
+    close(cptr->fd);
+    cptr->fd = -1;
+    return -1;
+  }
+#endif
+  sprintf_irc(unixpath, "%s/%u", path, port);
+  unlink(unixpath);
+  strncpy(un.sun_path, unixpath, sizeof(un.sun_path) - 1);
+  un.sun_path[sizeof(un.sun_path) - 1] = 0;
+  strcpy(cptr->name, me.name);
+  errno = 0;
+  get_sockhost(cptr, unixpath);
+
+  if (bind(cptr->fd, (struct sockaddr *)&un, strlen(unixpath) + 2) == -1)
+  {
+    report_error("error binding unix socket %s: %s", cptr);
+    close(cptr->fd);
+    return -1;
+  }
+  if (cptr->fd > highest_fd)
+    highest_fd = cptr->fd;
+  listen(cptr->fd, 5);
+  chmod(unixpath, 0777);
+  cptr->flags |= FLAGS_UNIX;
+  cptr->port = 0;
+  loc_clients[cptr->fd] = cptr;
+
+  return 0;
+}
+#endif
+
+/*
+ * 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(aConfItem *aconf)
+{
+  aClient *cptr;
+
+  cptr = make_client(NULL, STAT_ME);
+  cptr->flags = FLAGS_LISTEN;
+  cptr->acpt = cptr;
+  cptr->from = cptr;
+  strncpy(cptr->name, aconf->host, sizeof(cptr->name) - 1);
+  cptr->name[sizeof(cptr->name) - 1] = 0;
+#ifdef UNIXPORT
+  if (*aconf->host == '/')
+  {
+    if (unixport(cptr, aconf->host, aconf->port))
+      cptr->fd = -2;
+  }
+  else
+#endif
+  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(void)
+{
+  Reg1 aClient *cptr;
+  Reg2 int i;
+  Reg3 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 = loc_clients[i]))
+      continue;
+    if (!IsMe(cptr) || cptr == &me || !IsListening(cptr))
+      continue;
+    aconf = cptr->confs->value.aconf;
+
+    if (IsIllegal(aconf) && aconf->clients == 0)
+    {
+#ifdef UNIXPORT
+      if (IsUnixSocket(cptr))
+      {
+       sprintf_irc(unixpath, "%s/%u", aconf->host, aconf->port);
+       unlink(unixpath);
+      }
+#endif
+      close_connection(cptr);
+    }
+  }
+}
+
+/*
+ * init_sys
+ */
+void init_sys(void)
+{
+  Reg1 int fd;
+#if defined(HAVE_SETRLIMIT) && defined(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
+    {
+      fprintf(stderr, "ircd fd table too big\n");
+      fprintf(stderr, "Hard Limit: " LIMIT_FMT " IRC max: %d\n",
+#ifdef pyr
+         limit.rlim_cur,
+#else
+         limit.rlim_max,
+#endif
+         (int)MAXCONNECTIONS);
+      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)
+    {
+      fprintf(stderr, "error setting max fd's to " LIMIT_FMT "\n",
+         limit.rlim_cur);
+      exit(-1);
+    }
+#endif
+  }
+#endif /* defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) */
+#ifdef DEBUGMODE
+  if (1)
+  {
+    static char logbuf[BUFSIZ];
+#if SETVBUF_REVERSED
+    setvbuf(stderr, _IOLBF, logbuf, sizeof(logbuf));
+#else
+    setvbuf(stderr, logbuf, _IOLBF, sizeof(logbuf));
+#endif
+  }
+#endif
+
+  for (fd = 3; fd < MAXCONNECTIONS; fd++)
+  {
+    close(fd);
+    loc_clients[fd] = NULL;
+  }
+  loc_clients[1] = NULL;
+  close(1);
+
+  if (bootopt & BOOT_TTY)      /* debugging is going to a tty */
+    goto init_dgram;
+  if (!(bootopt & BOOT_DEBUG))
+    close(2);
+
+  if (((bootopt & BOOT_CONSOLE) || isatty(0)) &&
+      !(bootopt & (BOOT_INETD | BOOT_OPER)))
+  {
+    if (fork())
+      exit(0);
+    running_in_background = 1;
+#ifdef TIOCNOTTY
+    if ((fd = open("/dev/tty", O_RDWR)) >= 0)
+    {
+      ioctl(fd, TIOCNOTTY, (char *)NULL);
+      close(fd);
+    }
+#endif
+#if defined(HPUX) || defined(SOL2) || defined(_SEQUENT_) || \
+    defined(_POSIX_SOURCE) || defined(SVR4)
+    setsid();
+#else
+    setpgid(0, 0);
+#endif
+    close(0);                  /* fd 0 opened by inetd */
+    loc_clients[0] = NULL;
+  }
+init_dgram:
+  resfd = init_resolver();
+
+  return;
+}
+
+void write_pidfile(void)
+{
+#ifdef PPATH
+  int fd;
+  char buff[20];
+  if ((fd = open(PPATH, O_CREAT | O_WRONLY, 0600)) >= 0)
+  {
+    memset(buff, 0, sizeof(buff));
+    sprintf(buff, "%5d\n", (int)getpid());
+    if (write(fd, buff, strlen(buff)) == -1)
+      Debug((DEBUG_NOTICE, "Error writing to pid file %s", PPATH));
+    close(fd);
+    return;
+  }
+#ifdef DEBUGMODE
+  else
+    Debug((DEBUG_NOTICE, "Error opening pid file \"%s\": %s",
+       PPATH, strerror(errno)));
+#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(aClient *cptr, char *sockn)
+{
+  struct sockaddr_in sk;
+  size_t len = sizeof(struct sockaddr_in);
+  sockn[HOSTLEN] = 0;
+
+#ifdef UNIXPORT
+  if (IsUnixSocket(cptr))
+  {
+    strncpy(sockn, cptr->acpt->sockhost, HOSTLEN);
+    get_sockhost(cptr, sockn);
+    return 0;
+  }
+#endif
+
+  /* If descriptor is a tty, special checking... */
+  if (isatty(cptr->fd))
+  {
+    strncpy(sockn, me.sockhost, HOSTLEN);
+    memset(&sk, 0, sizeof(struct sockaddr_in));
+  }
+  else if (getpeername(cptr->fd, (struct sockaddr *)&sk, &len) == -1)
+  {
+    report_error("connect failure: %s %s", cptr);
+    return -1;
+  }
+  strcpy(sockn, inetntoa(sk.sin_addr));
+  if (inet_netof(sk.sin_addr) == IN_LOOPBACKNET)
+  {
+    cptr->hostp = NULL;
+    strncpy(sockn, me.sockhost, HOSTLEN);
+  }
+  memcpy(&cptr->ip, &sk.sin_addr, sizeof(struct in_addr));
+#ifdef TESTNET
+  cptr->port = ntohs(sk.sin_port) - 10000;
+#else
+  cptr->port = ntohs(sk.sin_port);
+#endif
+
+  return 0;
+}
+
+/*
+ * Ordinary client access check. Look for conf lines which have the same
+ * status as the flags passed.
+ */
+enum AuthorizationCheckResult check_client(aClient *cptr)
+{
+  static char sockname[HOSTLEN + 1];
+  Reg2 struct hostent *hp = NULL;
+  Reg3 int i;
+  enum AuthorizationCheckResult acr;
+
+  ClearAccess(cptr);
+  Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
+      cptr->name, inetntoa(cptr->ip)));
+
+  if (check_init(cptr, sockname))
+    return ACR_BAD_SOCKET;
+
+  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 (!memcmp(hp->h_addr_list[i], &cptr->ip, sizeof(struct in_addr)))
+       break;
+    if (!hp->h_addr_list[i])
+    {
+      sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%08x]",
+         inetntoa(cptr->ip), hp->h_name, *((unsigned int *)hp->h_addr));
+      hp = NULL;
+    }
+  }
+
+  if ((acr = attach_Iline(cptr, hp, sockname)))
+  {
+    Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", cptr->name, sockname));
+    return acr;
+  }
+
+  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 ACR_OK;
+}
+
+#define CFLAG  CONF_CONNECT_SERVER
+#define NFLAG  CONF_NOCONNECT_SERVER
+
+/*
+ * 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.
+ *
+ * Returns
+ *  0 = Success
+ * -1 = Access denied
+ * -2 = Bad socket.
+ */
+int check_server(aClient *cptr)
+{
+  Reg1 const char *name;
+  Reg2 aConfItem *c_conf = NULL, *n_conf = NULL;
+  struct hostent *hp = NULL;
+  Link *lp;
+  char abuff[HOSTLEN + USERLEN + 2];
+  char sockname[HOSTLEN + 1], fullname[HOSTLEN + 1];
+  int i;
+
+  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;
+    }
+  }
+#ifdef UNIXPORT
+  if (IsUnixSocket(cptr))
+  {
+    if (!c_conf)
+      c_conf = find_conf(lp, name, CFLAG);
+    if (!n_conf)
+      n_conf = find_conf(lp, name, NFLAG);
+  }
+#endif
+
+  /*
+   * 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)
+  {
+    Reg1 aConfItem *aconf;
+
+    aconf = count_cnlines(lp);
+    if (aconf)
+    {
+      Reg1 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 = strchr(aconf->host, '@')))
+       s++;
+      else
+       s = aconf->host;
+      Debug((DEBUG_DNS, "sv_ci:cache lookup (%s)", s));
+      hp = gethost_byname(s, &lin);
+    }
+  }
+
+  lp = cptr->confs;
+
+  ClearAccess(cptr);
+  if (check_init(cptr, sockname))
+    return -2;
+
+check_serverback:
+  if (hp)
+  {
+    for (i = 0; hp->h_addr_list[i]; i++)
+      if (!memcmp(hp->h_addr_list[i], &cptr->ip, sizeof(struct in_addr)))
+       break;
+    if (!hp->h_addr_list[i])
+    {
+      sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%08x]",
+         inetntoa(cptr->ip), hp->h_name, *((unsigned int *)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++])
+    {
+      strncpy(fullname, name, sizeof(fullname) - 1);
+      fullname[sizeof(fullname) - 1] = 0;
+      add_local_domain(fullname, HOSTLEN - strlen(fullname));
+      Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s", sockname, fullname));
+      sprintf_irc(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))
+  {
+    sprintf_irc(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 %p n %p",
+       name, cptr->username, cptr->sockhost, c_conf, n_conf));
+    return -1;
+  }
+  /*
+   * attach the C and N lines to the client structure for later use.
+   */
+  attach_conf(cptr, n_conf);
+  attach_conf(cptr, c_conf);
+  attach_confs(cptr, name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
+
+  if ((c_conf->ipnum.s_addr == INADDR_NONE) && !IsUnixSocket(cptr))
+    memcpy(&c_conf->ipnum, &cptr->ip, 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));
+  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(aClient *cptr)
+{
+  aConfItem *aconf;
+  time_t newts;
+  aClient *acptr;
+  int i;
+
+  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;
+  }
+  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;
+  }
+  make_server(cptr);
+  /* Create a unique timestamp */
+  newts = TStime();
+  for (i = highest_fd; i >= 0; i--)
+  {
+    if (!(acptr = loc_clients[i]) || (!IsServer(acptr) && !IsHandshake(acptr)))
+      continue;
+    if (acptr->serv->timestamp >= newts)
+      newts = acptr->serv->timestamp + 1;
+  }
+  cptr->serv->timestamp = newts;
+  SetHandshake(cptr);
+  /* Make us timeout after twice the timeout for DNS look ups */
+  cptr->lasttime = now;
+  cptr->flags |= FLAGS_PINGSENT;
+  sendto_one(cptr, "SERVER %s 1 " TIME_T_FMT " " TIME_T_FMT " J%s %s%s :%s",
+      my_name_for_link(me.name, aconf), me.serv->timestamp,
+      newts, MAJOR_PROTOCOL, NumServCap(&me), me.info);
+  if (!IsDead(cptr))
+    start_auth(cptr);
+
+  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(aClient *cptr)
+{
+  Reg1 aConfItem *aconf;
+  Reg2 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 += now - 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 (IsUser(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 += now - 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.
+   */
+
+  if ((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 = now;
+    aconf->hold += (aconf->hold - cptr->since > HANGONGOODLINK) ?
+       HANGONRETRYDELAY : ConfConFreq(aconf);
+    if (nextconnect > aconf->hold)
+      nextconnect = aconf->hold;
+  }
+
+  if (cptr->authfd >= 0)
+    close(cptr->authfd);
+
+  if (cptr->fd >= 0)
+  {
+    flush_connections(cptr->fd);
+    loc_clients[cptr->fd] = NULL;
+    close(cptr->fd);
+    cptr->fd = -2;
+  }
+
+  DBufClear(&cptr->sendQ);
+  DBufClear(&cptr->recvQ);
+  memset(cptr->passwd, 0, sizeof(cptr->passwd));
+  set_snomask(cptr, 0, SNO_SET);
+  /*
+   * 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 (loc_clients[highest_fd])
+      break;
+
+  det_confs_butmask(cptr, 0);
+
+  /*
+   * fd remap to keep loc_clients[i] filled at the bottom.
+   */
+  if (empty > 0)
+    if ((j = highest_fd) > (i = empty) && !IsLog(loc_clients[j]))
+    {
+      if (IsListening(loc_clients[j]))
+       return;
+      if (dup2(j, i) == -1)
+       return;
+      loc_clients[i] = loc_clients[j];
+      loc_clients[i]->fd = i;
+      loc_clients[j] = NULL;
+      close(j);
+      while (!loc_clients[highest_fd])
+       highest_fd--;
+    }
+
+  return;
+}
+
+/*
+ *  set_sock_opts
+ */
+static void set_sock_opts(int fd, aClient *cptr)
+{
+  size_t 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
+#ifdef 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
+  opt = 8192;
+  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)
+       sprintf(s, "%02x:", *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(aClient *cptr)
+{
+  int errtmp = errno, err = 0;
+  size_t len = sizeof(err);
+#if defined(SO_ERROR) && !defined(SOL2)
+  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(int fd, aClient *cptr)
+{
+  int res;
+#ifndef NBLOCK_SYSV
+  int nonb = 0;
+#endif
+
+  /*
+   * 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;
+}
+
+extern unsigned short server_port;
+
+/*
+ * 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 yet since it doesn't have a name.
+ */
+aClient *add_connection(aClient *cptr, int fd, int type)
+{
+  Link lin;
+  aClient *acptr;
+  aConfItem *aconf = NULL;
+  acptr =
+      make_client(NULL,
+      (cptr->port == server_port) ? STAT_UNKNOWN_SERVER : STAT_UNKNOWN_USER);
+
+  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 (type == ADCON_TTY)       /* If descriptor is a tty,
+                                  special checking... */
+    get_sockhost(acptr, cptr->sockhost);
+  else
+  {
+    Reg1 char *s, *t;
+    struct sockaddr_in addr;
+    size_t 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);
+      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, inetntoa(addr.sin_addr));
+    memcpy(&acptr->ip, &addr.sin_addr, sizeof(struct in_addr));
+#ifdef TESTNET
+    acptr->port = ntohs(addr.sin_port) - 10000;
+#else
+    acptr->port = ntohs(addr.sin_port);
+#endif
+
+    /*
+     * Check that this socket (client) is allowed to accept
+     * connections from this IP#.
+     */
+    for (s = (char *)&cptr->ip, t = (char *)&acptr->ip, len = 4;
+       len > 0; len--, s++, t++)
+    {
+      if (!*s)
+       continue;
+      if (*s != *t)
+       break;
+    }
+
+    if (len)
+      goto add_con_refuse;
+
+    lin.flags = ASYNC_CLIENT;
+    lin.value.cptr = acptr;
+#ifdef NODNS
+    if (!strcmp("127.0.0.1", inetntoa(addr.sin_addr)))
+    {
+      static struct hostent lhe = { "localhost", NULL, 0, 0, NULL };
+      acptr->hostp = &lhe;
+      if (!DoingAuth(acptr))
+       SetAccess(acptr);
+    }
+    else
+    {
+#endif
+      Debug((DEBUG_DNS, "lookup %s", inetntoa(addr.sin_addr)));
+      acptr->hostp = gethost_byaddr(&acptr->ip, &lin);
+      if (!acptr->hostp)
+       SetDNS(acptr);
+      nextdnscheck = 1;
+#ifdef NODNS
+    }
+#endif
+  }
+
+  if (aconf)
+    aconf->clients++;
+  acptr->fd = fd;
+  if (fd > highest_fd)
+    highest_fd = fd;
+  loc_clients[fd] = acptr;
+  acptr->acpt = cptr;
+  Count_newunknown(nrof);
+  add_client_to_list(acptr);
+  set_non_blocking(acptr->fd, acptr);
+  set_sock_opts(acptr->fd, acptr);
+
+  /*
+   * Add this local client to the IPcheck registry.
+   * If it is a connection to a user port and if the site has been throttled,
+   * reject the user.
+   */
+  if (IPcheck_local_connect(acptr) == -1 && IsUserPort(acptr))
+  {
+    ircstp->is_ref++;
+    exit_client(cptr, acptr, &me,
+       "Your host is trying to (re)connect too fast -- throttled");
+    return NULL;
+  }
+
+  start_auth(acptr);
+  return acptr;
+}
+
+#ifdef UNIXPORT
+static void add_unixconnection(aClient *cptr, int fd)
+{
+  aClient *acptr;
+  aConfItem *aconf = NULL;
+
+  acptr = make_client(NULL, STAT_UNKNOWN);
+
+  /*
+   * Copy ascii address to 'sockhost' just in case. Then we
+   * have something valid to put into error messages...
+   */
+  get_sockhost(acptr, me.sockhost);
+  if (cptr != &me)
+    aconf = cptr->confs->value.aconf;
+  if (aconf)
+  {
+    if (IsIllegal(aconf))
+    {
+      ircstp->is_ref++;
+      acptr->fd = -2;
+      free_client(acptr);
+      close(fd);
+      return;
+    }
+    else
+      aconf->clients++;
+  }
+  acptr->fd = fd;
+  if (fd > highest_fd)
+    highest_fd = fd;
+  loc_clients[fd] = acptr;
+  acptr->acpt = cptr;
+  SetUnixSock(acptr);
+  memcpy(&acptr->ip, &me.ip, sizeof(struct in_addr));
+
+  Count_newunknown(nrof);
+  add_client_to_list(acptr);
+  set_non_blocking(acptr->fd, acptr);
+  set_sock_opts(acptr->fd, acptr);
+  SetAccess(acptr);
+  return;
+}
+#endif
+
+/*
+ * select/poll convert macro's by Run.
+ *
+ * The names are chosen to reflect what they means when NOT using poll().
+ */
+#ifndef USE_POLL
+typedef fd_set *fd_setp_t;
+#define RFD_ISSET(fd, rfd, index) FD_ISSET((fd), (rfd))
+#define WFD_ISSET(fd, wfd, index) FD_ISSET((fd), (wfd))
+#define RFD_SET(fd, rfd, index, cptr) FD_SET((fd), (rfd))
+#define WFD_SET(fd, wfd, index, cptr) FD_SET((fd), (wfd))
+#define RWFD_SET(fd, wfd, index) FD_SET((fd), (wfd))
+#define RFD_CLR_OUT(fd, rfd, index) FD_CLR((fd), (rfd))
+#define WFD_CLR_OUT(fd, wfd, index) FD_CLR((fd), (wfd))
+#define LOC_FD(index) (index)
+#define LOC_CLIENTS(index) loc_clients[index]
+#define HIGHEST_INDEX highest_fd
+#else /* USE_POLL */
+typedef unsigned int fd_setp_t;        /* Actually, an index to poll_fds[] */
+#ifdef _AIX
+#define POLLREADFLAGS (POLLIN|POLLMSG)
+#else
+#  if defined(POLLMSG) && defined(POLLIN) && defined(POLLRDNORM)
+#    define POLLREADFLAGS (POLLMSG|POLLIN|POLLRDNORM)
+#  else
+#    if defined(POLLIN) && defined(POLLRDNORM)
+#      define POLLREADFLAGS (POLLIN|POLLRDNORM)
+#    else
+#      if defined(POLLIN)
+#        define POLLREADFLAGS POLLIN
+#      else
+#        if defined(POLLRDNORM)
+#          define POLLREADFLAGS POLLRDNORM
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+#if defined(POLLOUT) && defined(POLLWRNORM)
+#define POLLWRITEFLAGS (POLLOUT|POLLWRNORM)
+#else
+#  if defined(POLLOUT)
+#    define POLLWRITEFLAGS POLLOUT
+#  else
+#    if defined(POLLWRNORM)
+#      define POLLWRITEFLAGS POLLWRNORM
+#    endif
+#  endif
+#endif
+#ifdef POLLHUP
+#define POLLERRORS (POLLHUP|POLLERR)
+#else
+#define POLLERRORS POLLERR
+#endif
+#define RFD_ISSET(fd, rfd, index) \
+  ((poll_fds[index].revents & POLLREADFLAGS) || \
+  ((poll_fds[index].events & POLLREADFLAGS) && \
+    (poll_fds[index].revents & POLLERRORS)))
+#define WFD_ISSET(fd, wfd, index) \
+  ((poll_fds[index].revents & POLLWRITEFLAGS) || \
+  ((poll_fds[index].events & POLLWRITEFLAGS) && \
+    (poll_fds[index].revents & POLLERRORS)))
+#define RFD_SET(fdes, rfd, index, cptr) \
+  do { \
+    poll_fds[index].fd = fdes; \
+    poll_cptr[index] = cptr; \
+    poll_fds[index].events = POLLREADFLAGS; \
+    added = TRUE; \
+  } while(0)
+#define WFD_SET(fdes, wfd, index, cptr) \
+  do { \
+    poll_fds[index].fd = fdes; \
+    poll_cptr[index] = cptr; \
+    if (added) \
+      poll_fds[index].events |= POLLWRITEFLAGS; \
+    else \
+    { \
+      poll_fds[index].events = POLLWRITEFLAGS; \
+      added = TRUE; \
+    } \
+  } while(0)
+/* This identical to WFD_SET() when used after a call to RFD_SET(): */
+#define RWFD_SET(fd, wfd, index) poll_fds[index].events |= POLLWRITEFLAGS
+/* [RW]FD_CLR_OUT() clears revents, not events */
+#define RFD_CLR_OUT(fd, rfd, index) poll_fds[index].revents &= ~POLLREADFLAGS
+#define WFD_CLR_OUT(fd, wfd, index) poll_fds[index].revents &= ~POLLWRITEFLAGS
+#define LOC_FD(index) (poll_fds[index].fd)
+#define LOC_CLIENTS(index) (poll_cptr[index])
+#define HIGHEST_INDEX (currfd_index - 1)
+#endif /* USE_POLL */
+
+/*
+ * 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(aClient *cptr, fd_setp_t rfd)
+{
+  size_t dolen = 0;
+  int length = 0;
+  int done;
+
+  if (RFD_ISSET(cptr->fd, rfd, rfd) &&
+      !(IsUser(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))
+  {
+    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))
+      return exit_client(cptr, cptr, &me, "dbuf_put fail");
+
+#ifndef NOFLOODCONTROL
+    if (IsUser(cptr) && DBufLength(&cptr->recvQ) > CLIENT_FLOOD)
+      return exit_client(cptr, cptr, &me, "Excess Flood");
+#endif
+
+    while (DBufLength(&cptr->recvQ) && !NoNewLine(cptr)
+#ifndef NOFLOODCONTROL
+       && (IsTrusted(cptr) || cptr->since - now < 10)
+#endif
+       )
+    {
+      /*
+       * If it has become registered as a Server
+       * then skip the per-message parsing below.
+       */
+      if (IsServer(cptr))
+      {
+       /*
+        * XXX - this blindly deletes data if no cr/lf is received at
+        * the end of a lot of messages and the data stored in the 
+        * dbuf is greater than sizeof(readbuf)
+        */
+       dolen = dbuf_get(&cptr->recvQ, readbuf, sizeof(readbuf));
+       if (0 == dolen)
+         break;
+       if ((done = dopacket(cptr, readbuf, dolen)))
+         return done;
+       break;
+      }
+      dolen = dbuf_getmsg(&cptr->recvQ, cptr->buffer, BUFSIZE);
+      /*
+       * 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
+       */
+      if (0 == dolen)
+      {
+       if (DBufLength(&cptr->recvQ) < 510)
+       {
+         cptr->flags |= FLAGS_NONL;
+         break;
+       }
+       DBufClear(&cptr->recvQ);
+       break;
+      }
+      else if (CPTR_KILLED == client_dopacket(cptr, dolen))
+       return CPTR_KILLED;
+    }
+  }
+  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.
+ *
+ * Don't ever use ZERO for `delay', unless you mean to poll and then
+ * you have to have sleep/wait somewhere else in the code.--msa
+ */
+int read_message(time_t delay)
+{
+  Reg1 aClient *cptr;
+  Reg2 int nfds;
+  struct timeval wait;
+#ifdef pyr
+  struct timeval nowt;
+  unsigned long us;
+#endif
+  time_t delay2 = delay;
+  unsigned long usec = 0;
+  int res, length, fd, i;
+  int auth = 0, ping = 0;
+#ifndef USE_POLL
+  fd_set read_set, write_set;
+#else /* USE_POLL */
+  unsigned int currfd_index = 0;
+  unsigned int udpfdindex = 0;
+  unsigned int resfdindex = 0;
+  unsigned long timeout;
+  int added;
+#endif /* USE_POLL */
+
+#ifdef pyr
+  gettimeofday(&nowt, NULL);
+  now = nowt.tv_sec;
+#endif
+
+  for (res = 0;;)
+  {
+#ifndef USE_POLL
+    FD_ZERO(&read_set);
+    FD_ZERO(&write_set);
+#endif /* not USE_POLL */
+    for (i = highest_fd; i >= 0; i--)
+    {
+#ifdef USE_POLL
+      added = FALSE;
+#endif /* USE_POLL */
+      if (!(cptr = loc_clients[i]))
+       continue;
+      if (IsLog(cptr))
+       continue;
+      if (DoingAuth(cptr))
+      {
+       auth++;
+       Debug((DEBUG_NOTICE, "auth on %p %d", cptr, i));
+       RFD_SET(cptr->authfd, &read_set, currfd_index, cptr);
+       if (cptr->flags & FLAGS_WRAUTH)
+         RWFD_SET(cptr->authfd, &write_set, currfd_index);
+      }
+      if (IsPing(cptr))
+      {
+       ping++;
+       Debug((DEBUG_NOTICE, "open ping on %p %d", cptr, i));
+       if (!cptr->firsttime || now <= cptr->firsttime)
+       {
+         RFD_SET(i, &read_set, currfd_index, cptr);
+         delay2 = 1;
+         if (DoPing(cptr) && now > cptr->lasttime)
+           RWFD_SET(i, &write_set, currfd_index);
+       }
+       else
+       {
+         del_queries((char *)cptr);
+         end_ping(cptr);
+       }
+#ifdef USE_POLL
+       if (added)
+         currfd_index++;
+#endif /* USE_POLL */
+       continue;
+      }
+      if (DoingDNS(cptr) || DoingAuth(cptr))
+      {
+#ifdef USE_POLL
+       if (added)
+         currfd_index++;
+#endif /* USE_POLL */
+       continue;
+      }
+      if (IsMe(cptr) && IsListening(cptr))
+       RFD_SET(i, &read_set, currfd_index, cptr);
+      else if (!IsMe(cptr))
+      {
+       if (DBufLength(&cptr->recvQ) && delay2 > 2)
+         delay2 = 1;
+       if (DBufLength(&cptr->recvQ) < 4088)
+         RFD_SET(i, &read_set, currfd_index, cptr);
+       if (DBufLength(&cptr->sendQ) || IsConnecting(cptr) ||
+           (cptr->listing && DBufLength(&cptr->sendQ) < 2048))
+#ifndef pyr
+         WFD_SET(i, &write_set, currfd_index, cptr);
+#else /* pyr */
+       {
+         if (!(cptr->flags & FLAGS_BLOCKED))
+           WFD_SET(i, &write_set, currfd_index, cptr);
+         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)
+         cptr->flags &= ~FLAGS_BLOCKED;
+#endif /* pyr */
+      }
+#ifdef USE_POLL
+      if (added)
+       currfd_index++;
+#endif /* USE_POLL */
+    }
+
+    if (udpfd >= 0)
+    {
+      RFD_SET(udpfd, &read_set, currfd_index, NULL);
+#ifdef USE_POLL
+      udpfdindex = currfd_index;
+      currfd_index++;
+#endif /* USE_POLL */
+    }
+    if (resfd >= 0)
+    {
+      RFD_SET(resfd, &read_set, currfd_index, NULL);
+#ifdef USE_POLL
+      resfdindex = currfd_index;
+      currfd_index++;
+#endif /* USE_POLL */
+    }
+
+    wait.tv_sec = MIN(delay2, delay);
+    wait.tv_usec = usec;
+#ifndef USE_POLL
+#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
+#else /* USE_POLL */
+    timeout = (wait.tv_sec * 1000) + (wait.tv_usec / 1000);
+    nfds = poll(poll_fds, currfd_index, timeout);
+#endif /* USE_POLL */
+    now = time(NULL);
+    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);
+    now += 10;
+  }
+
+  if (udpfd >= 0 && RFD_ISSET(udpfd, &read_set, udpfdindex))
+  {
+    polludp();
+    nfds--;
+    RFD_CLR_OUT(udpfd, &read_set, udpfdindex);
+  }
+  /*
+   * 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_INDEX; (ping > 0) && (i >= 0); i--)
+  {
+    if (!(cptr = LOC_CLIENTS(i)))
+      continue;
+    if (!IsPing(cptr))
+      continue;
+    ping--;
+    if ((nfds > 0) && RFD_ISSET(cptr->fd, &read_set, i))
+    {
+      nfds--;
+      RFD_CLR_OUT(cptr->fd, &read_set, i);
+      read_ping(cptr);         /* This can RunFree(cptr) ! */
+    }
+    else if ((nfds > 0) && WFD_ISSET(cptr->fd, &write_set, i))
+    {
+      nfds--;
+      cptr->lasttime = now;
+      WFD_CLR_OUT(cptr->fd, &write_set, i);
+      send_ping(cptr);         /* This can RunFree(cptr) ! */
+    }
+  }
+  if (resfd >= 0 && RFD_ISSET(resfd, &read_set, resfdindex))
+  {
+    do_dns_async();
+    nfds--;
+    RFD_CLR_OUT(resfd, &read_set, resfdindex);
+  }
+  /*
+   * Check fd sets for the auth fd's (if set and valid!) first
+   * because these can not be processed using the normal loops below.
+   * -avalon
+   */
+  for (i = HIGHEST_INDEX; (auth > 0) && (i >= 0); i--)
+  {
+    if (!(cptr = LOC_CLIENTS(i)))
+      continue;
+    if (cptr->authfd < 0)
+      continue;
+    auth--;
+    if ((nfds > 0) && WFD_ISSET(cptr->authfd, &write_set, i))
+    {
+      nfds--;
+      send_authports(cptr);
+    }
+    else if ((nfds > 0) && RFD_ISSET(cptr->authfd, &read_set, i))
+    {
+      nfds--;
+      read_authports(cptr);
+    }
+  }
+  for (i = HIGHEST_INDEX; i >= 0; i--)
+    if ((cptr = LOC_CLIENTS(i)) && RFD_ISSET(i, &read_set, i) &&
+       IsListening(cptr))
+    {
+      RFD_CLR_OUT(i, &read_set, i);
+      nfds--;
+      cptr->lasttime = now;
+      /*
+       * 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(LOC_FD(i), NULL, NULL)) < 0)
+      {
+       if (errno != EWOULDBLOCK)
+         report_error("accept() failed%s: %s", NULL);
+       break;
+      }
+#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECTS)
+      {                                /* get an early log of all connections   --dl */
+       static struct sockaddr_in peer;
+       static int len;
+       len = sizeof(peer);
+       getpeername(fd, (struct sockaddr *)&peer, &len);
+       syslog(LOG_DEBUG, "Conn: %s", inetntoa(peer.sin_addr));
+      }
+#endif
+      ircstp->is_ac++;
+      if (fd >= MAXCLIENTS)
+      {
+       /* Don't send more messages then one every 10 minutes */
+       static int count;
+       static time_t last_time;
+       ircstp->is_ref++;
+       ++count;
+       if (last_time < now - (time_t) 600)
+       {
+         if (count > 0)
+         {
+           if (!last_time)
+             last_time = me.since;
+           sendto_ops
+               ("All connections in use!  Had to refuse %d clients in the last "
+               STIME_T_FMT " minutes", count, (now - last_time) / 60);
+         }
+         else
+           sendto_ops("All connections in use. (%s)", get_client_name(cptr,
+               TRUE));
+         count = 0;
+         last_time = now;
+       }
+       send(fd, "ERROR :All connections in use\r\n", 32, 0);
+       close(fd);
+       break;
+      }
+      /*
+       * Use of add_connection (which never fails :) meLazy
+       */
+#ifdef UNIXPORT
+      if (IsUnixSocket(cptr))
+       add_unixconnection(cptr, fd);
+      else
+#endif
+      if (!add_connection(cptr, fd, ADCON_SOCKET))
+       continue;
+      nextping = now;
+      if (!cptr->acpt)
+       cptr->acpt = &me;
+    }
+
+  for (i = HIGHEST_INDEX; i >= 0; i--)
+  {
+    if (!(cptr = LOC_CLIENTS(i)) || IsMe(cptr))
+      continue;
+#ifdef USE_POLL
+    if (DoingDNS(cptr) || DoingAuth(cptr) || !(cptr = loc_clients[LOC_FD(i)]))
+      continue;
+#endif /* USE_POLL */
+#ifdef DEBUGMODE
+    if (IsLog(cptr))
+      continue;
+#endif
+    if (WFD_ISSET(i, &write_set, i))
+    {
+      int write_err = 0;
+      nfds--;
+      /*
+       *  ...room for writing, empty some queue then...
+       */
+      cptr->flags &= ~FLAGS_BLOCKED;
+      if (IsConnecting(cptr))
+       write_err = completed_connection(cptr);
+      if (!write_err)
+      {
+       if (cptr->listing && DBufLength(&cptr->sendQ) < 2048)
+         list_next_channels(cptr, 64);
+       send_queued(cptr);
+      }
+      if (IsDead(cptr) || write_err)
+      {
+      deadsocket:
+       if (RFD_ISSET(i, &read_set, i))
+       {
+         nfds--;
+         RFD_CLR_OUT(i, &read_set, i);
+       }
+       exit_client(cptr, cptr, &me,
+           IsDead(cptr) ? LastDeadComment(cptr) : strerror(get_sockerr(cptr)));
+       continue;
+      }
+    }
+    length = 1;                        /* for fall through case */
+    if ((!NoNewLine(cptr) || RFD_ISSET(i, &read_set, i)) && !IsDead(cptr))
+#ifndef USE_POLL
+      length = read_packet(cptr, &read_set);
+#else /* USE_POLL */
+      length = read_packet(cptr, i);
+#endif /* USE_POLL */
+#if 0
+    /* Bullshit, why would we want to flush sockets while using non-blocking?
+     * This uses > 4% cpu! --Run */
+    if (length > 0)
+      flush_connections(LOC_FD(i));
+#endif
+    if ((length != CPTR_KILLED) && IsDead(cptr))
+      goto deadsocket;
+    if (!RFD_ISSET(i, &read_set, i) && 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", LOC_FD(i), errno, length));
+
+    if (length == CPTR_KILLED)
+      continue;
+
+    if ((IsServer(cptr) || IsHandshake(cptr)) && errno == 0 && length == 0)
+      exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)",
+         get_client_name(cptr, FALSE), cptr->serv->last_error_msg);
+    else
+      exit_client_msg(cptr, cptr, &me, "Read error to %s: %s",
+         get_client_name(cptr, FALSE), (length < 0) ?
+         strerror(get_sockerr(cptr)) : "EOF from client");
+  }
+  return 0;
+}
+
+/*
+ * connect_server
+ */
+int connect_server(aConfItem *aconf, aClient *by, struct hostent *hp)
+{
+  Reg1 struct sockaddr *svp;
+  Reg2 aClient *cptr, *c2ptr;
+  Reg3 char *s;
+  int errtmp, len;
+
+  Debug((DEBUG_NOTICE, "Connect to %s[%s] @%s",
+      aconf->name, aconf->host, inetntoa(aconf->ipnum)));
+
+  if ((c2ptr = FindClient(aconf->name)))
+  {
+    if (IsServer(c2ptr) || IsMe(c2ptr))
+    {
+      sendto_ops("Server %s already present from %s",
+         aconf->name, c2ptr->from->name);
+      if (by && IsUser(by) && !MyUser(by))
+      {
+#ifndef NO_PROTOCOL9
+       if (Protocol(by->from) < 10)
+         sendto_one(by, ":%s NOTICE %s :Server %s already present from %s",
+             me.name, by->name, aconf->name, c2ptr->from->name);
+       else
+#endif
+         sendto_one(by, "%s NOTICE %s%s :Server %s already present from %s",
+             NumServ(&me), NumNick(by), aconf->name, c2ptr->from->name);
+      }
+      return -1;
+    }
+    else if (IsHandshake(c2ptr) || IsConnecting(c2ptr))
+    {
+      if (by && IsUser(by))
+      {
+       if (MyUser(by) || Protocol(by->from) < 10)
+         sendto_one(by, ":%s NOTICE %s :Connection to %s already in progress",
+             me.name, by->name, get_client_name(c2ptr, TRUE));
+       else
+         sendto_one(by,
+             "%s NOTICE %s%s :Connection to %s already in progress",
+             NumServ(&me), NumNick(by), 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)
+#ifdef UNIXPORT
+      && ((aconf->host[2]) != '/')     /* needed for Unix domain -- dl */
+#endif
+      )
+  {
+    Link lin;
+
+    lin.flags = ASYNC_CONNECT;
+    lin.value.aconf = aconf;
+    nextdnscheck = 1;
+    s = strchr(aconf->host, '@');
+    s++;                       /* should NEVER be NULL */
+    if ((aconf->ipnum.s_addr = inet_addr(s)) == INADDR_NONE)
+    {
+      aconf->ipnum.s_addr = INADDR_ANY;
+      hp = gethost_byname(s, &lin);
+      Debug((DEBUG_NOTICE, "co_sv: hp %p ac %p na %s ho %s",
+         hp, aconf, aconf->name, s));
+      if (!hp)
+       return 0;
+      memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+    }
+  }
+  cptr = make_client(NULL, STAT_UNKNOWN);
+  cptr->hostp = hp;
+  /*
+   * Copy these in so we have something for error detection.
+   */
+  strncpy(cptr->name, aconf->name, sizeof(cptr->name) - 1);
+  cptr->name[sizeof(cptr->name) - 1] = 0;
+  strncpy(cptr->sockhost, aconf->host, HOSTLEN);
+  cptr->sockhost[HOSTLEN] = 0;
+
+#ifdef UNIXPORT
+  if (aconf->host[2] == '/')   /* (/ starts a 2), Unix domain -- dl */
+    svp = connect_unix(aconf, cptr, &len);
+  else
+    svp = connect_inet(aconf, cptr, &len);
+#else
+  svp = connect_inet(aconf, cptr, &len);
+#endif
+
+  if (!svp)
+  {
+    if (cptr->fd >= 0)
+      close(cptr->fd);
+    cptr->fd = -2;
+    if (by && IsUser(by) && !MyUser(by))
+    {
+#ifndef NO_PROTOCOL9
+      if (Protocol(by->from) < 10)
+       sendto_one(by, ":%s NOTICE %s :Couldn't connect to %s",
+           me.name, by->name, get_client_name(cptr, TRUE));
+      else
+#endif
+       sendto_one(by, "%s NOTICE %s%s :Couldn't connect to %s",
+           NumServ(&me), NumNick(by), get_client_name(cptr, TRUE));
+    }
+    free_client(cptr);
+    return -1;
+  }
+
+  set_non_blocking(cptr->fd, cptr);
+  set_sock_opts(cptr->fd, cptr);
+  signal(SIGALRM, dummy);
+  alarm(4);
+  if (connect(cptr->fd, svp, len) < 0 && errno != EINPROGRESS)
+  {
+    int err = get_sockerr(cptr);
+    errtmp = errno;            /* other system calls may eat errno */
+    alarm(0);
+    report_error("Connect to host %s failed: %s", cptr);
+    if (by && IsUser(by) && !MyUser(by))
+    {
+#ifndef NO_PROTOCOL9
+      if (Protocol(by->from) < 10)
+       sendto_one(by, ":%s NOTICE %s :Connect to host %s failed: %s",
+           me.name, by->name, get_client_name(cptr, TRUE), strerror(err));
+      else
+#endif
+       sendto_one(by, "%s NOTICE %s%s :Connect to host %s failed: %s",
+           NumServ(&me), NumNick(by), get_client_name(cptr, TRUE),
+           strerror(err));
+    }
+    close(cptr->fd);
+    cptr->fd = -2;
+    free_client(cptr);
+    errno = errtmp;
+    if (errno == EINTR)
+      errno = ETIMEDOUT;
+    return -1;
+  }
+  alarm(0);
+
+  /*
+   * 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
+   */
+  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 && IsUser(by) && !MyUser(by))
+    {
+#ifndef NO_PROTOCOL9
+      if (Protocol(by->from) < 10)
+       sendto_one(by,
+           ":%s NOTICE %s :Connect to host %s failed: no C/N-lines",
+           me.name, by->name, get_client_name(cptr, TRUE));
+      else
+#endif
+       sendto_one(by,
+           "%s NOTICE %s%s :Connect to host %s failed: no C/N-lines",
+           NumServ(&me), NumNick(by), get_client_name(cptr, TRUE));
+    }
+    det_confs_butmask(cptr, 0);
+    close(cptr->fd);
+    cptr->fd = -2;
+    free_client(cptr);
+    return (-1);
+  }
+  /*
+   * The socket has been connected or connect is in progress.
+   */
+  make_server(cptr);
+  if (by && IsUser(by))
+  {
+    sprintf_irc(cptr->serv->by, "%s%s", NumNick(by));
+    if (cptr->serv->user)
+      free_user(cptr->serv->user, NULL);
+    cptr->serv->user = by->user;
+    by->user->refcnt++;
+  }
+  else
+  {
+    *cptr->serv->by = '\0';
+    if (cptr->serv->user)
+      free_user(cptr->serv->user, NULL);
+    cptr->serv->user = NULL;
+  }
+  cptr->serv->up = &me;
+  if (cptr->fd > highest_fd)
+    highest_fd = cptr->fd;
+  loc_clients[cptr->fd] = cptr;
+  cptr->acpt = &me;
+  SetConnecting(cptr);
+
+  get_sockhost(cptr, aconf->host);
+  Count_newunknown(nrof);
+  add_client_to_list(cptr);
+  hAddClient(cptr);
+  nextping = now;
+
+  return 0;
+}
+
+static struct sockaddr *connect_inet(aConfItem *aconf, aClient *cptr, int *lenp)
+{
+  static struct sockaddr_in server;
+  Reg3 struct hostent *hp;
+
+  /*
+   * Might as well get sockhost from here, the connection is attempted
+   * with it so if it fails its useless.
+   */
+  alarm(2);
+  cptr->fd = socket(AF_INET, SOCK_STREAM, 0);
+  alarm(0);
+  if (cptr->fd == -1 && errno == EAGAIN)
+  {
+    sendto_ops("opening stream socket to server %s: No more sockets",
+       get_client_name(cptr, TRUE));
+    return NULL;
+  }
+  if (cptr->fd == -1)
+  {
+    report_error("opening stream socket to server %s: %s", cptr);
+    return NULL;
+  }
+  if (cptr->fd >= MAXCLIENTS)
+  {
+    sendto_ops("No more connections allowed (%s)", cptr->name);
+    return NULL;
+  }
+  mysk.sin_port = 0;
+  memset(&server, 0, sizeof(server));
+  server.sin_family = AF_INET;
+  get_sockhost(cptr, aconf->host);
+
+#ifdef VIRTUAL_HOST
+  mysk.sin_addr = vserv.sin_addr;
+#endif
+
+  /*
+   * 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
+   */
+#ifdef VIRTUAL_HOST
+  /*
+   * No, we do bind it if we have virtual host support. If we don't
+   * explicitly bind it, it will default to IN_ADDR_ANY and we lose
+   * due to the other server not allowing our base IP --smg
+   */
+  if (bind(cptr->fd, (struct sockaddr *)&mysk, sizeof(mysk)) == -1)
+  {
+    report_error("error binding to local port for %s: %s", cptr);
+    return NULL;
+  }
+#endif
+
+  /*
+   * 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 == INADDR_NONE))
+    aconf->ipnum.s_addr = inet_addr(aconf->host);
+  if (aconf->ipnum.s_addr == INADDR_NONE)
+  {
+    hp = cptr->hostp;
+    if (!hp)
+    {
+      Debug((DEBUG_FATAL, "%s: unknown host", aconf->host));
+      return NULL;
+    }
+    memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+  }
+  memcpy(&server.sin_addr, &aconf->ipnum, sizeof(struct in_addr));
+  memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
+#ifdef TESTNET
+  server.sin_port = htons(((aconf->port > 0) ? aconf->port : portnum) + 10000);
+#else
+  server.sin_port = htons(((aconf->port > 0) ? aconf->port : portnum));
+#endif
+  *lenp = sizeof(server);
+  return (struct sockaddr *)&server;
+}
+
+#ifdef UNIXPORT
+/*
+ * connect_unix
+ *
+ * Build a socket structure for cptr so that it can connet to the unix
+ * socket defined by the conf structure aconf.
+ */
+static struct sockaddr *connect_unix(aConfItem *aconf, aClient *cptr, int *lenp)
+{
+  static struct sockaddr_un sock;
+
+  alarm(2);
+  cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+  alarm(0);
+  if (cptr->fd == -1 && errno == EAGAIN)
+  {
+    sendto_ops("Unix domain connect to host %s failed: No more sockets",
+       get_client_name(cptr, TRUE));
+    return NULL;
+  }
+  if (cptr->fd == -1)
+  {
+    report_error("Unix domain connect to host %s failed: %s", cptr);
+    return NULL;
+  }
+  else if (cptr->fd >= MAXCLIENTS)
+  {
+    sendto_ops("No more connections allowed (%s)", cptr->name);
+    return NULL;
+  }
+
+  get_sockhost(cptr, aconf->host);
+  /* +2 needed for working Unix domain -- dl */
+  strncpy(sock.sun_path, aconf->host + 2, sizeof(sock.sun_path) - 1);
+  sock.sun_path[sizeof(sock.sun_path) - 1] = 0;
+  sock.sun_family = AF_UNIX;
+  *lenp = strlen(sock.sun_path) + 2;
+
+  SetUnixSock(cptr);
+  return (struct sockaddr *)&sock;
+}
+
+#endif
+
+/*
+ * Find the real hostname for the host running the server (or one which
+ * matches the server's name) and its primary IP#.  Hostname is stored
+ * in the client structure passed as a pointer.
+ */
+void get_my_name(aClient *cptr, char *name, size_t len)
+{
+  static char tmp[HOSTLEN + 1];
+#if HAVE_UNAME
+  struct utsname utsn;
+#endif
+  struct hostent *hp;
+  char *cname = cptr->name;
+  size_t len2;
+
+  /*
+   * Setup local socket structure to use for binding to.
+   */
+  memset(&mysk, 0, sizeof(mysk));
+  mysk.sin_family = AF_INET;
+
+#if HAVE_UNAME
+  if (uname(&utsn) == -1)
+    return;
+  len2 = strlen(utsn.nodename);
+  if (len2 > len)
+    len2 = len;
+  strncpy(name, utsn.nodename, len2);
+#else /* HAVE_GETHOSTNAME */
+  if (gethostname(name, len) == -1)
+    return;
+#endif
+  name[len] = '\0';
+
+  /* Assume that a name containing '.' is a FQDN */
+  if (!strchr(name, '.'))
+    add_local_domain(name, len - strlen(name));
+
+  /*
+   * If hostname gives another name than cname, then check if there is
+   * a CNAME record for cname pointing to hostname. If so accept
+   * cname as our name.   meLazy
+   */
+  if (BadPtr(cname))
+    return;
+  if (
+#ifndef NODNS
+      /* I don't have DNS while testing, this delays too much */
+      (hp = gethostbyname(cname)) ||
+#endif
+      (hp = gethostbyname(name)))
+  {
+    const char *hname;
+    int i = 0;
+
+    for (hname = hp->h_name; hname; hname = hp->h_aliases[i++])
+    {
+      strncpy(tmp, hname, sizeof(tmp) - 1);
+      add_local_domain(tmp, sizeof(tmp) - 1 - strlen(tmp));
+
+      /*
+       * Copy the matching name over and store the
+       * 'primary' IP# as 'myip' which is used
+       * later for making the right one is used
+       * for connecting to other hosts.
+       */
+      if (!strCasediff(me.name, tmp))
+       break;
+    }
+    if (strCasediff(me.name, tmp))
+      strncpy(name, hp->h_name, len);
+    else
+      strncpy(name, tmp, len);
+    memcpy(&mysk.sin_addr, hp->h_addr, sizeof(struct in_addr));
+    Debug((DEBUG_DEBUG, "local name is %s", get_client_name(&me, TRUE)));
+  }
+  return;
+}
+
+/*
+ * Setup a UDP socket and listen for incoming packets
+ */
+int setup_ping(void)
+{
+  struct sockaddr_in from;
+  int on = 1;
+
+  memset(&from, 0, sizeof(from));
+#ifdef VIRTUAL_HOST
+  from.sin_addr = vserv.sin_addr;
+#else
+  from.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
+#ifdef TESTNET
+  from.sin_port = htons(atoi(UDP_PORT) + 10000);
+#else
+  from.sin_port = htons(atoi(UDP_PORT));
+#endif
+  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)));
+    close(udpfd);
+    udpfd = -1;
+    return -1;
+  }
+  on = 0;
+  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)));
+    close(udpfd);
+    udpfd = -1;
+    return -1;
+  }
+  if (fcntl(udpfd, F_SETFL, FNDELAY) == -1)
+  {
+    Debug((DEBUG_ERROR, "fcntl fndelay : %s", strerror(errno)));
+    close(udpfd);
+    udpfd = -1;
+    return -1;
+  }
+  return udpfd;
+}
+
+/*
+ * max # of pings set to 15/sec.
+ */
+static void polludp(void)
+{
+  Reg1 char *s;
+  struct sockaddr_in from;
+  int n;
+  size_t fromlen = sizeof(from);
+  static time_t last = 0;
+  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);
+  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;
+  strcpy(s, me.name);
+  s += strlen(s) + 1;
+  strcpy(s, version);
+  s += strlen(s);
+  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(void)
+{
+  static Link ln;
+  aClient *cptr;
+  aConfItem *aconf;
+  struct hostent *hp;
+
+  ln.flags = ASYNC_NONE;
+  hp = get_res((char *)&ln);
+
+  Debug((DEBUG_DNS, "%p = get_res(%d,%p)", 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);
+       ClearDNS(cptr);
+       if (!DoingAuth(cptr))
+         SetAccess(cptr);
+       cptr->hostp = hp;
+      }
+      break;
+    case ASYNC_CONNECT:
+      aconf = ln.value.aconf;
+      if (hp && aconf)
+      {
+       memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+       connect_server(aconf, NULL, hp);
+      }
+      else
+       sendto_ops("Connect to %s failed: host lookup",
+           (aconf) ? aconf->host : "unknown");
+      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) == -1)
+         end_ping(cptr);
+      }
+      else
+      {
+       sendto_ops("Udp ping to %s failed: host lookup", cptr->sockhost);
+       end_ping(cptr);
+      }
+      break;
+    case ASYNC_CONF:
+      aconf = ln.value.aconf;
+      if (hp && aconf)
+       memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr));
+      break;
+    default:
+      break;
+  }
+}
diff --git a/ircd/s_conf.c b/ircd/s_conf.c
new file mode 100644 (file)
index 0000000..4c5e3f2
--- /dev/null
@@ -0,0 +1,1541 @@
+/*
+ * 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.
+ */
+
+#include "sys.h"
+#include <sys/socket.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#include <sys/stat.h>
+#ifdef R_LINES
+#include <signal.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "opercmds.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "class.h"
+#include "s_misc.h"
+#include "match.h"
+#include "common.h"
+#include "s_err.h"
+#include "s_bsd.h"
+#include "ircd.h"
+#include "crule.h"
+#include "res.h"
+#include "support.h"
+#include "parse.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "IPcheck.h"
+#include "hash.h"
+#include "fileio.h"
+
+RCSTAG_CC("$Id$");
+
+static int check_time_interval(char *, char *);
+static int lookup_confhost(aConfItem *);
+static int is_comment(char *);
+static void killcomment(aClient *sptr, char *parv, char *filename);
+
+aConfItem *conf = NULL;
+aGline *gline = NULL;
+aMotdItem *motd = NULL;
+aMotdItem *rmotd = NULL;
+atrecord *tdata = NULL;
+struct tm motd_tm;
+
+/*
+ * field breakup for ircd.conf file.
+ */
+static char *gfline = NULL;
+char *getfield(char *newline, char fs)
+{
+  char *end, *field;
+
+  if (newline)
+    gfline = newline;
+
+  if (gfline == NULL)
+    return NULL;
+
+  end = field = gfline;
+
+  if (fs != ':')
+  {
+    if (*end == fs)
+      ++end;
+    else
+      fs = ':';
+  }
+  do
+  {
+    while (*end != fs)
+    {
+      if (!*end)
+      {
+       end = NULL;
+       break;
+      }
+      ++end;
+    }
+  }
+  while (end && fs != ':' && *++end != ':' && *end != '\n');
+
+  if (end == NULL)
+  {
+    gfline = NULL;
+    if ((end = strchr(field, '\n')) == NULL)
+      end = field + strlen(field);
+  }
+  else
+    gfline = end + 1;
+
+  *end = '\0';
+
+  return field;
+}
+
+/*
+ * Remove all conf entries from the client except those which match
+ * the status field mask.
+ */
+void det_confs_butmask(aClient *cptr, int mask)
+{
+  Reg1 Link *tmp, *tmp2;
+
+  for (tmp = cptr->confs; tmp; tmp = tmp2)
+  {
+    tmp2 = tmp->next;
+    if ((tmp->value.aconf->status & mask) == 0)
+      detach_conf(cptr, tmp->value.aconf);
+  }
+}
+
+/*
+ * Find the first (best) I line to attach.
+ */
+enum AuthorizationCheckResult attach_Iline(aClient *cptr, struct hostent *hp,
+    char *sockhost)
+{
+  Reg1 aConfItem *aconf;
+  Reg3 const char *hname;
+  Reg4 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)
+      continue;
+    if (hp)
+      for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++])
+      {
+       size_t fullnamelen = 0;
+       size_t label_count = 0;
+       int error;
+
+       strncpy(fullname, hname, HOSTLEN);
+       fullname[HOSTLEN] = 0;
+
+       /*
+        * Disallow a hostname label to contain anything but a [-a-zA-Z0-9].
+        * It may not start or end on a '.'.
+        * A label may not end on a '-', the maximum length of a label is
+        * 63 characters.
+        * On top of that (which seems to be the RFC) we demand that the
+        * top domain does not contain any digits.
+        */
+       error = (*hname == '.') ? 1 : 0;        /* May not start with a '.' */
+       if (!error)
+       {
+         char *p;
+         for (p = fullname; *p; ++p, ++fullnamelen)
+         {
+           if (*p == '.')
+           {
+             if (p[-1] == '-'  /* Label may not end on '-' */
+                 || p[1] == 0) /* May not end on a '.' */
+             {
+               error = 1;
+               break;
+             }
+             label_count = 0;
+             error = 0;        /* Was not top domain */
+             continue;
+           }
+           if (++label_count > 63)     /* Label not longer then 63 */
+           {
+             error = 1;
+             break;
+           }
+           if (*p >= '0' && *p <= '9')
+           {
+             error = 1;        /* In case this is top domain */
+             continue;
+           }
+           if (!(*p >= 'a' && *p <= 'z')
+               && !(*p >= 'A' && *p <= 'Z') && *p != '-')
+           {
+             error = 1;
+             break;
+           }
+         }
+       }
+       if (error)
+       {
+         hp = NULL;
+         break;
+       }
+
+       add_local_domain(fullname, HOSTLEN - fullnamelen);
+       Debug((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname));
+       if (strchr(aconf->name, '@'))
+       {
+         strcpy(uhost, cptr->username);
+         strcat(uhost, "@");
+       }
+       else
+         *uhost = '\0';
+       strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost));
+       uhost[sizeof(uhost) - 1] = 0;
+       if (!match(aconf->name, uhost))
+       {
+         if (strchr(uhost, '@'))
+           cptr->flags |= FLAGS_DOID;
+         goto attach_iline;
+       }
+      }
+
+    if (strchr(aconf->host, '@'))
+    {
+      strncpy(uhost, cptr->username, sizeof(uhost) - 2);
+      uhost[sizeof(uhost) - 2] = 0;
+      strcat(uhost, "@");
+    }
+    else
+      *uhost = '\0';
+    strncat(uhost, sockhost, sizeof(uhost) - 1 - strlen(uhost));
+    uhost[sizeof(uhost) - 1] = 0;
+    if (match(aconf->host, uhost))
+      continue;
+    if (strchr(uhost, '@'))
+      cptr->flags |= FLAGS_DOID;
+    if (hp && hp->h_name)
+    {
+      strncpy(uhost, hp->h_name, HOSTLEN);
+      uhost[HOSTLEN] = 0;
+      add_local_domain(uhost, HOSTLEN - strlen(uhost));
+    }
+  attach_iline:
+    get_sockhost(cptr, uhost);
+
+    if (aconf->passwd)
+    {
+      if (isDigit(*aconf->passwd) && !aconf->passwd[1])        /* Special case: exactly one digit */
+      {
+       /* Refuse connections when there are already <digit> clients connected with the same IP number */
+       unsigned short nr = *aconf->passwd - '0';
+       if (IPcheck_nr(cptr) > nr)
+         return ACR_TOO_MANY_FROM_IP;  /* Already got nr with that ip# */
+      }
+#ifdef USEONE
+      else if (!strcmp(aconf->passwd, "ONE"))
+      {
+       for (i = highest_fd; i >= 0; i--)
+         if (loc_clients[i] && MyUser(loc_clients[i]) &&
+             loc_clients[i]->ip.s_addr == cptr->ip.s_addr)
+           return ACR_TOO_MANY_FROM_IP;        /* Already got one with that ip# */
+      }
+#endif
+    }
+    return attach_conf(cptr, aconf);
+  }
+  return ACR_NO_AUTHORIZATION;
+}
+
+/*
+ * Find the single N line and return pointer to it (from list).
+ * If more than one then return NULL pointer.
+ */
+aConfItem *count_cnlines(Link *lp)
+{
+  Reg1 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.
+ */
+int detach_conf(aClient *cptr, aConfItem *aconf)
+{
+  Reg1 Link **lp, *tmp;
+
+  lp = &(cptr->confs);
+
+  while (*lp)
+  {
+    if ((*lp)->value.aconf == aconf)
+    {
+      if (aconf && (aconf->confClass)
+         && (aconf->status & CONF_CLIENT_MASK) && ConfLinks(aconf) > 0)
+       --ConfLinks(aconf);
+      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(aConfItem *aconf, aClient *cptr)
+{
+  Reg1 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...
+ */
+enum AuthorizationCheckResult attach_conf(aClient *cptr, aConfItem *aconf)
+{
+  Reg1 Link *lp;
+
+  if (is_attached(aconf, cptr))
+    return ACR_ALREADY_AUTHORIZED;
+  if (IsIllegal(aconf))
+    return ACR_NO_AUTHORIZATION;
+  if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT)) &&
+      ConfLinks(aconf) >= ConfMaxLinks(aconf) && ConfMaxLinks(aconf) > 0)
+    return ACR_TOO_MANY_IN_CLASS;      /* 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 ACR_OK;
+}
+
+aConfItem *find_admin(void)
+{
+  Reg1 aConfItem *aconf;
+
+  for (aconf = conf; aconf; aconf = aconf->next)
+    if (aconf->status & CONF_ADMIN)
+      break;
+
+  return (aconf);
+}
+
+aConfItem *find_me(void)
+{
+  Reg1 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(aClient *cptr, const char *name, int statmask)
+{
+  Reg1 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) == ACR_OK && !first)
+       first = tmp;
+    }
+    else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
+       (tmp->status & (CONF_SERVER_MASK | CONF_HUB)) &&
+       tmp->name && !strCasediff(tmp->name, name))
+    {
+      if (attach_conf(cptr, tmp) == ACR_OK && !first)
+       first = tmp;
+    }
+  }
+  return (first);
+}
+
+/*
+ * Added for new access check    meLazy
+ */
+aConfItem *attach_confs_host(aClient *cptr, char *host, int statmask)
+{
+  Reg1 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) == ACR_OK && !first)
+       first = tmp;
+    }
+    else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
+       (tmp->status & CONF_SERVER_MASK) &&
+       (tmp->host && strCasediff(tmp->host, host) == 0))
+    {
+      if (attach_conf(cptr, tmp) == ACR_OK && !first)
+       first = tmp;
+    }
+  }
+  return (first);
+}
+
+/*
+ * find a conf entry which matches the hostname and has the same name.
+ */
+aConfItem *find_conf_exact(char *name, char *user, char *host, int statmask)
+{
+  Reg1 aConfItem *tmp;
+  char userhost[USERLEN + HOSTLEN + 3];
+
+  sprintf_irc(userhost, "%s@%s", user, host);
+
+  for (tmp = conf; tmp; tmp = tmp->next)
+  {
+    if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
+       strCasediff(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(tmp->confClass))
+       return tmp;
+      else
+       continue;
+    }
+    else
+      return tmp;
+  }
+  return NULL;
+}
+
+aConfItem *find_conf(Link *lp, const char *name, int statmask)
+{
+  Reg1 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 && !strCasediff(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(Link *lp, char *host, int statmask)
+{
+  Reg1 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(Link *lp, char *ip, char *user, int statmask)
+{
+  Reg1 aConfItem *tmp;
+  Reg2 char *s;
+
+  for (; lp; lp = lp->next)
+  {
+    tmp = lp->value.aconf;
+    if (!(tmp->status & statmask))
+      continue;
+    s = strchr(tmp->host, '@');
+    *s = '\0';
+    if (match(tmp->host, user))
+    {
+      *s = '@';
+      continue;
+    }
+    *s = '@';
+    if (!memcmp(&tmp->ipnum, ip, sizeof(struct in_addr)))
+      return tmp;
+  }
+  return NULL;
+}
+
+/*
+ * find_conf_entry
+ *
+ * - looks for a match on all given fields.
+ */
+static aConfItem *find_conf_entry(aConfItem *aconf, unsigned int mask)
+{
+  Reg1 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) && strCasediff(bconf->host, aconf->host))
+      continue;
+
+    if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) ||
+       (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd)))
+      continue;
+    if (!BadPtr(bconf->passwd) && (!isDigit(*bconf->passwd) || bconf->passwd[1])
+#ifdef USEONE
+       && strCasediff(bconf->passwd, "ONE")
+#endif
+       && strCasediff(bconf->passwd, aconf->passwd))
+      continue;
+
+    if ((BadPtr(bconf->name) && !BadPtr(aconf->name)) ||
+       (BadPtr(aconf->name) && !BadPtr(bconf->name)))
+      continue;
+    if (!BadPtr(bconf->name) && strCasediff(bconf->name, aconf->name))
+      continue;
+    break;
+  }
+  return bconf;
+}
+
+/*
+ * 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(aClient *cptr, int sig)
+{
+  Reg1 aConfItem **tmp = &conf, *tmp2;
+  Reg2 aConfClass *cltmp;
+  Reg1 aClient *acptr;
+  Reg2 aMotdItem *temp;
+  Reg2 int i;
+  int ret = 0, found_g;
+
+  if (sig == 1)
+    sendto_ops("Got signal SIGHUP, reloading ircd conf. file");
+
+  for (i = 0; i <= highest_fd; i++)
+    if ((acptr = loc_clients[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;
+      /* free expression trees of connect rules */
+      if ((tmp2->status & (CONF_CRULEALL | CONF_CRULEAUTO)) &&
+         (tmp2->passwd != NULL))
+       crule_free(&(tmp2->passwd));
+      free_conf(tmp2);
+    }
+
+  /*
+   * 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))
+    MarkDelete(cltmp);
+
+  /*
+   * delete the juped nicks list
+   */
+  clearNickJupes();
+
+  if (sig != 2)
+    flush_cache();
+  if (initconf(0) == -1)       /* This calls check_class(), */
+    check_class();             /* unless it fails */
+  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);
+    }
+
+  for (i = 0; i <= highest_fd; i++) {
+    if ((acptr = loc_clients[i]) && !IsMe(acptr)) {
+      if (IsServer(acptr)) {
+       det_confs_butmask(acptr,
+           ~(CONF_HUB | CONF_LEAF | CONF_UWORLD | CONF_ILLEGAL));
+       attach_confs(acptr, acptr->name, CONF_HUB | CONF_LEAF | CONF_UWORLD);
+      }
+      if ((found_g = find_kill(acptr))) {
+       sendto_op_mask(found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
+           found_g == -2 ? "G-line active for %s" : "K-line active for %s",
+           get_client_name(acptr, FALSE));
+       if (exit_client(cptr, acptr, &me, found_g == -2 ? "G-lined" :
+           "K-lined") == CPTR_KILLED)
+         ret = CPTR_KILLED;
+      }
+#if defined(R_LINES) && defined(R_LINES_REHASH) && !defined(R_LINES_OFTEN)
+      if (find_restrict(acptr)) {
+       sendto_ops("Restricting %s, closing lp", get_client_name(acptr, FALSE));
+       if (exit_client(cptr, acptr, &me, "R-lined") == CPTR_KILLED)
+         ret = CPTR_KILLED;
+      }
+#endif
+    }
+  }
+
+  /* free old motd structs */
+  while (motd) {
+    temp = motd->next;
+    RunFree(motd);
+    motd = temp;
+  }
+  while (rmotd) {
+    temp = rmotd->next;
+    RunFree(rmotd);
+    rmotd = temp;
+  }
+  /* reload motd files */
+  read_tlines();
+  rmotd = read_motd(RPATH);
+  motd = read_motd(MPATH);
+
+  return ret;
+}
+
+/*
+ * initconf
+ *
+ * Read configuration file.
+ *
+ * returns -1, if file cannot be opened
+ *          0, if file opened
+ */
+
+#define MAXCONFLINKS 150
+
+unsigned short server_port;
+
+int initconf(int opt)
+{
+  static char quotes[9][2] = {
+    {'b', '\b'},
+    {'f', '\f'},
+    {'n', '\n'},
+    {'r', '\r'},
+    {'t', '\t'},
+    {'v', '\v'},
+    {'\\', '\\'},
+    {0, 0}
+  };
+  Reg1 char *tmp, *s;
+  FBFILE *file;
+  int i;
+  char line[512];
+  int ccount = 0, ncount = 0;
+  aConfItem *aconf = NULL;
+
+  Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile));
+  if (NULL == (file = fbopen(configfile, "r")))
+  {
+    return -1;
+  }
+  while (fbgets(line, sizeof(line) - 1, file))
+  {
+    if ((tmp = strchr(line, '\n')))
+      *tmp = '\0';
+    /*
+     * 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 */
+      case 'a':                /* of this server. */
+       aconf->status = CONF_ADMIN;
+       break;
+      case 'C':                /* Server where I should try to connect */
+      case 'c':                /* in case of lp failures             */
+       ccount++;
+       aconf->status = CONF_CONNECT_SERVER;
+       break;
+       /* Connect rule */
+      case 'D':
+       aconf->status = CONF_CRULEALL;
+       break;
+       /* Connect rule - autos only */
+      case 'd':
+       aconf->status = CONF_CRULEAUTO;
+       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           */
+       aconf->status = CONF_KILL;
+       break;
+      case 'k':                /* Kill user line based on IP in ircd.conf */
+       aconf->status = CONF_IPKILL;
+       break;
+       /* Operator. Line should contain at least */
+       /* password and host where connection is  */
+      case 'L':                /* guaranteed leaf server */
+      case 'l':
+       aconf->status = CONF_LEAF;
+       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) */
+      case 'o':
+       aconf->status = CONF_LOCOP;
+       break;
+      case 'P':                /* listen port line */
+      case 'p':
+       aconf->status = CONF_LISTEN_PORT;
+       break;
+#ifdef R_LINES
+      case 'R':                /* extended K line */
+      case 'r':                /* Offers more options of how to restrict */
+       aconf->status = CONF_RESTRICT;
+       break;
+#endif
+      case 'T':                /* print out different motd's */
+      case 't':                /* based on hostmask */
+       aconf->status = CONF_TLINES;
+       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;
+      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 */
+    {
+      if ((tmp = getfield(NULL, ':')) == NULL)
+       break;
+      DupString(aconf->host, tmp);
+      if ((tmp = getfield(NULL, (aconf->status == CONF_KILL
+         || aconf->status == CONF_IPKILL) ? '"' : ':')) == NULL)
+       break;
+      DupString(aconf->passwd, tmp);
+      if ((tmp = getfield(NULL, ':')) == NULL)
+       break;
+      DupString(aconf->name, tmp);
+      if ((tmp = getfield(NULL, ':')) == NULL)
+       break;
+      aconf->port = atoi(tmp);
+      tmp = getfield(NULL, ':');
+      if (aconf->status & CONF_ME)
+      {
+       server_port = aconf->port;
+       if (!tmp)
+       {
+         Debug((DEBUG_FATAL, "Your M: line must have the Numeric, "
+             "assigned to you by routing-com, behind the port number!\n"));
+#ifdef USE_SYSLOG
+         syslog(LOG_WARNING, "Your M: line must have the Numeric, "
+             "assigned to you by routing-com, behind the port number!\n");
+#endif
+         exit(-1);
+       }
+       SetYXXServerName(&me, atoi(tmp));       /* Our Numeric Nick */
+      }
+      else if (tmp)
+       aconf->confClass = find_class(atoi(tmp));
+      break;
+    }
+    /*
+     * 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 (aconf->confClass == 0)
+       aconf->confClass = 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)
+       {
+         char *passwd = bconf->passwd;
+         bconf->passwd = aconf->passwd;
+         aconf->passwd = passwd;
+         ConfLinks(bconf) -= bconf->clients;
+         bconf->confClass = aconf->confClass;
+         if (bconf->confClass)
+           ConfLinks(bconf) += bconf->clients;
+       }
+       free_conf(aconf);
+       aconf = bconf;
+      }
+      else if (aconf->host && aconf->status == CONF_LISTEN_PORT)
+       add_listener(aconf);
+    }
+    if (aconf->status & CONF_SERVER_MASK)
+      if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
+         !aconf->host || strchr(aconf->host, '*') ||
+         strchr(aconf->host, '?') || !aconf->name)
+       continue;
+
+    if (aconf->status & (CONF_SERVER_MASK | CONF_LOCOP | CONF_OPERATOR))
+      if (!strchr(aconf->host, '@') && *aconf->host != '/')
+      {
+       char *newhost;
+       int len = 3;            /* *@\0 = 3 */
+
+       len += strlen(aconf->host);
+       newhost = (char *)RunMalloc(len);
+       sprintf_irc(newhost, "*@%s", aconf->host);
+       RunFree(aconf->host);
+       aconf->host = newhost;
+      }
+    if (aconf->status & CONF_SERVER_MASK)
+    {
+      if (BadPtr(aconf->passwd))
+       continue;
+      else if (!(opt & BOOT_QUICK))
+       lookup_confhost(aconf);
+    }
+
+    /* Create expression tree from connect rule...
+     * If there's a parsing error, nuke the conf structure */
+    if (aconf->status & (CONF_CRULEALL | CONF_CRULEAUTO))
+    {
+      RunFree(aconf->passwd);
+      if ((aconf->passwd = (char *)crule_parse(aconf->name)) == NULL)
+      {
+       free_conf(aconf);
+       aconf = NULL;
+       continue;
+      }
+    }
+
+    /*
+     * 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)
+    {
+      strncpy(me.info, aconf->name, sizeof(me.info) - 1);
+      if (me.name[0] == '\0' && aconf->host[0])
+       strncpy(me.name, aconf->host, sizeof(me.name) - 1);
+      if (portnum == 0)
+       portnum = aconf->port;
+    }
+
+    /*
+     * Juped nicks are listed in the 'password' field of U:lines,
+     * the list is comma separated and might be empty and/or contain
+     * empty elements... the only limit is that it MUST be shorter
+     * than 512 chars, or they will be cutted out :)
+     */
+    if ((aconf->status == CONF_UWORLD) && (aconf->passwd) && (*aconf->passwd))
+      addNickJupes(aconf->passwd);
+
+    if (aconf->status & CONF_ADMIN)
+      if (!aconf->host || !aconf->passwd || !aconf->name)
+      {
+       Debug((DEBUG_FATAL, "Your A: line must have 4 fields!\n"));
+#ifdef USE_SYSLOG
+       syslog(LOG_WARNING, "Your A: line must have 4 fields!\n");
+#endif
+       exit(-1);
+      }
+
+    collapse(aconf->host);
+    collapse(aconf->name);
+    Debug((DEBUG_NOTICE,
+       "Read Init: (%d) (%s) (%s) (%s) (%u) (%p)",
+       aconf->status, aconf->host, aconf->passwd,
+       aconf->name, aconf->port, aconf->confClass));
+    aconf->next = conf;
+    conf = aconf;
+    aconf = NULL;
+  }
+  if (aconf)
+    free_conf(aconf);
+  fbclose(file);
+  check_class();
+  nextping = nextconnect = now;
+  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(aConfItem *aconf)
+{
+  Reg2 char *s;
+  Reg3 struct hostent *hp;
+  Link ln;
+
+  if (BadPtr(aconf->host) || BadPtr(aconf->name))
+    goto badlookup;
+  if ((s = strchr(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)))
+    memcpy(&(aconf->ipnum), hp->h_addr, sizeof(struct in_addr));
+
+  if (aconf->ipnum.s_addr == INADDR_NONE)
+    goto badlookup;
+  return 0;
+badlookup:
+  if (aconf->ipnum.s_addr == INADDR_NONE)
+    memset(&aconf->ipnum, 0, sizeof(struct in_addr));
+  Debug((DEBUG_ERROR, "Host/server name error: (%s) (%s)",
+      aconf->host, aconf->name));
+  return -1;
+}
+
+/* read_tlines 
+ * Read info from T:lines into trecords which include the file 
+ * timestamp, the hostmask, and the contents of the motd file 
+ * -Ghostwolf 7sep97
+ */
+void read_tlines()
+{
+  aConfItem *tmp;
+  atrecord *temp, *last = NULL;        /* Init. to avoid compiler warning */
+  aMotdItem *amotd;
+
+  /* Free the old trecords and the associated motd contents first */
+  while (tdata)
+  {
+    last = tdata->next;
+    while (tdata->tmotd)
+    {
+      amotd = tdata->tmotd->next;
+      RunFree(tdata->tmotd);
+      tdata->tmotd = amotd;
+    }
+    RunFree(tdata);
+    tdata = last;
+  }
+
+  for (tmp = conf; tmp; tmp = tmp->next)
+    if (tmp->status == CONF_TLINES && tmp->host && tmp->passwd)
+    {
+      temp = (atrecord *) RunMalloc(sizeof(atrecord));
+      if (!temp)
+       outofmemory();
+      temp->hostmask = tmp->host;
+      temp->tmotd = read_motd(tmp->passwd);
+      temp->tmotd_tm = motd_tm;
+      temp->next = NULL;
+      if (!tdata)
+       tdata = temp;
+      else
+       last->next = temp;
+      last = temp;
+    }
+}
+
+int find_kill(aClient *cptr)
+{
+  char reply[256], *host, *name;
+  aConfItem *tmp;
+  aGline *agline = NULL;
+
+  if (!cptr->user)
+    return 0;
+
+  host = cptr->sockhost;
+  name = cptr->user->username;
+
+  if (strlen(host) > (size_t)HOSTLEN ||
+      (name ? strlen(name) : 0) > (size_t)HOSTLEN)
+    return (0);
+
+  reply[0] = '\0';
+
+  for (tmp = conf; tmp; tmp = tmp->next)
+    /* Added a check against the user's IP address as well.
+     * If the line is either CONF_KILL or CONF_IPKILL, check it; if and only
+     * if it's CONF_IPKILL, check the IP address as well (the && below will
+     * short circuit and the match won't even get run) -Kev
+     */
+    if ((tmp->status & CONF_KLINE) && tmp->host && tmp->name &&
+       (match(tmp->host, host) == 0 ||
+       ((tmp->status == CONF_IPKILL) &&
+       match(tmp->host, inetntoa(cptr->ip)) == 0)) &&
+       (!name || match(tmp->name, name) == 0) &&
+       (!tmp->port || (tmp->port == cptr->acpt->port)))
+    {
+      /*
+       * Can short-circuit evaluation - not taking chances
+       * because check_time_interval destroys tmp->passwd
+       * - Mmmm
+       */
+      if (BadPtr(tmp->passwd))
+       break;
+      else if (is_comment(tmp->passwd))
+       break;
+      else if (check_time_interval(tmp->passwd, reply))
+       break;
+    }
+
+  if (reply[0])
+    sendto_one(cptr, reply, me.name, ERR_YOUREBANNEDCREEP, cptr->name);
+  else if (tmp)
+  {
+    if (BadPtr(tmp->passwd))
+      sendto_one(cptr,
+         ":%s %d %s :Connection from your host is refused on this server.",
+         me.name, ERR_YOUREBANNEDCREEP, cptr->name);
+    else
+    {
+      if (*tmp->passwd == '"')
+      {
+       char *sbuf =
+           sprintf_irc(sendbuf, ":%s %d %s :%s", me.name, ERR_YOUREBANNEDCREEP,
+           cptr->name, &tmp->passwd[1]);
+       sbuf[-1] = '.';         /* Overwrite last quote with a dot */
+       sendbufto_one(cptr);
+      }
+      else if (*tmp->passwd == '!')
+       killcomment(cptr, cptr->name, &tmp->passwd[1]);
+      else
+#ifdef COMMENT_IS_FILE
+       killcomment(cptr, cptr->name, tmp->passwd);
+#else
+       sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
+           cptr->name, tmp->passwd);
+#endif
+    }
+  }
+
+  /* find active glines */
+  /* added a check against the user's IP address to find_gline() -Kev */
+  else if ((agline = find_gline(cptr, NULL)) && GlineIsActive(agline))
+    sendto_one(cptr, ":%s %d %s :%s.", me.name, ERR_YOUREBANNEDCREEP,
+       cptr->name, agline->reason);
+  else
+    agline = NULL;             /* if a gline was found, it was inactive */
+
+  return (tmp ? -1 : (agline ? -2 : 0));
+}
+
+#ifdef R_LINES
+/*
+ * find_restrict
+ *
+ * Works against host/name and calls an outside program
+ * to determine whether a client is allowed to connect.  This allows
+ * more freedom to determine who is legal and who isn't, for example
+ * machine load considerations.  The outside program is expected to
+ * return a reply line where the first word is either 'Y' or 'N' meaning
+ * "Yes Let them in" or "No don't let them in."  If the first word
+ * begins with neither 'Y' or 'N' the default is to let the person on.
+ * It returns a value of 0 if the user is to be let through -Hoppie
+ */
+int find_restrict(aClient *cptr)
+{
+  aConfItem *tmp;
+  char reply[80], temprpl[80];
+  char *rplhold = reply, *host, *name, *s;
+  char rplchar = 'Y';
+  int pi[2], rc = 0, n;
+  FBFILE *file = NULL;
+
+  if (!cptr->user)
+    return 0;
+  name = cptr->user->username;
+  host = cptr->sockhost;
+  Debug((DEBUG_INFO, "R-line check for %s[%s]", name, host));
+
+  for (tmp = conf; tmp; tmp = tmp->next)
+  {
+    if (tmp->status != CONF_RESTRICT ||
+       (tmp->host && host && match(tmp->host, host)) ||
+       (tmp->name && name && match(tmp->name, name)))
+      continue;
+
+    if (BadPtr(tmp->passwd))
+    {
+      sendto_ops("Program missing on R-line %s/%s, ignoring", name, host);
+      continue;
+    }
+
+    if (pipe(pi) == -1)
+    {
+      report_error("Error creating pipe for R-line %s: %s", &me);
+      return 0;
+    }
+    switch (rc = fork())
+    {
+      case -1:
+       report_error("Error forking for R-line %s: %s", &me);
+       return 0;
+      case 0:
+      {
+       Reg1 int i;
+
+       close(pi[0]);
+       for (i = 2; i < MAXCONNECTIONS; i++)
+         if (i != pi[1])
+           close(i);
+       if (pi[1] != 2)
+         dup2(pi[1], 2);
+       dup2(2, 1);
+       if (pi[1] != 2 && pi[1] != 1)
+         close(pi[1]);
+       execlp(tmp->passwd, tmp->passwd, name, host, 0);
+       exit(-1);
+      }
+      default:
+       close(pi[1]);
+       break;
+    }
+    *reply = '\0';
+    file = fdbopen(pi[0], "r");
+    while (fbgets(temprpl, sizeof(temprpl) - 1, file))
+    {
+      if ((s = strchr(temprpl, '\n')))
+       *s = '\0';
+      if (strlen(temprpl) + strlen(reply) < sizeof(reply) - 2)
+       sprintf_irc(rplhold, "%s %s", rplhold, temprpl);
+      else
+      {
+       sendto_ops("R-line %s/%s: reply too long!", name, host);
+       break;
+      }
+    }
+    fbclose(file);
+    kill(rc, SIGKILL);         /* cleanup time */
+    wait(0);
+
+    rc = 0;
+    while (*rplhold == ' ')
+      rplhold++;
+    rplchar = *rplhold;                /* Pull out the yes or no */
+    while (*rplhold != ' ')
+      rplhold++;
+    while (*rplhold == ' ')
+      rplhold++;
+    strcpy(reply, rplhold);
+    rplhold = reply;
+
+    if ((rc = (rplchar == 'n' || rplchar == 'N')))
+      break;
+  }
+  if (rc)
+  {
+    sendto_one(cptr, ":%s %d %s :Restriction: %s",
+       me.name, ERR_YOUREBANNEDCREEP, cptr->name, reply);
+    return -1;
+  }
+  return 0;
+}
+#endif
+
+/*
+ * 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
+ */
+static void killcomment(aClient *sptr, char *parv, char *filename)
+{
+  FBFILE *file = NULL;
+  char line[80];
+  Reg1 char *tmp;
+  struct stat sb;
+  struct tm *tm;
+
+  if (NULL == (file = fbopen(filename, "r")))
+  {
+    sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv);
+    sendto_one(sptr,
+       ":%s %d %s :Connection from your host is refused on this server.",
+       me.name, ERR_YOUREBANNEDCREEP, parv);
+    return;
+  }
+  fbstat(&sb, file);
+  tm = localtime((time_t *) & sb.st_mtime);    /* NetBSD needs cast */
+  while (fbgets(line, sizeof(line) - 1, file))
+  {
+    if ((tmp = strchr(line, '\n')))
+      *tmp = '\0';
+    if ((tmp = strchr(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 :Connection from your host is refused on this server.",
+      me.name, ERR_YOUREBANNEDCREEP, parv);
+  fbclose(file);
+  return;
+}
+
+/*
+ *  is the K line field an interval or a comment? - Mmmm
+ */
+static int is_comment(char *comment)
+{
+  size_t i;
+  for (i = 0; i < strlen(comment); i++)
+    if ((comment[i] != ' ') && (comment[i] != '-')
+       && (comment[i] != ',') && ((comment[i] < '0') || (comment[i] > '9')))
+      return (1);
+
+  return (0);
+}
+
+/*
+ *  check against a set of time intervals
+ */
+static int check_time_interval(char *interval, char *reply)
+{
+  struct tm *tptr;
+  char *p;
+  int perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes;
+  int nowm, perm_min, perm_max;
+
+  tptr = localtime(&now);
+  nowm = tptr->tm_hour * 60 + tptr->tm_min;
+
+  while (interval)
+  {
+    p = strchr(interval, ',');
+    if (p)
+      *p = '\0';
+    if (sscanf(interval, "%2d%2d-%2d%2d", &perm_min_hours, &perm_min_minutes,
+       &perm_max_hours, &perm_max_minutes) != 4)
+    {
+      if (p)
+       *p = ',';
+      return (0);
+    }
+    if (p)
+      *(p++) = ',';
+    perm_min = 60 * perm_min_hours + perm_min_minutes;
+    perm_max = 60 * perm_max_hours + perm_max_minutes;
+    /*
+     * The following check allows intervals over midnight ...
+     */
+    if ((perm_min < perm_max)
+       ? (perm_min <= nowm && nowm <= perm_max)
+       : (perm_min <= nowm || nowm <= perm_max))
+    {
+      printf(reply,
+         ":%%s %%d %%s :%s %d:%02d to %d:%02d.",
+         "You are not allowed to connect from",
+         perm_min_hours, perm_min_minutes, perm_max_hours, perm_max_minutes);
+      return (ERR_YOUREBANNEDCREEP);
+    }
+    if ((perm_min < perm_max)
+       ? (perm_min <= nowm + 5 && nowm + 5 <= perm_max)
+       : (perm_min <= nowm + 5 || nowm + 5 <= perm_max))
+    {
+      sprintf_irc(reply, ":%%s %%d %%s :%d minute%s%s",
+         perm_min - nowm, (perm_min - nowm) > 1 ? "s " : " ",
+         "and you will be denied for further access");
+      return (ERR_YOUWILLBEBANNED);
+    }
+    interval = p;
+  }
+  return (0);
+}
+
+aMotdItem *read_motd(char *motdfile)
+{
+  FBFILE *file = NULL;
+  register aMotdItem *temp, *newmotd, *last;
+  struct stat sb;
+  char line[80];
+  register char *tmp;
+
+  if (NULL == (file = fbopen(motdfile, "r")))
+  {
+    Debug((DEBUG_ERROR, "Couldn't open \"%s\": %s", motdfile, strerror(errno)));
+    return NULL;
+  }
+  if (-1 == fbstat(&sb, file))
+  {
+    return NULL;
+  }
+  newmotd = last = NULL;
+  motd_tm = *localtime((time_t *) & sb.st_mtime);      /* NetBSD needs cast */
+  while (fbgets(line, sizeof(line) - 1, file))
+  {
+    if ((tmp = (char *)strchr(line, '\n')))
+      *tmp = '\0';
+    if ((tmp = (char *)strchr(line, '\r')))
+      *tmp = '\0';
+    temp = (aMotdItem *) RunMalloc(sizeof(aMotdItem));
+    if (!temp)
+      outofmemory();
+    strcpy(temp->line, line);
+    temp->next = NULL;
+    if (!newmotd)
+      newmotd = temp;
+    else
+      last->next = temp;
+    last = temp;
+  }
+  fbclose(file);
+  return newmotd;
+}
diff --git a/ircd/s_debug.c b/ircd/s_debug.c
new file mode 100644 (file)
index 0000000..39940ae
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ * 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 "sys.h"
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if defined(HPUX) && HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HPUX
+#include <sys/syscall.h>
+#define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b)
+#endif
+#if HAVE_GETRUSAGE
+#include <sys/resource.h>
+#else
+#if HAVE_TIMES
+#include <sys/times.h>
+#endif
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdarg.h>
+#include "h.h"
+#include "struct.h"
+#include "numeric.h"
+#include "hash.h"
+#include "send.h"
+#include "s_conf.h"
+#include "class.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "bsd.h"
+#include "whowas.h"
+#include "s_serv.h"
+#include "res.h"
+#include "channel.h"
+#include "numnicks.h"
+
+RCSTAG_CC("$Id$");
+
+/* *INDENT-OFF* */
+
+/*
+ * Option string.  Must be before #ifdef DEBUGMODE.
+ */
+char serveropts[] = {
+#if BUFFERPOOL < 1000000
+    'b',
+#if BUFFERPOOL > 99999
+    (char)('0' + (BUFFERPOOL/100000)),
+#endif
+#if BUFFERPOOL > 9999
+    (char)('0' + (BUFFERPOOL/10000) % 10),
+#endif
+    (char)('0' + (BUFFERPOOL/1000) % 10),
+#else
+    'B',
+#if BUFFERPOOL > 99999999
+    (char)('0' + (BUFFERPOOL/100000000)),
+#endif
+#if BUFFERPOOL > 9999999
+    (char)('0' + (BUFFERPOOL/10000000) % 10),
+#endif
+    (char)('0' + (BUFFERPOOL/1000000) % 10),
+#endif
+#ifdef CHROOTDIR
+    'c',
+#endif
+#ifdef CMDLINE_CONFIG
+    'C',
+#endif
+#ifdef DO_ID
+    'd',
+#endif
+#ifdef DEBUGMODE
+    'D',
+#endif
+#ifdef LOCOP_REHASH
+    'e',
+#endif
+#ifdef OPER_REHASH
+    'E',
+#endif
+#ifdef HUB
+    'H',
+#endif
+#if defined(SHOW_INVISIBLE_USERS) ||  defined(SHOW_ALL_INVISIBLE_USERS)
+#ifdef SHOW_ALL_INVISIBLE_USERS
+    'I',
+#else
+    'i',
+#endif
+#endif
+#ifdef OPER_KILL
+#ifdef LOCAL_KILL_ONLY
+    'k',
+#else
+    'K',
+#endif
+#endif
+#ifdef LEAST_IDLE
+    'L',
+#endif
+#ifdef IDLE_FROM_MSG
+    'M',
+#endif
+#ifdef USEONE
+    'O',
+#endif
+#ifdef CRYPT_OPER_PASSWORD
+    'p',
+#endif
+#ifdef CRYPT_LINK_PASSWORD
+    'P',
+#endif
+#ifdef DEBUGMALLOC
+#ifdef MEMLEAKSTATS
+    'Q',
+#else
+    'q',
+#endif
+#endif
+#ifdef RELIABLE_CLOCK
+    'R',
+#endif
+#ifdef LOCOP_RESTART
+    's',
+#endif
+#ifdef OPER_RESTART
+    'S',
+#endif
+#ifdef OPER_REMOTE
+    't',
+#endif
+#if defined(USE_POLL) && defined(HAVE_POLL_H)
+    'U',
+#endif
+#ifdef VIRTUAL_HOST
+    'v',
+#endif
+#ifdef UNIXPORT
+    'X',
+#endif
+#ifdef USE_SYSLOG
+    'Y',
+#endif
+    '\0'
+};
+
+/* *INDENT-ON* */
+
+#ifdef DEBUGMODE
+static char debugbuf[1024];
+
+void vdebug(int level, const char *form, va_list vl)
+{
+  int err = errno;
+
+  if ((debuglevel >= 0) && (level <= debuglevel))
+  {
+    vsprintf(debugbuf, form, vl);
+    if (loc_clients[2])
+    {
+      loc_clients[2]->sendM++;
+      loc_clients[2]->sendB += strlen(debugbuf);
+    }
+    fprintf(stderr, "%s", debugbuf);
+    fputc('\n', stderr);
+  }
+  errno = err;
+}
+
+void debug(int level, const char *form, ...)
+{
+  va_list vl;
+  va_start(vl, form);
+  vdebug(level, form, vl);
+  va_end(vl);
+}
+
+/*
+ * 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(aClient *cptr, char *nick)
+{
+
+#if HAVE_GETRUSAGE
+  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 (MyUser(cptr) || Protocol(cptr->from) < 10)
+      sendto_one(cptr, ":%s NOTICE %s :Getruseage error: %s.",
+         me.name, nick, sys_errlist[errno]);
+    else
+      sendto_one(cptr, "%s NOTICE %s%s :Getruseage error: %s.",
+         NumServ(&me), NumNick(cptr), sys_errlist[errno]);
+    return;
+  }
+  secs = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
+  rup = now - me.since;
+  if (secs == 0)
+    secs = 1;
+
+#if defined(__sun__) || defined(__bsdi__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+  sendto_one(cptr, ":%s %d %s :CPU Secs %ld:%ld User %ld:%ld System %ld:%ld",
+#else
+  sendto_one(cptr, ":%s %d %s :CPU Secs %ld:%ld User %d:%d System %d:%d",
+#endif
+      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 %ld ShMem %ld Data %ld Stack %ld",
+      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 %ld Reclaims %ld Faults %ld",
+      me.name, RPL_STATSDEBUG, nick, rus.ru_nswap,
+      rus.ru_minflt, rus.ru_majflt);
+  sendto_one(cptr, ":%s %d %s :Block in %ld out %ld",
+      me.name, RPL_STATSDEBUG, nick, rus.ru_inblock, rus.ru_oublock);
+  sendto_one(cptr, ":%s %d %s :Msg Rcv %ld Send %ld",
+      me.name, RPL_STATSDEBUG, nick, rus.ru_msgrcv, rus.ru_msgsnd);
+  sendto_one(cptr, ":%s %d %s :Signals %ld Context Vol. %ld Invol %ld",
+      me.name, RPL_STATSDEBUG, nick, rus.ru_nsignals,
+      rus.ru_nvcsw, rus.ru_nivcsw);
+#else /* HAVE_GETRUSAGE */
+#if HAVE_TIMES
+  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 /* HAVE_TIMES */
+#endif /* HAVE_GETRUSAGE */
+  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 used %d",
+      me.name, RPL_STATSDEBUG, nick, DBufAllocCount, DBufUsedCount);
+  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 /* DEBUGMODE */
+
+void count_memory(aClient *cptr, char *nick)
+{
+  Reg1 aClient *acptr;
+  Reg2 Link *link;
+  Reg3 aChannel *chptr;
+  Reg4 aConfItem *aconf;
+  Reg5 aConfClass *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 */
+      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 */
+
+  size_t 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 */
+      dbufs_allocated = 0,     /* memory used by dbufs */
+      dbufs_used = 0,          /* memory used by dbufs */
+      rm = 0,                  /* res memory used */
+      totcl = 0, totch = 0, totww = 0, tot = 0;
+
+  count_whowas_memory(&wwu, &wwm, &wwa, &wwam);
+  wwm += sizeof(aWhowas) * NICKNAMEHISTORYLENGTH;
+  wwm += sizeof(aWhowas *) * WW_MAX;
+
+  for (acptr = client; acptr; acptr = acptr->next)
+  {
+    if (IsPing(acptr))
+      continue;
+    if (MyConnect(acptr))
+    {
+      lc++;
+      for (link = acptr->confs; link; link = link->next)
+       lcc++;
+    }
+    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 (link = chptr->banlist; link; link = link->next)
+    {
+      chb++;
+      chbm += (strlen(link->value.cp) + 1 + sizeof(Link));
+    }
+  }
+
+  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(" SIZE_T_FMT
+      ") Remote %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, lc, lcm, rc, rcm);
+  sendto_one(cptr, ":%s %d %s :Users %d(" SIZE_T_FMT
+      ") Invites %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, us, us * sizeof(anUser), usi,
+      usi * sizeof(Link));
+  sendto_one(cptr, ":%s %d %s :User channels %d(" SIZE_T_FMT
+      ") Aways %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, usc, usc * sizeof(Link), aw, awm);
+  sendto_one(cptr, ":%s %d %s :Attached confs %d(" SIZE_T_FMT ")",
+      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);
+
+  sendto_one(cptr, ":%s %d %s :Conflines %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, co, com);
+
+  sendto_one(cptr, ":%s %d %s :Classes %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, cl, cl * sizeof(aConfClass));
+
+  sendto_one(cptr, ":%s %d %s :Channels %d(" SIZE_T_FMT
+      ") Bans %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, ch, chm, chb, chbm);
+  sendto_one(cptr, ":%s %d %s :Channel membrs %d(" SIZE_T_FMT
+      ") invite %d(" SIZE_T_FMT ")",
+      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(" SIZE_T_FMT
+      ") away %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, wwu, wwu * sizeof(anUser), wwa, wwam);
+  sendto_one(cptr, ":%s %d %s :Whowas array %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, NICKNAMEHISTORYLENGTH, wwm);
+
+  totww = wwu * sizeof(anUser) + wwam + wwm;
+
+  sendto_one(cptr, ":%s %d %s :Hash: client %d(" SIZE_T_FMT
+      "), chan is the same",
+      me.name, RPL_STATSDEBUG, nick, HASHSIZE, sizeof(void *) * HASHSIZE);
+
+  /*
+   * NOTE: this count will be accurate only for the exact instant that this
+   * message is being sent, so the count is affected by the dbufs that
+   * are being used to send this message out. If this is not desired, move
+   * the dbuf_count_memory call to a place before we start sending messages
+   * and cache DBufAllocCount and DBufUsedCount in variables until they 
+   * are sent.
+   */
+  dbuf_count_memory(&dbufs_allocated, &dbufs_used);
+  sendto_one(cptr,
+      ":%s %d %s :DBufs allocated %d(" SIZE_T_FMT ") used %d(" SIZE_T_FMT ")",
+      me.name, RPL_STATSDEBUG, nick, DBufAllocCount, dbufs_allocated,
+      DBufUsedCount, dbufs_used);
+
+  rm = cres_mem(cptr);
+
+  tot =
+      totww + totch + totcl + com + cl * sizeof(aConfClass) + dbufs_allocated +
+      rm;
+  tot += sizeof(void *) * HASHSIZE * 3;
+
+  sendto_one(cptr, ":%s %d %s :Total: ww " SIZE_T_FMT " ch " SIZE_T_FMT
+      " cl " SIZE_T_FMT " co " SIZE_T_FMT " db " SIZE_T_FMT,
+      me.name, RPL_STATSDEBUG, nick, totww, totch, totcl, com, dbufs_allocated);
+  return;
+}
+
+#ifdef MSGLOG_ENABLED
+
+/* Define here what level of messages you want to log */
+#define LOG_MASK_LEVEL LEVEL_MODE      /* This that change some data */
+
+static struct log_entry log_table[MSGLOG_SIZE];
+
+static int unused_log_entries = MSGLOG_SIZE;
+static int last_log_entry = -1;        /* Nothing stored yet */
+static int entry_stored_forlog = 0;    /* Just a flag */
+
+/*
+ * RollBackMsgLog
+ *
+ * Just a little utility function used to retract
+ * an half stored Message log entry
+ */
+void RollBackMsgLog(void)
+{
+  /* We won't log this, abort and free the entry */
+  last_log_entry--;
+  unused_log_entries++;
+  return;
+}
+
+/*
+ * Log_Message (macroed as LogMessage)
+ *
+ * Permanently stores a log entry into the recent log memory area
+ * Store_Buffer MUST have been called before calling Log_Message
+ */
+void Log_Message(aClient *sptr, int msgclass)
+{
+  register int n = last_log_entry;
+
+  /* Clear our flag, since we are going to
+   * finish the processing of this entry */
+  entry_stored_forlog = 0;
+
+  /* Check  if the level of this message is high enough */
+  if (msgclass < LOG_MASK_LEVEL)
+  {
+    RollBackMsgLog();
+    return;
+  }
+
+  /* Check if we wanna log the type of connection from
+   * where this message did come from */
+  if (!((0x8000 >> (8 + log_table[n].cptr_status)) & LOG_MASK_TYPE))
+  {
+    RollBackMsgLog();
+    return;
+  }
+
+  /* Complete the entry */
+  if (sptr)
+  {
+    log_table[n].sptr_status = sptr->status;
+    strncpy(log_table[n].sptr_name, sptr->name, HOSTLEN);
+    log_table[n].sptr_name[HOSTLEN] = '\0';
+    strncpy(log_table[n].sptr_yxx, sptr->yxx, 4);
+    log_table[n].sptr = sptr;
+
+    if (sptr->from)
+    {
+      strncpy(log_table[n].sptr_from_name, sptr->name, HOSTLEN);
+      log_table[n].sptr_from_name[HOSTLEN] = '\0';
+    }
+    else
+    {
+      memset(log_table[n].sptr_from_name, 0, HOSTLEN);
+    }
+  }
+  else
+  {
+    log_table[n].sptr_status = 0xFF;   /* Dummy value */
+    memset(&log_table[n].sptr_name, 0, HOSTLEN);
+    memset(log_table[n].sptr_yxx, 0, 4);
+
+    log_table[n].sptr = 0;
+    memset(&log_table[n].sptr_from_name, 0, HOSTLEN);
+  }
+}
+
+/*
+ * Store_Buffer (macroed as StoreBuffer)
+ *
+ * Saves the buffer and cptr info at the very first stage
+ * of parsing, if Log_Message doesn't get called between
+ * two Store_Buffer calls this function assumes that the parser
+ * has rejected the message and therefore calls Log_Message
+ * as if the message class was 0 and the sptr null
+ */
+void Store_Buffer(char *buf, aClient *cptr)
+{
+  register int n;
+
+  /* Check if we have an entry pending, if so
+   * complete it's processing */
+  if (entry_stored_forlog)
+    Log_Message((aClient *)NULL, 0);
+
+  /* Update the "half used entry" flag */
+  entry_stored_forlog = 1;
+
+  /* First update the free entries counter */
+  if (unused_log_entries)
+    unused_log_entries--;
+
+  /* Get an entry */
+  n = (last_log_entry + 1) % MSGLOG_SIZE;
+
+  /* Update the last_log_entry index */
+  last_log_entry = n;
+
+  /* Store what we have by now in it */
+  log_table[n].cptr_status = cptr->status;
+  strncpy(log_table[n].cptr_name, cptr->name, HOSTLEN);
+  log_table[n].cptr_name[HOSTLEN] = '\0';
+  strncpy(log_table[n].cptr_yxx, cptr->yxx, 4);
+  log_table[n].cptr_fd = cptr->fd;
+  log_table[n].cptr = cptr;    /* No checking for this, is lossy */
+  strncpy(log_table[n].buffer, buf, 511);
+}
+
+#endif /* MSGLOG_ENABLED */
diff --git a/ircd/s_err.c b/ircd/s_err.c
new file mode 100644 (file)
index 0000000..ec40bd0
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+ * IRC - Internet Relay Chat, ircd/s_err.c
+ * 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.
+ */
+
+#include "sys.h"
+#include "h.h"
+#include "numeric.h"
+#include "s_err.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+typedef struct {
+  int num_val;
+  char *num_form;
+} Numeric;
+
+/* *INDENT-OFF* */
+
+static Numeric local_replies[] = {
+/* 000 */
+    {0, (char *)NULL},
+/* 001 */
+    {RPL_WELCOME, ":Welcome to the Internet Relay Network %s"},
+/* 002 */
+    {RPL_YOURHOST, ":Your host is %s, running version %s"},
+/* 003 */
+    {RPL_CREATED, ":This server was created %s"},
+/* 004 */
+    {RPL_MYINFO, "%s %s dioswkg biklmnopstv"},
+/* 005 */
+    {RPL_MAP, ":%s%s"},
+/* 006 */
+    {RPL_MAPMORE, ":%s%s --> *more*"},
+/* 007 */
+    {RPL_MAPEND, ":End of /MAP"},
+/* 008 */
+    {RPL_SNOMASK, "%d :: Server notice mask (%#x)"},
+/* 009 */
+    {RPL_STATMEMTOT, "%u %u :Bytes Blocks"},
+/* 010 */
+#ifdef MEMSIZESTATS
+    {RPL_STATMEM, "%u %u %s %u"},
+#else
+    {RPL_STATMEM, "%u %u %s"},
+#endif
+    {0, (char *)NULL}
+};
+
+static Numeric numeric_errors[] = {
+/* 401 */
+    {ERR_NOSUCHNICK, "%s :No such nick"},
+/* 402 */
+    {ERR_NOSUCHSERVER, "%s :No such server"},
+/* 403 */
+    {ERR_NOSUCHCHANNEL, "%s :No such channel"},
+/* 404 */
+    {ERR_CANNOTSENDTOCHAN, "%s :Cannot send to channel"},
+/* 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 */
+    {0, (char *)NULL},
+/* 409 */
+    {ERR_NOORIGIN, ":No origin specified"},
+/* 410 */
+    {0, (char *)NULL},
+/* 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 */
+    {0, (char *)NULL},
+/* 416 */
+    {ERR_QUERYTOOLONG, "%s :Too many lines in the output, restrict your query"},
+/* 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 */
+    {0, (char *)NULL},
+/* 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 :Erroneus Nickname"},
+/* 433 */
+    {ERR_NICKNAMEINUSE, "%s :Nickname is already in use."},
+/* 434 */
+    {0, (char *)NULL},
+/* 435 */
+    {0, (char *)NULL},
+/* 436 */
+    {ERR_NICKCOLLISION, "%s :Nickname collision KILL"},
+/* 437 */
+    {ERR_BANNICKCHANGE, "%s :Cannot change nickname while banned on channel"},
+/* 438 */
+    {ERR_NICKTOOFAST, "%s :Nick change too fast. Please wait %d seconds."},
+/* 439 */
+    {ERR_TARGETTOOFAST, "%s :Target change too fast. Please wait %d seconds."},
+/* 440 */
+    {0, (char *)NULL},
+/* 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 %s :is already on channel"},
+/* 444 */
+    {0, (char *)NULL},
+/* 445 */
+    {0, (char *)NULL},
+/* 446 */
+    {0, (char *)NULL},
+/* 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 */
+    {0, (char *)NULL},
+/* 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"},
+/* 466 */
+    {ERR_YOUWILLBEBANNED, (char *)NULL},
+/* 467 */
+    {ERR_KEYSET, "%s :Channel key already set"},
+/* 468 */
+    {ERR_INVALIDUSERNAME, (char *)NULL},
+/* 469 */
+    {0, (char *)NULL},
+/* 470 */
+    {0, (char *)NULL},
+/* 471 */
+    {ERR_CHANNELISFULL, "%s :Cannot join channel (+l)"},
+/* 472 */
+    {ERR_UNKNOWNMODE, "%c :is unknown mode char to me"},
+/* 473 */
+    {ERR_INVITEONLYCHAN, "%s :Cannot join channel (+i)"},
+/* 474 */
+    {ERR_BANNEDFROMCHAN, "%s :Cannot join channel (+b)"},
+/* 475 */
+    {ERR_BADCHANNELKEY, "%s :Cannot join channel (+k)"},
+/* 476 */
+    {ERR_BADCHANMASK, "%s :Bad Channel Mask"},
+/* 477 */
+    {0, (char *)NULL},
+/* 478 */
+    {ERR_BANLISTFULL, "%s %s :Channel ban/ignore list is full"},
+/* 479 */
+    {0, (char *)NULL},
+/* 480 */
+    {0, (char *)NULL},
+/* 481 */
+    {ERR_NOPRIVILEGES, ":Permission Denied- You're not an IRC operator"},
+/* 482 */
+    {ERR_CHANOPRIVSNEEDED, "%s :You're not channel operator"},
+/* 483 */
+    {ERR_CANTKILLSERVER, ":You cant kill a server!"},
+/* 484 */
+    {ERR_ISCHANSERVICE, "%s %s :Cannot kill, kick or deop channel service"},
+/* 485 */
+    {0, (char *)NULL},
+/* 486 */
+    {0, (char *)NULL},
+/* 487 */
+    {0, (char *)NULL},
+/* 488 */
+    {0, (char *)NULL},
+/* 489 */
+    {ERR_VOICENEEDED, "%s :You're neither voiced nor channel operator"},
+/* 490 */
+    {0, (char *)NULL},
+/* 491 */
+    {ERR_NOOPERHOST, ":No O-lines for your host"},
+/* 492 */
+    {0, (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, ":Cant 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_NOSUCHGLINE, "%s@%s :No such gline"},
+/* 513 */
+    {ERR_BADPING, (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 */
+    {RPL_USERIP, ":"},
+/* 308 */
+    {0, (char *)NULL},
+/* 309 */
+    {0, (char *)NULL},
+/* 310 */
+    {0, (char *)NULL},
+/* 311 */
+    {RPL_WHOISUSER, "%s %s %s * :%s"},
+/* 312 */
+    {RPL_WHOISSERVER, "%s %s :%s"},
+/* 313 */
+    {RPL_WHOISOPERATOR, "%s :is an IRC Operator"},
+/* 314 */
+    {RPL_WHOWASUSER, "%s %s %s * :%s"},
+/* 315 */
+    {RPL_ENDOFWHO, "%s :End of /WHO list."},
+/* 316 */
+    {0, (char *)NULL},
+/* 317 */
+    {RPL_WHOISIDLE, "%s %ld %ld :seconds idle, signon time"},
+/* 318 */
+    {RPL_ENDOFWHOIS, "%s :End of /WHOIS list."},
+/* 319 */
+    {RPL_WHOISCHANNELS, "%s :%s"},
+/* 320 */
+    {0, (char *)NULL},
+/* 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 " TIME_T_FMT},
+/* 330 */
+    {0, (char *)NULL},
+/* 331 */
+    {RPL_NOTOPIC, "%s :No topic is set."},
+/* 332 */
+    {RPL_TOPIC, "%s :%s"},
+/* 333 */
+    {RPL_TOPICWHOTIME, "%s %s " TIME_T_FMT},
+/* 334 */
+    {RPL_LISTUSAGE, ":%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 */
+    {0, (char *)NULL},
+/* 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"},
+/* 353 */
+    {RPL_NAMREPLY, "%s"},
+/* 354 */
+    {RPL_WHOSPCRPL, "%s"},
+/* 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 */
+#ifndef GODMODE
+    {RPL_LINKS, "%s %s :%d P%u %s"},
+#else /* GODMODE */
+    {RPL_LINKS, "%s %s :%d P%u " TIME_T_FMT " (%s) %s"},
+#endif /* GODMODE */
+/* 365 */
+    {RPL_ENDOFLINKS, "%s :End of /LINKS list."},
+/* 366 */
+    {RPL_ENDOFNAMES, "%s :End of /NAMES list."},
+/* 367 */
+    {RPL_BANLIST, "%s %s %s " TIME_T_FMT},
+/* 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 */
+    {0, (char *)NULL},
+/* 379 */
+    {0, (char *)NULL},
+/* 380 */
+    {0, (char *)NULL},
+/* 381 */
+    {RPL_YOUREOPER, ":You are now an IRC Operator"},
+/* 382 */
+    {RPL_REHASHING, "%s :Rehashing"},
+/* 383 */
+    {0, (char *)NULL},
+/* 384 */
+    {RPL_MYPORTIS, "%d :Port to local server is\r\n"},
+/* 385 */
+    {RPL_NOTOPERANYMORE, (char *)NULL},
+/* 386 */
+    {0, (char *)NULL},
+/* 387 */
+    {0, (char *)NULL},
+/* 388 */
+    {0, (char *)NULL},
+/* 389 */
+    {0, (char *)NULL},
+/* 390 */
+    {0, (char *)NULL},
+/* 391 */
+    {RPL_TIME, "%s " TIME_T_FMT " %ld :%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},
+/* 200 */
+#ifndef GODMODE
+    {RPL_TRACELINK, "Link %s%s %s %s"},
+#else /* GODMODE */
+    {RPL_TRACELINK, "Link %s%s %s %s " TIME_T_FMT},
+#endif /* GODMODE */
+/* 201 */
+    {RPL_TRACECONNECTING, "Try. %d %s"},
+/* 202 */
+    {RPL_TRACEHANDSHAKE, "H.S. %d %s"},
+/* 203 */
+    {RPL_TRACEUNKNOWN, "???? %d %s"},
+/* 204 */
+    {RPL_TRACEOPERATOR, "Oper %d %s %ld"},
+/* 205 */
+    {RPL_TRACEUSER, "User %d %s %ld"},
+/* 206 */
+    {RPL_TRACESERVER, "Serv %d %dS %dC %s %s!%s@%s %ld %ld"},
+/* 207 */
+    {0, (char *)NULL},
+/* 208 */
+    {RPL_TRACENEWTYPE, "<newtype> 0 %s"},
+/* 209 */
+    {RPL_TRACECLASS, "Class %d %d"},
+/* 210 */
+    {0, (char *)NULL},
+/* 211 */
+    {RPL_STATSLINKINFO, (char *)NULL},
+/* 212 */
+    {RPL_STATSCOMMANDS, "%s %u %u"},
+/* 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_STATSPLINE, "%c %d %d %#x"},
+/* 218 */
+    {RPL_STATSYLINE, "%c %d %d %d %d %ld"},
+/* 219 */
+    {RPL_ENDOFSTATS, "%c :End of /STATS report"},
+/* 220 */
+    {0, (char *)NULL},
+/* 221 */
+    {RPL_UMODEIS, "%s"},
+/* 222 */
+    {0, (char *)NULL},
+/* 223 */
+    {0, (char *)NULL},
+/* 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 */
+    {0, (char *)NULL},
+/* 237 */
+    {0, (char *)NULL},
+/* 238 */
+    {0, (char *)NULL},
+/* 239 */
+    {0, (char *)NULL},
+/* 240 */
+    {0, (char *)NULL},
+/* 241 */
+    {RPL_STATSLLINE, "%c %s * %s %d %d"},
+/* 242 */
+    {RPL_STATSUPTIME, ":Server Up %d days, %d:%02d:%02d"},
+/* 243 */
+    {RPL_STATSOLINE, "%c %s * %s %d %d"},
+/* 244 */
+    {RPL_STATSHLINE, "%c %s * %s %d %d"},
+/* 245 */
+    {0, (char *)NULL},
+/* 246 */
+    {RPL_STATSTLINE, "%c %s %s"},
+/* 247 */
+    {RPL_STATSGLINE, "%c %s@%s " TIME_T_FMT " :%s"},
+/* 248 */
+    {RPL_STATSULINE, "%c %s %s %s %d %d"},
+/* 249 */
+    {0, (char *)NULL},
+/* 250 */
+    {RPL_STATSCONN, ":Highest connection count: %d (%d clients)"},
+/* 251 */
+    {RPL_LUSERCLIENT, ":There are %d users and %d invisible on %d servers"},
+/* 252 */
+    {RPL_LUSEROP, "%d :operator(s) online"},
+/* 253 */
+    {RPL_LUSERUNKNOWN, "%d :unknown connection(s)"},
+/* 254 */
+    {RPL_LUSERCHANNELS, "%d :channels formed"},
+/* 255 */
+    {RPL_LUSERME, ":I have %d clients and %d servers"},
+/* 256 */
+    {RPL_ADMINME, ":Administrative info about %s"},
+/* 257 */
+    {RPL_ADMINLOC1, ":%s"},
+/* 258 */
+    {RPL_ADMINLOC2, ":%s"},
+/* 259 */
+    {RPL_ADMINEMAIL, ":%s"},
+/* 260 */
+    {0, (char *)NULL},
+/* 261 */
+    {RPL_TRACELOG, "File %s %d"},
+/* 262 */
+    {RPL_TRACEPING, "Ping %s %s"},
+/* 263 */
+    {0, (char *)NULL},
+/* 264 */
+    {0, (char *)NULL},
+/* 265 */
+    {0, (char *)NULL},
+/* 266 */
+    {0, (char *)NULL},
+/* 267 */
+    {0, (char *)NULL},
+/* 268 */
+    {0, (char *)NULL},
+/* 269 */
+    {0, (char *)NULL},
+/* 270 */
+    {0, (char *)NULL},
+/* 271 */
+    {RPL_SILELIST, "%s %s"},
+/* 272 */
+    {RPL_ENDOFSILELIST, "%s :End of Silence List"},
+/* 273 */
+    {0, (char *)NULL},
+/* 274 */
+    {0, (char *)NULL},
+/* 275 */
+    {RPL_STATSDLINE, "%c %s %s"},
+/* 276 */
+    {0, (char *)NULL},
+/* 277 */
+    {0, (char *)NULL},
+/* 278 */
+    {0, (char *)NULL},
+/* 279 */
+    {0, (char *)NULL},
+/* 280 */
+    {RPL_GLIST, "%s@%s " TIME_T_FMT " %s%s"},
+/* 281 */
+    {RPL_ENDOFGLIST, ":End of G-line List"}
+};
+
+/* *INDENT-ON* */
+
+static char numbuff[512];
+
+/* "inline" */
+#define prepbuf(buffer, num, tail)                     \
+{                                                      \
+  register char *s = buffer + 4;                       \
+  register const char *ap = atoi_tab + (num << 2);     \
+                                                       \
+  strcpy(buffer, ":%s 000 %s ");                       \
+  *s++ = *ap++;                                                \
+  *s++ = *ap++;                                                \
+  *s = *ap;                                            \
+  strcpy(s + 5, tail);                                 \
+}
+
+char *err_str(int numeric)
+{
+  Reg1 Numeric *nptr;
+  Reg2 int num = numeric;
+
+  num -= numeric_errors[0].num_val;
+
+#ifdef DEBUGMODE
+  if (num < 0 || num > ERR_USERSDONTMATCH)
+    sprintf_irc(numbuff,
+       ":%%s %d %%s :INTERNAL ERROR: BAD NUMERIC! %d", numeric, num);
+  else
+  {
+    nptr = &numeric_errors[num];
+    if (!nptr->num_form || !nptr->num_val)
+      sprintf_irc(numbuff, ":%%s %d %%s :NO ERROR FOR NUMERIC ERROR %d",
+         numeric, num);
+    else
+      prepbuf(numbuff, nptr->num_val, nptr->num_form);
+  }
+#else
+  nptr = &numeric_errors[num];
+  prepbuf(numbuff, nptr->num_val, nptr->num_form);
+#endif
+
+  return numbuff;
+}
+
+char *rpl_str(int numeric)
+{
+  Reg1 Numeric *nptr;
+  Reg2 int num = numeric;
+
+  if (num > (int)(sizeof(local_replies) / sizeof(Numeric) - 2))
+    num -= (num > 300) ? 300 : 100;
+
+#ifdef DEBUGMODE
+  if (num < 0 || num > 200)
+    sprintf_irc(numbuff, ":%%s %d %%s :INTERNAL REPLY ERROR: BAD NUMERIC! %d",
+       numeric, num);
+  else
+  {
+    if (numeric > 99)
+      nptr = &numeric_replies[num];
+    else
+      nptr = &local_replies[num];
+    Debug((DEBUG_NUM, "rpl_str: numeric %d num %d nptr %p %d %p",
+       numeric, num, nptr, nptr->num_val, nptr->num_form));
+    if (!nptr->num_form || !nptr->num_val)
+      sprintf_irc(numbuff, ":%%s %d %%s :NO REPLY FOR NUMERIC ERROR %d",
+         numeric, num);
+    else
+      prepbuf(numbuff, nptr->num_val, nptr->num_form);
+  }
+#else
+  if (numeric > 99)
+    nptr = &numeric_replies[num];
+  else
+    nptr = &local_replies[num];
+  prepbuf(numbuff, nptr->num_val, nptr->num_form);
+#endif
+
+  return numbuff;
+}
diff --git a/ircd/s_misc.c b/ircd/s_misc.c
new file mode 100644 (file)
index 0000000..fa9b078
--- /dev/null
@@ -0,0 +1,699 @@
+/*
+ * 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.h"
+#include <sys/stat.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "common.h"
+#include "match.h"
+#include "hash.h"
+#include "s_bsd.h"
+#include "res.h"
+#include "list.h"
+#include "ircd.h"
+#include "s_ping.h"
+#include "channel.h"
+#include "s_err.h"
+#include "support.h"
+#include "userload.h"
+#include "parse.h"
+#include "s_user.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+
+#include <assert.h>
+
+RCSTAG_CC("$Id$");
+
+static void exit_one_client(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(time_t clock)
+{
+  static char buf[80], plus;
+  Reg1 struct tm *lt, *gm;
+  struct tm gmbuf;
+  int minswest;
+
+  if (!clock)
+    clock = now;
+  gm = gmtime(&clock);
+  memcpy(&gmbuf, gm, 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;
+
+  sprintf(buf, "%s %s %d %d -- %02d:%02d %c%02d:%02d",
+      weekdays[lt->tm_wday], months[lt->tm_mon], lt->tm_mday,
+      1900 + lt->tm_year, lt->tm_hour, lt->tm_min,
+      plus, minswest / 60, minswest % 60);
+
+  return buf;
+}
+
+/*
+ * 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(time_t value)
+{
+  static char buf[28];
+  Reg1 char *p;
+
+  strcpy(buf, ctime(&value));
+  if ((p = strchr(buf, '\n')) != NULL)
+    *p = '\0';
+
+  return buf;
+}
+
+/*
+ *  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(aClient *sptr, int showip)
+{
+  static char nbuf[HOSTLEN * 2 + USERLEN + 5];
+
+  if (MyConnect(sptr))
+  {
+    if (IsUnixSocket(sptr))
+    {
+      if (showip)
+       sprintf_irc(nbuf, "%s[%s]", sptr->name, sptr->sockhost);
+      else
+       sprintf_irc(nbuf, "%s[%s]", sptr->name, me.sockhost);
+    }
+    else
+    {
+      if (showip)
+       sprintf_irc(nbuf, "%s[%s@%s]", sptr->name,
+           (!(sptr->flags & FLAGS_GOTID)) ? "" :
+           sptr->username, inetntoa(sptr->ip));
+      else
+      {
+       if (strCasediff(sptr->name, sptr->sockhost))
+         sprintf_irc(nbuf, "%s[%s]", sptr->name, sptr->sockhost);
+       else
+         return sptr->name;
+      }
+    }
+    return nbuf;
+  }
+  return sptr->name;
+}
+
+char *get_client_host(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))
+    sprintf_irc(nbuf, "%s[%s]", cptr->name, me.name);
+  else
+    sprintf(nbuf, "%s[%-.*s@%-.*s]", cptr->name, USERLEN,
+       (!(cptr->flags & FLAGS_GOTID)) ? "" : 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(aClient *cptr, char *host)
+{
+  Reg3 char *s;
+  if ((s = strchr(host, '@')))
+    s++;
+  else
+    s = host;
+  strncpy(cptr->sockhost, s, sizeof(cptr->sockhost) - 1);
+}
+
+/*
+ * Return wildcard name of my server name according to given config entry
+ * --Jto
+ */
+char *my_name_for_link(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 = strchr(name, '.');
+  }
+  if (!name)
+    return start;
+
+  namebuf[0] = '*';
+  strncpy(&namebuf[1], name, HOSTLEN - 1);
+  namebuf[HOSTLEN - 1] = '\0';
+
+  return namebuf;
+}
+
+/*
+ * exit_downlinks - added by Run 25-9-94
+ *
+ * Removes all clients and downlinks (+clients) of any server
+ * QUITs are generated and sent to local users.
+ *
+ * cptr    : server that must have all dependents removed
+ * sptr    : source who thought that this was a good idea
+ * comment : comment sent as sign off message to local clients
+ */
+static void exit_downlinks(aClient *cptr, aClient *sptr, char *comment)
+{
+  Reg1 aClient *acptr;
+  Reg2 Dlink *next;
+  Reg3 Dlink *lp;
+  aClient **acptrp;
+  int i;
+
+  /* Run over all its downlinks */
+  for (lp = cptr->serv->down; lp; lp = next)
+  {
+    next = lp->next;
+    acptr = lp->value.cptr;
+    /* Remove the downlinks and client of the downlink */
+    exit_downlinks(acptr, sptr, comment);
+    /* Remove the downlink itself */
+    exit_one_client(acptr, me.name);
+  }
+  /* Remove all clients of this server */
+  acptrp = cptr->serv->client_list;
+  for (i = 0; i <= cptr->serv->nn_mask; ++acptrp, ++i)
+    if (*acptrp)
+      exit_one_client(*acptrp, comment);
+}
+
+/*
+ * exit_client, rewritten 25-9-94 by Run
+ *
+ * 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.
+ *
+ * This function implicitly exits all other clients depending on
+ * this connection.
+ *
+ * For convenience, this function returns a suitable value for
+ * m_funtion return value:
+ *
+ *   CPTR_KILLED     if (cptr == bcptr)
+ *   0                if (cptr != bcptr)
+ *
+ * This function can be called in two ways:
+ * 1) From before or in parse(), exitting the 'cptr', in which case it was
+ *    invoked as exit_client(cptr, cptr, &me,...), causing it to always
+ *    return CPTR_KILLED.
+ * 2) Via parse from a m_function call, in which case it was invoked as
+ *    exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client
+ *    that generated the message in a way that we can assume he already
+ *    did remove acptr from memory himself (or in other cases we don't mind
+ *    because he will be delinked.) Or invoked as:
+ *    exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should
+ *    be removed.
+ * In general: No generated SQUIT or QUIT should be sent to source link
+ * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too).
+ *
+ * --Run
+ */
+int exit_client(aClient *cptr, /* Connection being handled by
+                                  read_message right now */
+    aClient *bcptr,            /* Client being killed */
+    aClient *sptr,             /* The client that made the decision
+                                  to remove this one, never NULL */
+    char *comment)             /* Reason for the exit */
+{
+  Reg1 aClient *acptr;
+  Reg3 Dlink *dlp;
+#ifdef FNAME_USERLOG
+  time_t on_for;
+#endif
+  char comment1[HOSTLEN + HOSTLEN + 2];
+
+  if (MyConnect(bcptr))
+  {
+    bcptr->flags |= FLAGS_CLOSING;
+#ifdef ALLOW_SNO_CONNEXIT
+#ifdef SNO_CONNEXIT_IP
+    if (IsUser(bcptr))
+    {
+      sprintf_irc(sendbuf,
+         ":%s NOTICE * :*** Notice -- Client exiting: %s (%s@%s) [%s] [%s]",
+         me.name, bcptr->name, bcptr->user->username, bcptr->user->host,
+         comment, inetntoa(bcptr->ip));
+      sendbufto_op_mask(SNO_CONNEXIT);
+    }
+#else /* SNO_CONNEXIT_IP */
+    if (IsUser(bcptr))
+    {
+      sprintf_irc(sendbuf,
+         ":%s NOTICE * :*** Notice -- Client exiting: %s (%s@%s) [%s]",
+         me.name, bcptr->name, bcptr->user->username, bcptr->user->host,
+         comment);
+      sendbufto_op_mask(SNO_CONNEXIT);
+    }
+#endif /* SNO_CONNEXIT_IP */
+#endif /* ALLOW_SNO_CONNEXIT */
+    update_load();
+#ifdef FNAME_USERLOG
+    on_for = now - bcptr->firsttime;
+#if defined(USE_SYSLOG) && defined(SYSLOG_USERS)
+    if (IsUser(bcptr))
+      syslog(LOG_NOTICE, "%s (%3d:%02d:%02d): %s@%s (%s)\n",
+         myctime(bcptr->firsttime), on_for / 3600, (on_for % 3600) / 60,
+         on_for % 60, bcptr->user->username, bcptr->sockhost, bcptr->name);
+#else
+    if (IsUser(bcptr))
+      write_log(FNAME_USERLOG,
+         "%s (%3d:%02d:%02d): %s@%s [%s]\n",
+         myctime(bcptr->firsttime),
+         on_for / 3600, (on_for % 3600) / 60,
+         on_for % 60,
+         bcptr->user->username, bcptr->user->host, bcptr->username);
+#endif
+#endif
+    if (bcptr != sptr->from    /* The source knows already */
+       && IsClient(bcptr))     /* Not a Ping struct or Log file */
+    {
+      if (IsServer(bcptr) || IsHandshake(bcptr))
+       sendto_one(bcptr, ":%s SQUIT %s 0 :%s", sptr->name, me.name, comment);
+      else if (!IsConnecting(bcptr))
+       sendto_one(bcptr, "ERROR :Closing Link: %s by %s (%s)",
+           get_client_name(bcptr, FALSE), sptr->name, comment);
+      if ((IsServer(bcptr) || IsHandshake(bcptr) || IsConnecting(bcptr)) &&
+         (sptr == &me || (IsServer(sptr) &&
+         (strncmp(comment, "Leaf-only link", 14) ||
+         strncmp(comment, "Non-Hub link", 12)))))
+      {
+       if (bcptr->serv->user && *bcptr->serv->by &&
+           (acptr = findNUser(bcptr->serv->by)) &&
+           acptr->user == bcptr->serv->user)
+       {
+         if (MyUser(acptr) || Protocol(acptr->from) < 10)
+           sendto_one(acptr,
+               ":%s NOTICE %s :Link with %s cancelled: %s",
+               me.name, acptr->name, bcptr->name, comment);
+         else
+           sendto_one(acptr,
+               "%s NOTICE %s%s :Link with %s cancelled: %s",
+               NumServ(&me), NumNick(acptr), bcptr->name, comment);
+       }
+       else
+         acptr = NULL;
+       if (sptr == &me)
+         sendto_lops_butone(acptr, "Link with %s cancelled: %s",
+             bcptr->name, comment);
+      }
+    }
+    /*
+     *  Close the Client connection first.
+     */
+    close_connection(bcptr);
+  }
+
+  if (IsServer(bcptr))
+  {
+    strcpy(comment1, bcptr->serv->up->name);
+    strcat(comment1, " ");
+    strcat(comment1, bcptr->name);
+    if (IsUser(sptr))
+      sendto_lops_butone(sptr, "%s SQUIT by %s [%s]:",
+         (sptr->user->server == bcptr ||
+         sptr->user->server == bcptr->serv->up) ? "Local" : "Remote",
+         get_client_name(sptr, TRUE), sptr->user->server->name);
+    else if (sptr != &me && bcptr->serv->up != sptr)
+      sendto_ops("Received SQUIT %s from %s :", bcptr->name,
+         IsServer(sptr) ? sptr->name : get_client_name(sptr, TRUE));
+    sendto_op_mask(SNO_NETWORK, "Net break: %s (%s)", comment1, comment);
+  }
+
+  /*
+   * First generate the needed protocol for the other server links
+   * except the source:
+   */
+  for (dlp = me.serv->down; dlp; dlp = dlp->next)
+    if (dlp->value.cptr != sptr->from && dlp->value.cptr != bcptr)
+    {
+      if (IsServer(bcptr))
+       sendto_one(dlp->value.cptr, ":%s SQUIT %s " TIME_T_FMT " :%s",
+           sptr->name, bcptr->name, bcptr->serv->timestamp, comment);
+      else if (IsUser(bcptr) && (bcptr->flags & FLAGS_KILLED) == 0)
+       sendto_one(dlp->value.cptr, ":%s QUIT :%s", bcptr->name, comment);
+    }
+
+  /* Then remove the client structures */
+  if (IsServer(bcptr))
+    exit_downlinks(bcptr, sptr, comment1);
+  exit_one_client(bcptr, comment);
+
+  /*
+   *  cptr can only have been killed if it was cptr itself that got killed here,
+   *  because cptr can never have been a dependant of bcptr    --Run
+   */
+  return (cptr == bcptr) ? CPTR_KILLED : 0;
+}
+
+/*
+ * Exit client with formatted message, added 25-9-94 by Run
+ */
+int vexit_client_msg(aClient *cptr, aClient *bcptr, aClient *sptr,
+    char *pattern, va_list vl)
+{
+  char msgbuf[1024];
+  vsprintf_irc(msgbuf, pattern, vl);
+  return exit_client(cptr, bcptr, sptr, msgbuf);
+}
+
+int exit_client_msg(aClient *cptr, aClient *bcptr,
+    aClient *sptr, char *pattern, ...)
+{
+  va_list vl;
+  char msgbuf[1024];
+
+  va_start(vl, pattern);
+  vsprintf_irc(msgbuf, pattern, vl);
+  va_end(vl);
+
+  return exit_client(cptr, bcptr, sptr, msgbuf);
+}
+
+/*
+ * Exit one client, local or remote. Assuming for local client that
+ * all dependants already have been removed, and socket is closed.
+ *
+ * Rewritten by Run - 24 sept 94
+ *
+ * bcptr : client being (s)quitted
+ * sptr : The source (prefix) of the QUIT or SQUIT
+ *
+ * --Run
+ */
+static void exit_one_client(aClient *bcptr, char *comment)
+{
+  Link *lp;
+
+  if (bcptr->serv && bcptr->serv->client_list) /* Was SetServerYXX called ? */
+    ClearServerYXX(bcptr);     /* Removes server from server_list[] */
+  if (IsUser(bcptr))
+  {
+    /* Stop a running /LIST clean */
+    if (MyUser(bcptr) && bcptr->listing)
+    {
+      bcptr->listing->chptr->mode.mode &= ~MODE_LISTED;
+      RunFree(bcptr->listing);
+      bcptr->listing = NULL;
+    }
+
+    if (AskedPing(bcptr))
+      cancel_ping(bcptr, NULL);
+    /*
+     * 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*)
+     */
+    sendto_common_channels(bcptr, ":%s QUIT :%s", bcptr->name, comment);
+
+    while ((lp = bcptr->user->channel))
+      remove_user_from_channel(bcptr, lp->value.chptr);
+
+    /* Clean up invitefield */
+    while ((lp = bcptr->user->invited))
+      del_invite(bcptr, lp->value.chptr);
+
+    /* Clean up silencefield */
+    while ((lp = bcptr->user->silence))
+      del_silence(bcptr, lp->value.cp);
+
+    if (IsInvisible(bcptr))
+      --nrof.inv_clients;
+    if (IsOper(bcptr))
+      --nrof.opers;
+    if (MyConnect(bcptr))
+      Count_clientdisconnects(bcptr, nrof);
+    else
+      Count_remoteclientquits(nrof);
+  }
+  else if (IsServer(bcptr))
+  {
+    /* Remove downlink list node of uplink */
+    remove_dlink(&bcptr->serv->up->serv->down, bcptr->serv->updown);
+
+    if (MyConnect(bcptr))
+      Count_serverdisconnects(nrof);
+    else
+      Count_remoteserverquits(nrof);
+  }
+  else if (IsPing(bcptr))      /* Apperently, we are closing ALL links */
+  {
+    del_queries((char *)bcptr);
+    end_ping(bcptr);
+    return;
+  }
+  else if (IsMe(bcptr))
+  {
+    sendto_ops("ERROR: tried to exit me! : %s", comment);
+    return;                    /* ...must *never* exit self! */
+  }
+  else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr))
+    Count_unknowndisconnects(nrof);
+
+  /* Update IPregistry */
+  if (IsIPChecked(bcptr))
+    IPcheck_disconnect(bcptr);
+
+  /* 
+   * Remove from serv->client_list
+   * NOTE: user is *always* NULL if this is a server
+   */
+  if (bcptr->user)
+  {
+    assert(!IsServer(bcptr));
+    /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */
+    RemoveYXXClient(bcptr->user->server, bcptr->yxx);
+  }
+
+  /* Remove bcptr from the client list */
+#ifdef DEBUGMODE
+  if (hRemClient(bcptr) != 0)
+    Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p",
+       bcptr, bcptr->name, bcptr->from ? bcptr->from->sockhost : "??host",
+       bcptr->from, bcptr->next, bcptr->prev, bcptr->fd,
+       bcptr->status, bcptr->user));
+#else
+  hRemClient(bcptr);
+#endif
+  remove_client_from_list(bcptr);
+  return;
+}
+
+void checklist(void)
+{
+  Reg1 aClient *acptr;
+  Reg2 int i, j;
+
+  if (!(bootopt & BOOT_AUTODIE))
+    return;
+  for (j = i = 0; i <= highest_fd; i++)
+    if (!(acptr = loc_clients[i]))
+      continue;
+    else if (IsUser(acptr))
+      j++;
+  if (!j)
+  {
+#ifdef USE_SYSLOG
+    syslog(LOG_WARNING, "ircd exiting: autodie");
+#endif
+    exit(0);
+  }
+  return;
+}
+
+void initstats(void)
+{
+  memset(&ircst, 0, sizeof(ircst));
+}
+
+void tstats(aClient *cptr, char *name)
+{
+  Reg1 aClient *acptr;
+  Reg2 int i;
+  Reg3 struct stats *sp;
+  struct stats tmp;
+
+  sp = &tmp;
+  memcpy(sp, ircstp, sizeof(*sp));
+  for (i = 0; i < MAXCONNECTIONS; i++)
+  {
+    if (!(acptr = loc_clients[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 (IsUser(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 :auth successes %u fails %u",
+      me.name, RPL_STATSDEBUG, name, sp->is_asuc, sp->is_abad);
+  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 " TIME_T_FMT " " TIME_T_FMT,
+      me.name, RPL_STATSDEBUG, name, sp->is_cti, sp->is_sti);
+}
diff --git a/ircd/s_numeric.c b/ircd/s_numeric.c
new file mode 100644 (file)
index 0000000..7cf0fef
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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 "sys.h"
+#include "h.h"
+#include "struct.h"
+#include "s_serv.h"
+#include "s_bsd.h"
+#include "send.h"
+#include "support.h"
+#include "parse.h"
+#include "numeric.h"
+#include "channel.h"
+#include "ircd.h"
+#include "hash.h"
+#include "numnicks.h"
+#include "s_numeric.h"
+
+RCSTAG_CC("$Id$");
+
+static char buffer[1024];
+
+/*
+ * do_numeric()
+ * Rewritten by Nemesi, Jan 1999, to support numeric nicks in parv[1]
+ *
+ * Called when we get a numeric message from a remote _server_ and we are
+ * supposed to forward it somewhere. Note that we always ignore numerics sent
+ * to 'me' and simply drop the message if we can't handle with this properly:
+ * the savy approach is NEVER generate an error in response to an... error :)
+ */
+
+int do_numeric(int numeric, int nnn, aClient *cptr, aClient *sptr,
+    int parc, char *parv[])
+{
+  aClient *acptr = NULL;
+  aChannel *achptr = NULL;
+  char *p, *b;
+  int i;
+
+  /* Avoid trash, we need it to come from a server and have a target  */
+  if ((parc < 2) || !IsServer(sptr))
+    return 0;
+
+  /* Who should receive this message ? Will we do something with it ?
+     Note that we use findUser functions, so the target can't be neither
+     a server, nor a channel (?) nor a list of targets (?) .. u2.10
+     should never generate numeric replies to non-users anyway
+     Ahem... it can be a channel actually, csc bots use it :\ --Nem */
+
+  if (IsChannelName(parv[1]))
+    achptr = FindChannel(parv[1]);
+  else
+    acptr = (nnn) ? (findNUser(parv[1])) : (FindUser(parv[1]));
+
+  if (((!acptr) || (acptr->from == cptr)) && !achptr)
+    return 0;
+
+  /* Remap low number numerics, not that I understand WHY.. --Nemesi  */
+  if (numeric < 100)
+    numeric += 100;
+
+  /* Rebuild the buffer with all the parv[] without wasting cycles :) */
+  b = buffer;
+  if (parc > 2)
+  {
+    for (i = 2; i < (parc - 1); i++)
+      for (*b++ = ' ', p = parv[i]; *p; p++)
+       *b++ = *p;
+    for (*b++ = ' ', *b++ = ':', p = parv[parc - 1]; *p; p++)
+      *b++ = *p;
+  }
+  *b = '\000';
+
+  /* Since .06 this will implicitly use numeric nicks when needed     */
+
+  if (acptr)
+    sendto_prefix_one(acptr, sptr, ":%s %d %s%s",
+       sptr->name, numeric, acptr->name, buffer);
+  else
+    sendto_channel_butone(cptr, sptr, achptr, ":%s %d %s%s",
+       sptr->name, numeric, achptr->chname, buffer);
+
+  return 0;
+}
diff --git a/ircd/s_ping.c b/ircd/s_ping.c
new file mode 100644 (file)
index 0000000..aa10dd9
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ * IRC - Internet Relay Chat, ircd/s_ping.c
+ * Copyright (C) 1994 Carlo Wood ( 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 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.
+ */
+
+#include "sys.h"
+#include <sys/socket.h>
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef UNIXPORT
+#include <sys/un.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "h.h"
+#include "struct.h"
+#include "send.h"
+#include "s_conf.h"
+#include "match.h"
+#include "res.h"
+#include "s_bsd.h"
+#include "s_serv.h"
+#include "ircd.h"
+#include "s_ping.h"
+#include "support.h"
+#include "numeric.h"
+#include "s_user.h"
+#include "s_err.h"
+#include "common.h"
+#include "s_user.h"
+#include "numnicks.h"
+
+RCSTAG_CC("$Id$");
+
+#define UPINGBUFSIZE 2000      /* Lot bigger then 1024,
+                                  bit smaller then 2048 */
+#define UPINGTIMEOUT 120       /* Timeout waitting for first ping response */
+
+/*
+ * start_ping
+ *
+ * As for now, I am abusing the client structure for a ping connection.
+ *                 .... And I really don't like this solution --Nemesi
+ * 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
+ *   sendB    : Number of pings left to send.
+ *   receiveB : Number of pings left to be received.
+ *   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(aClient *cptr)
+{
+  struct sockaddr_in remote_addr;
+
+  Debug((DEBUG_NOTICE, "start_ping(%p) status %d", cptr, cptr->status));
+
+  if (!(cptr->acpt))
+    return -1;
+
+  memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
+#ifdef TESTNET
+  remote_addr.sin_port = htons(cptr->port + 10000);
+#else
+  remote_addr.sin_port = htons(cptr->port);
+#endif
+  remote_addr.sin_family = AF_INET;
+
+  if (MyUser(cptr->acpt) || Protocol(cptr->acpt->from) < 10)
+  {
+    sendto_one(cptr->acpt,
+       ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %u",
+       me.name, cptr->acpt->name, cptr->hopcount,
+       (cptr->hopcount == 1) ? "" : "s", cptr->name,
+#ifdef TESTNET
+       inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000);
+#else
+       inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
+#endif
+  }
+  else
+  {
+    sendto_one(cptr->acpt,
+       "%s NOTICE %s%s :Sending %d ping%s to %s[%s] port %u",
+       NumServ(&me), NumNick(cptr->acpt), cptr->hopcount,
+       (cptr->hopcount == 1) ? "" : "s", cptr->name,
+#ifdef TESTNET
+       inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000);
+#else
+       inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
+#endif
+  }
+
+  cptr->firsttime = now + UPINGTIMEOUT;
+  cptr->since = UPINGTIMEOUT;
+  cptr->flags |= (FLAGS_PING);
+
+  return 0;
+}
+
+/*
+ * send_ping
+ *
+ */
+void send_ping(aClient *cptr)
+{
+  struct sockaddr_in remote_addr;
+  struct timeval tv;
+
+  memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
+#ifdef TESTNET
+  remote_addr.sin_port = htons(cptr->port + 10000);
+#else
+  remote_addr.sin_port = htons(cptr->port);
+#endif
+  remote_addr.sin_family = AF_INET;
+
+  gettimeofday(&tv, NULL);
+#if defined(__sun__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
+  sprintf((char *)cptr->confs, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
+#else
+  sprintf((char *)cptr->confs, " %10u%c%6u", tv.tv_sec, '\0', tv.tv_usec);
+#endif
+
+  Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
+      (char *)cptr->confs, (char *)cptr->confs + 12,
+      inetntoa(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)
+  {
+#ifdef DEBUGMODE
+    int err = errno;
+#endif
+    if (cptr->acpt)
+    {
+      if (MyUser(cptr->acpt)
+#ifndef NO_PROTOCOL9
+         || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10)
+#endif
+         )
+       sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: sendto() failed: %s",
+           me.name, cptr->acpt->name, strerror(get_sockerr(cptr)));
+      else
+       sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING: sendto() failed: %s",
+           NumServ(&me), NumNick(cptr->acpt), strerror(get_sockerr(cptr)));
+    }
+    Debug((DEBUG_SEND, "send_ping: sendto failed on %d (%d)", cptr->fd, err));
+    end_ping(cptr);
+  }
+  else if (--(cptr->sendB) <= 0)
+  {
+    ClearPing(cptr);
+    if (cptr->receiveB <= 0)
+      end_ping(cptr);
+  }
+
+  return;
+}
+
+/*
+ * read_ping
+ */
+void read_ping(aClient *cptr)
+{
+  size_t addr_len = sizeof(struct sockaddr_in);
+  struct sockaddr_in remote_addr;
+  struct timeval tv;
+  int len;
+  unsigned long int pingtime;
+  char *s;
+
+  memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
+#ifdef TESTNET
+  remote_addr.sin_port = htons(cptr->port + 10000);
+#else
+  remote_addr.sin_port = htons(cptr->port);
+#endif
+  remote_addr.sin_family = AF_INET;
+
+  gettimeofday(&tv, NULL);
+
+  if ((len = recvfrom(cptr->fd, (char *)cptr->confs, UPINGBUFSIZE, 0,
+      (struct sockaddr *)&remote_addr, &addr_len)) == -1)
+  {
+    int err = errno;
+    if (MyUser(cptr->acpt)
+#ifndef NO_PROTOCOL9
+       || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10)
+#endif
+       )
+      sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: recvfrom: %s",
+         me.name, cptr->acpt->name, strerror(get_sockerr(cptr)));
+    else
+      sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING: recvfrom: %s",
+         NumServ(&me), NumNick(cptr->acpt), 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->receiveB + 1))) < 2)
+    cptr->since = 2;
+  cptr->firsttime = tv.tv_sec + cptr->since;
+
+  Debug((DEBUG_SEND, "read_ping: %d bytes, ti " TIME_T_FMT ": [%s %s] %lu 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->receiveB) <= 0 && !DoPing(cptr)) || !(cptr->acpt))
+    end_ping(cptr);
+
+  return;
+}
+
+int ping_server(aClient *cptr)
+{
+  if ((!cptr->ip.s_addr)
+#ifdef UNIXPORT
+      && ((cptr->sockhost[2]) != '/')
+#endif
+      )
+  {
+    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 = strchr(cptr->sockhost, '@');
+    s++;                       /* should never be NULL;
+                                  cptr->sockhost is actually a conf->host */
+    if ((cptr->ip.s_addr = inet_addr(s)) == INADDR_NONE)
+    {
+      cptr->ip.s_addr = INADDR_ANY;
+      hp = gethost_byname(s, &lin);
+      Debug((DEBUG_NOTICE, "ping_sv: hp %p ac %p ho %s", hp, cptr, s));
+      if (!hp)
+       return 0;
+      memcpy(&cptr->ip, hp->h_addr, 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(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aConfItem *aconf;
+  unsigned short int port;
+  int fd, opt;
+
+  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 (MyUser(sptr))
+  {
+    if (parc == 2)
+    {
+      parv[parc++] = UDP_PORT;
+      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] = UDP_PORT;
+      }
+      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] = UDP_PORT;
+      }
+    }
+  }
+  if (hunt_server(1, cptr, sptr,
+      ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
+    return 0;
+
+  if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :UPING: Invalid number of packets: %s",
+         me.name, parv[0], parv[4]);
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :UPING: Invalid number of packets: %s",
+         NumServ(&me), NumNick(sptr), 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], strchr(aconf->host, '@') + 1) == 0))
+       break;
+  if (!aconf)
+  {
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
+         me.name, parv[0], parv[1]);
+    else
+      sendto_one(sptr,
+         "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
+         NumServ(&me), NumNick(sptr), parv[1]);
+    return 0;
+  }
+
+  if (AskedPing(sptr))
+    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(UDP_PORT);
+
+  alarm(2);
+  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+  {
+    int err = errno;
+    alarm(0);
+    sendto_ops("m_uping: socket: %s", (err != EAGAIN) ?
+       strerror(err) : "No more sockets");
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :UPING: Unable to create udp ping socket",
+         me.name, parv[0]);
+    else
+      sendto_one(sptr,
+         "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
+         NumServ(&me), NumNick(sptr));
+#ifdef USE_SYSLOG
+    syslog(LOG_ERR, "Unable to create udp ping socket");
+#endif
+    return 0;
+  }
+  alarm(0);
+
+  if (fcntl(fd, F_SETFL, FNDELAY) == -1)
+  {
+    sendto_ops("m_uping: fcntl FNDELAY: %s", strerror(errno));
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
+         me.name, parv[0]);
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
+         NumServ(&me), NumNick(sptr));
+    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));
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :UPING: error in setsockopt: %s",
+         me.name, parv[0], strerror(err));
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :UPING: error in setsockopt: %s",
+         NumServ(&me), NumNick(sptr), strerror(err));
+    close(fd);
+    return 0;
+  }
+
+  if (fd >= MAXCONNECTIONS)
+  {
+    sendto_ops("Can't allocate fd for uping (all connections in use)");
+    if (MyUser(sptr) || Protocol(cptr) < 10)
+      sendto_one(sptr, ":%s NOTICE %s :UPING: All connections in use",
+         me.name, parv[0]);
+    else
+      sendto_one(sptr, "%s NOTICE %s%s :UPING: All connections in use",
+         NumServ(&me), NumNick(sptr));
+    close(fd);
+    return 0;
+  }
+
+  if (fd > highest_fd)
+    highest_fd = fd;
+  loc_clients[fd] = cptr = make_client(NULL, STAT_PING);
+  cptr->confs = (Link *)RunMalloc(UPINGBUFSIZE);       /* Really a (char *) */
+  cptr->fd = fd;
+  cptr->port = port;
+  cptr->hopcount = cptr->receiveB = cptr->sendB = MIN(20, atoi(parv[4]));
+  strcpy(cptr->sockhost, aconf->host);
+  cptr->acpt = sptr;
+  SetAskedPing(sptr);
+  memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
+  strcpy(cptr->name, aconf->name);
+  cptr->firsttime = 0;
+
+  switch (ping_server(cptr))
+  {
+    case 0:
+      break;
+    case -1:
+      del_queries((char *)cptr);
+      end_ping(cptr);
+      break;
+  }
+  return 0;
+}
+
+void end_ping(aClient *cptr)
+{
+  Debug((DEBUG_DEBUG, "end_ping: %p", cptr));
+  if (cptr->acpt)
+  {
+    if (MyUser(cptr->acpt)
+       || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10))
+    {
+      if (cptr->firsttime)     /* Started at all ? */
+      {
+       if (cptr->receiveB != cptr->hopcount)   /* Received any pings at all? */
+       {
+         sendto_one(cptr->acpt, ":%s NOTICE %s :UPING %s%s",
+             me.name, cptr->acpt->name, cptr->name, cptr->buffer);
+         sendto_one(cptr->acpt,
+             ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
+             "min/avg/max = %u/%u/%u ms",
+             me.name, cptr->acpt->name, cptr->hopcount - cptr->sendB,
+             cptr->hopcount - cptr->receiveB, cptr->receiveK,
+             (2 * cptr->sendM + cptr->hopcount - cptr->receiveB) /
+             (2 * (cptr->hopcount - cptr->receiveB)), cptr->receiveM);
+       }
+       else
+         sendto_one(cptr->acpt,
+             ":%s NOTICE %s :UPING: no response from %s within %d seconds",
+             me.name, cptr->acpt->name, cptr->name,
+             (int)(now + cptr->since - cptr->firsttime));
+      }
+      else
+       sendto_one(cptr->acpt,
+           ":%s NOTICE %s :UPING: Could not start ping to %s %u",
+           me.name, cptr->acpt->name, cptr->name, cptr->port);
+    }
+    else
+    {
+      if (cptr->firsttime)     /* Started at all ? */
+      {
+       if (cptr->receiveB != cptr->hopcount)   /* Received any pings at all? */
+       {
+         sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING %s%s",
+             NumServ(&me), NumNick(cptr->acpt), cptr->name, cptr->buffer);
+         sendto_one(cptr->acpt,
+             "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
+             "min/avg/max = %u/%u/%u ms",
+             NumServ(&me), NumNick(cptr->acpt), cptr->hopcount - cptr->sendB,
+             cptr->hopcount - cptr->receiveB, cptr->receiveK,
+             (2 * cptr->sendM + cptr->hopcount - cptr->receiveB) /
+             (2 * (cptr->hopcount - cptr->receiveB)), cptr->receiveM);
+       }
+       else
+         sendto_one(cptr->acpt,
+             "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
+             NumServ(&me), NumNick(cptr->acpt), cptr->name,
+             (int)(now + cptr->since - cptr->firsttime));
+      }
+      else
+       sendto_one(cptr->acpt,
+           "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
+           NumServ(&me), NumNick(cptr->acpt), cptr->name, cptr->port);
+    }
+  }
+  close(cptr->fd);
+  loc_clients[cptr->fd] = NULL;
+  if (cptr->acpt)
+    ClearAskedPing(cptr->acpt);
+  RunFree((char *)cptr->confs);
+  free_client(cptr);
+}
+
+void cancel_ping(aClient *sptr, aClient *acptr)
+{
+  int i;
+  aClient *cptr;
+
+  Debug((DEBUG_DEBUG, "Cancelling uping for %p (%s)", sptr, sptr->name));
+  for (i = highest_fd; i >= 0; i--)
+    if ((cptr = loc_clients[i]) && IsPing(cptr) && cptr->acpt == sptr)
+    {
+      cptr->acpt = acptr;
+      del_queries((char *)cptr);
+      end_ping(cptr);
+      break;
+    }
+
+  ClearAskedPing(sptr);
+}
diff --git a/ircd/s_serv.c b/ircd/s_serv.c
new file mode 100644 (file)
index 0000000..ab5ff17
--- /dev/null
@@ -0,0 +1,1117 @@
+/*
+ * 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 "sys.h"
+#include <stdlib.h>
+#include "h.h"
+#include "struct.h"
+#include "ircd.h"
+#include "s_serv.h"
+#include "s_misc.h"
+#include "sprintf_irc.h"
+#include "send.h"
+#include "s_err.h"
+#include "numeric.h"
+#include "s_bsd.h"
+#include "s_conf.h"
+#include "hash.h"
+#include "common.h"
+#include "match.h"
+#include "crule.h"
+#include "parse.h"
+#include "numnicks.h"
+#include "userload.h"
+#include "s_user.h"
+#include "channel.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+
+RCSTAG_CC("$Id$");
+
+static int exit_new_server(aClient *cptr, aClient *sptr,
+    char *host, time_t timestamp, char *fmt, ...)
+    __attribute__ ((format(printf, 5, 6)));
+
+/* *INDENT-OFF* */
+
+#ifdef CRYPT_LINK_PASSWORD
+__BEGIN_DECLS
+/* This is not ANSI, but we have it anyway... */
+char *crypt(const char *key, const char *salt);
+__END_DECLS
+#endif /* CRYPT_LINK_PASSWORD */
+
+/* *INDENT-ON* */
+
+unsigned int max_connection_count = 0, max_client_count = 0;
+
+static int exit_new_server(aClient *cptr, aClient *sptr,
+    char *host, time_t timestamp, char *fmt, ...)
+{
+  va_list vl;
+  char *buf =
+      (char *)RunMalloc(strlen(me.name) + strlen(host) + 22 + strlen(fmt));
+  va_start(vl, fmt);
+  if (!IsServer(sptr))
+    return vexit_client_msg(cptr, cptr, &me, fmt, vl);
+  sprintf_irc(buf, ":%s SQUIT %s " TIME_T_FMT " :", me.name, host, timestamp);
+  strcat(buf, fmt);
+  vsendto_one(cptr, buf, vl);
+  va_end(vl);
+  RunFree(buf);
+  return 0;
+}
+
+static int a_kills_b_too(aClient *a, aClient *b)
+{
+  for (; b != a && b != &me; b = b->serv->up);
+  return (a == b ? 1 : 0);
+}
+
+extern unsigned short server_port;
+
+/*
+ *  m_server
+ *
+ *    parv[0] = sender prefix
+ *    parv[1] = servername
+ *    parv[2] = hopcount
+ *    parv[3] = start timestamp
+ *    parv[4] = link timestamp
+ *    parv[5] = major protocol version: P09/P10
+ *    parv[parc-1] = serverinfo
+ *  If cptr is P10:
+ *    parv[6] = "YMM", where 'Y' is the server numeric and "MM" is the
+ *              numeric nick mask of this server.
+ *    parv[7] = 0 (not used yet, mandatory unsigned int after u2.10.06)
+ */
+int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 char *ch;
+  Reg2 int i;
+  char info[REALLEN + 1], *inpath, *host, *s;
+  aClient *acptr, *bcptr, *LHcptr = NULL;
+  aConfItem *aconf = NULL, *bconf = NULL, *cconf, *lhconf = NULL;
+  int hop, ret, active_lh_line = 0;
+  unsigned short int prot;
+  time_t start_timestamp, timestamp = 0, recv_time, ghost = 0;
+
+  if (IsUser(cptr))
+  {
+    sendto_one(cptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]);
+    return 0;
+  }
+
+  if (IsUserPort(cptr))
+    return exit_client_msg(cptr, cptr, &me,
+       "You cannot connect a server to a user port; connect to %s port %u",
+       me.name, server_port);
+
+  recv_time = TStime();
+  info[0] = '\0';
+  inpath = get_client_name(cptr, TRUE);
+  if (parc < 7)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SERVER");
+    return exit_client(cptr, cptr, &me, "Need more parameters");
+  }
+  host = parv[1];
+  /* Detect protocol */
+  if (strlen(parv[5]) != 3 || (parv[5][0] != 'P' && parv[5][0] != 'J'))
+    return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]);
+  if (!IsServer(cptr))         /* Don't allow silently connecting a server */
+    *parv[5] = 'J';
+  prot = atoi(parv[5] + 1);
+  if (prot > atoi(MAJOR_PROTOCOL))
+    prot = atoi(MAJOR_PROTOCOL);
+  /* Because the previous test is only in 2.10, the following is needed
+   * till all servers are 2.10: */
+  if (IsServer(cptr) && prot > Protocol(cptr))
+    prot = Protocol(cptr);
+  hop = atoi(parv[2]);
+  start_timestamp = atoi(parv[3]);
+  timestamp = atoi(parv[4]);
+  Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age " TIME_T_FMT " ("
+      TIME_T_FMT ")", host, parv[4], start_timestamp, me.serv->timestamp));
+  if ((timestamp < 780000000 || (hop == 1 && start_timestamp < 780000000)))
+  {
+    return exit_client_msg(cptr, sptr, &me,
+       "Bogus timestamps (%s %s)", parv[3], parv[4]);
+  }
+  strncpy(info, parv[parc - 1], REALLEN);
+  info[REALLEN] = 0;
+  if (prot < atoi(MINOR_PROTOCOL))
+  {
+    sendto_ops("Got incompatible protocol version (%s) from %s",
+       parv[5], get_client_name(cptr, TRUE));
+    return exit_new_server(cptr, sptr, host, timestamp,
+       "Incompatible protocol: %s", parv[5]);
+  }
+  /*
+   * 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 || !strchr(host, '.'))
+  {
+    sendto_ops("Bogus server name (%s) from %s",
+       host, get_client_name(cptr, TRUE));
+    return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host);
+  }
+
+  if (IsServer(cptr))
+  {
+    /*
+     * A local server introduces a new server behind this link.
+     * Check if this is allowed according L:, H: and Q: lines.
+     */
+    if (info[0] == '\0')
+      return exit_client_msg(cptr, cptr, &me,
+         "No server info specified for %s", host);
+
+    /*
+     * See if the newly found server is behind a guaranteed
+     * leaf (L-line). If so, close the link.
+     */
+    if ((lhconf = find_conf_host(cptr->confs, host, CONF_LEAF)) &&
+       (!lhconf->port || (hop > lhconf->port)))
+    {
+      /*
+       * L: lines normally come in pairs, here we try to
+       * make sure that the oldest link is squitted, not
+       * both.
+       */
+      active_lh_line = 1;
+      if (timestamp <= cptr->serv->timestamp)
+       LHcptr = NULL;          /* Kill incoming server */
+      else
+       LHcptr = cptr;          /* Squit ourselfs */
+    }
+    else if (!(lhconf = find_conf_host(cptr->confs, host, CONF_HUB)) ||
+       (lhconf->port && (hop > lhconf->port)))
+    {
+      aClient *ac3ptr;
+      active_lh_line = 2;
+      /* Look for net junction causing this: */
+      LHcptr = NULL;           /* incoming server */
+      if (*parv[5] != 'J')
+       for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up)
+         if (IsJunction(ac3ptr))
+         {
+           LHcptr = ac3ptr;
+           break;
+         }
+    }
+  }
+
+  if (IsUnknown(cptr) || IsHandshake(cptr))
+  {
+    char *encr;
+
+    /*
+     * A local link that is still in undefined state wants
+     * to be a SERVER. Check if this is allowed and change
+     * status accordingly...
+     */
+    /*
+     * If there is more then one server on the same machine
+     * that we try to connect to, it could be that the /CONNECT
+     * <mask> caused this connect to be put at the wrong place
+     * in the hashtable.        --Run
+     * Same thing for Unknown connections that first send NICK.
+     *                          --Xorath
+     * Better check if the two strings are (caseless) identical 
+     * and not mess with hash internals. 
+     *                          --Nemesi
+     */
+    if ((!(BadPtr(cptr->name)))
+       && (IsUnknown(cptr) || IsHandshake(cptr))
+       && strCasediff(cptr->name, host))
+      hChangeClient(cptr, host);
+    strncpy(cptr->name, host, sizeof(cptr->name) - 1);
+    strncpy(cptr->info, info[0] ? info : me.name, sizeof(cptr->info) - 1);
+    cptr->hopcount = hop;
+
+    /* check connection rules */
+    for (cconf = conf; cconf; cconf = cconf->next)
+      if ((cconf->status == CONF_CRULEALL) && (match(cconf->host, host) == 0))
+       if (crule_eval(cconf->passwd))
+       {
+         ircstp->is_ref++;
+         sendto_ops("Refused connection from %s.", get_client_host(cptr));
+         return exit_client(cptr, cptr, &me, "Disallowed by connection rule");
+       }
+
+    if (check_server(cptr))
+    {
+      ircstp->is_ref++;
+      sendto_ops("Received unauthorized connection from %s.",
+         get_client_host(cptr));
+      return exit_client(cptr, cptr, &me, "No C/N conf lines");
+    }
+
+    host = cptr->name;
+
+    update_load();
+
+    if (!(aconf = find_conf(cptr->confs, host, CONF_NOCONNECT_SERVER)))
+    {
+      ircstp->is_ref++;
+#ifndef GODMODE
+      sendto_ops("Access denied. No N line for server %s", inpath);
+      return exit_client_msg(cptr, cptr, &me,
+         "Access denied. No N line for server %s", inpath);
+#else /* GODMODE */
+      sendto_ops("General C/N: line active: No N line for server %s", inpath);
+      aconf =
+         find_conf(cptr->confs, "general.undernet.org", CONF_NOCONNECT_SERVER);
+      bconf =
+         find_conf(cptr->confs, "general.undernet.org", CONF_CONNECT_SERVER);
+      if (!aconf || !bconf)
+      {
+       sendto_ops("Neither C/N lines for server %s nor "
+           "\"general.undernet.org\"", inpath);
+       return exit_client_msg(cptr, cptr, &me,
+           "No C/N lines for server %s", inpath);
+      }
+#endif /* GODMODE */
+    }
+    else if (!(bconf = find_conf(cptr->confs, host, CONF_CONNECT_SERVER)))
+    {
+      ircstp->is_ref++;
+      sendto_ops("Only N (no C) field for server %s", inpath);
+      return exit_client_msg(cptr, cptr, &me,
+         "Only N (no C) field for server %s", inpath);
+    }
+
+#ifdef CRYPT_LINK_PASSWORD
+    /* passwd may be NULL. Head it off at the pass... */
+    if (*cptr->passwd)
+    {
+      char salt[3];
+
+      salt[0] = aconf->passwd[0];
+      salt[1] = aconf->passwd[1];
+      salt[2] = '\0';
+      encr = crypt(cptr->passwd, salt);
+    }
+    else
+      encr = "";
+#else
+    encr = cptr->passwd;
+#endif /* CRYPT_LINK_PASSWORD */
+#ifndef GODMODE
+    if (*aconf->passwd && !!strcmp(aconf->passwd, encr))
+    {
+      ircstp->is_ref++;
+      sendto_ops("Access denied (passwd mismatch) %s", inpath);
+      return exit_client_msg(cptr, cptr, &me,
+         "No Access (passwd mismatch) %s", inpath);
+    }
+#endif /* not GODMODE */
+    memset(cptr->passwd, 0, sizeof(cptr->passwd));
+
+#ifndef HUB
+    for (i = 0; i <= highest_fd; i++)
+      if (loc_clients[i] && IsServer(loc_clients[i]))
+      {
+       active_lh_line = 3;
+       LHcptr = NULL;
+       break;
+      }
+#endif
+    if (!IsUnknown(cptr))
+    {
+      s = strchr(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));
+       return exit_client(cptr, cptr, &me, "Bad Username");
+      }
+      *s = '@';
+    }
+  }
+
+  /*
+   *  We want to find IsConnecting() and IsHandshake() too,
+   *  use FindClient().
+   *  The second finds collisions with numeric representation of existing
+   *  servers - these shouldn't happen anymore when all upgraded to 2.10.
+   *  -- Run
+   */
+  while ((acptr = FindClient(host)) ||
+      (parc > 7 && (acptr = FindNServer(parv[6]))))
+  {
+    /*
+     *  This link is trying feed me a server that I already have
+     *  access through another path
+     *
+     *  Do not allow Uworld to do this.
+     *  Do not allow servers that are juped.
+     *  Do not allow servers that have older link timestamps
+     *    then this try.
+     *  Do not allow servers that use the same numeric as an existing
+     *    server, but have a different name.
+     *
+     *  If my ircd.conf sucks, I can try to connect to myself:
+     */
+    if (acptr == &me)
+      return exit_client_msg(cptr, cptr, &me,
+         "nick collision with me (%s)", host);
+    /*
+     * Detect wrong numeric.
+     */
+    if (strCasediff(acptr->name, host))
+    {
+      sendto_serv_butone(cptr,
+         ":%s WALLOPS :SERVER Numeric Collision: %s != %s",
+         me.name, acptr->name, host);
+      return exit_client_msg(cptr, cptr, &me,
+         "NUMERIC collision between %s and %s."
+         " Is your server numeric correct ?", host, acptr->name);
+    }
+    /*
+     *  Kill our try, if we had one.
+     */
+    if (IsConnecting(acptr))
+    {
+      if (!active_lh_line && exit_client(cptr, acptr, &me,
+         "Just connected via another link") == CPTR_KILLED)
+       return CPTR_KILLED;
+      /*
+       * We can have only ONE 'IsConnecting', 'IsHandshake' or
+       * 'IsServer', because new 'IsConnecting's are refused to
+       * the same server if we already had it.
+       */
+      break;
+    }
+    /*
+     * Avoid other nick collisions...
+     * This is a doubtfull test though, what else would it be
+     * when it has a server.name ?
+     */
+    else if (!IsServer(acptr) && !IsHandshake(acptr))
+      return exit_client_msg(cptr, cptr, &me,
+         "Nickname %s already exists!", host);
+    /*
+     * Our new server might be a juped server,
+     * or someone trying abuse a second Uworld:
+     */
+    else if (IsServer(acptr) && (strnCasecmp(acptr->info, "JUPE", 4) == 0 ||
+       find_conf_host(cptr->confs, acptr->name, CONF_UWORLD)))
+    {
+      if (!IsServer(sptr))
+       return exit_client(cptr, sptr, &me, acptr->info);
+      sendto_one(cptr, ":%s WALLOPS :Received :%s SERVER %s from %s !?!",
+         me.name, parv[0], parv[1], cptr->name);
+      return exit_new_server(cptr, sptr, host, timestamp, "%s", acptr->info);
+    }
+    /*
+     * Of course we find the handshake this link was before :)
+     */
+    else if (IsHandshake(acptr) && acptr == cptr)
+      break;
+    /*
+     * Here we have a server nick collision...
+     * We don't want to kill the link that was last /connected,
+     * but we neither want to kill a good (old) link.
+     * Therefor we kill the second youngest link.
+     */
+    if (1)
+    {
+      aClient *c2ptr = NULL, *c3ptr = acptr;
+      aClient *ac2ptr, *ac3ptr;
+
+      /* Search youngest link: */
+      for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up)
+       if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp)
+         c3ptr = ac3ptr;
+      if (IsServer(sptr))
+      {
+       for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up)
+         if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp)
+           c3ptr = ac3ptr;
+      }
+      if (timestamp > c3ptr->serv->timestamp)
+      {
+       c3ptr = NULL;
+       c2ptr = acptr;          /* Make sure they differ */
+      }
+      /* Search second youngest link: */
+      for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up)
+       if (ac2ptr != c3ptr &&
+           ac2ptr->serv->timestamp >
+           (c2ptr ? c2ptr->serv->timestamp : timestamp))
+         c2ptr = ac2ptr;
+      if (IsServer(sptr))
+      {
+       for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up)
+         if (ac2ptr != c3ptr &&
+             ac2ptr->serv->timestamp >
+             (c2ptr ? c2ptr->serv->timestamp : timestamp))
+           c2ptr = ac2ptr;
+      }
+      if (c3ptr && timestamp > (c2ptr ? c2ptr->serv->timestamp : timestamp))
+       c2ptr = NULL;
+      /* If timestamps are equal, decide which link to break
+       *  by name.
+       */
+      if ((c2ptr ? c2ptr->serv->timestamp : timestamp) ==
+         (c3ptr ? c3ptr->serv->timestamp : timestamp))
+      {
+       char *n2, *n2up;
+       char *n3, *n3up;
+       if (c2ptr)
+       {
+         n2 = c2ptr->name;
+         n2up = MyConnect(c2ptr) ? me.name : c2ptr->serv->up->name;
+       }
+       else
+       {
+         n2 = host;
+         n2up = IsServer(sptr) ? sptr->name : me.name;
+       }
+       if (c3ptr)
+       {
+         n3 = c3ptr->name;
+         n3up = MyConnect(c3ptr) ? me.name : c3ptr->serv->up->name;
+       }
+       else
+       {
+         n3 = host;
+         n3up = IsServer(sptr) ? sptr->name : me.name;
+       }
+       if (strcmp(n2, n2up) > 0)
+         n2 = n2up;
+       if (strcmp(n3, n3up) > 0)
+         n3 = n3up;
+       if (strcmp(n3, n2) > 0)
+       {
+         ac2ptr = c2ptr;
+         c2ptr = c3ptr;
+         c3ptr = ac2ptr;
+       }
+      }
+      /* Now squit the second youngest link: */
+      if (!c2ptr)
+       return exit_new_server(cptr, sptr, host, timestamp,
+           "server %s already exists and is %ld seconds younger.",
+           host, (long)acptr->serv->timestamp - (long)timestamp);
+      else if (c2ptr->from == cptr || IsServer(sptr))
+      {
+       aClient *killedptrfrom = c2ptr->from;
+       if (active_lh_line)
+       {
+         /*
+          * If the L: or H: line also gets rid of this link,
+          * we sent just one squit.
+          */
+         if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
+           break;
+         /*
+          * If breaking the loop here solves the L: or H:
+          * line problem, we don't squit that.
+          */
+         if (c2ptr->from == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr)))
+           active_lh_line = 0;
+         else
+         {
+           /*
+            * If we still have a L: or H: line problem,
+            * we prefer to squit the new server, solving
+            * loop and L:/H: line problem with only one squit.
+            */
+           LHcptr = NULL;
+           break;
+         }
+       }
+       /*
+        * If the new server was introduced by a server that caused a
+        * Ghost less then 20 seconds ago, this is probably also
+        * a Ghost... (20 seconds is more then enough because all
+        * SERVER messages are at the beginning of a net.burst). --Run
+        */
+       if (now - cptr->serv->ghost < 20)
+       {
+         killedptrfrom = acptr->from;
+         if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
+           return CPTR_KILLED;
+       }
+       else if (exit_client_msg(cptr, c2ptr, &me,
+           "Loop <-- %s (new link is %ld seconds younger)", host,
+           (c3ptr ? (long)c3ptr->serv->timestamp : timestamp) -
+           (long)c2ptr->serv->timestamp) == CPTR_KILLED)
+         return CPTR_KILLED;
+       /*
+        * Did we kill the incoming server off already ?
+        */
+       if (killedptrfrom == cptr)
+         return 0;
+      }
+      else
+      {
+       if (active_lh_line)
+       {
+         if (LHcptr && a_kills_b_too(LHcptr, acptr))
+           break;
+         if (acptr->from == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr)))
+           active_lh_line = 0;
+         else
+         {
+           LHcptr = NULL;
+           break;
+         }
+       }
+       /*
+        * We can't believe it is a lagged server message
+        * when it directly connects to us...
+        * kill the older link at the ghost, rather then
+        * at the second youngest link, assuming it isn't
+        * a REAL loop.
+        */
+       ghost = now;            /* Mark that it caused a ghost */
+       if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
+         return CPTR_KILLED;
+       break;
+      }
+    }
+  }
+
+  if (active_lh_line)
+  {
+    if (LHcptr == NULL)
+      return exit_new_server(cptr, sptr, host, timestamp,
+         (active_lh_line == 2) ?
+         "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)",
+         get_client_name(cptr, TRUE), host,
+         lhconf ? (lhconf->host ? lhconf->host : "*") : "!");
+    else
+    {
+      register int killed = a_kills_b_too(LHcptr, sptr);
+      if (active_lh_line < 3)
+      {
+       if (exit_client_msg(cptr, LHcptr, &me,
+           (active_lh_line == 2) ?
+           "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)",
+           get_client_name(cptr, TRUE), host,
+           lhconf ? (lhconf->host ? lhconf->host : "*") : "!") == CPTR_KILLED)
+         return CPTR_KILLED;
+      }
+      else
+      {
+       ircstp->is_ref++;
+       if (exit_client(cptr, LHcptr, &me, "I'm a leaf") == CPTR_KILLED)
+         return CPTR_KILLED;
+      }
+      /*
+       * Did we kill the incoming server off already ?
+       */
+      if (killed)
+       return 0;
+    }
+  }
+
+  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...
+     */
+
+    acptr = make_client(cptr, STAT_SERVER);
+    make_server(acptr);
+    acptr->serv->prot = prot;
+    acptr->serv->timestamp = timestamp;
+    acptr->hopcount = hop;
+    strncpy(acptr->name, host, sizeof(acptr->name) - 1);
+    strncpy(acptr->info, info, sizeof(acptr->info) - 1);
+    acptr->serv->up = sptr;
+    acptr->serv->updown = add_dlink(&sptr->serv->down, acptr);
+    /* Use cptr, because we do protocol 9 -> 10 translation
+       for numeric nicks ! */
+    SetServerYXX(cptr, acptr, parv[6]);
+    Count_newremoteserver(nrof);
+    if (Protocol(acptr) < 10)
+      acptr->flags |= FLAGS_TS8;
+    add_client_to_list(acptr);
+    hAddClient(acptr);
+    if (*parv[5] == 'J')
+    {
+      if (Protocol(acptr) > 9)
+       SetBurst(acptr);
+      sendto_op_mask(SNO_NETWORK, "Net junction: %s %s",
+         sptr->name, acptr->name);
+      SetJunction(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 = loc_clients[i]) || !IsServer(bcptr) ||
+         bcptr == cptr || IsMe(bcptr))
+       continue;
+      if (!(cconf = bcptr->serv->nline))
+      {
+       sendto_ops("Lost N-line for %s on %s. Closing",
+           get_client_name(cptr, TRUE), host);
+       return exit_client(cptr, cptr, &me, "Lost N line");
+      }
+      if (match(my_name_for_link(me.name, cconf), acptr->name) == 0)
+       continue;
+      if (Protocol(bcptr) > 9)
+       sendto_one(bcptr, "%s SERVER %s %d 0 %s %s %s%s 0 :%s",
+           NumServ(sptr), acptr->name, hop + 1, parv[4], parv[5],
+           NumServCap(acptr), acptr->info);
+      else
+       sendto_one(bcptr, ":%s SERVER %s %d 0 %s %s %s%s 0 :%s",
+           parv[0], acptr->name, hop + 1, parv[4], parv[5],
+           NumServCap(acptr), acptr->info);
+    }
+    return 0;
+  }
+
+  if (IsUnknown(cptr) || IsHandshake(cptr))
+  {
+    make_server(cptr);
+    cptr->serv->timestamp = timestamp;
+    cptr->serv->prot = prot;
+    cptr->serv->ghost = ghost;
+    SetServerYXX(cptr, cptr, parv[6]);
+    if (start_timestamp > 780000000)
+    {
+#ifndef RELIABLE_CLOCK
+#ifdef TESTNET
+      sendto_ops("Debug: my start time: " TIME_T_FMT " ; others start time: "
+         TIME_T_FMT, me.serv->timestamp, start_timestamp);
+      sendto_ops("Debug: receive time: " TIME_T_FMT " ; received timestamp: "
+         TIME_T_FMT " ; difference %ld",
+         recv_time, timestamp, timestamp - recv_time);
+#endif
+      if (start_timestamp < me.serv->timestamp)
+      {
+       sendto_ops("got earlier start time: " TIME_T_FMT " < " TIME_T_FMT,
+           start_timestamp, me.serv->timestamp);
+       me.serv->timestamp = start_timestamp;
+       TSoffset += timestamp - recv_time;
+       sendto_ops("clock adjusted by adding %d", (int)(timestamp - recv_time));
+      }
+      else if ((start_timestamp > me.serv->timestamp) && IsUnknown(cptr))
+       cptr->serv->timestamp = TStime();
+
+      else if (timestamp != recv_time)
+       /* Equal start times, we have a collision.  Let the connected-to server
+          decide. This assumes leafs issue more than half of the connection
+          attempts. */
+      {
+       if (IsUnknown(cptr))
+         cptr->serv->timestamp = TStime();
+       else if (IsHandshake(cptr))
+       {
+         sendto_ops("clock adjusted by adding %d",
+             (int)(timestamp - recv_time));
+         TSoffset += timestamp - recv_time;
+       }
+      }
+#else /* RELIABLE CLOCK IS TRUE, we _always_ use our own clock */
+      if (start_timestamp < me.serv->timestamp)
+       me.serv->timestamp = start_timestamp;
+      if (IsUnknown(cptr))
+       cptr->serv->timestamp = TStime();
+#endif
+    }
+
+    ret = m_server_estab(cptr, aconf, bconf);
+  }
+  else
+    ret = 0;
+#ifdef RELIABLE_CLOCK
+  if (abs(cptr->serv->timestamp - recv_time) > 30)
+  {
+    sendto_ops("Connected to a net with a timestamp-clock"
+       " difference of " STIME_T_FMT " seconds! Used SETTIME to correct"
+       " this.", timestamp - recv_time);
+    sendto_one(cptr, ":%s SETTIME " TIME_T_FMT " :%s",
+       me.name, TStime(), me.name);
+  }
+#endif
+
+  return ret;
+}
+
+/*
+ * m_server_estab
+ *
+ * May only be called after a SERVER was received from cptr,
+ * and thus make_server was called, and serv->prot set. --Run
+ */
+int m_server_estab(aClient *cptr, aConfItem *aconf, aConfItem *bconf)
+{
+  Reg3 aClient *acptr;
+  char *inpath, *host;
+  int split, i;
+
+  split = (strCasediff(cptr->name, cptr->sockhost)
+      && strnCasecmp(cptr->info, "JUPE", 4));
+  inpath = get_client_name(cptr, TRUE);
+  host = cptr->name;
+
+  if (IsUnknown(cptr))
+  {
+    if (bconf->passwd[0])
+      sendto_one(cptr, "PASS :%s", bconf->passwd);
+    /*
+     *  Pass my info to the new server
+     */
+    sendto_one(cptr, "SERVER %s 1 " TIME_T_FMT " " TIME_T_FMT " J%s %s%s :%s",
+       my_name_for_link(me.name, aconf), me.serv->timestamp,
+       cptr->serv->timestamp, MAJOR_PROTOCOL, NumServCap(&me),
+       (me.info[0]) ? (me.info) : "IRCers United");
+
+    IPcheck_connect_fail(cptr);        /* Don't charge this IP# for connecting */
+  }
+
+  det_confs_butmask(cptr,
+      CONF_LEAF | CONF_HUB | CONF_NOCONNECT_SERVER | CONF_UWORLD);
+
+  if (!IsHandshake(cptr))
+    hAddClient(cptr);
+  SetServer(cptr);
+  Count_unknownbecomesserver(nrof);
+  if (Protocol(cptr) > 9)
+    SetBurst(cptr);
+  else
+    cptr->flags |= FLAGS_TS8;
+  nextping = now;
+  if (cptr->serv->user && *cptr->serv->by &&
+      (acptr = findNUser(cptr->serv->by)) && acptr->user == cptr->serv->user)
+  {
+    if (MyUser(acptr) || Protocol(acptr->from) < 10)
+      sendto_one(acptr, ":%s NOTICE %s :Link with %s established.",
+         me.name, acptr->name, inpath);
+    else
+      sendto_one(acptr, "%s NOTICE %s%s :Link with %s established.",
+         NumServ(&me), NumNick(acptr), inpath);
+  }
+  else
+    acptr = NULL;
+  sendto_lops_butone(acptr, "Link with %s established.", inpath);
+  cptr->serv->up = &me;
+  cptr->serv->updown = add_dlink(&me.serv->down, cptr);
+  cptr->serv->nline = aconf;
+  sendto_op_mask(SNO_NETWORK, "Net junction: %s %s", me.name, cptr->name);
+  SetJunction(cptr);
+  /*
+   * 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 = loc_clients[i]) || !IsServer(acptr) ||
+       acptr == cptr || IsMe(acptr))
+      continue;
+    if ((aconf = acptr->serv->nline) &&
+       !match(my_name_for_link(me.name, aconf), cptr->name))
+      continue;
+    if (split)
+    {
+      if (Protocol(acptr) > 9)
+       sendto_one(acptr,
+           "%s SERVER %s 2 0 " TIME_T_FMT " %s%u %s%s 0 :[%s] %s",
+           NumServ(&me), cptr->name, cptr->serv->timestamp,
+           (Protocol(cptr) > 9) ? "J" : "J0", Protocol(cptr), NumServCap(cptr),
+           cptr->sockhost, cptr->info);
+      else
+       sendto_one(acptr,
+           ":%s SERVER %s 2 0 " TIME_T_FMT " %s%u %s%s 0 :[%s] %s", me.name,
+           cptr->name, cptr->serv->timestamp,
+           (Protocol(cptr) > 9) ? "J" : "J0", Protocol(cptr), NumServCap(cptr),
+           cptr->sockhost, cptr->info);
+    }
+    else
+    {
+      if (Protocol(acptr) > 9)
+       sendto_one(acptr, "%s SERVER %s 2 0 " TIME_T_FMT " %s%u %s%s 0 :%s",
+           NumServ(&me), cptr->name, cptr->serv->timestamp,
+           (Protocol(cptr) > 9) ? "J" : "J0", Protocol(cptr),
+           NumServCap(cptr), cptr->info);
+      else
+       sendto_one(acptr, ":%s SERVER %s 2 0 " TIME_T_FMT " %s%u %s%s 0 :%s",
+           me.name, cptr->name, cptr->serv->timestamp,
+           (Protocol(cptr) > 9) ? "J" : "J0", Protocol(cptr),
+           NumServCap(cptr), 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...
+   */
+
+  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))
+    {
+      char *protocol_str =
+         (Protocol(acptr) > 9) ? (IsBurst(acptr) ? "J" : "P") : "P0";
+      if (match(my_name_for_link(me.name, aconf), acptr->name) == 0)
+       continue;
+      split = (MyConnect(acptr) && strCasediff(acptr->name, acptr->sockhost) &&
+         strnCasecmp(acptr->info, "JUPE", 4));
+      if (split)
+      {
+       if (Protocol(cptr) > 9)
+         sendto_one(cptr,
+             "%s SERVER %s %d 0 " TIME_T_FMT " %s%u %s%s 0 :[%s] %s",
+             NumServ(acptr->serv->up), acptr->name,
+             acptr->hopcount + 1, acptr->serv->timestamp,
+             protocol_str, Protocol(acptr),
+             NumServCap(acptr), acptr->sockhost, acptr->info);
+       else
+         sendto_one(cptr,
+             ":%s SERVER %s %d 0 " TIME_T_FMT " %s%u %s%s 0 :[%s] %s",
+             acptr->serv->up->name, acptr->name,
+             acptr->hopcount + 1, acptr->serv->timestamp,
+             protocol_str, Protocol(acptr),
+             NumServCap(acptr), acptr->sockhost, acptr->info);
+      }
+      else
+      {
+       if (Protocol(cptr) > 9)
+         sendto_one(cptr,
+             "%s SERVER %s %d 0 " TIME_T_FMT " %s%u %s%s 0 :%s",
+             NumServ(acptr->serv->up), acptr->name,
+             acptr->hopcount + 1, acptr->serv->timestamp,
+             protocol_str, Protocol(acptr), NumServCap(acptr), acptr->info);
+       else
+         sendto_one(cptr,
+             ":%s SERVER %s %d 0 " TIME_T_FMT " %s%u %s%s 0 :%s",
+             acptr->serv->up->name, acptr->name,
+             acptr->hopcount + 1, acptr->serv->timestamp,
+             protocol_str, Protocol(acptr), NumServCap(acptr), acptr->info);
+      }
+    }
+  }
+
+  for (acptr = &me; acptr; acptr = acptr->prev)
+  {
+    /* acptr->from == acptr for acptr == cptr */
+    if (acptr->from == cptr)
+      continue;
+    if (IsUser(acptr))
+    {
+      if (Protocol(cptr) < 10)
+      {
+       /*
+        * IsUser(x) is true only *BOTH* NICK and USER have
+        * been received. -avalon
+        * Or only NICK in new format. --Run
+        */
+       sendto_one(cptr, ":%s NICK %s %d " TIME_T_FMT " %s %s %s :%s",
+           acptr->user->server->name,
+           acptr->name, acptr->hopcount + 1, acptr->lastnick,
+           acptr->user->username, acptr->user->host,
+           acptr->user->server->name, acptr->info);
+       send_umode(cptr, acptr, 0, SEND_UMODES);
+       send_user_joins(cptr, acptr);
+      }
+      else
+      {
+       char xxx_buf[8];
+       char *s = umode_str(acptr);
+       sendto_one(cptr, *s ?
+           "%s NICK %s %d " TIME_T_FMT " %s %s +%s %s %s%s :%s" :
+           "%s NICK %s %d " TIME_T_FMT " %s %s %s%s %s%s :%s",
+           NumServ(acptr->user->server),
+           acptr->name, acptr->hopcount + 1, acptr->lastnick,
+           acptr->user->username, acptr->user->host,
+           s, inttobase64(xxx_buf, ntohl(acptr->ip.s_addr), 6),
+           NumNick(acptr), acptr->info);
+      }
+    }
+  }
+  /*
+   * Last, send the BURST.
+   * (Or for 2.9 servers: pass all channels plus statuses)
+   */
+  {
+    Reg1 aChannel *chptr;
+    for (chptr = channel; chptr; chptr = chptr->nextch)
+      send_channel_modes(cptr, chptr);
+  }
+  if (Protocol(cptr) >= 10)
+    sendto_one(cptr, "%s END_OF_BURST", NumServ(&me));
+  return 0;
+}
+
+/*
+ * m_error
+ *
+ * parv[0] = sender prefix
+ * parv[parc-1] = text
+ */
+int m_error(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 char *para;
+
+  para = (parc > 1 && *parv[parc - 1] != '\0') ? parv[parc - 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 (IsUser(cptr))
+    return 0;
+  if (IsUnknown(cptr))
+    return exit_client_msg(cptr, cptr, &me, "Register first");
+
+  if (cptr == sptr)
+    sendto_ops("ERROR :from %s -- %s", get_client_name(cptr, FALSE), para);
+  else
+    sendto_ops("ERROR :from %s via %s -- %s",
+       sptr->name, get_client_name(cptr, FALSE), para);
+
+  if (sptr->serv)
+  {
+    RunFree(sptr->serv->last_error_msg);
+    DupString(sptr->serv->last_error_msg, para);
+  }
+
+  return 0;
+}
+
+/*
+ * m_end_of_burst  - Added Xorath 6-14-96, rewritten by Run 24-7-96
+ *                 - and fixed by record and Kev 8/1/96
+ *                 - and really fixed by Run 15/8/96 :p
+ * This the last message in a net.burst.
+ * It clears a flag for the server sending the burst.
+ *
+ * parv[0] - sender prefix
+ */
+int m_end_of_burst(aClient *cptr, aClient *sptr, int UNUSED(parc),
+    char **UNUSED(parv))
+{
+  if (!IsServer(sptr))
+    return 0;
+
+  sendto_op_mask(SNO_NETWORK, "Completed net.burst from %s.", sptr->name);
+#ifdef NO_PROTOCOL9
+  sendto_serv_butone(cptr, "%s END_OF_BURST", NumServ(sptr));
+#else
+  sendto_highprot_butone(cptr, 10, "%s END_OF_BURST", NumServ(sptr));
+#endif
+  ClearBurst(sptr);
+  SetBurstAck(sptr);
+  if (MyConnect(sptr))
+    sendto_one(sptr, "%s EOB_ACK", NumServ(&me));
+
+  return 0;
+}
+
+/*
+ * m_end_of_burst_ack
+ *
+ * This the acknowledge message of the `END_OF_BURST' message.
+ * It clears a flag for the server receiving the burst.
+ *
+ * parv[0] - sender prefix
+ */
+int m_end_of_burst_ack(aClient *cptr, aClient *sptr, int UNUSED(parc),
+    char **UNUSED(parv))
+{
+  if (!IsServer(sptr))
+    return 0;
+
+  sendto_op_mask(SNO_NETWORK, "%s acknowledged end of net.burst.", sptr->name);
+#ifdef NO_PROTOCOL9
+  sendto_serv_butone(cptr, "%s EOB_ACK", NumServ(sptr));
+#else
+  sendto_highprot_butone(cptr, 10, "%s EOB_ACK", NumServ(sptr));
+#endif
+  ClearBurstAck(sptr);
+
+  return 0;
+}
+
+/*
+ * m_desynch
+ *
+ * Writes to all +g users; for sending wall type debugging/anti-hack info.
+ * Added 23 Apr 1998  --Run
+ *
+ * parv[0] - sender prefix
+ * parv[parc-1] - message text
+ */
+int m_desynch(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  if (IsServer(sptr) && parc >= 2)
+  {
+    int i;
+    aClient *acptr;
+    /* Send message to local +g clients as if it were a wallops */
+    sprintf_irc(sendbuf, ":%s WALLOPS :%s", parv[0], parv[parc - 1]);
+    for (i = 0; i <= highest_fd; i++)
+      if ((acptr = loc_clients[i]) && !IsServer(acptr) && !IsMe(acptr) &&
+         SendDebug(acptr))
+       sendbufto_one(acptr);
+    /* Send message to remote +g clients */
+    sendto_g_serv_butone(cptr, "%s DESYNCH :%s", NumServ(sptr), parv[parc - 1]);
+  }
+  return 0;
+}
diff --git a/ircd/s_user.c b/ircd/s_user.c
new file mode 100644 (file)
index 0000000..cbfb599
--- /dev/null
@@ -0,0 +1,3009 @@
+/*
+ * 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 "sys.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "common.h"
+#include "s_serv.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "match.h"
+#include "hash.h"
+#include "s_bsd.h"
+#include "whowas.h"
+#include "list.h"
+#include "s_err.h"
+#include "parse.h"
+#include "ircd.h"
+#include "s_user.h"
+#include "support.h"
+#include "s_user.h"
+#include "channel.h"
+#include "random.h"
+#include "version.h"
+#include "msg.h"
+#include "userload.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+#include "class.h"
+
+RCSTAG_CC("$Id$");
+
+/* *INDENT-OFF* */
+
+/* This is not ANSI, but we have it anyway... */
+__BEGIN_DECLS
+char *crypt(const char *key, const char *salt);
+__END_DECLS
+
+/* *INDENT-ON* */
+
+static char buf[BUFSIZE], buf2[BUFSIZE];
+
+/*
+ * 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(aClient *next, char *ch)
+{
+  Reg3 aClient *tmp = next;
+
+  if (!tmp)
+    return NULL;
+
+  next = FindClient(ch);
+  next = next ? next : tmp;
+  if (tmp->prev == next)
+    return NULL;
+  if (next != tmp)
+    return next;
+  for (; next; next = next->next)
+    if (!match(ch, next->name))
+      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(int MustBeOper, aClient *cptr, aClient *sptr, char *command,
+    int server, int parc, char *parv[])
+{
+  aClient *acptr;
+  char y[8];
+
+  /* Assume it's me, if no server or an unregistered client */
+  if (parc <= server || BadPtr(parv[server]) || IsUnknown(sptr))
+    return (HUNTED_ISME);
+
+  /* Make sure it's a server */
+  if (MyUser(sptr) || Protocol(cptr) < 10)
+  {
+    /* Make sure it's a server */
+    if (!strchr(parv[server], '*'))
+    {
+      if (0 == (acptr = FindClient(parv[server])))
+       return HUNTED_NOSUCH;
+      if (acptr->user)
+       acptr = acptr->user->server;
+    }
+    else if (!(acptr = find_match_server(parv[server])))
+    {
+      sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
+         me.name, parv[0], parv[server]);
+      return (HUNTED_NOSUCH);
+    }
+  }
+  else if (!(acptr = FindNServer(parv[server])))
+    return (HUNTED_NOSUCH);    /* Server broke off in the meantime */
+
+  if (IsMe(acptr))
+    return (HUNTED_ISME);
+
+  if (MustBeOper && !IsPrivileged(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name);
+    return HUNTED_NOSUCH;
+  }
+
+#ifdef NO_PROTOCOL9
+  if (MyUser(sptr))
+  {
+    strcpy(y, acptr->yxx);
+    parv[server] = y;
+  }
+#else
+  if (Protocol(acptr->from) > 9)
+  {
+    strcpy(y, acptr->yxx);
+    parv[server] = y;
+  }
+  else
+    parv[server] = acptr->name;
+#endif
+
+  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);
+}
+
+/*
+ * '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 invalid)
+ *
+ * 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:
+ *  The '~'-character should be allowed, but a change should be global,
+ *  some confusion would result if only few servers allowed it...
+ */
+
+static int do_nick_name(char *nick)
+{
+  Reg1 char *ch;
+
+  if (*nick == '-' || isDigit(*nick))  /* first character in [0..9-] */
+    return 0;
+
+  for (ch = nick; *ch && (ch - nick) < NICKLEN; ch++)
+    if (!isIrcNk(*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(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)
+    {
+      p2 = NULL;
+      for (t = strtoken(&p2, cbuf, ","); t; t = strtoken(&p2, NULL, ","))
+       if (!strCasediff(s, t))
+         break;
+       else if (p2)
+         p2[-1] = ',';
+    }
+    else
+      t = NULL;
+    if (!t)
+    {
+      if (l)
+       *(cp - 1) = ',';
+      else
+       l = 1;
+      strcpy(cp, s);
+      if (p)
+       cp += (p - s);
+    }
+    else if (p2)
+      p2[-1] = ',';
+  }
+  return cbuf;
+}
+
+/*
+ * clean_user_id
+ *
+ * Copy `source' to `dest', replacing all occurances of '~' and characters that
+ * are not `isIrcUi' by an underscore.
+ * Copies at most USERLEN - 1 characters or up till the first control character.
+ * If `tilde' is true, then a tilde is prepended to `dest'.
+ * Note that `dest' and `source' can point to the same area or to different
+ * non-overlapping areas.
+ */
+
+static char *clean_user_id(char *dest, char *source, int tilde)
+{
+  register char ch;
+  char *d = dest;
+  char *s = source;
+  int rlen = USERLEN;
+
+  ch = *s++;                   /* Store first character to copy: */
+  if (tilde)
+  {
+    *d++ = '~';                        /* If `dest' == `source', then this overwrites `ch' */
+    --rlen;
+  }
+  while (ch && !isCntrl(ch) && rlen--)
+  {
+    register char nch = *s++;  /* Store next character to copy */
+    *d++ = isIrcUi(ch) ? ch : '_';     /* This possibly overwrites it */
+    if (nch == '~')
+      ch = '_';
+    else
+      ch = nch;
+  }
+  *d = 0;
+  return dest;
+}
+
+/*
+ * 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(aClient *cptr, aClient *sptr,
+    char *nick, char *username)
+{
+  Reg1 aConfItem *aconf;
+  char *parv[3], *tmpstr, *tmpstr2;
+  char c = 0 /* not alphanum */ , d = 'a' /* not a digit */ ;
+  short oldstatus_ismaster = IsMaster(sptr), upper = 0, lower = 0;
+  short pos = 0, leadcaps = 0, other = 0, digits = 0, badid = 0;
+  short digitgroups = 0;
+  anUser *user = sptr->user;
+  Dlink *lp;
+  char ip_base64[8];
+
+  user->last = now;
+  parv[0] = sptr->name;
+  parv[1] = parv[2] = NULL;
+
+  if (MyConnect(sptr))
+  {
+    static time_t last_too_many1, last_too_many2;
+    switch (check_client(sptr))
+    {
+      case ACR_OK:
+       break;
+      case ACR_NO_AUTHORIZATION:
+       sendto_op_mask(SNO_UNAUTH, "Unauthorized connection from %s.",
+           get_client_host(sptr));
+       ircstp->is_ref++;
+       return exit_client(cptr, sptr, &me,
+           "No Authorization - use another server");
+      case ACR_TOO_MANY_IN_CLASS:
+       if (now - last_too_many1 >= (time_t) 60)
+       {
+         last_too_many1 = now;
+         sendto_op_mask(SNO_TOOMANY, "Too many connections in class for %s.",
+             get_client_host(sptr));
+       }
+       IPcheck_connect_fail(sptr);
+       ircstp->is_ref++;
+       return exit_client(cptr, sptr, &me,
+           "Sorry, your connection class is full - try again later or try another server");
+      case ACR_TOO_MANY_FROM_IP:
+       if (now - last_too_many2 >= (time_t) 60)
+       {
+         last_too_many2 = now;
+         sendto_op_mask(SNO_TOOMANY,
+             "Too many connections from same IP for %s.",
+             get_client_host(sptr));
+       }
+       ircstp->is_ref++;
+       return exit_client(cptr, sptr, &me,
+           "Too many connections from your host");
+      case ACR_ALREADY_AUTHORIZED:
+       /* Can this ever happen? */
+      case ACR_BAD_SOCKET:
+       IPcheck_connect_fail(sptr);
+       return exit_client(cptr, sptr, &me, "Unknown error -- Try again");
+    }
+    if (IsUnixSocket(sptr))
+      strncpy(user->host, me.sockhost, HOSTLEN);
+    else
+      strncpy(user->host, sptr->sockhost, HOSTLEN);
+    aconf = sptr->confs->value.aconf;
+
+    clean_user_id(user->username,
+       (sptr->flags & FLAGS_GOTID) ? sptr->username : username,
+       (sptr->flags & FLAGS_DOID) && !(sptr->flags & FLAGS_GOTID));
+
+    if ((user->username[0] == '\000')
+       || ((user->username[0] == '~') && (user->username[1] == '\000')))
+      return exit_client(cptr, sptr, &me, "USER: Bogus userid.");
+
+    if (!BadPtr(aconf->passwd)
+       && !(isDigit(*aconf->passwd) && !aconf->passwd[1])
+#ifdef USEONE
+       && strcmp("ONE", aconf->passwd)
+#endif
+       && strcmp(sptr->passwd, aconf->passwd))
+    {
+      ircstp->is_ref++;
+      sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
+      IPcheck_connect_fail(sptr);
+      return exit_client(cptr, sptr, &me, "Bad Password");
+    }
+    memset(sptr->passwd, 0, sizeof(sptr->passwd));
+    /*
+     * 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 R_LINES
+    if (find_restrict(sptr))
+    {
+      ircstp->is_ref++;
+      return exit_client(cptr, sptr, &me, "R-lined");
+    }
+#endif
+
+    /*
+     * Check for mixed case usernames, meaning probably hacked.  Jon2 3-94
+     * Summary of rules now implemented in this patch:         Ensor 11-94
+     * In a mixed-case name, if first char is upper, one more upper may
+     * appear anywhere.  (A mixed-case name *must* have an upper first
+     * char, and may have one other upper.)
+     * A third upper may appear if all 3 appear at the beginning of the
+     * name, separated only by "others" (-/_/.).
+     * A single group of digits is allowed anywhere.
+     * Two groups of digits are allowed if at least one of the groups is
+     * at the beginning or the end.
+     * Only one '-', '_', or '.' is allowed (or two, if not consecutive).
+     * But not as the first or last char.
+     * No other special characters are allowed.
+     * Name must contain at least one letter.
+     */
+    tmpstr2 = tmpstr = (username[0] == '~' ? &username[1] : username);
+    while (*tmpstr && !badid)
+    {
+      pos++;
+      c = *tmpstr;
+      tmpstr++;
+      if (isLower(c))
+      {
+       lower++;
+      }
+      else if (isUpper(c))
+      {
+       upper++;
+       if ((leadcaps || pos == 1) && !lower && !digits)
+         leadcaps++;
+      }
+      else if (isDigit(c))
+      {
+       digits++;
+       if (pos == 1 || !isDigit(d))
+       {
+         digitgroups++;
+         if (digitgroups > 2)
+           badid = 1;
+       }
+      }
+      else if (c == '-' || c == '_' || c == '.')
+      {
+       other++;
+       if (pos == 1)
+         badid = 1;
+       else if (d == '-' || d == '_' || d == '.' || other > 2)
+         badid = 1;
+      }
+      else
+       badid = 1;
+      d = c;
+    }
+    if (!badid)
+    {
+      if (lower && upper && (!leadcaps || leadcaps > 3 ||
+         (upper > 2 && upper > leadcaps)))
+       badid = 1;
+      else if (digitgroups == 2 && !(isDigit(tmpstr2[0]) || isDigit(c)))
+       badid = 1;
+      else if ((!lower && !upper) || !isAlnum(c))
+       badid = 1;
+    }
+    if (badid && (!(sptr->flags & FLAGS_GOTID) ||
+       strcmp(sptr->username, username) != 0))
+    {
+      ircstp->is_ref++;
+      sendto_one(cptr, ":%s %d %s :Your username is invalid.",
+         me.name, ERR_INVALIDUSERNAME, cptr->name);
+      sendto_one(cptr,
+         ":%s %d %s :Connect with your real username, in lowercase.",
+         me.name, ERR_INVALIDUSERNAME, cptr->name);
+      sendto_one(cptr, ":%s %d %s :If your mail address were foo@bar.com, "
+         "your username would be foo.",
+         me.name, ERR_INVALIDUSERNAME, cptr->name);
+      return exit_client(cptr, sptr, &me, "USER: Bad username");
+    }
+
+    if (oldstatus_ismaster && MyConnect(sptr))
+      m_oper(&me, sptr, 1, parv);
+
+    Count_unknownbecomesclient(sptr, nrof);
+  }
+  else
+  {
+    strncpy(user->username, username, USERLEN);
+    Count_newremoteclient(nrof);
+  }
+  SetUser(sptr);
+  if (IsInvisible(sptr))
+    ++nrof.inv_clients;
+  if (IsOper(sptr))
+    ++nrof.opers;
+
+  if (MyConnect(sptr))
+  {
+    sendto_one(sptr, rpl_str(RPL_WELCOME), me.name, nick, nick);
+    /* This is a duplicate of the NOTICE but see below... */
+    sendto_one(sptr, rpl_str(RPL_YOURHOST), me.name, nick,
+       get_client_name(&me, FALSE), 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);
+    m_lusers(sptr, sptr, 1, parv);
+    update_load();
+#ifdef NODEFAULTMOTD
+    m_motd(sptr, NULL, 1, parv);
+#else
+    m_motd(sptr, sptr, 1, parv);
+#endif
+    nextping = now;
+    if (sptr->snomask & SNO_NOISY)
+      set_snomask(sptr, sptr->snomask & SNO_NOISY, SNO_ADD);
+#ifdef ALLOW_SNO_CONNEXIT
+#ifdef SNO_CONNEXIT_IP
+    sprintf_irc(sendbuf,
+       ":%s NOTICE * :*** Notice -- Client connecting: %s (%s@%s) [%s] {%d}",
+       me.name, nick, user->username, user->host, inetntoa(sptr->ip),
+       get_client_class(sptr));
+    sendbufto_op_mask(SNO_CONNEXIT);
+#else /* SNO_CONNEXIT_IP */
+    sprintf_irc(sendbuf,
+       ":%s NOTICE * :*** Notice -- Client connecting: %s (%s@%s)",
+       me.name, nick, user->username, user->host);
+    sendbufto_op_mask(SNO_CONNEXIT);
+#endif /* SNO_CONNEXIT_IP */
+#endif /* ALLOW_SNO_CONNEXIT */
+    IPcheck_connect_succeeded(sptr);
+  }
+  else
+    /* if (IsServer(cptr)) */
+  {
+    aClient *acptr;
+
+    acptr = user->server;
+    if (acptr->from != sptr->from)
+    {
+      if (Protocol(cptr) < 10)
+       sendto_one(cptr, ":%s KILL %s :%s (%s != %s[%s])",
+           me.name, sptr->name, me.name, user->server->name, acptr->from->name,
+           acptr->from->sockhost);
+      else
+       sendto_one(cptr, "%s KILL %s%s :%s (%s != %s[%s])",
+           NumServ(&me), NumNick(sptr), me.name, user->server->name,
+           acptr->from->name, acptr->from->sockhost);
+      sptr->flags |= FLAGS_KILLED;
+      return exit_client(cptr, sptr, &me, "NICK server wrong direction");
+    }
+    else
+      sptr->flags |= (acptr->flags & FLAGS_TS8);
+
+    /*
+     * Check to see if this user is being propogated
+     * as part of a net.burst, or is using protocol 9.
+     * FIXME: This can be speeded up - its stupid to check it for
+     * every NICK message in a burst again  --Run.
+     */
+    for (acptr = user->server; acptr != &me; acptr = acptr->serv->up)
+      if (IsBurst(acptr) || Protocol(acptr) < 10)
+       break;
+    if (IPcheck_remote_connect(sptr, user->host, (acptr != &me)) == -1)
+      /* We ran out of bits to count this */
+      return exit_client(cptr, sptr, &me,
+         "More then 255 connections from this IP number");
+  }
+#ifdef NO_PROTOCOL9            /* Use this when all servers are 2.10 (but test it first) --Run */
+
+  tmpstr = umode_str(sptr);
+  sendto_serv_butone(cptr, *tmpstr ?
+      "%s NICK %s %d %d %s %s +%s %s %s%s :%s" :
+      "%s NICK %s %d %d %s %s %s%s %s%s :%s",
+      NumServ(user->server), nick, sptr->hopcount + 1, sptr->lastnick,
+      user->username, user->host, tmpstr,
+      inttobase64(ip_base64, ntohl(sptr->ip.s_addr), 6),
+      NumNick(sptr), sptr->info);
+
+#else /* Remove the following when all servers are 2.10 */
+
+  /* First send message to all 2.9 servers */
+  sprintf_irc(sendbuf, ":%s NICK %s %d " TIME_T_FMT " %s %s %s :%s",
+      user->server->name, nick, sptr->hopcount + 1, sptr->lastnick,
+      user->username, user->host, user->server->name, sptr->info);
+  for (lp = me.serv->down; lp; lp = lp->next)
+  {
+    if (lp->value.cptr == cptr)
+      continue;
+    if (Protocol(lp->value.cptr) < 10)
+      sendbufto_one(lp->value.cptr);
+  }
+
+  /* If the user has no umode, no need to generate a user MODE */
+  if (*(tmpstr = umode_str(sptr)) && (MyConnect(sptr) || Protocol(cptr) > 9))
+    /* Is it necessary to generate an user MODE message ? */
+  {
+    for (lp = me.serv->down; lp; lp = lp->next)
+    {
+      if (lp->value.cptr == cptr)
+       continue;
+      if (Protocol(lp->value.cptr) < 10)
+       sendto_one(lp->value.cptr, ":%s MODE %s :%s", sptr->name,
+           sptr->name, tmpstr);
+    }
+  }
+
+  /* Now send message to all 2.10 servers */
+  sprintf_irc(sendbuf, *tmpstr ?
+      "%s NICK %s %d %d %s %s +%s %s %s%s :%s" :
+      "%s NICK %s %d %d %s %s %s%s %s%s :%s",
+      NumServ(user->server), nick, sptr->hopcount + 1, sptr->lastnick,
+      user->username, user->host, tmpstr,
+      inttobase64(ip_base64, ntohl(sptr->ip.s_addr), 6),
+      NumNick(sptr), sptr->info);
+  for (lp = me.serv->down; lp; lp = lp->next)
+  {
+    if (lp->value.cptr == cptr || Protocol(lp->value.cptr) < 10)
+      continue;
+    sendbufto_one(lp->value.cptr);
+  }
+
+#endif
+
+  /* Send umode to client */
+  if (MyUser(sptr))
+  {
+    send_umode(cptr, sptr, 0, ALL_UMODES);
+    if (sptr->snomask != SNO_DEFAULT && (sptr->flags & FLAGS_SERVNOTICE))
+      sendto_one(sptr, rpl_str(RPL_SNOMASK), me.name, sptr->name,
+         sptr->snomask, sptr->snomask);
+  }
+
+  return 0;
+}
+
+/* *INDENT-OFF* */
+
+static int user_modes[] = {
+  FLAGS_OPER,          'o',
+  FLAGS_LOCOP,         'O',
+  FLAGS_INVISIBLE,     'i',
+  FLAGS_WALLOP,                'w',
+  FLAGS_SERVNOTICE,    's',
+  FLAGS_DEAF,          'd',
+  FLAGS_CHSERV,                'k',
+  FLAGS_DEBUG,          'g',
+  0,                   0
+};
+
+/* *INDENT-ON* */
+
+#define COOKIE_VERIFIED ((unsigned int)-1)
+
+/*
+ * m_nick
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nickname
+ *
+ * If from server, source is client:
+ *   parv[2] = timestamp
+ *
+ * Source is server:
+ *   parv[2] = hopcount
+ *   parv[3] = timestamp
+ *   parv[4] = username
+ *   parv[5] = hostname
+ *   parv[6] = umode (optional)
+ *   parv[parc-3] = IP#                 <- Only Protocol >= 10
+ *   parv[parc-2] = YXX, numeric nick   <- Only Protocol >= 10
+ *   parv[parc-1] = info
+ *   parv[0] = server
+ */
+int m_nick(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr;
+  aClient *server = NULL;
+  char nick[NICKLEN + 2];
+  char *s;
+  Link *lp;
+  time_t lastnick = (time_t) 0;
+  int differ = 1;
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+    return 0;
+  }
+  else if ((IsServer(sptr) && parc < 8) || (IsServer(cptr) && parc < 3))
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "NICK");
+    sendto_ops("bad NICK param count for %s from %s",
+       parv[1], get_client_name(cptr, FALSE));
+    return 0;
+  }
+  if (MyConnect(sptr) && (s = strchr(parv[1], '~')))
+    *s = '\0';
+  strncpy(nick, parv[1], NICKLEN + 1);
+  nick[sizeof(nick) - 1] = 0;
+  /*
+   * 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]);
+
+    if (IsServer(cptr))
+    {
+      ircstp->is_kill++;
+      sendto_ops("Bad Nick: %s From: %s %s",
+         parv[1], parv[0], get_client_name(cptr, FALSE));
+      if (Protocol(cptr) < 10)
+       sendto_one(cptr, ":%s KILL %s :%s (%s <- %s[%s])",
+           me.name, parv[1], me.name, parv[1], nick, cptr->name);
+      else
+       sendto_one(cptr, "%s KILL %s :%s (%s <- %s[%s])",
+           NumServ(&me), IsServer(sptr) ? parv[parc - 2] : parv[0], me.name,
+           parv[1], nick, cptr->name);
+      if (!IsServer(sptr))     /* bad nick _change_ */
+      {
+       sendto_lowprot_butone(cptr, 9, ":%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->name : cptr->name);
+       sendto_highprot_butone(cptr, 10, "%s KILL %s :%s (%s <- %s!%s@%s)",
+           NumServ(&me), parv[0], me.name, get_client_name(cptr, FALSE),
+           parv[0], sptr->user ? sptr->username : "",
+           sptr->user ? sptr->user->server->name : cptr->name);
+       sptr->flags |= FLAGS_KILLED;
+       return exit_client(cptr, sptr, &me, "BadNick");
+      }
+    }
+    return 0;
+  }
+
+  /* 
+   * Check if this is a LOCAL user trying to use a reserved (Juped)
+   * nick, if so tell him that it's a nick in use...
+   */
+
+  if ((!IsServer(cptr)) && isNickJuped(nick))
+  {
+    sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name,
+       /* parv[0] is empty when connecting */
+       BadPtr(parv[0]) ? "*" : parv[0], nick);
+    return 0;                  /* NICK message ignored */
+  }
+
+  /*
+   * 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 = FindServer(nick)))
+    if (MyConnect(sptr))
+    {
+      sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name,
+         BadPtr(parv[0]) ? "*" : parv[0], nick);
+      return 0;                        /* NICK message ignored */
+    }
+  /*
+   * acptr already has result from previous FindServer()
+   */
+  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++;
+    if (Protocol(cptr) < 10)
+      sendto_one(cptr, ":%s KILL %s :%s (%s <- %s)",
+         me.name, sptr->name, me.name, acptr->from->name,
+         get_client_name(cptr, FALSE));
+    else
+      sendto_one(cptr, "%s KILL %s%s :%s (%s <- %s)",
+         NumServ(&me), NumNick(sptr), 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 (!(acptr = FindClient(nick)))
+    goto nickkilldone;         /* No collisions, all clear... */
+  /*
+   * 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).
+   */
+  /*
+   * 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...
+   */
+  if (IsUnknown(acptr) && MyConnect(acptr))
+  {
+    IPcheck_connect_fail(acptr);
+    exit_client(cptr, acptr, &me, "Overridden by other sign on");
+    goto nickkilldone;
+  }
+  /*
+   * 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), me.name,
+       /* parv[0] is empty when connecting */
+       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 the TimeStamps are equal, we kill both (or only 'new'
+   * if it was a ":server NICK new ...").
+   * Otherwise we kill the youngest when user@host differ,
+   * or the oldest when they are the same.
+   * We treat user and ~user as different, because if it wasn't
+   * a faked ~user the AUTH wouldn't have added the '~'.
+   * --Run
+   *
+   */
+  if (IsServer(sptr))
+  {
+    /*
+     * A new NICK being introduced by a neighbouring
+     * server (e.g. message type ":server NICK new ..." received)
+     */
+    lastnick = atoi(parv[3]);
+    differ = (strCasediff(acptr->user->username, parv[4]) ||
+       strCasediff(acptr->user->host, parv[5]));
+    sendto_ops("Nick collision on %s (%s " TIME_T_FMT " <- %s " TIME_T_FMT
+       " (%s user@host))", acptr->name, acptr->from->name, acptr->lastnick,
+       get_client_name(cptr, FALSE), lastnick, differ ? "Different" : "Same");
+  }
+  else
+  {
+    /*
+     * A NICK change has collided (e.g. message type ":old NICK new").
+     */
+    lastnick = atoi(parv[2]);
+    differ = (strCasediff(acptr->user->username, sptr->user->username) ||
+       strCasediff(acptr->user->host, sptr->user->host));
+    sendto_ops("Nick change collision from %s to %s (%s " TIME_T_FMT " <- %s "
+       TIME_T_FMT ")", 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 ((differ && lastnick >= acptr->lastnick) ||
+       (!differ && lastnick <= acptr->lastnick))
+    {
+      if (!IsServer(sptr))
+      {
+       ircstp->is_kill++;
+       sendto_lowprot_butone(cptr, 9,  /* Kill old from outgoing servers */
+           ":%s KILL %s :%s (%s <- %s (Nick collision))",
+           me.name, sptr->name, me.name, acptr->from->name,
+           get_client_name(cptr, FALSE));
+       sendto_highprot_butone(cptr, 10,        /* Kill old from outgoing servers */
+           "%s KILL %s%s :%s (%s <- %s (Nick collision))",
+           NumServ(&me), NumNick(sptr), me.name, acptr->from->name,
+           get_client_name(cptr, FALSE));
+       if (MyConnect(sptr) && IsServer(cptr) && Protocol(cptr) > 9)
+         sendto_one(cptr, "%s KILL %s%s :%s (Ghost2)",
+             NumServ(&me), NumNick(sptr), me.name);
+       sptr->flags |= FLAGS_KILLED;
+       exit_client(cptr, sptr, &me, "Nick collision (you're a ghost)");
+      }
+      if (lastnick != acptr->lastnick)
+       return 0;               /* Ignore the NICK */
+    }
+    sendto_one(acptr, err_str(ERR_NICKCOLLISION), me.name, acptr->name, nick);
+  }
+  ircstp->is_kill++;
+  acptr->flags |= FLAGS_KILLED;
+  if (differ)
+  {
+    sendto_lowprot_butone(cptr, 9,     /* Kill our old from outgoing servers */
+       ":%s KILL %s :%s (%s <- %s (older nick overruled))",
+       me.name, acptr->name, me.name, acptr->from->name,
+       get_client_name(cptr, FALSE));
+    sendto_highprot_butone(cptr, 10,   /* Kill our old from outgoing servers */
+       "%s KILL %s%s :%s (%s <- %s (older nick overruled))",
+       NumServ(&me), NumNick(acptr), me.name, acptr->from->name,
+       get_client_name(cptr, FALSE));
+    if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
+      sendto_one(cptr, "%s%s QUIT :Local kill by %s (Ghost)",
+         NumNick(acptr), me.name);
+    exit_client(cptr, acptr, &me, "Nick collision (older nick overruled)");
+  }
+  else
+  {
+    sendto_lowprot_butone(cptr, 9,     /* Kill our old from outgoing servers */
+       ":%s KILL %s :%s (%s <- %s (nick collision from same user@host))",
+       me.name, acptr->name, me.name, acptr->from->name,
+       get_client_name(cptr, FALSE));
+    sendto_highprot_butone(cptr, 10,   /* Kill our old from outgoing servers */
+       "%s KILL %s%s :%s (%s <- %s (nick collision from same user@host))",
+       NumServ(&me), NumNick(acptr), me.name, acptr->from->name,
+       get_client_name(cptr, FALSE));
+    if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
+      sendto_one(cptr,
+         "%s%s QUIT :Local kill by %s (Ghost: switched servers too fast)",
+         NumNick(acptr), me.name);
+    exit_client(cptr, acptr, &me, "Nick collision (You collided yourself)");
+  }
+  if (lastnick == acptr->lastnick)
+    return 0;
+
+nickkilldone:
+  if (IsServer(sptr))
+  {
+    int flag, *s;
+    char *p;
+#ifndef NO_PROTOCOL9
+    const char *nnp9 = NULL;   /* Init. to avoid compiler warning */
+#endif
+
+    /* A server introducing a new client, change source */
+    if (!server)
+      server = sptr;
+#ifndef NO_PROTOCOL9
+    /*
+     * Numeric Nicks does, in contrast to all other protocol enhancements,
+     * translation from protocol 9 -> protocol 10 !
+     * The reason is that I just can't know what protocol it is when I
+     * receive a "MODE #channel +o Run", because I can't 'find' "Run"
+     * before I know the protocol, and I can't know the protocol if I
+     * first have to find the server of "Run".
+     * Therefore, in THIS case, the protocol is determined by the Connected
+     * server: cptr.
+     */
+    if (Protocol(cptr) < 10 && !(nnp9 = CreateNNforProtocol9server(server)))
+      return exit_client_msg(cptr, server, &me,
+         "Too many clients (> %d) from P09 server (%s)", 64, server->name);
+#endif
+    sptr = make_client(cptr, STAT_UNKNOWN);
+    sptr->hopcount = atoi(parv[2]);
+    sptr->lastnick = atoi(parv[3]);
+    if (Protocol(cptr) > 9 && parc > 7 && *parv[6] == '+')
+      for (p = parv[6] + 1; *p; p++)
+       for (s = user_modes; (flag = *s); s += 2)
+         if (((char)*(s + 1)) == *p)
+         {
+           sptr->flags |= flag;
+           break;
+         }
+    /*
+     * Set new nick name.
+     */
+    strcpy(sptr->name, nick);
+    sptr->user = make_user(sptr);
+    sptr->user->server = server;
+#ifndef NO_PROTOCOL9
+    if (Protocol(cptr) < 10)
+    {
+      if (!SetRemoteNumNick(sptr, nnp9))
+      {
+       /*
+        * if this fails squit the server and free the client
+        */
+       free_client(sptr);
+       return exit_client_msg(cptr, server, &me, "Invalid numeric index");
+      }
+      sptr->ip.s_addr = 0;
+    }
+    else
+    {
+#endif
+      if (!SetRemoteNumNick(sptr, parv[parc - 2]))
+      {
+       /*
+        * if this fails squit the server and free the client
+        */
+       free_client(sptr);
+       return exit_client_msg(cptr, server, &me, "Invalid numeric index");
+      }
+      sptr->ip.s_addr = htonl(base64toint(parv[parc - 3]));
+      /* IP# of remote client */
+#ifndef NO_PROTOCOL9
+    }
+#endif
+    add_client_to_list(sptr);
+    hAddClient(sptr);
+
+    server->serv->ghost = 0;   /* :server NICK means end of net.burst */
+    strncpy(sptr->info, parv[parc - 1], sizeof(sptr->info) - 1);
+    strncpy(sptr->user->host, parv[5], sizeof(sptr->user->host) - 1);
+    return register_user(cptr, sptr, sptr->name, parv[4]);
+  }
+  else if (sptr->name[0])
+  {
+    /*
+     * Client changing its nick
+     *
+     * If the client belongs to me, then check to see
+     * if client is on any channels where it is currently
+     * banned.  If so, do not allow the nick change to occur.
+     */
+    if (MyUser(sptr))
+    {
+      for (lp = cptr->user->channel; lp; lp = lp->next)
+       if (can_send(cptr, lp->value.chptr) == MODE_BAN)
+       {
+         sendto_one(cptr, err_str(ERR_BANNICKCHANGE), me.name, parv[0],
+             lp->value.chptr->chname);
+         return 0;
+       }
+      /*
+       * Refuse nick change if the last nick change was less
+       * then 30 seconds ago. This is intended to get rid of
+       * clone bots doing NICK FLOOD. -SeKs
+       * If someone didn't change their nick for more then 60 seconds
+       * however, allow to do two nick changes immedately after another
+       * before limiting the nick flood. -Run
+       */
+      if (now < cptr->nextnick)
+      {
+       cptr->nextnick += 2;
+       sendto_one(cptr, err_str(ERR_NICKTOOFAST),
+           me.name, parv[0], parv[1], cptr->nextnick - now);
+       /* Send error message */
+       sendto_prefix_one(cptr, cptr, ":%s NICK %s", parv[0], parv[0]);
+       /* bounce NICK to user */
+       return 0;               /* ignore nick change! */
+      }
+      else
+      {
+       /* Limit total to 1 change per NICK_DELAY seconds: */
+       cptr->nextnick += NICK_DELAY;
+       /* However allow _maximal_ 1 extra consecutive nick change: */
+       if (cptr->nextnick < now)
+         cptr->nextnick = now;
+      }
+    }
+    /*
+     * Also set 'lastnick' to current time, if changed.
+     */
+    if (strCasediff(parv[0], nick))
+      sptr->lastnick = (sptr == cptr) ? TStime() : atoi(parv[2]);
+
+    /*
+     * 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 (IsUser(sptr))
+    {
+      sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
+      add_history(sptr, 1);
+#ifdef NO_PROTOCOL9
+      sendto_serv_butone(cptr,
+         "%s%s NICK %s " TIME_T_FMT, NumNick(sptr), nick, sptr->lastnick);
+#else
+      sendto_lowprot_butone(cptr, 9,
+         ":%s NICK %s " TIME_T_FMT, parv[0], nick, sptr->lastnick);
+      sendto_highprot_butone(cptr, 10,
+         "%s%s NICK %s " TIME_T_FMT, NumNick(sptr), nick, sptr->lastnick);
+#endif
+    }
+    else
+      sendto_one(sptr, ":%s NICK :%s", parv[0], nick);
+    if (sptr->name[0])
+      hRemClient(sptr);
+    strcpy(sptr->name, nick);
+    hAddClient(sptr);
+  }
+  else
+  {
+    /* Local client setting NICK the first time */
+
+    strcpy(sptr->name, nick);
+    if (!sptr->user)
+    {
+      sptr->user = make_user(sptr);
+      sptr->user->server = &me;
+    }
+    SetLocalNumNick(sptr);
+    hAddClient(sptr);
+
+    /*
+     * If the client hasn't gotten a cookie-ping yet,
+     * choose a cookie and send it. -record!jegelhof@cloud9.net
+     */
+    if (!sptr->cookie)
+    {
+      do
+       sptr->cookie = (ircrandom() & 0x7fffffff);
+      while (!sptr->cookie);
+      sendto_one(cptr, "PING :%u", sptr->cookie);
+    }
+    else if (*sptr->user->host && sptr->cookie == COOKIE_VERIFIED)
+    {
+      /*
+       * USER and PONG already received, now we have NICK.
+       * register_user may reject the client and call exit_client
+       * for it - must test this and exit m_nick too !
+       */
+      sptr->lastnick = TStime();       /* Always local client */
+      if (register_user(cptr, sptr, nick, sptr->user->username) == CPTR_KILLED)
+       return CPTR_KILLED;
+    }
+  }
+  return 0;
+}
+
+/*
+ * add_target
+ *
+ * sptr must be a local client!
+ *
+ * Cannonifies target for client `sptr'.
+ */
+void add_target(aClient *sptr, void *target)
+{
+  register unsigned char *p;
+  register unsigned int tmp = ((size_t)target & 0xffff00) >> 8;
+  unsigned char hash = (tmp * tmp) >> 12;
+  if (sptr->targets[0] == hash)        /* Last person that we messaged ourself? */
+    return;
+  for (p = sptr->targets; p < &sptr->targets[MAXTARGETS - 1];)
+    if (*++p == hash)
+      return;                  /* Already in table */
+
+  /* New target */
+  memmove(&sptr->targets[RESERVEDTARGETS + 1],
+      &sptr->targets[RESERVEDTARGETS], MAXTARGETS - RESERVEDTARGETS - 1);
+  sptr->targets[RESERVEDTARGETS] = hash;
+  return;
+}
+
+/*
+ * check_target_limit
+ *
+ * sptr must be a local client !
+ *
+ * Returns 'true' (1) when too many targets are addressed.
+ * Returns 'false' (0) when it's ok to send to this target.
+ */
+int check_target_limit(aClient *sptr, void *target, const char *name,
+    int created)
+{
+  register unsigned char *p;
+  register unsigned int tmp = ((size_t)target & 0xffff00) >> 8;
+  unsigned char hash = (tmp * tmp) >> 12;
+  if (sptr->targets[0] == hash)        /* Same target as last time ? */
+    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;
+    }
+
+  /* New target */
+  if (!created)
+  {
+    if (now < sptr->nexttarget)
+    {
+      if (sptr->nexttarget - now < TARGET_DELAY + 8)   /* No server flooding */
+      {
+       sptr->nexttarget += 2;
+       sendto_one(sptr, err_str(ERR_TARGETTOOFAST),
+           me.name, sptr->name, name, sptr->nexttarget - now);
+      }
+      return 1;
+    }
+    else
+    {
+#ifdef GODMODE
+      sendto_one(sptr, ":%s NOTICE %s :New target: %s; ft " TIME_T_FMT,
+         me.name, sptr->name, name, (now - sptr->nexttarget) / TARGET_DELAY);
+#endif
+      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;
+}
+
+/*
+ * 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[parc-1] = message text
+ *
+ * massive cleanup
+ * rev argv 6/91
+ */
+static int m_message(aClient *cptr, aClient *sptr,
+    int parc, char *parv[], int notice)
+{
+  Reg1 aClient *acptr;
+  Reg2 char *s;
+  aChannel *chptr;
+  char *nick, *server, *p, *cmd, *host;
+
+  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[parc - 1] == '\0')
+  {
+    sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+    return -1;
+  }
+
+  if (MyUser(sptr))
+    parv[1] = canonize(parv[1]);
+  for (p = NULL, nick = strtoken(&p, parv[1], ","); nick;
+      nick = strtoken(&p, NULL, ","))
+  {
+    /*
+     * channel msg?
+     */
+    if (IsChannelName(nick))
+    {
+      if ((chptr = FindChannel(nick)))
+      {
+       if (can_send(sptr, chptr) == 0  /* This first: Almost never a server/service */
+           || IsChannelService(sptr) || IsServer(sptr))
+       {
+         if (MyUser(sptr) && (chptr->mode.mode & MODE_NOPRIVMSGS) &&
+             check_target_limit(sptr, chptr, chptr->chname, 0))
+           continue;
+         sendto_channel_butone(cptr, sptr, chptr,
+             ":%s %s %s :%s", parv[0], cmd, chptr->chname, parv[parc - 1]);
+       }
+       else if (!notice)
+         sendto_one(sptr, err_str(ERR_CANNOTSENDTOCHAN),
+             me.name, parv[0], chptr->chname);
+       continue;
+      }
+    }
+    else if (*nick != '$' && !strchr(nick, '@'))
+    {
+      /*
+       * nickname addressed?
+       */
+      if (MyUser(sptr) || Protocol(cptr) < 10)
+       acptr = FindUser(nick);
+      else if ((acptr = findNUser(nick)) && !IsUser(acptr))
+       acptr = NULL;
+      if (acptr)
+      {
+       if (MyUser(sptr) && check_target_limit(sptr, acptr, acptr->name, 0))
+         continue;
+       if (!is_silenced(sptr, acptr))
+       {
+         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);
+         if (MyUser(acptr) || Protocol(acptr->from) < 10)
+         {
+           if (MyUser(acptr))
+             add_target(acptr, sptr);
+           sendto_prefix_one(acptr, sptr, ":%s %s %s :%s",
+               parv[0], cmd, acptr->name, parv[parc - 1]);
+         }
+         else
+           sendto_prefix_one(acptr, sptr, ":%s %s %s%s :%s",
+               parv[0], cmd, NumNick(acptr), parv[parc - 1]);
+       }
+      }
+      else if (MyUser(sptr) || Protocol(cptr) < 10)
+       sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
+      else
+       sendto_one(sptr,
+           ":%s %d %s * :Target left UnderNet. Failed to deliver: [%.50s]",
+           me.name, ERR_NOSUCHNICK, sptr->name, parv[parc - 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 (MyConnect(sptr))
+      {
+       if (!(s = strrchr(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[parc - 1]);
+      continue;
+    }
+    else if ((server = strchr(nick, '@')) && (acptr = FindServer(server + 1)))
+    {
+      /*
+       * NICK[%host]@server addressed? See if <server> is me first
+       */
+      if (!IsMe(acptr))
+      {
+       sendto_one(acptr, ":%s %s %s :%s", parv[0], cmd, nick, parv[parc - 1]);
+       continue;
+      }
+
+      /* Look for an user whose NICK is equal to <nick> and then
+       * check if it's hostname matches <host> and if it's a local
+       * user. */
+      *server = '\0';
+      if ((host = strchr(nick, '%')))
+       *host++ = '\0';
+
+      if ((!(acptr = FindUser(nick))) ||
+         (!(MyUser(acptr))) ||
+         ((!(BadPtr(host))) && match(host, acptr->user->host)))
+       acptr = NULL;
+
+      *server = '@';
+      if (host)
+       *--host = '%';
+
+      if (acptr)
+      {
+       if (!(is_silenced(sptr, acptr)))
+         sendto_prefix_one(acptr, sptr, ":%s %s %s :%s",
+             parv[0], cmd, nick, parv[parc - 1]);
+       continue;
+      }
+    }
+    if (IsChannelName(nick))
+      sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], nick);
+    else
+      sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
+  }
+  return 0;
+}
+
+/*
+ * m_private
+ *
+ * parv[0] = sender prefix
+ * parv[1] = receiver list
+ * parv[parc-1] = message text
+ */
+int m_private(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  return m_message(cptr, sptr, parc, parv, 0);
+}
+
+/*
+ * m_notice
+ *
+ * parv[0] = sender prefix
+ * parv[1] = receiver list
+ * parv[parc-1] = notice text
+ */
+int m_notice(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  if (MyUser(sptr) && parv[1] && parv[1][0] == '@' &&
+      IsChannelName(&parv[1][1]))
+  {
+    parv[1]++;                 /* Get rid of '@' */
+    return m_wallchops(cptr, sptr, parc, parv);
+  }
+  return m_message(cptr, sptr, parc, parv, 1);
+}
+
+
+/*
+ * whisper - called from m_cnotice and m_cprivmsg.
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nick
+ * parv[2] = #channel
+ * parv[3] = Private message text
+ *
+ * Added 971023 by Run.
+ * Reason: Allows channel operators to sent an arbitrary number of private
+ *   messages to users on their channel, avoiding the max.targets limit.
+ *   Building this into m_private would use too much cpu because we'd have
+ *   to a cross channel lookup for every private message!
+ * Note that we can't allow non-chan ops to use this command, it would be
+ *   abused by mass advertisers.
+ */
+int whisper(aClient *sptr, int parc, char *parv[], int notice)
+{
+  int s_is_member = 0, s_is_voiced = 0, t_is_member = 0;
+  aClient *tcptr;
+  aChannel *chptr;
+  register Link *lp;
+
+  if (!MyUser(sptr))
+    return 0;
+  if (parc < 4 || BadPtr(parv[3]))
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
+       me.name, parv[0], notice ? "CNOTICE" : "CPRIVMSG");
+    return 0;
+  }
+  if (!(chptr = FindChannel(parv[2])))
+  {
+    sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[2]);
+    return 0;
+  }
+  if (!(tcptr = FindUser(parv[1])))
+  {
+    sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]);
+    return 0;
+  }
+  for (lp = chptr->members; lp; lp = lp->next)
+  {
+    register aClient *mcptr = lp->value.cptr;
+    if (mcptr == sptr)
+    {
+      s_is_member = 1;
+      if ((lp->flags & (CHFL_CHANOP | CHFL_VOICE)))
+       s_is_voiced = 1;
+      else
+       break;
+      if (t_is_member)
+       break;
+    }
+    if (mcptr == tcptr)
+    {
+      t_is_member = 1;
+      if (s_is_voiced)
+       break;
+    }
+  }
+  if (!s_is_voiced)
+  {
+    sendto_one(sptr, err_str(s_is_member ? ERR_VOICENEEDED : ERR_NOTONCHANNEL),
+       me.name, parv[0], chptr->chname);
+    return 0;
+  }
+  if (!t_is_member)
+  {
+    sendto_one(sptr, err_str(ERR_USERNOTINCHANNEL),
+       me.name, parv[0], tcptr->name, chptr->chname);
+    return 0;
+  }
+  if (is_silenced(sptr, tcptr))
+    return 0;
+
+  if (tcptr->user && tcptr->user->away)
+    sendto_one(sptr, rpl_str(RPL_AWAY),
+       me.name, parv[0], tcptr->name, tcptr->user->away);
+  if (MyUser(tcptr) || Protocol(tcptr->from) < 10)
+    sendto_prefix_one(tcptr, sptr, ":%s %s %s :%s",
+       parv[0], notice ? "NOTICE" : "PRIVMSG", tcptr->name, parv[3]);
+  else
+    sendto_prefix_one(tcptr, sptr, ":%s %s %s%s :%s",
+       parv[0], notice ? "NOTICE" : "PRIVMSG", NumNick(tcptr), parv[3]);
+
+  return 0;
+}
+
+int m_cnotice(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  return whisper(sptr, parc, parv, 1);
+}
+
+int m_cprivmsg(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  return whisper(sptr, parc, parv, 0);
+}
+
+/*
+ * m_wallchops
+ *
+ * parv[0] = sender prefix
+ * parv[1] = target channel
+ * parv[parc - 1] = wallchops text
+ */
+int m_wallchops(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aChannel *chptr;
+
+  sptr->flags &= ~FLAGS_TS8;
+
+  if (parc < 2 || *parv[1] == '\0')
+  {
+    sendto_one(sptr, err_str(ERR_NORECIPIENT), me.name, parv[0], "WALLCHOPS");
+    return -1;
+  }
+
+  if (parc < 3 || *parv[parc - 1] == '\0')
+  {
+    sendto_one(sptr, err_str(ERR_NOTEXTTOSEND), me.name, parv[0]);
+    return -1;
+  }
+
+  if (MyUser(sptr))
+    parv[1] = canonize(parv[1]);
+
+  if (IsChannelName(parv[1]))
+  {
+    if ((chptr = FindChannel(parv[1])))
+    {
+      if (can_send(sptr, chptr) == 0)
+      {
+       if (MyUser(sptr) && (chptr->mode.mode & MODE_NOPRIVMSGS) &&
+           check_target_limit(sptr, chptr, chptr->chname, 0))
+         return 0;
+       /* Send to local clients: */
+       sendto_lchanops_butone(cptr, sptr, chptr,
+           ":%s NOTICE @%s :%s", parv[0], parv[1], parv[parc - 1]);
+#ifdef NO_PROTOCOL9
+       /* And to other servers: */
+       sendto_chanopsserv_butone(cptr, sptr, chptr,
+           ":%s WC %s :%s", parv[0], parv[1], parv[parc - 1]);
+#else
+       /*
+        * WARNING: `sendto_chanopsserv_butone' is heavily hacked when
+        * `NO_PROTOCOL9' is not defined ! Therefore this is the ONLY
+        * place you may use `sendto_chanopsserv_butone', until all
+        * servers are 2.10.
+        */
+       sendto_chanopsserv_butone(cptr, sptr, chptr,
+           ":%s WC %s :%s", parv[0], parv[1], parv[parc - 1]);
+#endif
+      }
+      else
+       sendto_one(sptr, err_str(ERR_CANNOTSENDTOCHAN),
+           me.name, parv[0], parv[1]);
+    }
+  }
+  else
+    sendto_one(sptr, err_str(ERR_NOSUCHCHANNEL), me.name, parv[0], parv[1]);
+
+  return 0;
+}
+
+/*
+ * m_user
+ *
+ * parv[0] = sender prefix
+ * parv[1] = username (login name, account)
+ * parv[2] = umode mask
+ * parv[3] = server notice mask
+ * parv[4] = users real name info
+ */
+int m_user(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+#define UFLAGS (FLAGS_INVISIBLE|FLAGS_WALLOP|FLAGS_SERVNOTICE)
+  char *username, *host, *server, *realname;
+  anUser *user;
+
+  if (IsServer(cptr))
+    return 0;
+
+  if (IsServerPort(cptr))
+    return exit_client(cptr, cptr, &me, "Use a different port");
+
+  if (parc > 2 && (username = strchr(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");
+    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];
+  realname = (parc < 5 || BadPtr(parv[4])) ? "<bad-realname>" : parv[4];
+
+  user = make_user(sptr);
+
+  if (!IsUnknown(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]);
+    return 0;
+  }
+
+  if (!strchr(host, '.'))      /* Not an IP# as hostname ? */
+    sptr->flags |= (UFLAGS & atoi(host));
+  if ((sptr->flags & FLAGS_SERVNOTICE))
+    set_snomask(sptr, (isDigit(*server) && !strchr(server, '.')) ?
+       (atoi(server) & SNO_USER) : SNO_DEFAULT, SNO_SET);
+  user->server = &me;
+  strncpy(sptr->info, realname, sizeof(sptr->info) - 1);
+  if (sptr->name[0] && sptr->cookie == COOKIE_VERIFIED)
+    /* NICK and PONG already received, now we have USER... */
+    return register_user(cptr, sptr, sptr->name, username);
+  else
+  {
+    strncpy(sptr->user->username, username, USERLEN);
+    strncpy(user->host, host, sizeof(user->host) - 1);
+  }
+  return 0;
+}
+
+/*
+ * m_quit
+ *
+ * parv[0] = sender prefix
+ * parv[parc-1] = comment
+ */
+int m_quit(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  register char *comment = (parc > 1
+      && parv[parc - 1]) ? parv[parc - 1] : cptr->name;
+
+  if (MyUser(sptr))
+  {
+    if (!strncmp("Local Kill", comment, 10) || !strncmp(comment, "Killed", 6))
+      comment = parv[0];
+    if (sptr->user)
+    {
+      Link *lp;
+      for (lp = sptr->user->channel; lp; lp = lp->next)
+       if (can_send(sptr, lp->value.chptr) != 0)
+         return exit_client(cptr, sptr, sptr, "Signed off");
+    }
+  }
+  if (strlen(comment) > (size_t)TOPICLEN)
+    comment[TOPICLEN] = '\0';
+  return IsServer(sptr) ? 0 : exit_client(cptr, sptr, sptr, comment);
+}
+
+/*
+ * m_kill
+ *
+ * parv[0] = sender prefix
+ * parv[1] = kill victim
+ * parv[parc-1] = kill path
+ */
+int m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr;
+  char *inpath = get_client_name(cptr, FALSE);
+  char *user, *path, *killer;
+  int chasing = 0;
+
+  if (parc < 3 || *parv[1] == '\0')
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KILL");
+    return 0;
+  }
+
+  user = parv[1];
+  path = parv[parc - 1];       /* Either defined or NULL (parc >= 3) */
+
+#ifdef OPER_KILL
+  if (!IsPrivileged(cptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+#else
+  if (!IsServer(cptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+#endif
+  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 (MyUser(sptr) || Protocol(cptr) < 10)
+  {
+    if (!(acptr = FindClient(user)))
+    {
+      /*
+       * 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(user, (long)15)))
+      {
+       sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], user);
+       return 0;
+      }
+      sendto_one(sptr, ":%s NOTICE %s :Changed KILL %s into %s",
+         me.name, parv[0], user, acptr->name);
+      chasing = 1;
+    }
+  }
+  else if (!(acptr = findNUser(user)))
+  {
+    if (Protocol(cptr) < 10 && IsUser(sptr))
+      sendto_one(sptr,
+         ":%s NOTICE %s :KILL target disconnected before I got him :(",
+         me.name, parv[0]);
+    else if (IsUser(sptr))
+      sendto_one(sptr,
+         "%s NOTICE %s%s :KILL target disconnected before I got him :(",
+         NumServ(&me), NumNick(sptr));
+    return 0;
+  }
+  if (!MyConnect(acptr) && IsLocOp(cptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+  if (IsServer(acptr) || IsMe(acptr))
+  {
+    sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]);
+    return 0;
+  }
+
+  /* if the user is +k, prevent a kill from local user */
+  if (IsChannelService(acptr) && MyUser(sptr))
+  {
+    sendto_one(sptr, err_str(ERR_ISCHANSERVICE), me.name,
+       parv[0], "KILL", acptr->name);
+    return 0;
+  }
+
+#ifdef LOCAL_KILL_ONLY
+  if (MyConnect(sptr) && !MyConnect(acptr))
+  {
+    sendto_one(sptr, ":%s NOTICE %s :Nick %s isnt on your server",
+       me.name, parv[0], acptr->name);
+    return 0;
+  }
+#endif
+  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 */
+      inpath = me.sockhost;
+    else
+      inpath = cptr->sockhost;
+    if (!BadPtr(path))
+    {
+      sprintf_irc(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.
+   */
+  if (IsLocOp(sptr) && !MyConnect(acptr))
+  {
+    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
+    return 0;
+  }
+  sendto_op_mask(IsServer(sptr) ? SNO_SERVKILL : SNO_OPERKILL,
+      "Received KILL message for %s. From %s Path: %s!%s",
+      acptr->name, parv[0], inpath, path);
+#if defined(USE_SYSLOG) && defined(SYSLOG_KILL)
+  if (MyUser(acptr))
+  {                            /* get more infos when your local
+                                  clients are killed -- _dl */
+    if (IsServer(sptr))
+      syslog(LOG_DEBUG,
+         "A local client %s!%s@%s KILLED from %s [%s] Path: %s!%s)",
+         acptr->name, acptr->user->username, acptr->user->host,
+         parv[0], sptr->name, inpath, path);
+    else
+      syslog(LOG_DEBUG,
+         "A local client %s!%s@%s KILLED by %s [%s!%s@%s] (%s!%s)",
+         acptr->name, acptr->user->username, acptr->user->host,
+         parv[0], sptr->name, sptr->user->username, sptr->user->host,
+         inpath, path);
+  }
+  else 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_lowprot_butone(cptr, 9, ":%s KILL %s :%s!%s",
+       parv[0], acptr->name, inpath, path);
+    sendto_highprot_butone(cptr, 10, ":%s KILL %s%s :%s!%s",
+       parv[0], NumNick(acptr), inpath, path);
+#ifndef NO_PROTOCOL9
+    if (chasing && IsServer(cptr))     /* Can be removed when all are Protocol 10 */
+      sendto_one(cptr, ":%s KILL %s :%s!%s",
+         me.name, acptr->name, inpath, path);
+#endif
+    /* We *can* have crossed a NICK with this numeric... --Run */
+    /* Note the following situation:
+     *  KILL SAA -->       X
+     *  <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
+     * Where the KILL reaches point X before the QUIT does.
+     * This would then *still* cause an orphan because the KILL doesn't reach S
+     * (because of the SQUIT), the QUIT is ignored (because of the KILL)
+     * and the second NICK ... SAA causes an orphan on the server at the
+     * right (which then isn't removed when the SQUIT arrives).
+     * Therefore we still need to detect numeric nick collisions too.
+     */
+    if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
+      sendto_one(cptr, "%s KILL %s%s :%s!%s (Ghost5)",
+         NumServ(&me), NumNick(acptr), inpath, path);
+    acptr->flags |= FLAGS_KILLED;
+  }
+
+  /*
+   * 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))
+    sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s",
+       parv[0], acptr->name, inpath, 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))
+    sprintf_irc(buf2, "Local kill by %s (%s)", sptr->name,
+       BadPtr(parv[parc - 1]) ? sptr->name : parv[parc - 1]);
+  else
+  {
+    if ((killer = strchr(path, ' ')))
+    {
+      while (*killer && *killer != '!')
+       killer--;
+      if (!*killer)
+       killer = path;
+      else
+       killer++;
+    }
+    else
+      killer = path;
+    sprintf_irc(buf2, "Killed (%s)", killer);
+  }
+  return exit_client(cptr, acptr, sptr, buf2);
+}
+
+/*
+ * m_away                               - Added 14 Dec 1988 by jto.
+ *
+ * parv[0] = sender prefix
+ * parv[1] = away message
+ */
+int m_away(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 char *away, *awy2 = parv[1];
+
+  away = sptr->user->away;
+
+  if (parc < 2 || !*awy2)
+  {
+    /* Marking as not away */
+    if (away)
+    {
+      RunFree(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]);
+    return 0;
+  }
+
+  /* Marking as away */
+
+  if (strlen(awy2) > (size_t)TOPICLEN)
+    awy2[TOPICLEN] = '\0';
+  sendto_serv_butone(cptr, ":%s AWAY :%s", parv[0], awy2);
+
+  if (away)
+    away = (char *)RunRealloc(away, strlen(awy2) + 1);
+  else
+    away = (char *)RunMalloc(strlen(awy2) + 1);
+
+  sptr->user->away = away;
+  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(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr;
+  char *origin, *destination;
+
+  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 = FindClient(origin);
+  if (acptr && acptr != sptr)
+    origin = cptr->name;
+
+  if (!BadPtr(destination) && strCasediff(destination, me.name) != 0)
+  {
+    if ((acptr = FindServer(destination)))
+      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, me.name, origin);
+  return 0;
+}
+
+/*
+ * m_pong
+ *
+ * parv[0] = sender prefix
+ * parv[1] = origin
+ * parv[2] = destination
+ */
+int m_pong(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aClient *acptr;
+  char *origin, *destination;
+
+  if (MyUser(sptr))
+    return 0;
+
+  /* Check to see if this is a PONG :cookie reply from an
+   * unregistered user.  If so, process it. -record       */
+
+  if ((!IsRegistered(sptr)) && (sptr->cookie != 0) &&
+      (sptr->cookie != COOKIE_VERIFIED) && (parc > 1))
+  {
+    if (atol(parv[parc - 1]) == (long)sptr->cookie)
+    {
+      sptr->cookie = COOKIE_VERIFIED;
+      if (sptr->user && *sptr->user->host && sptr->name[0])    /* NICK and
+                                                                  USER OK */
+       return register_user(cptr, sptr, sptr->name, sptr->user->username);
+    }
+    else
+      sendto_one(sptr, ":%s %d %s :To connect, type /QUOTE PONG %u",
+         me.name, ERR_BADPING, sptr->name, sptr->cookie);
+
+    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];
+  cptr->flags &= ~FLAGS_PINGSENT;
+  sptr->flags &= ~FLAGS_PINGSENT;
+
+  if (!BadPtr(destination) && strCasediff(destination, me.name) != 0)
+  {
+    if ((acptr = FindClient(destination)))
+      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;
+}
+
+static char umode_buf[2 * sizeof(user_modes) / sizeof(int)];
+
+/*
+ * added Sat Jul 25 07:30:42 EST 1992
+ */
+static void send_umode_out(aClient *cptr, aClient *sptr, int old)
+{
+  Reg1 int i;
+  Reg2 aClient *acptr;
+
+  send_umode(NULL, sptr, old, SEND_UMODES);
+
+  for (i = highest_fd; i >= 0; i--)
+    if ((acptr = loc_clients[i]) && IsServer(acptr) &&
+       (acptr != cptr) && (acptr != sptr) && *umode_buf)
+      sendto_one(acptr, ":%s MODE %s :%s", sptr->name, sptr->name, umode_buf);
+
+  if (cptr && MyUser(cptr))
+    send_umode(cptr, sptr, old, ALL_UMODES);
+}
+
+/*
+ *  m_oper
+ *    parv[0] = sender prefix
+ *    parv[1] = oper name
+ *    parv[2] = oper password
+ */
+int m_oper(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  aConfItem *aconf;
+  char *name, *password, *encr;
+#ifdef CRYPT_OPER_PASSWORD
+  char salt[3];
+#endif /* CRYPT_OPER_PASSWORD */
+
+  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))
+  {
+    ++nrof.opers;
+    sptr->flags |= FLAGS_OPER;
+    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]);
+    return 0;
+  }
+  else if (IsAnOper(sptr))
+  {
+    if (MyConnect(sptr))
+      sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
+    return 0;
+  }
+  if (!(aconf = find_conf_exact(name, sptr->username, sptr->sockhost,
+      CONF_OPS)) && !(aconf = find_conf_exact(name, sptr->username,
+      inetntoa(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);
+    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)
+  {
+    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) &&
+      !strcmp(encr, aconf->passwd) && attach_conf(sptr, aconf) == ACR_OK)
+  {
+    int old = (sptr->flags & ALL_UMODES);
+    char *s;
+
+    s = strchr(aconf->host, '@');
+    *s++ = '\0';
+#ifdef OPER_REMOTE
+    if (aconf->status == CONF_LOCOP)
+    {
+#else
+    if ((match(s, me.sockhost) && !IsLocal(sptr)) ||
+       aconf->status == CONF_LOCOP)
+    {
+#endif
+      ClearOper(sptr);
+      SetLocOp(sptr);
+    }
+    else
+    {
+      /* prevent someone from being both oper and local oper */
+      ClearLocOp(sptr);
+      SetOper(sptr);
+      ++nrof.opers;
+    }
+    *--s = '@';
+    sendto_ops("%s (%s@%s) is now operator (%c)", parv[0],
+       sptr->user->username, sptr->sockhost, IsOper(sptr) ? 'O' : 'o');
+    sptr->flags |= (FLAGS_WALLOP | FLAGS_SERVNOTICE | FLAGS_DEBUG);
+    set_snomask(sptr, SNO_OPERDEFAULT, SNO_ADD);
+    send_umode_out(cptr, sptr, old);
+    sendto_one(sptr, rpl_str(RPL_YOUREOPER), me.name, parv[0]);
+#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
+    if (IsUser(sptr))
+      write_log(FNAME_OPERLOG,
+         "%s OPER (%s) (%s) by (%s!%s@%s)\n", myctime(now), name,
+         encr, parv[0], sptr->user->username, sptr->sockhost);
+#endif
+  }
+  else
+  {
+    detach_conf(sptr, aconf);
+    sendto_one(sptr, err_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
+    sendto_realops("Failed OPER attempt by %s (%s@%s)",
+       parv[0], sptr->user->username, sptr->sockhost);
+  }
+  return 0;
+}
+
+/*
+ * m_pass
+ *
+ * parv[0] = sender prefix
+ * parv[1] = password
+ */
+int m_pass(aClient *cptr, aClient *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;
+  }
+  strncpy(cptr->passwd, password, sizeof(cptr->passwd) - 1);
+  return 0;
+}
+
+/*
+ * m_userhost
+ *
+ * Added by Darren Reed 13/8/91 to aid clients and reduce the need for
+ * complicated requests like WHOIS.
+ *
+ * Returns user/host information only (no spurious AWAY labels or channels).
+ *
+ * Rewritten to speed it up by Carlo Wood 3/8/97.
+ */
+int m_userhost(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  Reg1 char *s;
+  Reg2 int i, j = 5;
+  char *p = NULL, *sbuf;
+  aClient *acptr;
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "USERHOST");
+    return 0;
+  }
+
+  sbuf = sprintf_irc(sendbuf, rpl_str(RPL_USERHOST), me.name, parv[0]);
+  for (i = j, s = strtoken(&p, parv[1], " "); i && s;
+      s = strtoken(&p, (char *)NULL, " "), i--)
+    if ((acptr = FindUser(s)))
+    {
+      if (i < j)
+       *sbuf++ = ' ';
+      sbuf = sprintf_irc(sbuf, "%s%s=%c%s@%s", acptr->name,
+         IsAnOper(acptr) ? "*" : "", (acptr->user->away) ? '-' : '+',
+         acptr->user->username, acptr->user->host);
+    }
+    else
+    {
+      if (i < j)
+       sendbufto_one(sptr);
+      sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], s);
+      sbuf = sprintf_irc(sendbuf, rpl_str(RPL_USERHOST), me.name, parv[0]);
+      j = i - 1;
+    }
+  if (j)
+    sendbufto_one(sptr);
+  return 0;
+}
+
+/*
+ * m_userip added by Carlo Wood 3/8/97.
+ *
+ * The same as USERHOST, but with the IP-number instead of the hostname.
+ */
+int m_userip(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  Reg1 char *s;
+  Reg3 int i, j = 5;
+  char *p = NULL, *sbuf;
+  aClient *acptr;
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "USERIP");
+    return 0;
+  }
+
+  sbuf = sprintf_irc(sendbuf, rpl_str(RPL_USERIP), me.name, parv[0]);
+  for (i = j, s = strtoken(&p, parv[1], " "); i && s;
+      s = strtoken(&p, (char *)NULL, " "), i--)
+    if ((acptr = FindUser(s)))
+    {
+      if (i < j)
+       *sbuf++ = ' ';
+      sbuf = sprintf_irc(sbuf, "%s%s=%c%s@%s", acptr->name,
+         IsAnOper(acptr) ? "*" : "", (acptr->user->away) ? '-' : '+',
+         acptr->user->username, inetntoa(acptr->ip));
+    }
+    else
+    {
+      if (i < j)
+       sendbufto_one(sptr);
+      sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], s);
+      sbuf = sprintf_irc(sendbuf, rpl_str(RPL_USERIP), me.name, parv[0]);
+      j = i - 1;
+    }
+  if (i < j)
+    sendbufto_one(sptr);
+  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(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  Reg1 aClient *acptr;
+  Reg2 char *s, **pav = parv;
+  Reg3 size_t len;
+  char *p = NULL;
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "ISON");
+    return 0;
+  }
+
+  sprintf_irc(buf, rpl_str(RPL_ISON), me.name, *parv);
+  len = strlen(buf);
+  buf[sizeof(buf) - 1] = 0;
+
+  for (s = strtoken(&p, *++pav, " "); s; s = strtoken(&p, NULL, " "))
+    if ((acptr = FindUser(s)))
+    {
+      strncat(buf, acptr->name, sizeof(buf) - 1 - len);
+      len += strlen(acptr->name);
+      if (len >= sizeof(buf) - 1)
+       break;
+      strcat(buf, " ");
+      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(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg1 int flag;
+  Reg2 int *s;
+  Reg3 char **p, *m;
+  aClient *acptr;
+  int what, setflags;
+  snomask_t tmpmask = 0;
+  int snomask_given = 0;
+
+  what = MODE_ADD;
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "MODE");
+    return 0;
+  }
+
+  if (!(acptr = FindUser(parv[1])))
+  {
+    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_ops_butone(NULL, &me, ":%s WALLOPS :MODE for User %s From %s!%s",
+         me.name, parv[1], get_client_name(cptr, FALSE), sptr->name);
+    else
+      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->flags & flag)
+       *m++ = (char)(*(s + 1));
+    *m = '\0';
+    sendto_one(sptr, rpl_str(RPL_UMODEIS), me.name, parv[0], buf);
+    if ((sptr->flags & FLAGS_SERVNOTICE) && MyConnect(sptr)
+       && sptr->snomask !=
+       (unsigned int)(IsOper(sptr) ? SNO_OPERDEFAULT : SNO_DEFAULT))
+      sendto_one(sptr, rpl_str(RPL_SNOMASK), me.name, parv[0], sptr->snomask,
+         sptr->snomask);
+    return 0;
+  }
+
+  /* find flags already set for user */
+  setflags = 0;
+  for (s = user_modes; (flag = *s); s += 2)
+    if (sptr->flags & flag)
+      setflags |= flag;
+  if (MyConnect(sptr))
+    tmpmask = sptr->snomask;
+
+  /*
+   * parse mode change string(s)
+   */
+  for (p = &parv[2]; *p; p++)  /* p is changed in loop too */
+    for (m = *p; *m; m++)
+      switch (*m)
+      {
+       case '+':
+         what = MODE_ADD;
+         break;
+       case '-':
+         what = MODE_DEL;
+         break;
+       case 's':
+         if (*(p + 1) && is_snomask(*(p + 1)))
+         {
+           snomask_given = 1;
+           tmpmask = umode_make_snomask(tmpmask, *++p, what);
+           tmpmask &= (IsAnOper(sptr) ? SNO_ALL : SNO_USER);
+         }
+         else
+           tmpmask = (what == MODE_ADD) ?
+               (IsAnOper(sptr) ? SNO_OPERDEFAULT : SNO_DEFAULT) : 0;
+         if (tmpmask)
+           sptr->flags |= FLAGS_SERVNOTICE;
+         else
+           sptr->flags &= ~FLAGS_SERVNOTICE;
+         break;
+         /*
+          * We may not get these, but they shouldnt be in default:
+          */
+       case ' ':
+       case '\n':
+       case '\r':
+       case '\t':
+         break;
+       default:
+         for (s = user_modes; (flag = *s); s += 2)
+           if (*m == (char)(*(s + 1)))
+           {
+             if (what == MODE_ADD)
+               sptr->flags |= flag;
+             else if ((flag & (FLAGS_OPER | FLAGS_LOCOP)))
+             {
+               sptr->flags &= ~(FLAGS_OPER | FLAGS_LOCOP);
+               if (MyConnect(sptr))
+                 tmpmask = sptr->snomask & ~SNO_OPER;
+             }
+             /* allow either -o or -O to reset all operator status's... */
+             else
+               sptr->flags &= ~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 & FLAGS_OPER) && IsOper(sptr) && !IsServer(cptr))
+    ClearOper(sptr);
+  if (!(setflags & FLAGS_LOCOP) && IsLocOp(sptr) && !IsServer(cptr))
+    sptr->flags &= ~FLAGS_LOCOP;
+  if ((setflags & (FLAGS_OPER | FLAGS_LOCOP)) && !IsAnOper(sptr) &&
+      MyConnect(sptr))
+    det_confs_butmask(sptr, CONF_CLIENT & ~CONF_OPS);
+  /* new umode; servers can set it, local users cannot;
+   * prevents users from /kick'ing or /mode -o'ing */
+  if (!(setflags & FLAGS_CHSERV) && !IsServer(cptr))
+    sptr->flags &= ~FLAGS_CHSERV;
+  /*
+   * Compare new flags with old flags and send string which
+   * will cause servers to update correctly.
+   */
+  if ((setflags & FLAGS_OPER) && !IsOper(sptr))
+    --nrof.opers;
+  if (!(setflags & FLAGS_OPER) && IsOper(sptr))
+    ++nrof.opers;
+  if ((setflags & FLAGS_INVISIBLE) && !IsInvisible(sptr))
+    --nrof.inv_clients;
+  if (!(setflags & FLAGS_INVISIBLE) && IsInvisible(sptr))
+    ++nrof.inv_clients;
+  send_umode_out(cptr, sptr, setflags);
+
+  if (MyConnect(sptr))
+  {
+    if (tmpmask != sptr->snomask)
+      set_snomask(sptr, tmpmask, SNO_SET);
+    if (sptr->snomask && snomask_given)
+      sendto_one(sptr, rpl_str(RPL_SNOMASK), me.name, sptr->name,
+         sptr->snomask, sptr->snomask);
+  }
+
+  return 0;
+}
+
+/*
+ * Build umode string for BURST command
+ * --Run
+ */
+char *umode_str(aClient *cptr)
+{
+  char *m = umode_buf;         /* Maximum string size: "owidg\0" */
+  int *s, flag, c_flags;
+
+  c_flags = cptr->flags & SEND_UMODES; /* cleaning up the original code */
+
+  for (s = user_modes; (flag = *s); s += 2)
+    if ((c_flags & flag))
+      *m++ = *(s + 1);
+  *m = '\0';
+
+  return umode_buf;            /* Note: static buffer, gets
+                                  overwritten by send_umode() */
+}
+
+/*
+ * Send the MODE string for user (user) to connection cptr
+ * -avalon
+ */
+void send_umode(aClient *cptr, aClient *sptr, int old, int sendmask)
+{
+  Reg1 int *s, flag;
+  Reg2 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 (MyUser(sptr) && !(flag & sendmask))
+      continue;
+    if ((flag & old) && !(sptr->flags & flag))
+    {
+      if (what == MODE_DEL)
+       *m++ = *(s + 1);
+      else
+      {
+       what = MODE_DEL;
+       *m++ = '-';
+       *m++ = *(s + 1);
+      }
+    }
+    else if (!(flag & old) && (sptr->flags & 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 MODE %s :%s", sptr->name, sptr->name, umode_buf);
+}
+
+/*
+ * Check to see if this resembles a sno_mask.  It is if 1) there is
+ * at least one digit and 2) The first digit occurs before the first
+ * alphabetic character.
+ */
+int is_snomask(char *word)
+{
+  if (word)
+  {
+    for (; *word; word++)
+      if (isDigit(*word))
+       return 1;
+      else if (isAlpha(*word))
+       return 0;
+  }
+  return 0;
+}
+
+/*
+ * If it begins with a +, count this as an additive mask instead of just
+ * a replacement.  If what == MODE_DEL, "+" has no special effect.
+ */
+snomask_t umode_make_snomask(snomask_t oldmask, char *arg, int what)
+{
+  snomask_t sno_what;
+  snomask_t newmask;
+  if (*arg == '+')
+  {
+    arg++;
+    if (what == MODE_ADD)
+      sno_what = SNO_ADD;
+    else
+      sno_what = SNO_DEL;
+  }
+  else if (*arg == '-')
+  {
+    arg++;
+    if (what == MODE_ADD)
+      sno_what = SNO_DEL;
+    else
+      sno_what = SNO_ADD;
+  }
+  else
+    sno_what = (what == MODE_ADD) ? SNO_SET : SNO_DEL;
+  /* pity we don't have strtoul everywhere */
+  newmask = (snomask_t)atoi(arg);
+  if (sno_what == SNO_DEL)
+    newmask = oldmask & ~newmask;
+  else if (sno_what == SNO_ADD)
+    newmask |= oldmask;
+  return newmask;
+}
+
+/*
+ * This function sets a Client's server notices mask, according to
+ * the parameter 'what'.  This could be even faster, but the code
+ * gets mighty hard to read :)
+ */
+void delfrom_list(aClient *, Link **);
+void set_snomask(aClient *cptr, snomask_t newmask, int what)
+{
+  snomask_t oldmask, diffmask; /* unsigned please */
+  int i;
+  Link *tmp;
+
+  oldmask = cptr->snomask;
+
+  if (what == SNO_ADD)
+    newmask |= oldmask;
+  else if (what == SNO_DEL)
+    newmask = oldmask & ~newmask;
+  else if (what != SNO_SET)    /* absolute set, no math needed */
+    sendto_ops("setsnomask called with %d ?!", what);
+
+  newmask &= (IsAnOper(cptr) ? SNO_ALL : SNO_USER);
+
+  diffmask = oldmask ^ newmask;
+
+  for (i = 0; diffmask >> i; i++)
+    if (((diffmask >> i) & 1))
+    {
+      if (((newmask >> i) & 1))
+      {
+       tmp = make_link();
+       tmp->next = opsarray[i];
+       tmp->value.cptr = cptr;
+       opsarray[i] = tmp;
+      }
+      else
+       /* not real portable :( */
+       delfrom_list(cptr, &opsarray[i]);
+    }
+  cptr->snomask = newmask;
+}
+
+void delfrom_list(aClient *cptr, Link **list)
+{
+  Link *tmp, *prv = NULL;
+  for (tmp = *list; tmp; tmp = tmp->next)
+  {
+    if (tmp->value.cptr == cptr)
+    {
+      if (prv)
+       prv->next = tmp->next;
+      else
+       *list = tmp->next;
+      free_link(tmp);
+      break;
+    }
+    prv = tmp;
+  }
+}
+
+/*
+ * 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.
+ */
+int is_silenced(aClient *sptr, aClient *acptr)
+{
+  Reg1 Link *lp;
+  Reg2 anUser *user;
+  static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
+  static char senderip[16 + NICKLEN + USERLEN + 5];
+
+  if (!(acptr->user) || !(lp = acptr->user->silence) || !(user = sptr->user))
+    return 0;
+  sprintf_irc(sender, "%s!%s@%s", sptr->name, user->username, user->host);
+  sprintf_irc(senderip, "%s!%s@%s", sptr->name, user->username,
+      inetntoa(sptr->ip));
+  for (; lp; lp = lp->next)
+  {
+    if ((!(lp->flags & CHFL_SILENCE_IPMASK) && !match(lp->value.cp, sender)) ||
+       ((lp->flags & CHFL_SILENCE_IPMASK) && !match(lp->value.cp, senderip)))
+    {
+      if (!MyConnect(sptr))
+      {
+       if (Protocol(sptr->from) < 10)
+         sendto_one(sptr->from, ":%s SILENCE %s %s", acptr->name,
+             sptr->name, lp->value.cp);
+       else
+         sendto_one(sptr->from, ":%s SILENCE %s%s %s", acptr->name,
+             NumNick(sptr), lp->value.cp);
+      }
+      return 1;
+    }
+  }
+  return 0;
+}
+
+/*
+ * del_silence
+ *
+ * Removes all silence masks from the list of sptr that fall within `mask'
+ * Returns -1 if none where found, 0 otherwise.
+ */
+int del_silence(aClient *sptr, char *mask)
+{
+  Reg1 Link **lp;
+  Reg2 Link *tmp;
+  int ret = -1;
+
+  for (lp = &sptr->user->silence; *lp;)
+    if (!mmatch(mask, (*lp)->value.cp))
+    {
+      tmp = *lp;
+      *lp = tmp->next;
+      RunFree(tmp->value.cp);
+      free_link(tmp);
+      ret = 0;
+    }
+    else
+      lp = &(*lp)->next;
+
+  return ret;
+}
+
+static int add_silence(aClient *sptr, char *mask)
+{
+  Reg1 Link *lp, **lpp;
+  Reg3 int cnt = 0, len = strlen(mask);
+  char *ip_start;
+
+  for (lpp = &sptr->user->silence, lp = *lpp; lp;)
+  {
+    if (!strCasediff(mask, lp->value.cp))
+      return -1;
+    if (!mmatch(mask, lp->value.cp))
+    {
+      Link *tmp = lp;
+      *lpp = lp = lp->next;
+      RunFree(tmp->value.cp);
+      free_link(tmp);
+      continue;
+    }
+    if (MyUser(sptr))
+    {
+      len += strlen(lp->value.cp);
+      if ((len > MAXSILELENGTH) || (++cnt >= MAXSILES))
+      {
+       sendto_one(sptr, err_str(ERR_SILELISTFULL), me.name, sptr->name, mask);
+       return -1;
+      }
+      else if (!mmatch(lp->value.cp, mask))
+       return -1;
+    }
+    lpp = &lp->next;
+    lp = *lpp;
+  }
+  lp = make_link();
+  memset(lp, 0, sizeof(Link));
+  lp->next = sptr->user->silence;
+  lp->value.cp = (char *)RunMalloc(strlen(mask) + 1);
+  strcpy(lp->value.cp, mask);
+  if ((ip_start = strrchr(mask, '@')) && check_if_ipmask(ip_start + 1))
+    lp->flags = CHFL_SILENCE_IPMASK;
+  sptr->user->silence = lp;
+  return 0;
+}
+
+/*
+ * m_silence() - Added 19 May 1994 by Run.
+ *
+ *   parv[0] = sender prefix
+ * From local client:
+ *   parv[1] = mask (NULL sends the list)
+ * From remote client:
+ *   parv[1] = Numeric nick that must be silenced
+ *   parv[2] = mask
+ */
+int m_silence(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Link *lp;
+  aClient *acptr;
+  char c, *cp;
+
+  if (MyUser(sptr))
+  {
+    acptr = sptr;
+    if (parc < 2 || *parv[1] == '\0' || (acptr = FindUser(parv[1])))
+    {
+      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, sptr->name,
+         acptr->name);
+      return 0;
+    }
+    cp = parv[1];
+    c = *cp;
+    if (c == '-' || c == '+')
+      cp++;
+    else if (!(strchr(cp, '@') || strchr(cp, '.') ||
+       strchr(cp, '!') || strchr(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 (Protocol(cptr) < 10)
+      acptr = FindClient(parv[1]);     /* In case of NOTE notice, parv[1] */
+    else if (parv[1][1])       /* can be a server */
+      acptr = findNUser(parv[1]);
+    else
+      acptr = FindNServer(parv[1]);
+
+    if (*parv[2] == '-')
+    {
+      if (!del_silence(sptr, parv[2] + 1))
+       sendto_serv_butone(cptr, ":%s SILENCE * %s", parv[0], parv[2]);
+    }
+    else
+    {
+      add_silence(sptr, parv[2]);
+      if (acptr && IsServer(acptr->from))
+      {
+       if (Protocol(acptr->from) < 10)
+         sendto_one(acptr, ":%s SILENCE %s %s", parv[0], acptr->name, parv[2]);
+       else if (IsServer(acptr))
+         sendto_one(acptr, ":%s SILENCE %s %s",
+             parv[0], NumServ(acptr), parv[2]);
+       else
+         sendto_one(acptr, ":%s SILENCE %s%s %s",
+             parv[0], NumNick(acptr), parv[2]);
+      }
+    }
+  }
+  return 0;
+}
diff --git a/ircd/send.c b/ircd/send.c
new file mode 100644 (file)
index 0000000..08a70e7
--- /dev/null
@@ -0,0 +1,894 @@
+/*
+ * IRC - Internet Relay Chat, common/send.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 "sys.h"
+#include <stdio.h>
+#include "h.h"
+#include "struct.h"
+#include "s_bsd.h"
+#include "s_serv.h"
+#include "send.h"
+#include "s_misc.h"
+#include "common.h"
+#include "match.h"
+#include "s_bsd.h"
+#include "list.h"
+#include "ircd.h"
+#include "channel.h"
+#include "bsd.h"
+#include "class.h"
+#include "s_user.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+char sendbuf[2048];
+static int sentalong[MAXCONNECTIONS];
+static int sentalong_marker;
+struct SLink *opsarray[32];    /* don't use highest bit unless you change
+                                  atoi to strtoul in sendto_op_mask() */
+#ifdef GODMODE
+char sendbuf2[2048];
+int sdbflag;
+#endif /* GODMODE */
+
+/*
+ * 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 void dead_link(aClient *to, char *notice)
+{
+  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);
+
+  /* Keep a copy of the last comment, for later use... */
+  strncpy(LastDeadComment(to), notice, sizeof(LastDeadComment(to)));
+  LastDeadComment(to)[sizeof(LastDeadComment(to)) - 1] = 0;
+
+  if (!IsUser(to) && !IsUnknown(to) && !(to->flags & FLAGS_CLOSING))
+    sendto_ops("%s for %s", LastDeadComment(to), get_client_name(to, FALSE));
+  Debug((DEBUG_ERROR, LastDeadComment(to)));
+}
+
+/*
+ * 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(int fd)
+{
+  Reg1 int i;
+  Reg2 aClient *cptr;
+
+  if (fd == me.fd)
+  {
+    for (i = highest_fd; i >= 0; i--)
+      if ((cptr = loc_clients[i]) && DBufLength(&cptr->sendQ) > 0)
+       send_queued(cptr);
+  }
+  else if (fd >= 0 && (cptr = loc_clients[fd]) && DBufLength(&cptr->sendQ) > 0)
+    send_queued(cptr);
+}
+
+/*
+ * send_queued
+ *
+ * This function is called from the main select-loop (or whatever)
+ * when there is a chance that some output would be possible. This
+ * attempts to empty the send queue as far as possible...
+ */
+void send_queued(aClient *to)
+{
+#ifndef pyr
+  if (to->flags & FLAGS_BLOCKED)
+    return;                    /* Don't bother */
+#endif
+  /*
+   * 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
+     *
+     * But we DO get here since flush_connections() is called
+     * from the main loop when a server still had remaining data
+     * in its buffer (not ending on a new-line).
+     * I rather leave the test here then move it to the main loop
+     * though: It wouldn't save cpu and it might introduce a bug :/.
+     * --Run
+     */
+    return;
+  }
+  while (DBufLength(&to->sendQ) > 0)
+  {
+    const char *msg;
+    size_t len, rlen;
+    int tmp;
+
+    msg = dbuf_map(&to->sendQ, &len);
+    /* Returns always len > 0 */
+    if ((tmp = deliver_it(to, msg, len)) < 0)
+    {
+      dead_link(to, "Write error, closing link");
+      return;
+    }
+    rlen = tmp;
+    dbuf_delete(&to->sendQ, rlen);
+    to->lastsq = DBufLength(&to->sendQ) / 1024;
+    if (rlen < len)
+    {
+      to->flags |= FLAGS_BLOCKED;      /* Wait till select() says
+                                          we can write again */
+      break;
+    }
+  }
+
+  return;
+}
+
+/*
+ *  send message to single client
+ */
+void sendto_one(aClient *to, char *pattern, ...)
+{
+  va_list vl;
+  va_start(vl, pattern);
+  vsendto_one(to, pattern, vl);
+  va_end(vl);
+}
+
+void vsendto_one(aClient *to, char *pattern, va_list vl)
+{
+  vsprintf_irc(sendbuf, pattern, vl);
+  sendbufto_one(to);
+}
+
+void sendbufto_one(aClient *to)
+{
+  int len;
+
+  Debug((DEBUG_SEND, "Sending [%s] to %s", sendbuf, to->name));
+
+  if (to->from)
+    to = to->from;
+  if (IsDead(to))
+    return;                    /* This socket has already
+                                  been marked as dead */
+  if (to->fd < 0)
+  {
+    /* This is normal when 'to' was being closed (via exit_client
+     *  and close_connection) --Run
+     * Print the debug message anyway...
+     */
+    Debug((DEBUG_ERROR, "Local socket %s with negative fd %d... AARGH!",
+       to->name, to->fd));
+    return;
+  }
+
+  len = strlen(sendbuf);
+  if (sendbuf[len - 1] != '\n')
+  {
+    if (len > 510)
+      len = 510;
+    sendbuf[len++] = '\r';
+    sendbuf[len++] = '\n';
+    sendbuf[len] = '\0';
+  }
+
+  if (IsMe(to))
+  {
+    char tmp_sendbuf[sizeof(sendbuf)];
+
+    strcpy(tmp_sendbuf, sendbuf);
+    sendto_ops("Trying to send [%s] to myself!", tmp_sendbuf);
+    return;
+  }
+
+  if (DBufLength(&to->sendQ) > get_sendq(to))
+  {
+    if (IsServer(to))
+      sendto_ops("Max SendQ limit exceeded for %s: "
+         SIZE_T_FMT " > " SIZE_T_FMT,
+         get_client_name(to, FALSE), DBufLength(&to->sendQ), get_sendq(to));
+    dead_link(to, "Max sendQ exceeded");
+    return;
+  }
+
+  else if (!dbuf_put(&to->sendQ, sendbuf, len))
+  {
+    dead_link(to, "Buffer allocation error");
+    return;
+  }
+#ifdef GODMODE
+
+  if (!sdbflag && !IsUser(to))
+  {
+    size_t len = strlen(sendbuf) - 2;  /* Remove "\r\n" */
+    sdbflag = 1;
+    strncpy(sendbuf2, sendbuf, len);
+    sendbuf2[len] = '\0';
+    if (len > 402)
+    {
+      char c = sendbuf2[200];
+      sendbuf2[200] = 0;
+      sendto_ops("SND:%-8.8s(%.4d): \"%s...%s\"",
+         to->name, len, sendbuf2, &sendbuf2[len - 200]);
+      sendbuf2[200] = c;
+    }
+    else
+      sendto_ops("SND:%-8.8s(%.4d): \"%s\"", to->name, len, sendbuf2);
+    strcpy(sendbuf, sendbuf2);
+    strcat(sendbuf, "\r\n");
+    sdbflag = 0;
+  }
+
+#endif /* GODMODE */
+  /*
+   * 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).
+   */
+  if (DBufLength(&to->sendQ) / 1024 > to->lastsq)
+    send_queued(to);
+}
+
+static void vsendto_prefix_one(register aClient *to, register aClient *from,
+    char *pattern, va_list vl)
+{
+  if (to && from && MyUser(to) && IsUser(from))
+  {
+    static char sender[HOSTLEN + NICKLEN + USERLEN + 5];
+    char *par;
+    int flag = 0;
+    Reg3 anUser *user = from->user;
+
+    par = va_arg(vl, char *);
+    strcpy(sender, from->name);
+    if (user)
+    {
+      if (*user->username)
+      {
+       strcat(sender, "!");
+       strcat(sender, user->username);
+      }
+      if (*user->host && !MyConnect(from))
+      {
+       strcat(sender, "@");
+       strcat(sender, user->host);
+       flag = 1;
+      }
+    }
+    /*
+     * Flag is used instead of strchr(sender, '@') for speed and
+     * also since username/nick may have had a '@' in them. -avalon
+     */
+    if (!flag && MyConnect(from) && *user->host)
+    {
+      strcat(sender, "@");
+      if (IsUnixSocket(from))
+       strcat(sender, user->host);
+      else
+       strcat(sender, from->sockhost);
+    }
+    *sendbuf = ':';
+    strcpy(&sendbuf[1], sender);
+    /* Assuming 'pattern' always starts with ":%s ..." */
+    vsprintf_irc(sendbuf + strlen(sendbuf), &pattern[3], vl);
+  }
+  else
+    vsprintf_irc(sendbuf, pattern, vl);
+  sendbufto_one(to);
+}
+
+void sendto_channel_butone(aClient *one, aClient *from, aChannel *chptr,
+    char *pattern, ...)
+{
+  va_list vl;
+  Reg1 Link *lp;
+  Reg2 aClient *acptr;
+  Reg3 int i;
+
+  va_start(vl, pattern);
+
+  ++sentalong_marker;
+  for (lp = chptr->members; lp; lp = lp->next)
+  {
+    acptr = lp->value.cptr;
+    if (acptr->from == one ||  /* ...was the one I should skip */
+       (lp->flags & CHFL_ZOMBIE) || IsDeaf(acptr))
+      continue;
+    if (MyConnect(acptr))      /* (It is always a client) */
+      vsendto_prefix_one(acptr, from, pattern, vl);
+    else if (sentalong[(i = acptr->from->fd)] != sentalong_marker)
+    {
+      sentalong[i] = sentalong_marker;
+      /* Don't send channel messages to links that are still eating
+         the net.burst: -- Run 2/1/1997 */
+      if (!IsBurstOrBurstAck(acptr->from))
+       vsendto_prefix_one(acptr, from, pattern, vl);
+    }
+  }
+  va_end(vl);
+  return;
+}
+
+void sendto_lchanops_butone(aClient *one, aClient *from, aChannel *chptr,
+    char *pattern, ...)
+{
+  va_list vl;
+  Reg1 Link *lp;
+  Reg2 aClient *acptr;
+
+  va_start(vl, pattern);
+
+  for (lp = chptr->members; lp; lp = lp->next)
+  {
+    acptr = lp->value.cptr;
+    if (acptr == one ||                /* ...was the one I should skip */
+       !(lp->flags & CHFL_CHANOP) ||   /* Skip non chanops */
+       (lp->flags & CHFL_ZOMBIE) || IsDeaf(acptr))
+      continue;
+    if (MyConnect(acptr))      /* (It is always a client) */
+      vsendto_prefix_one(acptr, from, pattern, vl);
+  }
+  va_end(vl);
+  return;
+}
+
+void sendto_chanopsserv_butone(aClient *one, aClient *from, aChannel *chptr,
+    char *pattern, ...)
+{
+  va_list vl;
+  Reg1 Link *lp;
+  Reg2 aClient *acptr;
+  Reg3 int i;
+#ifndef NO_PROTOCOL9
+  char target[128];
+  char *source, *tp, *msg;
+#endif
+
+  va_start(vl, pattern);
+
+  ++sentalong_marker;
+  for (lp = chptr->members; lp; lp = lp->next)
+  {
+    acptr = lp->value.cptr;
+    if (acptr->from == acptr ||        /* Skip local clients */
+#ifndef NO_PROTOCOL9
+       Protocol(acptr->from) < 10 ||   /* Skip P09 links */
+#endif
+       acptr->from == one ||   /* ...was the one I should skip */
+       !(lp->flags & CHFL_CHANOP) ||   /* Skip non chanops */
+       (lp->flags & CHFL_ZOMBIE) || IsDeaf(acptr))
+      continue;
+    if (sentalong[(i = acptr->from->fd)] != sentalong_marker)
+    {
+      sentalong[i] = sentalong_marker;
+      /* Don't send channel messages to links that are
+         still eating the net.burst: -- Run 2/1/1997 */
+      if (!IsBurstOrBurstAck(acptr->from))
+       vsendto_prefix_one(acptr, from, pattern, vl);
+    }
+  }
+
+#ifndef NO_PROTOCOL9
+  /* Send message to all 2.9 servers */
+  /* This is a hack, because it assumes that we know how `vl' is build up */
+  source = va_arg(vl, char *);
+  tp = va_arg(vl, char *);     /* Channel */
+  msg = va_arg(vl, char *);
+  for (lp = chptr->members; lp; lp = lp->next)
+  {
+    acptr = lp->value.cptr;
+    if (acptr->from == acptr ||        /* Skip local clients */
+       Protocol(acptr->from) > 9 ||    /* Skip P10 servers */
+       acptr->from == one ||   /* ...was the one I should skip */
+       !(lp->flags & CHFL_CHANOP) ||   /* Skip non chanops */
+       (lp->flags & CHFL_ZOMBIE) || IsDeaf(acptr))
+      continue;
+    if (sentalong[(i = acptr->from->fd)] != sentalong_marker)
+    {
+      sentalong[i] = sentalong_marker;
+      /* Don't send channel messages to links that are
+         still eating the net.burst: -- Run 2/1/1997 */
+      if (!IsBurstOrBurstAck(acptr->from))
+      {
+       Link *lp2;
+       aClient *acptr2;
+       tp = target;
+       *tp = 0;
+       /* Find all chanops in this direction: */
+       for (lp2 = chptr->members; lp2; lp2 = lp2->next)
+       {
+         acptr2 = lp2->value.cptr;
+         if (acptr2->from == acptr->from && acptr2->from != one &&
+             (lp2->flags & CHFL_CHANOP) && !(lp2->flags & CHFL_ZOMBIE) &&
+             !IsDeaf(acptr2))
+         {
+           int len = strlen(acptr2->name);
+           if (tp + len + 2 > target + sizeof(target))
+           {
+             sendto_prefix_one(acptr, from,
+                 ":%s NOTICE %s :%s", source, target, msg);
+             tp = target;
+             *tp = 0;
+           }
+           if (*target)
+             strcpy(tp++, ",");
+           strcpy(tp, acptr2->name);
+           tp += len;
+         }
+       }
+       sendto_prefix_one(acptr, from,
+           ":%s NOTICE %s :%s", source, target, msg);
+      }
+    }
+  }
+#endif
+
+  va_end(vl);
+  return;
+}
+
+/*
+ * sendto_server_butone
+ *
+ * Send a message to all connected servers except the client 'one'.
+ */
+void sendto_serv_butone(aClient *one, char *pattern, ...)
+{
+  va_list vl;
+  Reg1 Dlink *lp;
+
+  va_start(vl, pattern);
+  vsprintf_irc(sendbuf, pattern, vl);
+  va_end(vl);
+
+  for (lp = me.serv->down; lp; lp = lp->next)
+  {
+    if (one && lp->value.cptr == one->from)
+      continue;
+    sendbufto_one(lp->value.cptr);
+  }
+
+}
+
+/*
+ * sendbufto_serv_butone()
+ *
+ * Send prepared sendbuf to all connected servers except the client 'one'
+ *  -Ghostwolf 18-May-97
+ */
+void sendbufto_serv_butone(aClient *one)
+{
+  Reg1 Dlink *lp;
+
+  for (lp = me.serv->down; lp; lp = lp->next)
+  {
+    if (one && lp->value.cptr == one->from)
+      continue;
+    sendbufto_one(lp->value.cptr);
+  }
+}
+
+/*
+ * sendto_common_channels()
+ *
+ * Sends a message to all people (inclusing `acptr') on local server
+ * who are in same channel with client `acptr'.
+ */
+void sendto_common_channels(aClient *acptr, char *pattern, ...)
+{
+  va_list vl;
+  Reg1 Link *chan;
+  Reg2 Link *member;
+
+  va_start(vl, pattern);
+
+  ++sentalong_marker;
+  if (acptr->fd >= 0)
+    sentalong[acptr->fd] = sentalong_marker;
+  /* loop through acptr's channels, and the members on their channels */
+  if (acptr->user)
+    for (chan = acptr->user->channel; chan; chan = chan->next)
+      for (member = chan->value.chptr->members; member; member = member->next)
+      {
+       Reg3 aClient *cptr = member->value.cptr;
+       if (MyConnect(cptr) && sentalong[cptr->fd] != sentalong_marker)
+       {
+         sentalong[cptr->fd] = sentalong_marker;
+         vsendto_prefix_one(cptr, acptr, pattern, vl);
+       }
+      }
+  if (MyConnect(acptr))
+    vsendto_prefix_one(acptr, acptr, pattern, vl);
+  va_end(vl);
+  return;
+}
+
+/*
+ * sendto_channel_butserv
+ *
+ * Send a message to all members of a channel that
+ * are connected to this server.
+ */
+void sendto_channel_butserv(aChannel *chptr, aClient *from, char *pattern, ...)
+{
+  va_list vl;
+  Reg1 Link *lp;
+  Reg2 aClient *acptr;
+
+  for (va_start(vl, pattern), lp = chptr->members; lp; lp = lp->next)
+    if (MyConnect(acptr = lp->value.cptr) && !(lp->flags & CHFL_ZOMBIE))
+      vsendto_prefix_one(acptr, from, pattern, vl);
+  va_end(vl);
+  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(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->name) == 0);
+  }
+}
+
+/*
+ * 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(aClient *one, aClient *from,
+    char *mask, int what, char *pattern, ...)
+{
+  va_list vl;
+  Reg1 int i;
+  Reg2 aClient *cptr, *acptr;
+
+  va_start(vl, pattern);
+  for (i = 0; i <= highest_fd; i++)
+  {
+    if (!(cptr = loc_clients[i]))
+      continue;                        /* that clients are not mine */
+    if (cptr == one)           /* must skip the origin !! */
+      continue;
+    if (IsServer(cptr))
+    {
+      for (acptr = client; acptr; acptr = acptr->next)
+       if (IsUser(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 (!(IsUser(cptr) && match_it(cptr, mask, what)))
+      continue;
+    vsendto_prefix_one(cptr, from, pattern, vl);
+  }
+  va_end(vl);
+
+  return;
+}
+
+/*
+ * sendto_lops_butone
+ *
+ * Send to *local* ops but one.
+ */
+void sendto_lops_butone(aClient *one, char *pattern, ...)
+{
+  va_list vl;
+  Reg1 aClient *cptr;
+  aClient **cptrp;
+  int i;
+  char nbuf[1024];
+
+  sprintf_irc(nbuf, ":%s NOTICE %%s :*** Notice -- ", me.name);
+  va_start(vl, pattern);
+  vsprintf_irc(nbuf + strlen(nbuf), pattern, vl);
+  va_end(vl);
+  for (cptrp = me.serv->client_list, i = 0; i <= me.serv->nn_mask; ++cptrp, ++i)
+    if ((cptr = *cptrp) && cptr != one && SendServNotice(cptr))
+    {
+      sprintf_irc(sendbuf, nbuf, cptr->name);
+      sendbufto_one(cptr);
+    }
+  return;
+}
+
+/*
+ * sendto_op_mask
+ *
+ * Sends message to the list indicated by the bitmask field.
+ * Don't try to send to more than one list! That is not supported.
+ * Xorath 5/1/97
+ */
+void vsendto_op_mask(register snomask_t mask, const char *pattern, va_list vl)
+{
+  static char fmt[1024];
+  char *fmt_target;
+  register int i = 0;          /* so that 1 points to opsarray[0] */
+  Link *opslist;
+
+  while ((mask >>= 1))
+    i++;
+  if (!(opslist = opsarray[i]))
+    return;
+
+  fmt_target = sprintf_irc(fmt, ":%s NOTICE ", me.name);
+  do
+  {
+    strcpy(fmt_target, opslist->value.cptr->name);
+    strcat(fmt_target, " :*** Notice -- ");
+    strcat(fmt_target, pattern);
+    vsendto_one(opslist->value.cptr, fmt, vl);
+    opslist = opslist->next;
+  }
+  while (opslist);
+}
+
+/*
+ * sendbufto_op_mask
+ *
+ * Send a prepared sendbuf to the list indicated by the bitmask field.
+ * Ghostwolf 16-May-97
+ */
+void sendbufto_op_mask(snomask_t mask)
+{
+  register int i = 0;          /* so that 1 points to opsarray[0] */
+  Link *opslist;
+  while ((mask >>= 1))
+    i++;
+  if (!(opslist = opsarray[i]))
+    return;
+  do
+  {
+    sendbufto_one(opslist->value.cptr);
+    opslist = opslist->next;
+  }
+  while (opslist);
+}
+
+
+/*
+ * sendto_ops
+ *
+ * Send to *local* ops only.
+ */
+void vsendto_ops(const char *pattern, va_list vl)
+{
+  Reg1 aClient *cptr;
+  Reg2 int i;
+  char fmt[1024];
+  char *fmt_target;
+
+  fmt_target = sprintf_irc(fmt, ":%s NOTICE ", me.name);
+
+  for (i = 0; i <= highest_fd; i++)
+    if ((cptr = loc_clients[i]) && !IsServer(cptr) && !IsMe(cptr) &&
+       SendServNotice(cptr))
+    {
+      strcpy(fmt_target, cptr->name);
+      strcat(fmt_target, " :*** Notice -- ");
+      strcat(fmt_target, pattern);
+      vsendto_one(cptr, fmt, vl);
+    }
+
+  return;
+}
+
+void sendto_op_mask(snomask_t mask, const char *pattern, ...)
+{
+  va_list vl;
+  va_start(vl, pattern);
+  vsendto_op_mask(mask, pattern, vl);
+  va_end(vl);
+}
+
+void sendto_ops(const char *pattern, ...)
+{
+  va_list vl;
+  va_start(vl, pattern);
+  vsendto_op_mask(SNO_OLDSNO, pattern, vl);
+  va_end(vl);
+}
+
+/*
+ * 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(aClient *one, aClient *from, char *pattern, ...)
+{
+  va_list vl;
+  Reg1 int i;
+  Reg2 aClient *cptr;
+
+  va_start(vl, pattern);
+  ++sentalong_marker;
+  for (cptr = client; cptr; cptr = cptr->next)
+  {
+    if (!SendWallops(cptr))
+      continue;
+    i = cptr->from->fd;                /* find connection oper is on */
+    if (sentalong[i] == sentalong_marker)      /* sent message along it already ? */
+      continue;
+    if (cptr->from == one)
+      continue;                        /* ...was the one I should skip */
+    sentalong[i] = sentalong_marker;
+    vsendto_prefix_one(cptr->from, from, pattern, vl);
+  }
+  va_end(vl);
+
+  return;
+}
+
+/*
+ * sendto_g_serv_butone
+ *
+ * Send message to all remote +g users (server links).
+ *
+ * one - server not to send message to.
+ */
+void sendto_g_serv_butone(aClient *one, char *pattern, ...)
+{
+  va_list vl;
+  aClient *cptr;
+  int i;
+
+  va_start(vl, pattern);
+  ++sentalong_marker;
+  vsprintf_irc(sendbuf, pattern, vl);
+  for (cptr = client; cptr; cptr = cptr->next)
+  {
+    if (!SendDebug(cptr))
+      continue;
+    i = cptr->from->fd;                /* find connection user is on */
+    if (sentalong[i] == sentalong_marker)      /* sent message along it already ? */
+      continue;
+    if (MyConnect(cptr))
+      continue;
+    sentalong[i] = sentalong_marker;
+    if (cptr->from == one)
+      continue;
+    sendbufto_one(cptr);
+  }
+  va_end(vl);
+
+  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(Reg1 aClient *to, Reg2 aClient *from, char *pattern, ...)
+{
+  va_list vl;
+  va_start(vl, pattern);
+  vsendto_prefix_one(to, from, pattern, vl);
+  va_end(vl);
+}
+
+/*
+ * sendto_realops
+ *
+ * Send to *local* ops only but NOT +s nonopers.
+ */
+void sendto_realops(const char *pattern, ...)
+{
+  va_list vl;
+
+  va_start(vl, pattern);
+  vsendto_op_mask(SNO_OLDREALOP, pattern, vl);
+
+  va_end(vl);
+  return;
+}
+
+/*
+ * Send message to all servers of protocol 'p' and lower.
+ */
+void sendto_lowprot_butone(aClient *cptr, int p, char *pattern, ...)
+{
+  va_list vl;
+  Dlink *lp;
+  va_start(vl, pattern);
+  for (lp = me.serv->down; lp; lp = lp->next)
+    if (lp->value.cptr != cptr && Protocol(lp->value.cptr) <= p)
+      vsendto_one(lp->value.cptr, pattern, vl);
+  va_end(vl);
+}
+
+/*
+ * Send message to all servers of protocol 'p' and higher.
+ */
+void sendto_highprot_butone(aClient *cptr, int p, char *pattern, ...)
+{
+  va_list vl;
+  Dlink *lp;
+  va_start(vl, pattern);
+  for (lp = me.serv->down; lp; lp = lp->next)
+    if (lp->value.cptr != cptr && Protocol(lp->value.cptr) >= p)
+      vsendto_one(lp->value.cptr, pattern, vl);
+  va_end(vl);
+}
diff --git a/ircd/sprintf_irc.c b/ircd/sprintf_irc.c
new file mode 100644 (file)
index 0000000..ab85492
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * IRC - Internet Relay Chat, ircd/s_ping.c
+ *
+ * (C) Copyright 1997
+ *
+ * Author:
+ *
+ * 1024/624ACAD5 1997/01/26 Carlo Wood, Run on IRC <carlo@runaway.xs4all.nl>
+ * Key fingerprint = 32 EC A7 B6 AC DB 65 A6  F6 F6 55 DD 1C DC FF 61
+ * Get key from pgp-public-keys server or
+ * finger carlo@runaway.xs4all.nl for public key (dialin, try at 21-22h GMT).
+ *
+ * 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.
+ */
+
+#include "sys.h"
+#include <stdio.h>
+#include "h.h"
+#include "sprintf_irc.h"
+
+RCSTAG_CC("$Id$");
+
+/* *INDENT-OFF* */
+
+const char atoi_tab[4000] = {
+    '0','0','0',0, '0','0','1',0, '0','0','2',0, '0','0','3',0, '0','0','4',0,
+    '0','0','5',0, '0','0','6',0, '0','0','7',0, '0','0','8',0, '0','0','9',0,
+    '0','1','0',0, '0','1','1',0, '0','1','2',0, '0','1','3',0, '0','1','4',0,
+    '0','1','5',0, '0','1','6',0, '0','1','7',0, '0','1','8',0, '0','1','9',0,
+    '0','2','0',0, '0','2','1',0, '0','2','2',0, '0','2','3',0, '0','2','4',0,
+    '0','2','5',0, '0','2','6',0, '0','2','7',0, '0','2','8',0, '0','2','9',0,
+    '0','3','0',0, '0','3','1',0, '0','3','2',0, '0','3','3',0, '0','3','4',0,
+    '0','3','5',0, '0','3','6',0, '0','3','7',0, '0','3','8',0, '0','3','9',0,
+    '0','4','0',0, '0','4','1',0, '0','4','2',0, '0','4','3',0, '0','4','4',0,
+    '0','4','5',0, '0','4','6',0, '0','4','7',0, '0','4','8',0, '0','4','9',0,
+    '0','5','0',0, '0','5','1',0, '0','5','2',0, '0','5','3',0, '0','5','4',0,
+    '0','5','5',0, '0','5','6',0, '0','5','7',0, '0','5','8',0, '0','5','9',0,
+    '0','6','0',0, '0','6','1',0, '0','6','2',0, '0','6','3',0, '0','6','4',0,
+    '0','6','5',0, '0','6','6',0, '0','6','7',0, '0','6','8',0, '0','6','9',0,
+    '0','7','0',0, '0','7','1',0, '0','7','2',0, '0','7','3',0, '0','7','4',0,
+    '0','7','5',0, '0','7','6',0, '0','7','7',0, '0','7','8',0, '0','7','9',0,
+    '0','8','0',0, '0','8','1',0, '0','8','2',0, '0','8','3',0, '0','8','4',0,
+    '0','8','5',0, '0','8','6',0, '0','8','7',0, '0','8','8',0, '0','8','9',0,
+    '0','9','0',0, '0','9','1',0, '0','9','2',0, '0','9','3',0, '0','9','4',0,
+    '0','9','5',0, '0','9','6',0, '0','9','7',0, '0','9','8',0, '0','9','9',0,
+    '1','0','0',0, '1','0','1',0, '1','0','2',0, '1','0','3',0, '1','0','4',0,
+    '1','0','5',0, '1','0','6',0, '1','0','7',0, '1','0','8',0, '1','0','9',0,
+    '1','1','0',0, '1','1','1',0, '1','1','2',0, '1','1','3',0, '1','1','4',0,
+    '1','1','5',0, '1','1','6',0, '1','1','7',0, '1','1','8',0, '1','1','9',0,
+    '1','2','0',0, '1','2','1',0, '1','2','2',0, '1','2','3',0, '1','2','4',0,
+    '1','2','5',0, '1','2','6',0, '1','2','7',0, '1','2','8',0, '1','2','9',0,
+    '1','3','0',0, '1','3','1',0, '1','3','2',0, '1','3','3',0, '1','3','4',0,
+    '1','3','5',0, '1','3','6',0, '1','3','7',0, '1','3','8',0, '1','3','9',0,
+    '1','4','0',0, '1','4','1',0, '1','4','2',0, '1','4','3',0, '1','4','4',0,
+    '1','4','5',0, '1','4','6',0, '1','4','7',0, '1','4','8',0, '1','4','9',0,
+    '1','5','0',0, '1','5','1',0, '1','5','2',0, '1','5','3',0, '1','5','4',0,
+    '1','5','5',0, '1','5','6',0, '1','5','7',0, '1','5','8',0, '1','5','9',0,
+    '1','6','0',0, '1','6','1',0, '1','6','2',0, '1','6','3',0, '1','6','4',0,
+    '1','6','5',0, '1','6','6',0, '1','6','7',0, '1','6','8',0, '1','6','9',0,
+    '1','7','0',0, '1','7','1',0, '1','7','2',0, '1','7','3',0, '1','7','4',0,
+    '1','7','5',0, '1','7','6',0, '1','7','7',0, '1','7','8',0, '1','7','9',0,
+    '1','8','0',0, '1','8','1',0, '1','8','2',0, '1','8','3',0, '1','8','4',0,
+    '1','8','5',0, '1','8','6',0, '1','8','7',0, '1','8','8',0, '1','8','9',0,
+    '1','9','0',0, '1','9','1',0, '1','9','2',0, '1','9','3',0, '1','9','4',0,
+    '1','9','5',0, '1','9','6',0, '1','9','7',0, '1','9','8',0, '1','9','9',0,
+    '2','0','0',0, '2','0','1',0, '2','0','2',0, '2','0','3',0, '2','0','4',0,
+    '2','0','5',0, '2','0','6',0, '2','0','7',0, '2','0','8',0, '2','0','9',0,
+    '2','1','0',0, '2','1','1',0, '2','1','2',0, '2','1','3',0, '2','1','4',0,
+    '2','1','5',0, '2','1','6',0, '2','1','7',0, '2','1','8',0, '2','1','9',0,
+    '2','2','0',0, '2','2','1',0, '2','2','2',0, '2','2','3',0, '2','2','4',0,
+    '2','2','5',0, '2','2','6',0, '2','2','7',0, '2','2','8',0, '2','2','9',0,
+    '2','3','0',0, '2','3','1',0, '2','3','2',0, '2','3','3',0, '2','3','4',0,
+    '2','3','5',0, '2','3','6',0, '2','3','7',0, '2','3','8',0, '2','3','9',0,
+    '2','4','0',0, '2','4','1',0, '2','4','2',0, '2','4','3',0, '2','4','4',0,
+    '2','4','5',0, '2','4','6',0, '2','4','7',0, '2','4','8',0, '2','4','9',0,
+    '2','5','0',0, '2','5','1',0, '2','5','2',0, '2','5','3',0, '2','5','4',0,
+    '2','5','5',0, '2','5','6',0, '2','5','7',0, '2','5','8',0, '2','5','9',0,
+    '2','6','0',0, '2','6','1',0, '2','6','2',0, '2','6','3',0, '2','6','4',0,
+    '2','6','5',0, '2','6','6',0, '2','6','7',0, '2','6','8',0, '2','6','9',0,
+    '2','7','0',0, '2','7','1',0, '2','7','2',0, '2','7','3',0, '2','7','4',0,
+    '2','7','5',0, '2','7','6',0, '2','7','7',0, '2','7','8',0, '2','7','9',0,
+    '2','8','0',0, '2','8','1',0, '2','8','2',0, '2','8','3',0, '2','8','4',0,
+    '2','8','5',0, '2','8','6',0, '2','8','7',0, '2','8','8',0, '2','8','9',0,
+    '2','9','0',0, '2','9','1',0, '2','9','2',0, '2','9','3',0, '2','9','4',0,
+    '2','9','5',0, '2','9','6',0, '2','9','7',0, '2','9','8',0, '2','9','9',0,
+    '3','0','0',0, '3','0','1',0, '3','0','2',0, '3','0','3',0, '3','0','4',0,
+    '3','0','5',0, '3','0','6',0, '3','0','7',0, '3','0','8',0, '3','0','9',0,
+    '3','1','0',0, '3','1','1',0, '3','1','2',0, '3','1','3',0, '3','1','4',0,
+    '3','1','5',0, '3','1','6',0, '3','1','7',0, '3','1','8',0, '3','1','9',0,
+    '3','2','0',0, '3','2','1',0, '3','2','2',0, '3','2','3',0, '3','2','4',0,
+    '3','2','5',0, '3','2','6',0, '3','2','7',0, '3','2','8',0, '3','2','9',0,
+    '3','3','0',0, '3','3','1',0, '3','3','2',0, '3','3','3',0, '3','3','4',0,
+    '3','3','5',0, '3','3','6',0, '3','3','7',0, '3','3','8',0, '3','3','9',0,
+    '3','4','0',0, '3','4','1',0, '3','4','2',0, '3','4','3',0, '3','4','4',0,
+    '3','4','5',0, '3','4','6',0, '3','4','7',0, '3','4','8',0, '3','4','9',0,
+    '3','5','0',0, '3','5','1',0, '3','5','2',0, '3','5','3',0, '3','5','4',0,
+    '3','5','5',0, '3','5','6',0, '3','5','7',0, '3','5','8',0, '3','5','9',0,
+    '3','6','0',0, '3','6','1',0, '3','6','2',0, '3','6','3',0, '3','6','4',0,
+    '3','6','5',0, '3','6','6',0, '3','6','7',0, '3','6','8',0, '3','6','9',0,
+    '3','7','0',0, '3','7','1',0, '3','7','2',0, '3','7','3',0, '3','7','4',0,
+    '3','7','5',0, '3','7','6',0, '3','7','7',0, '3','7','8',0, '3','7','9',0,
+    '3','8','0',0, '3','8','1',0, '3','8','2',0, '3','8','3',0, '3','8','4',0,
+    '3','8','5',0, '3','8','6',0, '3','8','7',0, '3','8','8',0, '3','8','9',0,
+    '3','9','0',0, '3','9','1',0, '3','9','2',0, '3','9','3',0, '3','9','4',0,
+    '3','9','5',0, '3','9','6',0, '3','9','7',0, '3','9','8',0, '3','9','9',0,
+    '4','0','0',0, '4','0','1',0, '4','0','2',0, '4','0','3',0, '4','0','4',0,
+    '4','0','5',0, '4','0','6',0, '4','0','7',0, '4','0','8',0, '4','0','9',0,
+    '4','1','0',0, '4','1','1',0, '4','1','2',0, '4','1','3',0, '4','1','4',0,
+    '4','1','5',0, '4','1','6',0, '4','1','7',0, '4','1','8',0, '4','1','9',0,
+    '4','2','0',0, '4','2','1',0, '4','2','2',0, '4','2','3',0, '4','2','4',0,
+    '4','2','5',0, '4','2','6',0, '4','2','7',0, '4','2','8',0, '4','2','9',0,
+    '4','3','0',0, '4','3','1',0, '4','3','2',0, '4','3','3',0, '4','3','4',0,
+    '4','3','5',0, '4','3','6',0, '4','3','7',0, '4','3','8',0, '4','3','9',0,
+    '4','4','0',0, '4','4','1',0, '4','4','2',0, '4','4','3',0, '4','4','4',0,
+    '4','4','5',0, '4','4','6',0, '4','4','7',0, '4','4','8',0, '4','4','9',0,
+    '4','5','0',0, '4','5','1',0, '4','5','2',0, '4','5','3',0, '4','5','4',0,
+    '4','5','5',0, '4','5','6',0, '4','5','7',0, '4','5','8',0, '4','5','9',0,
+    '4','6','0',0, '4','6','1',0, '4','6','2',0, '4','6','3',0, '4','6','4',0,
+    '4','6','5',0, '4','6','6',0, '4','6','7',0, '4','6','8',0, '4','6','9',0,
+    '4','7','0',0, '4','7','1',0, '4','7','2',0, '4','7','3',0, '4','7','4',0,
+    '4','7','5',0, '4','7','6',0, '4','7','7',0, '4','7','8',0, '4','7','9',0,
+    '4','8','0',0, '4','8','1',0, '4','8','2',0, '4','8','3',0, '4','8','4',0,
+    '4','8','5',0, '4','8','6',0, '4','8','7',0, '4','8','8',0, '4','8','9',0,
+    '4','9','0',0, '4','9','1',0, '4','9','2',0, '4','9','3',0, '4','9','4',0,
+    '4','9','5',0, '4','9','6',0, '4','9','7',0, '4','9','8',0, '4','9','9',0,
+    '5','0','0',0, '5','0','1',0, '5','0','2',0, '5','0','3',0, '5','0','4',0,
+    '5','0','5',0, '5','0','6',0, '5','0','7',0, '5','0','8',0, '5','0','9',0,
+    '5','1','0',0, '5','1','1',0, '5','1','2',0, '5','1','3',0, '5','1','4',0,
+    '5','1','5',0, '5','1','6',0, '5','1','7',0, '5','1','8',0, '5','1','9',0,
+    '5','2','0',0, '5','2','1',0, '5','2','2',0, '5','2','3',0, '5','2','4',0,
+    '5','2','5',0, '5','2','6',0, '5','2','7',0, '5','2','8',0, '5','2','9',0,
+    '5','3','0',0, '5','3','1',0, '5','3','2',0, '5','3','3',0, '5','3','4',0,
+    '5','3','5',0, '5','3','6',0, '5','3','7',0, '5','3','8',0, '5','3','9',0,
+    '5','4','0',0, '5','4','1',0, '5','4','2',0, '5','4','3',0, '5','4','4',0,
+    '5','4','5',0, '5','4','6',0, '5','4','7',0, '5','4','8',0, '5','4','9',0,
+    '5','5','0',0, '5','5','1',0, '5','5','2',0, '5','5','3',0, '5','5','4',0,
+    '5','5','5',0, '5','5','6',0, '5','5','7',0, '5','5','8',0, '5','5','9',0,
+    '5','6','0',0, '5','6','1',0, '5','6','2',0, '5','6','3',0, '5','6','4',0,
+    '5','6','5',0, '5','6','6',0, '5','6','7',0, '5','6','8',0, '5','6','9',0,
+    '5','7','0',0, '5','7','1',0, '5','7','2',0, '5','7','3',0, '5','7','4',0,
+    '5','7','5',0, '5','7','6',0, '5','7','7',0, '5','7','8',0, '5','7','9',0,
+    '5','8','0',0, '5','8','1',0, '5','8','2',0, '5','8','3',0, '5','8','4',0,
+    '5','8','5',0, '5','8','6',0, '5','8','7',0, '5','8','8',0, '5','8','9',0,
+    '5','9','0',0, '5','9','1',0, '5','9','2',0, '5','9','3',0, '5','9','4',0,
+    '5','9','5',0, '5','9','6',0, '5','9','7',0, '5','9','8',0, '5','9','9',0,
+    '6','0','0',0, '6','0','1',0, '6','0','2',0, '6','0','3',0, '6','0','4',0,
+    '6','0','5',0, '6','0','6',0, '6','0','7',0, '6','0','8',0, '6','0','9',0,
+    '6','1','0',0, '6','1','1',0, '6','1','2',0, '6','1','3',0, '6','1','4',0,
+    '6','1','5',0, '6','1','6',0, '6','1','7',0, '6','1','8',0, '6','1','9',0,
+    '6','2','0',0, '6','2','1',0, '6','2','2',0, '6','2','3',0, '6','2','4',0,
+    '6','2','5',0, '6','2','6',0, '6','2','7',0, '6','2','8',0, '6','2','9',0,
+    '6','3','0',0, '6','3','1',0, '6','3','2',0, '6','3','3',0, '6','3','4',0,
+    '6','3','5',0, '6','3','6',0, '6','3','7',0, '6','3','8',0, '6','3','9',0,
+    '6','4','0',0, '6','4','1',0, '6','4','2',0, '6','4','3',0, '6','4','4',0,
+    '6','4','5',0, '6','4','6',0, '6','4','7',0, '6','4','8',0, '6','4','9',0,
+    '6','5','0',0, '6','5','1',0, '6','5','2',0, '6','5','3',0, '6','5','4',0,
+    '6','5','5',0, '6','5','6',0, '6','5','7',0, '6','5','8',0, '6','5','9',0,
+    '6','6','0',0, '6','6','1',0, '6','6','2',0, '6','6','3',0, '6','6','4',0,
+    '6','6','5',0, '6','6','6',0, '6','6','7',0, '6','6','8',0, '6','6','9',0,
+    '6','7','0',0, '6','7','1',0, '6','7','2',0, '6','7','3',0, '6','7','4',0,
+    '6','7','5',0, '6','7','6',0, '6','7','7',0, '6','7','8',0, '6','7','9',0,
+    '6','8','0',0, '6','8','1',0, '6','8','2',0, '6','8','3',0, '6','8','4',0,
+    '6','8','5',0, '6','8','6',0, '6','8','7',0, '6','8','8',0, '6','8','9',0,
+    '6','9','0',0, '6','9','1',0, '6','9','2',0, '6','9','3',0, '6','9','4',0,
+    '6','9','5',0, '6','9','6',0, '6','9','7',0, '6','9','8',0, '6','9','9',0,
+    '7','0','0',0, '7','0','1',0, '7','0','2',0, '7','0','3',0, '7','0','4',0,
+    '7','0','5',0, '7','0','6',0, '7','0','7',0, '7','0','8',0, '7','0','9',0,
+    '7','1','0',0, '7','1','1',0, '7','1','2',0, '7','1','3',0, '7','1','4',0,
+    '7','1','5',0, '7','1','6',0, '7','1','7',0, '7','1','8',0, '7','1','9',0,
+    '7','2','0',0, '7','2','1',0, '7','2','2',0, '7','2','3',0, '7','2','4',0,
+    '7','2','5',0, '7','2','6',0, '7','2','7',0, '7','2','8',0, '7','2','9',0,
+    '7','3','0',0, '7','3','1',0, '7','3','2',0, '7','3','3',0, '7','3','4',0,
+    '7','3','5',0, '7','3','6',0, '7','3','7',0, '7','3','8',0, '7','3','9',0,
+    '7','4','0',0, '7','4','1',0, '7','4','2',0, '7','4','3',0, '7','4','4',0,
+    '7','4','5',0, '7','4','6',0, '7','4','7',0, '7','4','8',0, '7','4','9',0,
+    '7','5','0',0, '7','5','1',0, '7','5','2',0, '7','5','3',0, '7','5','4',0,
+    '7','5','5',0, '7','5','6',0, '7','5','7',0, '7','5','8',0, '7','5','9',0,
+    '7','6','0',0, '7','6','1',0, '7','6','2',0, '7','6','3',0, '7','6','4',0,
+    '7','6','5',0, '7','6','6',0, '7','6','7',0, '7','6','8',0, '7','6','9',0,
+    '7','7','0',0, '7','7','1',0, '7','7','2',0, '7','7','3',0, '7','7','4',0,
+    '7','7','5',0, '7','7','6',0, '7','7','7',0, '7','7','8',0, '7','7','9',0,
+    '7','8','0',0, '7','8','1',0, '7','8','2',0, '7','8','3',0, '7','8','4',0,
+    '7','8','5',0, '7','8','6',0, '7','8','7',0, '7','8','8',0, '7','8','9',0,
+    '7','9','0',0, '7','9','1',0, '7','9','2',0, '7','9','3',0, '7','9','4',0,
+    '7','9','5',0, '7','9','6',0, '7','9','7',0, '7','9','8',0, '7','9','9',0,
+    '8','0','0',0, '8','0','1',0, '8','0','2',0, '8','0','3',0, '8','0','4',0,
+    '8','0','5',0, '8','0','6',0, '8','0','7',0, '8','0','8',0, '8','0','9',0,
+    '8','1','0',0, '8','1','1',0, '8','1','2',0, '8','1','3',0, '8','1','4',0,
+    '8','1','5',0, '8','1','6',0, '8','1','7',0, '8','1','8',0, '8','1','9',0,
+    '8','2','0',0, '8','2','1',0, '8','2','2',0, '8','2','3',0, '8','2','4',0,
+    '8','2','5',0, '8','2','6',0, '8','2','7',0, '8','2','8',0, '8','2','9',0,
+    '8','3','0',0, '8','3','1',0, '8','3','2',0, '8','3','3',0, '8','3','4',0,
+    '8','3','5',0, '8','3','6',0, '8','3','7',0, '8','3','8',0, '8','3','9',0,
+    '8','4','0',0, '8','4','1',0, '8','4','2',0, '8','4','3',0, '8','4','4',0,
+    '8','4','5',0, '8','4','6',0, '8','4','7',0, '8','4','8',0, '8','4','9',0,
+    '8','5','0',0, '8','5','1',0, '8','5','2',0, '8','5','3',0, '8','5','4',0,
+    '8','5','5',0, '8','5','6',0, '8','5','7',0, '8','5','8',0, '8','5','9',0,
+    '8','6','0',0, '8','6','1',0, '8','6','2',0, '8','6','3',0, '8','6','4',0,
+    '8','6','5',0, '8','6','6',0, '8','6','7',0, '8','6','8',0, '8','6','9',0,
+    '8','7','0',0, '8','7','1',0, '8','7','2',0, '8','7','3',0, '8','7','4',0,
+    '8','7','5',0, '8','7','6',0, '8','7','7',0, '8','7','8',0, '8','7','9',0,
+    '8','8','0',0, '8','8','1',0, '8','8','2',0, '8','8','3',0, '8','8','4',0,
+    '8','8','5',0, '8','8','6',0, '8','8','7',0, '8','8','8',0, '8','8','9',0,
+    '8','9','0',0, '8','9','1',0, '8','9','2',0, '8','9','3',0, '8','9','4',0,
+    '8','9','5',0, '8','9','6',0, '8','9','7',0, '8','9','8',0, '8','9','9',0,
+    '9','0','0',0, '9','0','1',0, '9','0','2',0, '9','0','3',0, '9','0','4',0,
+    '9','0','5',0, '9','0','6',0, '9','0','7',0, '9','0','8',0, '9','0','9',0,
+    '9','1','0',0, '9','1','1',0, '9','1','2',0, '9','1','3',0, '9','1','4',0,
+    '9','1','5',0, '9','1','6',0, '9','1','7',0, '9','1','8',0, '9','1','9',0,
+    '9','2','0',0, '9','2','1',0, '9','2','2',0, '9','2','3',0, '9','2','4',0,
+    '9','2','5',0, '9','2','6',0, '9','2','7',0, '9','2','8',0, '9','2','9',0,
+    '9','3','0',0, '9','3','1',0, '9','3','2',0, '9','3','3',0, '9','3','4',0,
+    '9','3','5',0, '9','3','6',0, '9','3','7',0, '9','3','8',0, '9','3','9',0,
+    '9','4','0',0, '9','4','1',0, '9','4','2',0, '9','4','3',0, '9','4','4',0,
+    '9','4','5',0, '9','4','6',0, '9','4','7',0, '9','4','8',0, '9','4','9',0,
+    '9','5','0',0, '9','5','1',0, '9','5','2',0, '9','5','3',0, '9','5','4',0,
+    '9','5','5',0, '9','5','6',0, '9','5','7',0, '9','5','8',0, '9','5','9',0,
+    '9','6','0',0, '9','6','1',0, '9','6','2',0, '9','6','3',0, '9','6','4',0,
+    '9','6','5',0, '9','6','6',0, '9','6','7',0, '9','6','8',0, '9','6','9',0,
+    '9','7','0',0, '9','7','1',0, '9','7','2',0, '9','7','3',0, '9','7','4',0,
+    '9','7','5',0, '9','7','6',0, '9','7','7',0, '9','7','8',0, '9','7','9',0,
+    '9','8','0',0, '9','8','1',0, '9','8','2',0, '9','8','3',0, '9','8','4',0,
+    '9','8','5',0, '9','8','6',0, '9','8','7',0, '9','8','8',0, '9','8','9',0,
+    '9','9','0',0, '9','9','1',0, '9','9','2',0, '9','9','3',0, '9','9','4',0,
+    '9','9','5',0, '9','9','6',0, '9','9','7',0, '9','9','8',0, '9','9','9',0 };
+
+/* *INDENT-ON* */
+
+static char scratch_buffer[32];
+
+/*
+ * sprintf_irc
+ *
+ * sprintf_irc is optimized for the formats: %c, %s, %lu, %d and %u.
+ * Where %lu actually equals %l09u (for printing time_t timestamps).
+ *
+ * sprintf_irc is NOT optimized for any other format and resorts to using
+ * the normal sprintf when it encounters a format it doesn't understand
+ * (including padding, width, precision etc).
+ *
+ * The following benchmark was measured on a PPro200 with linux 2.0.30,
+ * libc 5.4.28, compiled with gcc-2.7.2.1 with -O3.
+ *
+ * Format                       sprintf         sprintf_irc     Speed up factor
+ *
+ * "12345678901234567890"       3.385 us        0.859 us        3.94
+ * "%s", buffer (20 chars)      3.780 us        0.547 us        6.91
+ * "%c%c%c", c1, c2, c3         3.640 us        0.376 us        9.68
+ * "%lu", (time_t)t             5.851 us        0.842 us        6.95
+ *
+ * Less important:
+ * "%d", 31337                  4.487 us        0.852 us        5.27
+ * "%u.%u.%u.%u",               9.069 us        2.431 us        3.73
+ *
+ * Not used:
+ * "%03d", 401                  4.348 us
+ * "%N"                                         0.216 us        20.13
+ *
+ * --Run
+ */
+char *vsprintf_irc(register char *str,
+    register const char *format, register va_list vl)
+{
+  register char c;
+
+  while ((c = *format++))
+  {
+    if (c == '%')
+    {
+      c = *format++;           /* May never be '\0' ! */
+      if (c == 'c')
+      {
+       *str++ = (char)va_arg(vl, int);
+       continue;
+      }
+      if (c == 's')
+      {
+       register const char *p1 = va_arg(vl, const char *);
+       if ((*str = *p1))
+         while ((*++str = *++p1));
+       continue;
+      }
+      if (c == 'l' && *format == 'u')  /* Prints time_t value in interval
+                                          [ 100000000 , 4294967295 ]
+                                          Actually prints like "%09lu" */
+      {
+       register unsigned long v1, v2;
+       register const char *ap;
+       ++format;
+       v1 = va_arg(vl, unsigned long);
+       if (v1 > 999999999L)
+       {
+         v2 = v1 / 1000000000;
+         v1 -= v2 * 1000000000;
+         *str++ = '0' + v2;
+       }
+       v2 = v1 / 1000000;
+       v1 -= v2 * 1000000;
+       ap = atoi_tab + (v2 << 2);
+       *str++ = *ap++;
+       *str++ = *ap++;
+       *str++ = *ap;
+       v2 = v1 / 1000;
+       v1 -= v2 * 1000;
+       ap = atoi_tab + (v2 << 2);
+       *str++ = *ap++;
+       *str++ = *ap++;
+       *str++ = *ap;
+       ap = atoi_tab + (v1 << 2);
+       *str++ = *ap++;
+       *str++ = *ap++;
+       *str++ = *ap;
+       continue;
+      }
+#if 0                          /* Not used */
+      if (c == 'N')            /* Prints "%03u" a numeric value in the
+                                  range [ 0, 999 ], padded with zero's */
+      {
+       register unsigned int v1;
+       register const char *ap;
+       v1 = va_arg(vl, unsigned int);
+       ap = atoi_tab + (v1 << 2);
+       *str++ = *ap++;
+       *str++ = *ap++;
+       *str++ = *ap;
+       continue;
+      }
+#endif
+      if (c == 'd')
+      {
+       register unsigned int v1, v2;
+       register const char *ap;
+       register char *s = &scratch_buffer[sizeof(scratch_buffer) - 2];
+       v1 = va_arg(vl, int);
+       if ((int)v1 <= 0)
+       {
+         if (v1 == 0)
+         {
+           *str++ = '0';
+           continue;
+         }
+         *str++ = '-';
+         v1 = -v1;
+       }
+       do
+       {
+         v2 = v1 / 1000;
+         ap = atoi_tab + 2 + ((v1 - 1000 * v2) << 2);
+         *s-- = *ap--;
+         *s-- = *ap--;
+         *s-- = *ap;
+       }
+       while ((v1 = v2) > 0);
+       while ('0' == *++s);
+       *str = *s;
+       while ((*++str = *++s));
+       continue;
+      }
+      if (c == 'u')
+      {
+       register unsigned int v1, v2;
+       register const char *ap;
+       register char *s = &scratch_buffer[sizeof(scratch_buffer) - 2];
+       v1 = va_arg(vl, unsigned int);
+       if (v1 == 0)
+       {
+         *str++ = '0';
+         continue;
+       }
+       do
+       {
+         v2 = v1 / 1000;
+         ap = atoi_tab + 2 + ((v1 - 1000 * v2) << 2);
+         *s-- = *ap--;
+         *s-- = *ap--;
+         *s-- = *ap;
+       }
+       while ((v1 = v2) > 0);
+       while ('0' == *++s);
+       *str = *s;
+       while ((*++str = *++s));
+       continue;
+      }
+      if (c != '%')
+      {
+       format -= 2;
+       str += vsprintf(str, format, vl);
+       break;
+      }
+    }
+    *str++ = c;
+  }
+  *str = 0;
+  return str;
+}
+
+char *sprintf_irc(register char *str, const char *format, ...)
+{
+  register va_list vl;
+  register char *ret;
+  va_start(vl, format);
+  ret = vsprintf_irc(str, format, vl);
+  va_end(vl);
+  return ret;
+}
diff --git a/ircd/support.c b/ircd/support.c
new file mode 100644 (file)
index 0000000..ebc1795
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * 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 "sys.h"
+#ifdef _SEQUENT_
+#include <sys/timers.h>
+#include <stddef.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdarg.h>
+#include <signal.h>
+#include "h.h"
+#include "send.h"
+#include "ircd.h"
+#include "s_bsd.h"
+#include "support.h"
+#include "sprintf_irc.h"
+#include "common.h"
+#include "fileio.h"
+
+RCSTAG_CC("$Id$");
+
+#ifndef HAVE_STRTOKEN
+/*
+ * strtoken.c
+ *
+ * Walk through a string of tokens, using a set of separators.
+ * -argv 9/90
+ */
+char *strtoken(char **save, char *str, char *fs)
+{
+  char *pos = *save;           /* keep last position across calls */
+  Reg1 char *tmp;
+
+  if (str)
+    pos = str;                 /* new string scan */
+
+  while (pos && *pos && strchr(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 && strchr(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 */
+
+#ifndef HAVE_STRERROR
+/*
+ * strerror
+ *
+ * Returns an appropriate system error string to a given errno.
+ * -argv 11/90
+ */
+
+char *strerror(int err_no)
+{
+  static char buff[40];
+  char *errp;
+
+  errp = (err_no > sys_nerr ? (char *)NULL : sys_errlist[err_no]);
+
+  if (errp == (char *)NULL)
+  {
+    errp = buff;
+    sprintf_irc(errp, "Unknown Error %d", err_no);
+  }
+  return errp;
+}
+
+#endif /* !HAVE_STRERROR */
+
+/*
+ * inetntoa --    Changed the parameter to NOT take a pointer.
+ *                -Run 4/8/97
+ * inetntoa --    Changed name to remove collision possibility and
+ *                so behaviour is garanteed 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(struct in_addr in)
+{
+  static char buf[16];
+  Reg1 unsigned char *s = (unsigned char *)&in.s_addr;
+  Reg2 unsigned char a, b, c, d;
+
+  a = *s++;
+  b = *s++;
+  c = *s++;
+  d = *s++;
+  sprintf_irc(buf, "%u.%u.%u.%u", 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(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 */
+
+#ifndef HAVE_GETTIMEOFDAY
+/* This is copied from ircu3.0.0 (with permission), not vica versa. */
+int gettimeofday(struct timeval *tv, void * /*UNUSED*/)
+{
+  register int ret;
+  static struct timespec tp;
+
+  if ((ret = getclock(TIMEOFDAY, &tp)))
+    return ret;
+  tv->tv_sec = (long)tp.tv_sec;
+  tv->tv_usec = (tp.tv_nsec + 500) / 1000;
+  return 0;
+}
+#endif /* !HAVE_GETTIMEOFDAY */
+
+#ifdef DEBUGMODE
+
+void dumpcore(const char *pattern, ...)
+{
+  va_list vl;
+  static time_t lastd = 0;
+  static int dumps = 0;
+  char corename[12];
+  time_t now;
+  int p;
+
+  va_start(vl, pattern);
+
+  now = time(NULL);
+
+  if (!lastd)
+    lastd = now;
+  else if (now - lastd < 60 && dumps > 2)
+#ifdef __cplusplus
+    s_die(0);
+#else
+    s_die();
+#endif
+  if (now - lastd > 60)
+  {
+    lastd = now;
+    dumps = 1;
+  }
+  else
+    dumps++;
+  p = getpid();
+  if (fork() > 0)
+  {
+    kill(p, 3);
+    kill(p, 9);
+  }
+  write_pidfile();
+  sprintf_irc(corename, "core.%d", p);
+  rename("core", corename);
+  Debug((DEBUG_FATAL, "Dumped core : core.%d", p));
+  sendto_ops("Dumped core : core.%d", p);
+  vdebug(DEBUG_FATAL, pattern, vl);
+  vsendto_ops(pattern, vl);
+#ifdef __cplusplus
+  s_die(0);
+#else
+  s_die();
+#endif
+
+  va_end(vl);
+}
+#endif
+
+int check_if_ipmask(const char *mask)
+{
+  int has_digit = 0;
+  register const char *p;
+
+  for (p = mask; *p; ++p)
+    if (*p != '*' && *p != '?' && *p != '.')
+    {
+      if (!isDigit(*p))
+       return 0;
+      has_digit = -1;
+    }
+
+  return has_digit;
+}
+
+/* Moved from logf() in whocmds.c to here. Modified a 
+ * bit and used for most logging now.
+ *  -Ghostwolf 12-Jul-99
+ */
+
+extern void write_log(const char *filename, const char *pattern, ...)
+{
+  FBFILE *logfile;
+  va_list vl;
+  static char logbuf[1024];
+
+  logfile = fbopen(filename, "a");
+
+  if (logfile)
+  {
+    va_start(vl, pattern);
+    vsprintf_irc(logbuf, pattern, vl);
+    va_end(vl);
+
+    fbputs(logbuf, logfile);
+    fbclose(logfile);
+  }
+}
diff --git a/ircd/userload.c b/ircd/userload.c
new file mode 100644 (file)
index 0000000..4b0c8b9
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Userload module by Michael L. VanLoon (mlv) <michaelv@iastate.edu>
+ * Written 2/93.  Originally grafted into irc2.7.2g 4/93.
+ * 
+ * Rewritten 9/97 by Carlo Wood (Run) <carlo@runaway.xs4all.nl>
+ * because previous version used ridiculous amounts of memory
+ * (stored all loads of the passed three days ~ 8 megs).
+ *
+ * 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 "sys.h"
+#include <stdio.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include "h.h"
+#include "struct.h"
+#include "send.h"
+#include "s_misc.h"
+#include "userload.h"
+#include "ircd.h"
+#include "numnicks.h"
+#include "s_serv.h"
+#include "querycmds.h"
+
+RCSTAG_CC("$Id$");
+
+struct current_load_st current_load;   /* The current load */
+
+static struct current_load_st cspm_sum;        /* Number of connections times number
+                                          of seconds per minute. */
+static struct current_load_st csph_sum;        /* Number of connections times number
+                                          of seconds per hour. */
+static struct current_load_st cspm[60];        /* Last 60 minutes */
+static struct current_load_st csph[72];        /* Last 72 hours */
+
+static int m_index, h_index;   /* Array indexes */
+
+/*
+ * update_load
+ *
+ * A new connection was added or removed.
+ */
+void update_load(void)
+{
+  static struct tm tm_now;     /* Current time. */
+  static time_t last_sec;      /* Seconds of last time that
+                                  update_load() called. */
+  static time_t last_min;
+  static time_t last;          /* Last time that update_load() was called. */
+  static struct current_load_st last_load;     /* The load last time that
+                                                  update_load() was called. */
+  static int initialized;      /* Boolean, set when initialized. */
+  register int diff_time;      /* Temp. variable used to hold time intervals
+                                  in seconds, minutes or hours. */
+
+  /* Update `current_load' */
+  current_load.client_count = nrof.local_clients;
+  current_load.conn_count = nrof.local_clients + nrof.local_servers;
+
+  /* Nothing needed when still in the same second */
+  if (!(diff_time = now - last))
+  {
+    last_load = current_load;  /* Update last_load to be the load last
+                                  time that update_load() was called. */
+    return;
+  }
+
+  /* If we get here we entered a new second */
+
+  /*
+   * Make sure we keep the accurate time in 'tm_now'
+   */
+  if ((tm_now.tm_sec += diff_time) > 59)
+  {
+    /* This is done once every minute */
+    diff_time = tm_now.tm_sec / 60;
+    tm_now.tm_sec -= 60 * diff_time;
+    if ((tm_now.tm_min += diff_time) > 59)
+    {
+      /* This is done once every hour */
+      diff_time = tm_now.tm_min / 60;
+      tm_now.tm_min -= 60 * diff_time;
+      if ((tm_now.tm_hour += diff_time) > 23)
+      {
+       tm_now = *localtime(&now);      /* Only called once a day */
+       if (!initialized)
+       {
+         initialized = 1;
+         last_sec = 60;
+         last_min = tm_now.tm_min;
+       }
+      }
+    }
+
+    /* If we get here we entered a new minute */
+
+    /* Finish the calculation of cspm of the last minute first: */
+    diff_time = 60 - last_sec;
+    cspm_sum.conn_count += last_load.conn_count * diff_time;
+    cspm_sum.client_count += last_load.client_count * diff_time;
+    cspm_sum.local_count += last_load.local_count * diff_time;
+
+    /* Add the completed minute to the Connections*Seconds/Hour sum */
+    csph_sum.conn_count += cspm_sum.conn_count - cspm[m_index].conn_count;
+    csph_sum.client_count += cspm_sum.client_count - cspm[m_index].client_count;
+    csph_sum.local_count += cspm_sum.local_count - cspm[m_index].local_count;
+
+    /* Store the completed minute in an array */
+    cspm[m_index] = cspm_sum;
+
+    /* How long did last_cspm last ? */
+    diff_time = tm_now.tm_min - last_min;
+    last_min = tm_now.tm_min;
+
+    if (diff_time < 0)
+      diff_time += 60;         /* update_load() must be called at
+                                  _least_ once an hour */
+
+    if (diff_time > 1)         /* Did more then one minute pass ? */
+    {
+      /* Calculate the constant load during those extra minutes */
+      cspm_sum.conn_count = last_load.conn_count * 60;
+      cspm_sum.client_count = last_load.client_count * 60;
+      cspm_sum.local_count = last_load.local_count * 60;
+    }
+
+    for (;;)
+    {
+      /* Increase minute index */
+      if (++m_index == 60)
+      {
+       m_index = 0;
+       /* Keep a list of the last 72 hours */
+       csph[h_index] = csph_sum;
+       if (++h_index == 72)
+         h_index = 0;
+      }
+
+      if (--diff_time <= 0)    /* '<' to prevent endless loop if update_load()
+                                  was not called once an hour :/ */
+       break;
+
+      /* Add extra minutes to the Connections*Seconds/Hour sum */
+      csph_sum.conn_count += cspm_sum.conn_count - cspm[m_index].conn_count;
+      csph_sum.client_count +=
+         cspm_sum.client_count - cspm[m_index].client_count;
+      csph_sum.local_count += cspm_sum.local_count - cspm[m_index].local_count;
+
+      /* Store extra minutes in the array */
+      cspm[m_index] = cspm_sum;
+    }
+
+    /* Now start the calculation of the new minute: */
+    last_sec = tm_now.tm_sec;
+    cspm_sum.conn_count = last_load.conn_count * last_sec;
+    cspm_sum.client_count = last_load.client_count * last_sec;
+    cspm_sum.local_count = last_load.local_count * last_sec;
+  }
+  else
+  {
+    /* A new second, but the same minute as last time */
+    /* How long did last_load last ? */
+    diff_time = tm_now.tm_sec - last_sec;
+    last_sec = tm_now.tm_sec;
+    if (diff_time == 1)                /* Just one second ? */
+    {
+      cspm_sum.conn_count += last_load.conn_count;
+      cspm_sum.client_count += last_load.client_count;
+      cspm_sum.local_count += last_load.local_count;
+    }
+    else
+    {
+      /* More then one second */
+      /* At most 3 integer multiplication per second */
+      cspm_sum.conn_count += last_load.conn_count * diff_time;
+      cspm_sum.client_count += last_load.client_count * diff_time;
+      cspm_sum.local_count += last_load.local_count * diff_time;
+    }
+  }
+  last_load = current_load;    /* Update last_load to be the load last
+                                  time that update_load() was called. */
+  last = now;
+}
+
+void calc_load(aClient *sptr)
+{
+  /* *INDENT-OFF* */
+  static const char *header =
+  /*   ----.-  ----.-  ----  ----  ----   ------------ */
+      "Minute  Hour    Day   Yest. YYest. Userload for:";
+  /* *INDENT-ON* */
+  static const char *what[3] = {
+    DOMAINNAME " clients",
+    "total clients",
+    "total connections"
+  };
+  int i, j, times[5][3];       /* [min,hour,day,Yest,YYest]
+                                  [local,client,conn] */
+  int last_m_index = m_index, last_h_index = h_index;
+
+  update_load();               /* We want stats accurate as of *now* */
+
+  if (--last_m_index < 0)
+    last_m_index = 59;
+  times[0][0] = (cspm[last_m_index].local_count + 3) / 6;
+  times[0][1] = (cspm[last_m_index].client_count + 3) / 6;
+  times[0][2] = (cspm[last_m_index].conn_count + 3) / 6;
+
+  times[1][0] = (csph_sum.local_count + 180) / 360;
+  times[1][1] = (csph_sum.client_count + 180) / 360;
+  times[1][2] = (csph_sum.conn_count + 180) / 360;
+
+  for (i = 2; i < 5; ++i)
+  {
+    times[i][0] = 43200;
+    times[i][1] = 43200;
+    times[i][2] = 43200;
+    for (j = 0; j < 24; ++j)
+    {
+      if (--last_h_index < 0)
+       last_h_index = 71;
+      times[i][0] += csph[last_h_index].local_count;
+      times[i][1] += csph[last_h_index].client_count;
+      times[i][2] += csph[last_h_index].conn_count;
+    }
+    times[i][0] /= 86400;
+    times[i][1] /= 86400;
+    times[i][2] /= 86400;
+  }
+
+  if (MyUser(sptr) || Protocol(sptr->from) < 10)
+  {
+    sendto_one(sptr, ":%s NOTICE %s :%s", me.name, sptr->name, header);
+    for (i = 0; i < 3; ++i)
+      sendto_one(sptr,
+         ":%s NOTICE %s :%4d.%1d  %4d.%1d  %4d  %4d  %4d   %s",
+         me.name, sptr->name,
+         times[0][i] / 10, times[0][i] % 10,
+         times[1][i] / 10, times[1][i] % 10,
+         times[2][i], times[3][i], times[4][i], what[i]);
+  }
+  else
+  {
+    sendto_one(sptr, "%s NOTICE %s%s :%s", NumServ(&me), NumNick(sptr), header);
+    for (i = 0; i < 3; ++i)
+      sendto_one(sptr,
+         "%s NOTICE %s%s :%4d.%1d  %4d.%1d  %4d  %4d  %4d   %s",
+         NumServ(&me), NumNick(sptr),
+         times[0][i] / 10, times[0][i] % 10,
+         times[1][i] / 10, times[1][i] % 10,
+         times[2][i], times[3][i], times[4][i], what[i]);
+  }
+}
+
+void initload(void)
+{
+  memset(&current_load, 0, sizeof(current_load));
+  update_load();               /* Initialize the load list */
+}
diff --git a/ircd/version.c.SH b/ircd/version.c.SH
new file mode 100644 (file)
index 0000000..c0b52a9
--- /dev/null
@@ -0,0 +1,111 @@
+echo "Extracting 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`
+
+sum=sum
+if $sum s_serv.c 1> /dev/null 2>&1; then
+:
+else
+  sum=cksum
+fi
+sumsserv=`$sum s_serv.c 2> /dev/null`;
+sumsuser=`$sum s_user.c 2> /dev/null`;
+sumchan=`$sum channel.c 2> /dev/null`;
+sumsbsd=`$sum s_bsd.c 2> /dev/null`;
+sumhash=`$sum hash.c 2> /dev/null`;
+sumsmisc=`$sum s_misc.c 2> /dev/null`;
+sumircd=`$sum ircd.c 2> /dev/null`;
+
+creation=`date | \
+awk '{if (NF == 6) \
+        { print $1 " "  $2 " " $3 " "  $6 " at " $4 " " $5 } \
+else \
+        { print $1 " "  $2 " " $3 " " $7 " at " $4 " " $5 " " $6 }}'`
+
+cvsversion=`cat ../.patches | \
+    awk -F. '{ \
+        if ($(NF)~/\+$/) \
+           printf(".0"); \
+       else \
+           printf(".%d.(%s)", NF - 3, $(NF)); \
+    }'`
+
+/bin/cat >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 "sys.h"
+#include "h.h"
+#include "patchlevel.h"
+#include "version.h"
+
+const char *generation = "$generation";
+const char *creation = "$creation";
+const char *version = BASE_VERSION PATCH1 "$cvsversion" PATCH2 PATCH3 PATCH4 PATCH5 PATCH6\
+       PATCH7 PATCH8 PATCH9 PATCH10 PATCH11 PATCH12 PATCH13 PATCH14 PATCH15\
+       PATCH16 PATCH17 PATCH18 PATCH19 PATCH20 PATCH21 PATCH22 PATCH23 PATCH24\
+       PATCH25 PATCH26 PATCH27 PATCH28 PATCH29 PATCH30 PATCH31 PATCH32;
+
+const char *infotext[] = {
+    "IRC --",
+    "Based on the original code written by Jarkko Oikarinen, version 2.6:",
+    "Copyright 1988, 1989, 1990, 1991 University of Oulu, Computing Center",
+    "",
+    "Wiz         Jarkko Oikarinen         <jto@tolsun.oulu.fi>",
+    "",
+    "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.",
+    "",
+    "The UnderNet code is based upon version 2.8.10.",
+    "The main developers of version 2.7 and 2.8 are:",
+    "",
+    "Avalon      Darren Reed              <avalon@coombs.anu.edu.au>",
+    "msa         Markku Savela            <msa@tel4.tel.vtt.fi>",
+    "Wumpus      Greg Lindahl             <lindahl@pbm.com>",
+    "",
+    "The main developer of version u2.9 and u2.10 is:",
+    "",
+    "Run         Carlo Wood               <carlo@runaway.xs4all.nl>",
+    "",
+    "Thanks goes to all other people who contributed to any version.",
+    "A full listing of all coders can be found in doc/Authors in the",
+    "source.  Contributers to version u2.10 can be found on",
+    "http://coder-com.undernet.org/posters.html",
+    "Thanks also to those who provided me with accounts; the kind sys",
+    "admins who let me and others continue to develop IRC.",
+    "",
+    "[$sumsserv] [$sumchan] [$sumsbsd] [$sumsuser]",
+    "[$sumhash] [$sumsmisc] [$sumircd]",
+    0,
+};
+!SUB!THIS!
diff --git a/ircd/whocmds.c b/ircd/whocmds.c
new file mode 100644 (file)
index 0000000..58b8f31
--- /dev/null
@@ -0,0 +1,811 @@
+
+/*
+ * 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 "sys.h"
+#include <sys/stat.h>
+#include <utmp.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdlib.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef USE_SYSLOG
+#include <syslog.h>
+#endif
+#include "h.h"
+#include "struct.h"
+#include "common.h"
+#include "s_serv.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_conf.h"
+#include "s_misc.h"
+#include "match.h"
+#include "hash.h"
+#include "s_bsd.h"
+#include "whowas.h"
+#include "list.h"
+#include "s_err.h"
+#include "parse.h"
+#include "ircd.h"
+#include "s_user.h"
+#include "support.h"
+#include "s_user.h"
+#include "channel.h"
+#include "random.h"
+#include "version.h"
+#include "msg.h"
+#include "userload.h"
+#include "numnicks.h"
+#include "sprintf_irc.h"
+#include "querycmds.h"
+#include "IPcheck.h"
+
+RCSTAG_CC("$Id$");
+
+/*
+ * m_who() 
+ * m_who with support routines rewritten by Nemesi, August 1997
+ * - Alghoritm have been flattened (no more recursive)
+ * - Several bug fixes
+ * - Strong performance improvement
+ * - Added possibility to have specific fields in the output
+ * See readme.who for further details.
+ */
+
+/* Macros used only in here by m_who and its support functions */
+
+#define WHOSELECT_OPER 1
+#define WHOSELECT_EXTRA 2
+
+#define WHO_FIELD_QTY 1
+#define WHO_FIELD_CHA 2
+#define WHO_FIELD_UID 4
+#define WHO_FIELD_NIP 8
+#define WHO_FIELD_HOS 16
+#define WHO_FIELD_SER 32
+#define WHO_FIELD_NIC 64
+#define WHO_FIELD_FLA 128
+#define WHO_FIELD_DIS 256
+#define WHO_FIELD_REN 512
+
+#define WHO_FIELD_DEF ( WHO_FIELD_NIC | WHO_FIELD_UID | WHO_FIELD_HOS | WHO_FIELD_SER )
+
+#define IS_VISIBLE_USER(s,ac) ((s==ac) || (!IsInvisible(ac)))
+
+#if defined(SHOW_INVISIBLE_LUSERS) || defined(SHOW_ALL_INVISIBLE_USERS)
+#define SEE_LUSER(s, ac, b) (IS_VISIBLE_USER(s, ac) || ((b & WHOSELECT_EXTRA) && MyConnect(ac) && IsAnOper(s)))
+#else
+#define SEE_LUSER(s, ac, b) (IS_VISIBLE_USER(s, ac))
+#endif
+
+#ifdef SHOW_ALL_INVISIBLE_USERS
+#define SEE_USER(s, ac, b) (SEE_LUSER(s, ac, b) || ((b & WHOSELECT_EXTRA) && IsOper(s)))
+#else
+#define SEE_USER(s, ac, b) (SEE_LUSER(s, ac, b))
+#endif
+
+#ifdef UNLIMIT_OPER_QUERY
+#define SHOW_MORE(sptr, counter) (IsAnOper(sptr) || (!(counter-- < 0)) )
+#else
+#define SHOW_MORE(sptr, counter) (!(counter-- < 0))
+#endif
+
+#ifdef OPERS_SEE_IN_SECRET_CHANNELS
+#ifdef LOCOP_SEE_IN_SECRET_CHANNELS
+#define SEE_CHANNEL(s, chptr, b) (!SecretChannel(chptr) || ((b & WHOSELECT_EXTRA) && IsAnOper(s)))
+#else
+#define SEE_CHANNEL(s, chptr, b) (!SecretChannel(chptr) || ((b & WHOSELECT_EXTRA) && IsOper(s)))
+#endif
+#else
+#define SEE_CHANNEL(s, chptr, b) (!SecretChannel(chptr))
+#endif
+
+
+/*
+ * A little spin-marking utility to tell us wich clients we have already
+ * processed and wich not
+ */
+static int who_marker = 0;
+static void move_marker(void)
+{
+  if (!++who_marker)
+  {
+    aClient *cptr = client;
+    while (cptr)
+    {
+      cptr->marker = 0;
+      cptr = cptr->next;
+    }
+    who_marker++;
+  }
+}
+#define CheckMark(x, y) ((x == y) ? 0 : (x = y))
+#define Process(cptr) CheckMark(cptr->marker, who_marker)
+
+/*
+ * The function that actually prints out the WHO reply for a client found
+ */
+static void do_who(aClient *sptr, aClient *acptr, aChannel *repchan,
+    int fields, char *qrt)
+{
+  Reg1 char *p1;
+  Reg2 aChannel *chptr;
+
+  static char buf1[512];
+  /* NOTE: with current fields list and sizes this _cannot_ overrun, 
+     and also the message finally sent shouldn't ever be truncated */
+
+  p1 = buf1;
+  chptr = repchan;
+  buf1[1] = '\0';
+
+  /* If we don't have a channel and we need one... try to find it,
+     unless the listing is for a channel service, we already know
+     that there are no common channels, thus use PubChannel and not
+     SeeChannel */
+  if (!chptr && (!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) &&
+      !IsChannelService(acptr))
+  {
+    Reg3 Link *lp;
+    for (lp = acptr->user->channel; lp && !chptr; lp = lp->next)
+      if (PubChannel(lp->value.chptr) &&
+         (acptr == sptr || !is_zombie(acptr, chptr)))
+       chptr = lp->value.chptr;
+  }
+
+  /* Place the fields one by one in the buffer and send it
+     note that fields == NULL means "default query" */
+
+  if (fields & WHO_FIELD_QTY)  /* Query type */
+  {
+    *(p1++) = ' ';
+    if (BadPtr(qrt))
+      *(p1++) = '0';
+    else
+      while ((*qrt) && (*(p1++) = *(qrt++)));
+  }
+
+  if (!fields || (fields & WHO_FIELD_CHA))
+  {
+    Reg3 char *p2;
+    *(p1++) = ' ';
+    if ((p2 = (chptr ? chptr->chname : NULL)))
+      while ((*p2) && (*(p1++) = *(p2++)));
+    else
+      *(p1++) = '*';
+  }
+
+  if (!fields || (fields & WHO_FIELD_UID))
+  {
+    Reg3 char *p2 = acptr->user->username;
+    *(p1++) = ' ';
+    while ((*p2) && (*(p1++) = *(p2++)));
+  }
+
+  if (fields & WHO_FIELD_NIP)
+  {
+    Reg3 char *p2;
+    p2 = inetntoa(acptr->ip);
+    *(p1++) = ' ';
+    while ((*p2) && (*(p1++) = *(p2++)));
+  }
+
+  if (!fields || (fields & WHO_FIELD_HOS))
+  {
+    Reg3 char *p2 = acptr->user->host;
+    *(p1++) = ' ';
+    while ((*p2) && (*(p1++) = *(p2++)));
+  }
+
+  if (!fields || (fields & WHO_FIELD_SER))
+  {
+    Reg3 char *p2 = acptr->user->server->name;
+    *(p1++) = ' ';
+    while ((*p2) && (*(p1++) = *(p2++)));
+  }
+
+  if (!fields || (fields & WHO_FIELD_NIC))
+  {
+    Reg3 char *p2 = acptr->name;
+    *(p1++) = ' ';
+    while ((*p2) && (*(p1++) = *(p2++)));
+  }
+
+  if (!fields || (fields & WHO_FIELD_FLA))
+  {
+    *(p1++) = ' ';
+    if (acptr->user->away)
+      *(p1++) = 'G';
+    else
+      *(p1++) = 'H';
+    if (IsAnOper(acptr))
+      *(p1++) = '*';
+    if (chptr && is_chan_op(acptr, chptr))
+      *(p1++) = '@';
+    else if (chptr && has_voice(acptr, chptr))
+      *(p1++) = '+';
+    else if (chptr && is_zombie(acptr, chptr))
+      *(p1++) = '!';
+    if (IsDeaf(acptr))
+      *(p1++) = 'd';
+    if (IsAnOper(sptr))
+    {
+      if (IsInvisible(acptr))
+       *(p1++) = 'i';
+      if (SendWallops(acptr))
+       *(p1++) = 'w';
+      if (SendDebug(acptr))
+       *(p1++) = 'g';
+    }
+  }
+
+  if (!fields || (fields & WHO_FIELD_DIS))
+  {
+    *p1++ = ' ';
+    if (!fields)
+      *p1++ = ':';             /* Place colon here for default reply */
+    p1 = sprintf_irc(p1, "%d", acptr->hopcount);
+  }
+
+  if (!fields || (fields & WHO_FIELD_REN))
+  {
+    Reg3 char *p2 = acptr->info;
+    *p1++ = ' ';
+    if (fields)
+      *p1++ = ':';             /* Place colon here for special reply */
+    while ((*p2) && (*(p1++) = *(p2++)));
+  }
+
+  /* The first char will always be an useless blank and we 
+     need to terminate buf1 */
+  *p1 = '\0';
+  p1 = buf1;
+  sendto_one(sptr, rpl_str(fields ? RPL_WHOSPCRPL : RPL_WHOREPLY),
+      me.name, sptr->name, ++p1);
+}
+
+/*
+ *  m_who
+ *
+ *  parv[0] = sender prefix
+ *  parv[1] = nickname mask list
+ *  parv[2] = additional selection flag, only 'o' for now.
+ *            and %flags to specify what fields to output
+ *            plus a ,querytype if the t flag is specified
+ *            so the final thing will be like o%tnchu,777
+ *  parv[3] = _optional_ parameter that overrides parv[1]
+ *            This can be used as "/quote who foo % :The Black Hacker
+ *            to find me, parv[3] _can_ contain spaces !.
+ */
+
+int m_who(aClient *UNUSED(cptr), aClient *sptr, int parc, char *parv[])
+{
+  Reg1 char *mask;             /* The mask we are looking for              */
+  Reg2 char ch;                        /* Scratch char register                    */
+  Reg3 Link *lp, *lp2;
+  Reg4 aChannel *chptr;                /* Channel to show                          */
+  Reg5 aClient *acptr;         /* Client to show                           */
+
+  int bitsel;                  /* Mask of selectors to apply               */
+  int matchsel;                        /* Wich fields the match should apply on    */
+  int counter;                 /* Query size counter,
+                                  initially used to count fields           */
+  int commas;                  /* Does our mask contain any comma ?
+                                  If so is a list..                        */
+  int fields;                  /* Mask of fields to show                   */
+  int isthere = 0;             /* When this set the user is member of chptr */
+  char *nick;                  /* Single element extracted from
+                                  the mask list                            */
+  char *p;                     /* Scratch char pointer                     */
+  char *qrt;                   /* Pointer to the query type                */
+  static char mymask[512];     /* To save the mask before corrupting it    */
+
+  /* Let's find where is our mask, and if actually contains something */
+  mask = ((parc > 1) ? parv[1] : NULL);
+  if (parc > 3 && parv[3])
+    mask = parv[3];
+  if (mask && ((mask[0] == '\0') ||
+      (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*')))))
+    mask = NULL;
+
+  /* Evaluate the flags now, we consider the second parameter 
+     as "matchFlags%fieldsToInclude,querytype"           */
+  bitsel = fields = counter = matchsel = 0;
+  qrt = NULL;
+  if (parc > 2 && parv[2] && *parv[2])
+  {
+    p = parv[2];
+    while (((ch = *(p++))) && (ch != '%') && (ch != ','))
+      switch (ch)
+      {
+       case 'o':
+       case 'O':
+         bitsel |= WHOSELECT_OPER;
+         continue;
+       case 'x':
+       case 'X':
+         bitsel |= WHOSELECT_EXTRA;
+#ifdef WPATH
+         if (IsAnOper(sptr))
+           write_log(WPATH, "# " TIME_T_FMT " %s!%s@%s WHO %s %s\n",
+               now, sptr->name, sptr->user->username, sptr->user->host,
+               (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]);
+#endif /* WPATH */
+         continue;
+       case 'n':
+       case 'N':
+         matchsel |= WHO_FIELD_NIC;
+         continue;
+       case 'u':
+       case 'U':
+         matchsel |= WHO_FIELD_UID;
+         continue;
+       case 'h':
+       case 'H':
+         matchsel |= WHO_FIELD_HOS;
+         continue;
+       case 'i':
+       case 'I':
+         matchsel |= WHO_FIELD_NIP;
+         continue;
+       case 's':
+       case 'S':
+         matchsel |= WHO_FIELD_SER;
+         continue;
+       case 'r':
+       case 'R':
+         matchsel |= WHO_FIELD_REN;
+         continue;
+      }
+    if (ch == '%')
+      while ((ch = *p++) && (ch != ','))
+      {
+       counter++;
+       switch (ch)
+       {
+         case 'c':
+         case 'C':
+           fields |= WHO_FIELD_CHA;
+           break;
+         case 'd':
+         case 'D':
+           fields |= WHO_FIELD_DIS;
+           break;
+         case 'f':
+         case 'F':
+           fields |= WHO_FIELD_FLA;
+           break;
+         case 'h':
+         case 'H':
+           fields |= WHO_FIELD_HOS;
+           break;
+         case 'i':
+         case 'I':
+           fields |= WHO_FIELD_NIP;
+           break;
+         case 'n':
+         case 'N':
+           fields |= WHO_FIELD_NIC;
+           break;
+         case 'r':
+         case 'R':
+           fields |= WHO_FIELD_REN;
+           break;
+         case 's':
+         case 'S':
+           fields |= WHO_FIELD_SER;
+           break;
+         case 't':
+         case 'T':
+           fields |= WHO_FIELD_QTY;
+           break;
+         case 'u':
+         case 'U':
+           fields |= WHO_FIELD_UID;
+           break;
+         default:
+           break;
+       }
+      };
+    if (ch)
+      qrt = p;
+  }
+
+  if (!matchsel)
+    matchsel = WHO_FIELD_DEF;
+  if (!fields)
+    counter = 7;
+
+  if (qrt && (fields & WHO_FIELD_QTY))
+  {
+    p = qrt;
+    if (!((*p > '9') || (*p < '0')))
+      p++;
+    if (!((*p > '9') || (*p < '0')))
+      p++;
+    if (!((*p > '9') || (*p < '0')))
+      p++;
+    *p = '\0';
+  }
+  else
+    qrt = NULL;
+
+  /* I'd love to add also a check on the number of matches fields per time */
+  counter = (2048 / (counter + 4));
+  if (mask && (strlen(mask) > 510))
+    mask[510] = '\0';
+  move_marker();
+  commas = (mask && strchr(mask, ','));
+
+  /* First treat mask as a list of plain nicks/channels */
+  if (mask)
+  {
+    strcpy(mymask, mask);
+    for (p = NULL, nick = strtoken(&p, mymask, ","); nick;
+       nick = strtoken(&p, NULL, ","))
+    {
+      if (IsChannelName(nick) && (chptr = FindChannel(nick)))
+      {
+       isthere = (IsMember(sptr, chptr) != NULL);
+       if (isthere || SEE_CHANNEL(sptr, chptr, bitsel))
+       {
+         for (lp = chptr->members; lp; lp = lp->next)
+         {
+           acptr = lp->value.cptr;
+           if ((bitsel & WHOSELECT_OPER) && !(IsAnOper(acptr)))
+             continue;
+           if ((acptr != sptr) && (lp->flags & CHFL_ZOMBIE))
+             continue;
+           if (!(isthere || (SEE_USER(sptr, acptr, bitsel))))
+             continue;
+           if (!Process(acptr))        /* This can't be moved before other checks */
+             continue;
+           if (!(isthere || (SHOW_MORE(sptr, counter))))
+             break;
+           do_who(sptr, acptr, chptr, fields, qrt);
+         }
+       }
+      }
+      else
+      {
+       if ((acptr = FindUser(nick)) &&
+           ((!(bitsel & WHOSELECT_OPER)) || IsAnOper(acptr)) &&
+           Process(acptr) && SHOW_MORE(sptr, counter))
+       {
+         do_who(sptr, acptr, NULL, fields, qrt);
+       }
+      }
+    }
+  }
+
+  /* If we didn't have any comma in the mask treat it as a
+     real mask and try to match all relevant fields */
+  if (!(commas || (counter < 1)))
+  {
+    int minlen, cset;
+    static struct in_mask imask;
+    if (mask)
+    {
+      matchcomp(mymask, &minlen, &cset, mask);
+      if (matchcompIP(&imask, mask))
+       matchsel &= ~WHO_FIELD_NIP;
+      if ((minlen > NICKLEN) || !(cset & NTL_IRCNK))
+       matchsel &= ~WHO_FIELD_NIC;
+      if ((matchsel & WHO_FIELD_SER) &&
+         ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN))
+         || (!markMatchexServer(mymask, minlen))))
+       matchsel &= ~WHO_FIELD_SER;
+      if ((minlen > USERLEN) || !(cset & NTL_IRCUI))
+       matchsel &= ~WHO_FIELD_UID;
+      if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN))
+       matchsel &= ~WHO_FIELD_HOS;
+    }
+
+    /* First of all loop through the clients in common channels */
+    if ((!(counter < 1)) && matchsel)
+      for (lp = sptr->user->channel; lp; lp = lp->next)
+       for (chptr = lp->value.chptr, lp2 = chptr->members; lp2;
+           lp2 = lp2->next)
+       {
+         acptr = lp2->value.cptr;
+         if (!(IsUser(acptr) && Process(acptr)))
+           continue;           /* Now Process() is at the beginning, if we fail
+                                  we'll never have to show this acptr in this query */
+         if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
+           continue;
+         if ((mask) &&
+             ((!(matchsel & WHO_FIELD_NIC))
+             || matchexec(acptr->name, mymask, minlen))
+             && ((!(matchsel & WHO_FIELD_UID))
+             || matchexec(acptr->user->username, mymask, minlen))
+             && ((!(matchsel & WHO_FIELD_SER))
+             || (!(acptr->user->server->flags & FLAGS_MAP)))
+             && ((!(matchsel & WHO_FIELD_HOS))
+             || matchexec(acptr->user->host, mymask, minlen))
+             && ((!(matchsel & WHO_FIELD_REN))
+             || matchexec(acptr->info, mymask, minlen))
+             && ((!(matchsel & WHO_FIELD_NIP))
+             || ((((acptr->ip.s_addr & imask.mask.s_addr) !=
+             imask.bits.s_addr)) || (imask.fall
+             && matchexec(inet_ntoa(acptr->ip), mymask, minlen)))))
+           continue;
+         if (!SHOW_MORE(sptr, counter))
+           break;
+         do_who(sptr, acptr, chptr, fields, qrt);
+       }
+
+    /* Loop through all clients :-\, if we still have something to match to 
+       and we can show more clients */
+    if ((!(counter < 1)) && matchsel)
+      for (acptr = me.prev; acptr; acptr = acptr->prev)
+      {
+       if (!(IsUser(acptr) && Process(acptr)))
+         continue;
+       if ((bitsel & WHOSELECT_OPER) && !IsAnOper(acptr))
+         continue;
+       if (!(SEE_USER(sptr, acptr, bitsel)))
+         continue;
+       if ((mask) &&
+           ((!(matchsel & WHO_FIELD_NIC))
+           || matchexec(acptr->name, mymask, minlen))
+           && ((!(matchsel & WHO_FIELD_UID))
+           || matchexec(acptr->user->username, mymask, minlen))
+           && ((!(matchsel & WHO_FIELD_SER))
+           || (!(acptr->user->server->flags & FLAGS_MAP)))
+           && ((!(matchsel & WHO_FIELD_HOS))
+           || matchexec(acptr->user->host, mymask, minlen))
+           && ((!(matchsel & WHO_FIELD_REN))
+           || matchexec(acptr->info, mymask, minlen))
+           && ((!(matchsel & WHO_FIELD_NIP))
+           || ((((acptr->ip.s_addr & imask.mask.s_addr) != imask.bits.s_addr))
+           || (imask.fall
+           && matchexec(inet_ntoa(acptr->ip), mymask, minlen)))))
+         continue;
+       if (!SHOW_MORE(sptr, counter))
+         break;
+       do_who(sptr, acptr, NULL, fields, qrt);
+      }
+  }
+
+  /* Make a clean mask suitable to be sent in the "end of" */
+  if (mask && (p = strchr(mask, ' ')))
+    *p = '\0';
+  sendto_one(sptr, rpl_str(RPL_ENDOFWHO),
+      me.name, parv[0], BadPtr(mask) ? "*" : mask);
+
+  /* Notify the user if we decided that his query was too long */
+  if (counter < 0)
+    sendto_one(sptr, err_str(ERR_QUERYTOOLONG), me.name, parv[0], "WHO");
+
+  return 0;
+}
+
+#define MAX_WHOIS_LINES 50
+
+/*
+ * m_whois
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nickname masklist
+ *
+ * or
+ *
+ * parv[1] = target server
+ * parv[2] = nickname masklist
+ */
+int m_whois(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  Reg2 Link *lp;
+  Reg3 anUser *user;
+  aClient *acptr, *a2cptr;
+  aChannel *chptr;
+  char *nick, *tmp, *name;
+  char *p = NULL;
+  int found, len, mlen, total;
+  static char buf[512];
+
+  if (parc < 2)
+  {
+    sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]);
+    return 0;
+  }
+
+  if (parc > 2)
+  {
+    aClient *acptr;
+    /* For convenience: Accept a nickname as first parameter, by replacing
+       it with the correct servername - as is needed by hunt_server() */
+    if (MyUser(sptr) && (acptr = FindUser(parv[1])))
+      parv[1] = acptr->user->server->name;
+    if (hunt_server(0, cptr, sptr, ":%s WHOIS %s :%s", 1, parc, parv) !=
+       HUNTED_ISME)
+      return 0;
+    parv[1] = parv[2];
+  }
+
+  total = 0;
+  for (tmp = parv[1]; (nick = strtoken(&p, tmp, ",")); tmp = NULL)
+  {
+    int invis, showperson, member, wilds;
+
+    found = 0;
+    collapse(nick);
+    wilds = (strchr(nick, '?') || strchr(nick, '*'));
+    /* Do a hash lookup if the nick does not contain wilds */
+    if (wilds)
+    {
+      /*
+       * We're no longer allowing remote users to generate requests with wildcards.
+       */
+      if (!MyConnect(sptr))
+       continue;
+      for (acptr = client; (acptr = next_client(acptr, nick));
+         acptr = acptr->next)
+      {
+       if (!IsRegistered(acptr) || IsServer(acptr) || IsPing(acptr))
+         continue;
+       /*
+        * I'm always last :-) and acptr->next == NULL!!
+        */
+       if (IsMe(acptr))
+         break;
+       /*
+        * 'Rules' established for sending a WHOIS reply:
+        *
+        * - 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;
+        */
+       user = acptr->user;
+       name = (!*acptr->name) ? "?" : acptr->name;
+
+       invis = acptr != sptr && IsInvisible(acptr);
+       member = (user && user->channel) ? 1 : 0;
+       showperson = (wilds && !invis && !member) || !wilds;
+       if (user)
+         for (lp = user->channel; lp; lp = lp->next)
+         {
+           chptr = lp->value.chptr;
+           member = IsMember(sptr, chptr) ? 1 : 0;
+           if (invis && !member)
+             continue;
+           if (is_zombie(acptr, chptr))
+             continue;
+           if (member || (!invis && PubChannel(chptr)))
+           {
+             showperson = 1;
+             break;
+           }
+           if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr))
+             showperson = 1;
+         }
+       if (!showperson)
+         continue;
+
+       if (user)
+       {
+         a2cptr = user->server;
+         sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
+             parv[0], name, user->username, user->host, acptr->info);
+       }
+       else
+       {
+         a2cptr = &me;
+         sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
+             parv[0], name, "<unknown>", "<unknown>", "<unknown>");
+       }
+
+       found = 1;
+
+      exact_match:
+       if (user && !IsChannelService(acptr))
+       {
+         mlen = strlen(me.name) + strlen(parv[0]) + 12 + strlen(name);
+         for (len = 0, *buf = '\0', lp = user->channel; lp; lp = lp->next)
+         {
+           chptr = lp->value.chptr;
+           if (ShowChannel(sptr, chptr) &&
+               (acptr == sptr || !is_zombie(acptr, chptr)))
+           {
+             if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5)
+             {
+               sendto_one(sptr, ":%s %d %s %s :%s",
+                   me.name, RPL_WHOISCHANNELS, parv[0], name, buf);
+               *buf = '\0';
+               len = 0;
+             }
+             if (IsDeaf(acptr))
+               *(buf + len++) = '-';
+             if (is_chan_op(acptr, chptr))
+               *(buf + len++) = '@';
+             else if (has_voice(acptr, chptr))
+               *(buf + len++) = '+';
+             else if (is_zombie(acptr, chptr))
+               *(buf + len++) = '!';
+             if (len)
+               *(buf + len) = '\0';
+             strcpy(buf + len, chptr->chname);
+             len += strlen(chptr->chname);
+             strcat(buf + len, " ");
+             len++;
+           }
+         }
+         if (buf[0] != '\0')
+           sendto_one(sptr, rpl_str(RPL_WHOISCHANNELS),
+               me.name, parv[0], name, buf);
+       }
+
+       sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name,
+           parv[0], name, a2cptr->name, a2cptr->info);
+
+       if (user)
+       {
+         if (user->away)
+           sendto_one(sptr, rpl_str(RPL_AWAY), me.name,
+               parv[0], name, user->away);
+
+         if (IsAnOper(acptr))
+           sendto_one(sptr, rpl_str(RPL_WHOISOPERATOR),
+               me.name, parv[0], name);
+
+         if (MyConnect(acptr))
+           sendto_one(sptr, rpl_str(RPL_WHOISIDLE), me.name,
+               parv[0], name, now - user->last, acptr->firsttime);
+       }
+       if (found == 2 || total++ >= MAX_WHOIS_LINES)
+         break;
+      }
+    }
+    else
+    {
+      /* No wildcards */
+      if ((acptr = FindUser(nick)))
+      {
+       found = 2;              /* Make sure we exit the loop after passing it once */
+       user = acptr->user;
+       name = (!*acptr->name) ? "?" : acptr->name;
+       a2cptr = user->server;
+       sendto_one(sptr, rpl_str(RPL_WHOISUSER), me.name,
+           parv[0], name, user->username, user->host, acptr->info);
+       goto exact_match;
+      }
+    }
+    if (!found)
+      sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick);
+    if (p)
+      p[-1] = ',';
+    if (!MyConnect(sptr) || total >= MAX_WHOIS_LINES)
+      break;
+  }
+  sendto_one(sptr, rpl_str(RPL_ENDOFWHOIS), me.name, parv[0], parv[1]);
+
+  return 0;
+}
diff --git a/ircd/whowas.c b/ircd/whowas.c
new file mode 100644 (file)
index 0000000..a893490
--- /dev/null
@@ -0,0 +1,391 @@
+
+/*
+ * 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.
+ */
+
+/*
+ * --- comstud --- 25th March 1997
+ * Everything rewritten from scratch.  Avalon's code was bad.  My version
+ * is faster and more efficient.  No more hangs on /squits and you can
+ * safely raise NICKNAMEHISTORYLENGTH to a higher value without hurting
+ * performance.
+ */
+
+/*
+ * --- comstud --- 5th August 1997
+ * Fixed for Undernet..
+ */
+
+/*
+ * --- Run --- 27th August 1997
+ * Speeded up the code, added comments.
+ */
+
+#include "sys.h"
+#include <stdlib.h>
+#include "common.h"
+#include "h.h"
+#include "struct.h"
+#include "numeric.h"
+#include "send.h"
+#include "s_misc.h"
+#include "s_err.h"
+#include "whowas.h"
+#include "ircd.h"
+#include "list.h"
+#include "s_user.h"
+#include "support.h"
+
+RCSTAG_CC("$Id$");
+
+static aWhowas whowas[NICKNAMEHISTORYLENGTH];
+static aWhowas *whowashash[WW_MAX];
+static aWhowas *whowas_next = whowas;
+
+static unsigned int hash_whowas_name(register const char *name);
+
+extern char *canonize(char *);
+
+/*
+ * Since the introduction of numeric nicks (at least for upstream messages,
+ * like MODE +o <nick>, KICK #chan <nick>, KILL <nick> etc), there is no
+ * real important reason for a nick history anymore.
+ * Nevertheless, there are two reason why we might want to keep it:
+ * 1) The /WHOWAS command, which is often usefull to catch harrashing
+ *    users or abusers in general.
+ * 2) Clients still use the normal nicks in the client-server protocol,
+ *    and it might be considered a nice feature that here we still have
+ *    nick chasing.
+ * Note however that BOTH reasons make it redundant to keep a whowas history
+ * for users that split off.
+ *
+ * The rewrite of comstud was many to safe cpu during net.breaks and therefore
+ * a bit redundant imho (Run).
+ *
+ * But - it was written anyway.  So lets look at the structure of the
+ * whowas history now:
+ *
+ * We still have a static table of 'aWhowas' structures in which we add
+ * new nicks (plus info) as in a rotating buffer.  We keep a global pointer
+ * `whowas_next' that points to the next entry to be overwritten - or to
+ * the oldest entry in the table (which is the same).
+ *
+ * Each entry keeps pointers for two doubly linked lists (thus four pointers):
+ * A list of the entries that have the same hash value ('hashv list'), and
+ * a list of the entries that have the same online pointer (`online list').
+ * Note that the last list (pointers) is only updated as long as online points
+ * to the corresponding client: As soon as the client signs off, this list
+ * is not anymore maintained (and hopefully not used anymore either ;).
+ *
+ * So now we have two ways of accessing this database:
+ * 1) Given a <nick> we can calculate a hashv and then whowashash[hashv] will
+ *    point to the start of the 'hash list': all entries with the same hashv.
+ *    We'll have to search this list to find the entry with the correct <nick>.
+ *    Once we found the correct whowas entry, we have a pointer to the
+ *    corresponding client - if still online - for nich chasing purposes.
+ *    Note that the same nick can occur multiple times in the whowas history,
+ *    each of these having the same hash value of course.  While a /WHOWAS on
+ *    just a nick will return all entries, nick chasing will only find the
+ *    first in the list.  Because new entries are added at the start of the
+ *    'hash list' we will always find the youngest entry, which is what we want.
+ * 2) Given an online client we have a pointer to the first whowas entry
+ *    of the linked list of whowas entries that all belong to this client.
+ *    We ONLY need this to reset all `online' pointers when this client
+ *    signs off.
+ *
+ * 27/8/79:
+ *
+ * Note that following:
+ *
+ * a) We *only* (need to) change the 'hash list' and the 'online' list
+ *    in add_history().
+ * b) There we always ADD an entry to the BEGINNING of the 'hash list'
+ *    and the 'online list': *new* entries are at the start of the lists.
+ *    The oldest entries are at the end of the lists.
+ * c) We always REMOVE the oldest entry we have (whowas_next), this means
+ *    that this is always an entry that is at the *end* of the 'hash list'
+ *    and 'online list' that it is a part of: the next pointer will
+ *    always be NULL.
+ * d) The previous pointer is *only* used to update the next pointer of the
+ *    previous entry, therefore we could better use a pointer to this
+ *    next pointer: That is faster - saves us a 'if' test (it will never be
+ *    NULL because the last added entry will point to the pointer that
+ *    points to the start of the list) and we won't need special code to
+ *    update the list start pointers.
+ *
+ * I incorporated these considerations into the code below.
+ *
+ * --Run
+ */
+
+typedef union {
+  aWhowas *newww;
+  aWhowas *oldww;
+} Current;
+
+#define WHOWAS_UNUSED ((unsigned int)-1)
+
+/*
+ * add_history
+ *
+ * Add a client (cptr) that just changed nick (still_on == true), or
+ * just signed off (still_on == false) to the `whowas' table.
+ *
+ * If the entry used was already in use, then this entry is
+ * freed (lost).
+ */
+void add_history(aClient *cptr, int still_on)
+{
+  register Current ww;
+  ww.newww = whowas_next;
+
+  /* If this entry has already been used, remove it from the lists */
+  if (ww.newww->hashv != WHOWAS_UNUSED)
+  {
+    if (ww.oldww->online)      /* No need to update cnext/cprev when offline! */
+    {
+      /* Remove ww.oldww from the linked list with the same `online' pointers */
+      *ww.oldww->cprevnextp = ww.oldww->cnext;
+
+      if (ww.oldww->cnext)
+       MyCoreDump;
+#if 0
+      if (ww.oldww->cnext)     /* Never true, we always catch the
+                                  oldwwest nick of this client first */
+       ww.oldww->cnext->cprevnextp = ww.oldww->cprevnextp;
+#endif
+
+    }
+    /* Remove ww.oldww from the linked list with the same `hashv' */
+    *ww.oldww->hprevnextp = ww.oldww->hnext;
+
+    if (ww.oldww->hnext)
+      MyCoreDump;
+#if 0
+    if (ww.oldww->hnext)
+      ww.oldww->hnext->hprevnextp = ww.oldww->hprevnextp;
+#endif
+
+    if (ww.oldww->name)
+      RunFree(ww.oldww->name);
+    if (ww.oldww->username)
+      RunFree(ww.oldww->username);
+    if (ww.oldww->hostname)
+      RunFree(ww.oldww->hostname);
+    if (ww.oldww->servername)
+      RunFree(ww.oldww->servername);
+    if (ww.oldww->realname)
+      RunFree(ww.oldww->realname);
+    if (ww.oldww->away)
+      RunFree(ww.oldww->away);
+  }
+
+  /* Initialize aWhoWas struct `newww' */
+  ww.newww->hashv = hash_whowas_name(cptr->name);
+  ww.newww->logoff = now;
+  DupString(ww.newww->name, cptr->name);
+  DupString(ww.newww->username, cptr->user->username);
+  DupString(ww.newww->hostname, cptr->user->host);
+  /* Should be changed to server numeric */
+  DupString(ww.newww->servername, cptr->user->server->name);
+  DupString(ww.newww->realname, cptr->info);
+  if (cptr->user->away)
+    DupString(ww.newww->away, cptr->user->away);
+  else
+    ww.newww->away = NULL;
+
+  /* Update/initialize online/cnext/cprev: */
+  if (still_on)                        /* User just changed nicknames */
+  {
+    ww.newww->online = cptr;
+    /* Add aWhowas struct `newww' to start of 'online list': */
+    if ((ww.newww->cnext = cptr->whowas))
+      ww.newww->cnext->cprevnextp = &ww.newww->cnext;
+    ww.newww->cprevnextp = &cptr->whowas;
+    cptr->whowas = ww.newww;
+  }
+  else                         /* User quitting */
+    ww.newww->online = NULL;
+
+  /* Add aWhowas struct `newww' to start of 'hashv list': */
+  if ((ww.newww->hnext = whowashash[ww.newww->hashv]))
+    ww.newww->hnext->hprevnextp = &ww.newww->hnext;
+  ww.newww->hprevnextp = &whowashash[ww.newww->hashv];
+  whowashash[ww.newww->hashv] = ww.newww;
+
+  /* Advance `whowas_next' to next entry in the `whowas' table: */
+  if (++whowas_next == &whowas[NICKNAMEHISTORYLENGTH])
+    whowas_next = whowas;
+}
+
+/*
+ * off_history
+ *
+ * Client `cptr' signed off: Set all `online' pointers
+ * corresponding to this client to NULL.
+ */
+void off_history(const aClient *cptr)
+{
+  aWhowas *temp;
+
+  for (temp = cptr->whowas; temp; temp = temp->cnext)
+    temp->online = NULL;
+}
+
+/*
+ * get_history
+ *
+ * Return a pointer to a client that had nick `nick' not more then
+ * `timelimit' seconds ago, if still on line.  Otherwise return NULL.
+ *
+ * This function is used for "nick chasing"; since the use of numeric
+ * nicks for "upstream" messages in ircu2.10, this is only used for
+ * looking up non-existing nicks in client->server messages.
+ */
+aClient *get_history(const char *nick, time_t timelimit)
+{
+  aWhowas *temp = whowashash[hash_whowas_name(nick)];
+  timelimit = now - timelimit;
+
+  for (; temp; temp = temp->hnext)
+    if (!strCasediff(nick, temp->name) && temp->logoff > timelimit)
+      return temp->online;
+
+  return NULL;
+}
+
+void count_whowas_memory(int *wwu, size_t *wwum, int *wwa, size_t *wwam)
+{
+  register aWhowas *tmp;
+  register int i;
+  int u = 0, a = 0;
+  size_t um = 0, am = 0;
+
+  for (i = 0, tmp = whowas; i < NICKNAMEHISTORYLENGTH; i++, tmp++)
+    if (tmp->hashv != WHOWAS_UNUSED)
+    {
+      u++;
+      um += (strlen(tmp->name) + 1);
+      um += (strlen(tmp->username) + 1);
+      um += (strlen(tmp->hostname) + 1);
+      um += (strlen(tmp->servername) + 1);
+      if (tmp->away)
+      {
+       a++;
+       am += (strlen(tmp->away) + 1);
+      }
+    }
+
+  *wwu = u;
+  *wwum = um;
+  *wwa = a;
+  *wwam = am;
+}
+
+/*
+ * m_whowas
+ *
+ * parv[0] = sender prefix
+ * parv[1] = nickname queried
+ * parv[2] = maximum returned items (optional, default is unlimitted)
+ * parv[3] = remote server target (Opers only, max returned items 20)
+ */
+int m_whowas(aClient *cptr, aClient *sptr, int parc, char *parv[])
+{
+  register aWhowas *temp;
+  register int cur = 0;
+  int max = -1, found = 0;
+  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(1, cptr, sptr, ":%s WHOWAS %s %s :%s", 3, parc, parv))
+      return 0;
+
+  parv[1] = canonize(parv[1]);
+  if (!MyConnect(sptr) && (max > 20))
+    max = 20;                  /* Set max replies at 20 */
+  for (s = parv[1]; (nick = strtoken(&p, s, ",")); s = NULL)
+  {
+    /* Search through bucket, finding all nicknames that match */
+    found = 0;
+    for (temp = whowashash[hash_whowas_name(nick)]; temp; temp = temp->hnext)
+    {
+      if (!strCasediff(nick, temp->name))
+      {
+       sendto_one(sptr, rpl_str(RPL_WHOWASUSER),
+           me.name, parv[0], temp->name, temp->username,
+           temp->hostname, temp->realname);
+       sendto_one(sptr, rpl_str(RPL_WHOISSERVER), me.name, parv[0],
+           temp->name, temp->servername, myctime(temp->logoff));
+       if (temp->away)
+         sendto_one(sptr, rpl_str(RPL_AWAY),
+             me.name, parv[0], temp->name, temp->away);
+       cur++;
+       found++;
+      }
+      if (max >= 0 && cur >= max)
+       break;
+    }
+    if (!found)
+      sendto_one(sptr, err_str(ERR_WASNOSUCHNICK), me.name, parv[0], nick);
+    /* To keep parv[1] intact for ENDOFWHOWAS */
+    if (p)
+      p[-1] = ',';
+  }
+  sendto_one(sptr, rpl_str(RPL_ENDOFWHOWAS), me.name, parv[0], parv[1]);
+  return 0;
+}
+
+void initwhowas(void)
+{
+  register int i;
+
+  for (i = 0; i < NICKNAMEHISTORYLENGTH; i++)
+    whowas[i].hashv = WHOWAS_UNUSED;
+}
+
+static unsigned int hash_whowas_name(register const char *name)
+{
+  register unsigned int hash = 0;
+  register unsigned int hash2 = 0;
+  register char lower;
+
+  do
+  {
+    lower = toLower(*name);
+    hash = (hash << 1) + lower;
+    hash2 = (hash2 >> 1) + lower;
+  }
+  while (*++name);
+
+  return ((hash & WW_MAX_INITIAL_MASK) << BITS_PER_COL) +
+      (hash2 & BITS_PER_COL_MASK);
+}