Browse Source

Import upstream version 19

Alexander Barton 12 years ago
parent
commit
23fb9d37a9
73 changed files with 3718 additions and 1809 deletions
  1. 5 4
      AUTHORS
  2. 19 20
      COPYING
  3. 155 5
      ChangeLog
  4. 1 1
      INSTALL
  5. 72 6
      NEWS
  6. 21 17
      README
  7. 247 243
      config.guess
  8. 192 77
      config.sub
  9. 44 13
      configure
  10. 17 7
      configure.in
  11. 6 7
      contrib/Anope/README
  12. 12 0
      contrib/Debian/changelog
  13. 27 40
      contrib/Debian/control
  14. 8 1
      contrib/MacOSX/config.h
  15. 29 15
      contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj
  16. 1 0
      contrib/ngindent
  17. 12 11
      contrib/ngircd.spec
  18. 24 13
      doc/GIT.txt
  19. 9 10
      doc/Makefile.am
  20. 9 10
      doc/Makefile.in
  21. 76 0
      doc/Modes.txt
  22. 19 16
      doc/Platforms.txt
  23. 17 10
      doc/README-Interix.txt
  24. 18 1
      doc/sample-ngircd.conf.tmpl
  25. 19 3
      man/ngircd.conf.5.tmpl
  26. 6 0
      src/config.h.in
  27. 2 0
      src/ipaddr/ng_ipaddr.c
  28. 10 9
      src/ngircd/Makefile.am
  29. 33 28
      src/ngircd/Makefile.in
  30. 1 1
      src/ngircd/array.h
  31. 82 24
      src/ngircd/channel.c
  32. 9 4
      src/ngircd/channel.h
  33. 143 0
      src/ngircd/class.c
  34. 41 0
      src/ngircd/class.h
  35. 95 28
      src/ngircd/client.c
  36. 3 0
      src/ngircd/client.h
  37. 67 41
      src/ngircd/conf.c
  38. 4 1
      src/ngircd/conf.h
  39. 56 18
      src/ngircd/conn-func.c
  40. 3 1
      src/ngircd/conn-func.h
  41. 64 29
      src/ngircd/conn.c
  42. 4 2
      src/ngircd/conn.h
  43. 167 77
      src/ngircd/defines.h
  44. 73 92
      src/ngircd/io.c
  45. 89 62
      src/ngircd/irc-channel.c
  46. 337 191
      src/ngircd/irc-info.c
  47. 63 57
      src/ngircd/irc-login.c
  48. 387 206
      src/ngircd/irc-mode.c
  49. 170 27
      src/ngircd/irc-oper.c
  50. 3 1
      src/ngircd/irc-oper.h
  51. 101 51
      src/ngircd/irc.c
  52. 4 1
      src/ngircd/irc.h
  53. 260 75
      src/ngircd/lists.c
  54. 15 9
      src/ngircd/lists.h
  55. 26 31
      src/ngircd/log.c
  56. 2 1
      src/ngircd/log.h
  57. 15 6
      src/ngircd/messages.h
  58. 175 151
      src/ngircd/ngircd.c
  59. 21 2
      src/ngircd/numeric.c
  60. 17 6
      src/ngircd/op.c
  61. 2 2
      src/ngircd/op.h
  62. 1 1
      src/ngircd/pam.c
  63. 16 6
      src/ngircd/parse.c
  64. 20 7
      src/ngircd/proc.c
  65. 4 1
      src/ngircd/proc.h
  66. 4 2
      src/ngircd/resolve.c
  67. 4 0
      src/portab/portab.h
  68. 1 1
      src/testsuite/getpid.sh
  69. 3 3
      src/testsuite/message-test.e
  70. 1 0
      src/testsuite/ngircd-test1.conf
  71. 2 1
      src/testsuite/ngircd-test2.conf
  72. 48 18
      src/testsuite/who-test.e
  73. 5 5
      src/testsuite/whois-test.e

+ 5 - 4
AUTHORS

@@ -2,7 +2,7 @@
                      ngIRCd - Next Generation IRC Server
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
                            http://ngircd.barton.de/
 
 
-               (c)2001-2011 Alexander Barton and Contributors.
+               (c)2001-2012 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
                    terms of the GNU General Public License.
 
 
@@ -10,9 +10,10 @@
 
 
 
 
 Note: If you have critics, patches or something else, please feel free to
 Note: If you have critics, patches or something else, please feel free to
-post a mail to the ngIRCd mailing list: <ngircd-ml@arthur.ath.cx> (please see
-<http://ngircd.barton.de/#ml> for details). Don't mail the contributors
-directly, if possible!
+post a mail to the ngIRCd mailing list: <ngircd-ml@arthur.barton.de> (please
+see <http://ngircd.barton.de/#ml> for details).
+
+Don't mail the people listed here directly, if possible!
 
 
 
 
 Main Authors
 Main Authors

+ 19 - 20
COPYING

@@ -1,12 +1,12 @@
-		    GNU GENERAL PUBLIC LICENSE
-		       Version 2, June 1991
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
 
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  Everyone is permitted to copy and distribute verbatim copies
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
  of this license document, but changing it is not allowed.
 
 
-			    Preamble
+                            Preamble
 
 
   The licenses for most software are designed to take away your
   The licenses for most software are designed to take away your
 freedom to share and change it.  By contrast, the GNU General Public
 freedom to share and change it.  By contrast, the GNU General Public
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users.  This
 General Public License applies to most of the Free Software
 General Public License applies to most of the Free Software
 Foundation's software and to any other program whose authors commit to
 Foundation's software and to any other program whose authors commit to
 using it.  (Some other Free Software Foundation software is covered by
 using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
+the GNU Lesser General Public License instead.)  You can apply it to
 your programs, too.
 your programs, too.
 
 
   When we speak of free software, we are referring to freedom, not
   When we speak of free software, we are referring to freedom, not
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
 
 
   The precise terms and conditions for copying, distribution and
   The precise terms and conditions for copying, distribution and
 modification follow.
 modification follow.
-
-		    GNU GENERAL PUBLIC LICENSE
+
+                    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
 
   0. This License applies to any program or other work which contains
   0. This License applies to any program or other work which contains
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
     License.  (Exception: if the Program itself is interactive but
     License.  (Exception: if the Program itself is interactive but
     does not normally print such an announcement, your work based on
     does not normally print such an announcement, your work based on
     the Program is not required to print an announcement.)
     the Program is not required to print an announcement.)
-
+
 These requirements apply to the modified work as a whole.  If
 These requirements apply to the modified work as a whole.  If
 identifiable sections of that work are not derived from the Program,
 identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
 and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
 access to copy the source code from the same place counts as
 access to copy the source code from the same place counts as
 distribution of the source code, even though third parties are not
 distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
 compelled to copy the source along with the object code.
-
+
   4. You may not copy, modify, sublicense, or distribute the Program
   4. You may not copy, modify, sublicense, or distribute the Program
 except as expressly provided under this License.  Any attempt
 except as expressly provided under this License.  Any attempt
 otherwise to copy, modify, sublicense or distribute the Program is
 otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
 
 
 This section is intended to make thoroughly clear what is believed to
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
 be a consequence of the rest of this License.
-
+
   8. If the distribution and/or use of the Program is restricted in
   8. If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
 certain countries either by patents or by copyrighted interfaces, the
 original copyright holder who places the Program under this License
 original copyright holder who places the Program under this License
@@ -255,7 +255,7 @@ 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 preserving the free status of all derivatives of our free software and
 of promoting the sharing and reuse of software generally.
 of promoting the sharing and reuse of software generally.
 
 
-			    NO WARRANTY
+                            NO WARRANTY
 
 
   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
@@ -277,9 +277,9 @@ 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
 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGES.
 POSSIBILITY OF SUCH DAMAGES.
 
 
-		     END OF TERMS AND CONDITIONS
-
-	    How to Apply These Terms to Your New Programs
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
 
 
   If you develop a new program, and you want it to be of the greatest
   If you develop a new program, and you want it to be of the greatest
 possible use to the public, the best way to achieve this is to make it
 possible use to the public, the best way to achieve this is to make it
@@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found.
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
     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
-
+    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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 
 Also add information on how to contact you by electronic and paper mail.
 Also add information on how to contact you by electronic and paper mail.
 
 
@@ -336,5 +335,5 @@ necessary.  Here is a sample; alter the names:
 This General Public License does not permit incorporating your program into
 This General Public License does not permit incorporating your program into
 proprietary programs.  If your program is a subroutine library, you may
 proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
 consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
+library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.
 Public License instead of this License.

+ 155 - 5
ChangeLog

@@ -2,13 +2,163 @@
                      ngIRCd - Next Generation IRC Server
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
                            http://ngircd.barton.de/
 
 
-               (c)2001-2011 Alexander Barton and Contributors.
+               (c)2001-2012 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
                    terms of the GNU General Public License.
 
 
                                -- ChangeLog --
                                -- ChangeLog --
 
 
 
 
+ngIRCd Release 19 (2012-02-29)
+
+  - Update build system: bump config.guess and config.sub files used by
+    GNU autoconf/automake to recent versions.
+  - Fix configuration file parser: don't accept "[SSL]" blocks in the
+    configuration file when no SSL support is built in ngIRCd.
+  - Fix building ngIRCd with old gcc versions (e. g. 2.7.2).
+  - Correctly re-open syslog logging after reading of configuration
+    file: Syslog logging has been initialized before reading the
+    configuraton, so ngIRCd always used the default facility and ignored
+    the "SyslogFacility" configuration option ...
+    Thanks to Patrik Schindler for reporting this issue!
+
+  ngIRCd 19~rc1 (2012-02-12)
+  - Enhance command limits for server links: the limit now is dependent
+    on the number of users connected in the network and higher while
+    servers are joining the network to make the login of servers faster.
+  - Log more information about server synchronization.
+  - Update preliminary ngIRCd protocol module for Anope 1.9.6, which now
+    is the only supported version.
+  - New numeric RPL_WHOISHOST_MSG(378), which returns the DNS hostname
+    (if available) and the IP address of a client in the WHOIS reply.
+    Only the user itself and local IRC operators get this numeric.
+  - Implement channel exception list (mode 'e'). This allows a channel
+    operator to define exception masks that allow users to join the
+    channel even when a "ban" would match and prevent them from joining:
+    the exception list (e) overrides the ban list (b).
+  - PRIVMSG and NOTICE: Handle nick!user@host masks case-insensitive.
+  - Implement user mode 'C': If the target user of a PRIVMSG or NOTICE
+    command has the user mode 'C' set, it is required that both sender
+    and receiver are on the same channel. This prevents private flooding
+    by completely unknown clients.
+  - New RPL_WHOISREGNICK_MSG(307) numeric in WHOIS command replies: it
+    indicates if a nick name is registered (if user mode 'R' set).
+  - Limit channel invite, ban, and exception lists to 50 entries and fix
+    duplicate check and error messages when adding already listed entries
+    or deleting no (longer) existing ones.
+  - Fix both ERR_SUMMONDISABLED(445) and ERR_USERSDISABLED(446) replies.
+  - MODE command: correctly return ERR_UNKNOWNMODE(472) numeric for
+    unknown channel modes, instead of ERR_UMODEUNKNOWNFLAG(501).
+  - ISUPPORT(005) numeric: add "O", "R", and "z" modes to "CHANMODES",
+    add "EXCEPTS=e" and "INVEX=I", add "MAXLIST=beI:50".
+  - Limit the number of list items in the reply of LIST (100), WHO (25),
+    WHOIS (10), and WHOWAS (25) commands.
+  - LIST command: compare pattern case insensitive.
+  - Limit the MODE command to handle a maximum number of 5 channel modes
+    that require an argument (+Ibkl) per call and report this number
+    in the ISUPPORT(005) numeric: "MODES=5".
+  - Fix handling of channel mode sequence with/without arguments.
+    For example, don't generate wrong error messages when handling
+    "MODE #chan +IIIIItn *!aa@b *!bb@c *!cc@d *!dd@e *!ee@f".
+  - When sending data on a connection, only try to get the type of
+    the client if there still is one assigned. This could trigger an
+    assertion and end the daemon in some error paths.
+  - Don't try to close already closed/invalid sockets to forked child
+    processes. This could potentially crash the daemon in some cases
+    with IDENT lookups enabled.
+  - WHOIS command: make sure that the reply ends with RPL_ENDOFWHOIS,
+    don't answer queries for IRC servers, make sure mask matching is
+    case-insensitive, and that RPL_ENDOFWHOIS numeric is sent with the
+    unmodified mask (like it has been received from the client).
+  - LINKS command: support <mask> parameter to limit the reply.
+  - Add 1 second penalty for every further target on PRIVMSG/NOTICE
+    commands: this reduces the possibility of flooding channels with
+    commands like "PRIVMSG/NOTICE #a,#n,#c,... :message" a little bit.
+    Problem noticed by Cahata, thanks!
+  - Display correct error message when "Server{UID|GID}" variabe in the
+    configuration file is invalid (not a number and no existing user).
+  - Update Copyright notices for 2012 :-)
+  - JOIN command: don't stop handling of channel lists when a single
+    channel cannot be joined (because of bad name, wrong key or channel
+    limit reached), but report an error and continue. And don't check
+    the channel limit and don't report with "too many channels" when
+    trying to join a channel that the client already is a member of.
+  - ISON command: reply with the correct upper-/lowercase nick names.
+  - New configuration option "PAMIsOptional": when set, clients not
+    sending a password are still allowed to connect: they won't become
+    "identified" and keep the "~" character prepended to their supplied
+    user name. See "man 5 ngircd.conf" for details.
+  - Fixed handling of WHO commands. This fixes two bugs: "WHO <nick>"
+    returned nothing at all if the user was "+i" (reported by Cahata,
+    thanks) and "WHO <nick|nickmask>" returned channel names instead
+    of "*" when the user was member of a (visible) channel.
+  - Fixed some spelling errors in documentation and code comments
+    (Thanks to Christoph Biedl).
+  - contrib/Debian/control: Update and complete "Build-Depends" and
+    update our Debian package descriptions with "official" ones.
+  - Fixed typo in two error messages.
+  - LUSERS reply: only count channels that are visible to the requesting
+    client, so the existence of secret channels is no longer revealed by
+    using LUSERS. Reported by Cahata, thanks!
+  - Unknown user and channel modes no longer stop the mode parser, but
+    are simply ignored. Therefore modes after the unknown one are now
+    handled. This is how ircd2.10/ircd2.11/ircd-seven behave, at least.
+    Reported by Cahata, thanks!
+  - README: Update list of implemented commands.
+  - Log better error messages when rejecting clients.
+  - Implement IRC commands "GLINE" and "KLINE" to ban users. G-Lines are
+    synchronized between server on peering, K-Lines are local only.
+    If you use "*!<user>@<host>" or "*!*@<host>" masks, these connections
+    are blocked even before the user is fully logged in (before PASS,
+    NICK, and USER commands have been processed) and before the child
+    processes for authentication are forked, so resource usage is smaller.
+  - Xcode: update project file for Xcode 4.2 and define HAVE_GAI_STRERROR
+    for Mac OS X Xcode builds.
+  - ./configure: Fix logic and quoting of poll() detection code: only use
+    poll() when poll.h exists as well.
+  - Suppress 'Can't create pre-defined channel: invalid name: ""' message.
+  - whois-test: handle local hostname = "localhost.localdomain" using the
+    pattern "localhost*" for valid local hostnames.
+  - sample-ngircd.conf: show correct default for "PAM" variable: The
+    default of "PAM" is "yes" when ngIRCd has been configured to use it,
+    so show the correct default value in the sample configuration file.
+    (Closes #119)
+  - Update GPL 2 license text to current version.
+  - Only close "unrelated" sockets in forked child processes: This fixes
+    the problem that ngIRCd can't do any IDENT lookups because of the
+    socket has already been closed in the child process.
+    The bug has been introduced starting with ngIRCd 17 ... :-(
+    (commit ID 6ebb31ab35e)
+  - Added doc/Modes.txt: document modes supported by ngIRCd.
+  - Implement user mode "R": indicates that the nick name of this user
+    is "registered". This mode isn't handled by ngIRCd itself, but must
+    be set and unset by IRC services like Anope.
+  - Implement channel mode "R": only registered users (having the user
+    mode "R" set) are allowed to join this channel.
+  - Test suite: bind to loopback (127.0.0.1) interface only.
+  - New 2nd message "Nickname too long" for error code 432.
+  - Xcode: Mac OS X config.h: support 10.5 as well as 10.6/10.7 SDK.
+  - Xcode: exclude more Xcode 4 specific directories in ".gitignore".
+  - Disconnect directly linked servers sending QUIT. Without this,
+    the server becomes removed from the network and the client list,
+    but the connection isn't shut down at all ...
+  - contrib/ngindent: detect "gindent" as GNU indent.
+  - Handle unknown user and channel modes: these modes are saved and
+    forwarded to other servers, but ignored otherwise.
+  - Handle channel user modes 'a', 'h', and 'q' from remote servers.
+    These channel user modes aren't used for anything at the moment,
+    but ngIRCd knows that these three modes are "channel user modes"
+    and not "channel modes", that is that these modes take an "nick name"
+    argument. Like unknown user and channel modes, these modes are saved
+    and forwarded to other servers, but ignored otherwise.
+  - Correctly inform clients when other servers change their user modes.
+    This is required for some services to work correctly.
+  - Test suite: make getpid.sh work even when run as root.
+  - Spoofed prefixes: close connection on non-server links only.
+    On server-links, spoofed prefixes can happen because of the
+    asynchronous nature of the IRC protocol. So don't break server-
+    links, only log a message and ignore the command. (Closes #113)
+
 ngIRCd Release 18 (2011-07-10)
 ngIRCd Release 18 (2011-07-10)
 
 
   - Update timestamp of ngircd(8) manual page.
   - Update timestamp of ngircd(8) manual page.
@@ -797,7 +947,7 @@ ngIRCd 0.6.0, 2002-12-24
     werden (beide Server versuchen sich dann gegenseitig zu connectieren).
     werden (beide Server versuchen sich dann gegenseitig zu connectieren).
   - Test-Suite und Dokumentation an A/UX angepasst.
   - Test-Suite und Dokumentation an A/UX angepasst.
   - unter HP-UX definiert das configure-Script nun _XOPEN_SOURCE_EXTENDED.
   - unter HP-UX definiert das configure-Script nun _XOPEN_SOURCE_EXTENDED.
-  - Server identifizieren sich nun mit asyncronen Passwoertern, d.h. das
+  - Server identifizieren sich nun mit asynchronen Passwoertern, d.h. das
     Passwort, welches A an B schickt, kann ein anderes sein als das, welches
     Passwort, welches A an B schickt, kann ein anderes sein als das, welches
     B als Antwort an A sendet. In der Konfig.-Datei, Abschnitt "Server",
     B als Antwort an A sendet. In der Konfig.-Datei, Abschnitt "Server",
     wurde "Password" dazu durch "MyPassword" und "PeerPassword" ersetzt.
     wurde "Password" dazu durch "MyPassword" und "PeerPassword" ersetzt.
@@ -927,7 +1077,7 @@ ngIRCd 0.5.0, 20.09.2002
   - Protokoll- und Server-ID bei PASS-Befehlen auf neues Format umgestellt;
   - Protokoll- und Server-ID bei PASS-Befehlen auf neues Format umgestellt;
     bei empfangenen PASS-Befehlen werden diese zudem nun auch ausgewertet.
     bei empfangenen PASS-Befehlen werden diese zudem nun auch ausgewertet.
     Die unterstuetzten Flags sind in doc/Protocol.txt beschrieben.
     Die unterstuetzten Flags sind in doc/Protocol.txt beschrieben.
-  - mit dem neuen Befehl CHANINFO syncronisieren Server, die das IRC+-
+  - mit dem neuen Befehl CHANINFO synchronisieren Server, die das IRC+-
     Protokoll unterstuetzen, Channel-Modes und Topics.
     Protokoll unterstuetzen, Channel-Modes und Topics.
   - neue Option "--disable-ircplus" fuer das configure-Script, um das
   - neue Option "--disable-ircplus" fuer das configure-Script, um das
     IRC+-Protokoll abzuschalten (per Default ist es aktiviert).
     IRC+-Protokoll abzuschalten (per Default ist es aktiviert).
@@ -1032,7 +1182,7 @@ ngIRCd 0.3.0, 02.03.2002
   - PRIVMSG beachtet nun die Channel-Modes "n" und "m".
   - PRIVMSG beachtet nun die Channel-Modes "n" und "m".
   - AWAY implementiert. PRIVMSG, MODE, USERHOST und WHOIS angepasst.
   - AWAY implementiert. PRIVMSG, MODE, USERHOST und WHOIS angepasst.
   - der ngIRCd unterstuetzt nun Channel-Topics (TOPIC-Befehl).
   - der ngIRCd unterstuetzt nun Channel-Topics (TOPIC-Befehl).
-  - ausgehende Server-Verbindungen werden nun asyncron connectiert und
+  - ausgehende Server-Verbindungen werden nun asynchron connectiert und
     blockieren nicht mehr den ganzen Server, wenn die Gegenseite nicht
     blockieren nicht mehr den ganzen Server, wenn die Gegenseite nicht
     erreicht werden kann (bis zum Timeout konnten Minuten vergehen!).
     erreicht werden kann (bis zum Timeout konnten Minuten vergehen!).
   - Wert der Konfigurations-Variable "ConnectRetry" wird besser beachtet.
   - Wert der Konfigurations-Variable "ConnectRetry" wird besser beachtet.
@@ -1111,7 +1261,7 @@ ngIRCd 0.0.2, 06.01.2002
   - NICK kann nun die Gross- und Kleinschreibung eines Nicks aendern.
   - NICK kann nun die Gross- und Kleinschreibung eines Nicks aendern.
   - ein Server-Passwort ist nun konfigurierbar.
   - ein Server-Passwort ist nun konfigurierbar.
   - neue Befehle: ERROR, SERVER, NJOIN (nur als "Fake"), SQUIT.
   - neue Befehle: ERROR, SERVER, NJOIN (nur als "Fake"), SQUIT.
-  - Asyncroner Resolver Hostname->IP implementiert.
+  - Asynchroner Resolver Hostname->IP implementiert.
   - Server-Links teilweise implementiert: bisher kann der ngIRCd jedoch
   - Server-Links teilweise implementiert: bisher kann der ngIRCd jedoch
     nur "leafed server" sein, d.h. keine "Client-Server" haben. Einige
     nur "leafed server" sein, d.h. keine "Client-Server" haben. Einige
     Befehle sind auch noch nicht (optimal) angepasst: PRIVMSG funktioniert
     Befehle sind auch noch nicht (optimal) angepasst: PRIVMSG funktioniert

+ 1 - 1
INSTALL

@@ -2,7 +2,7 @@
                      ngIRCd - Next Generation IRC Server
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
                            http://ngircd.barton.de/
 
 
-               (c)2001-2011 Alexander Barton and Contributors.
+               (c)2001-2012 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
                    terms of the GNU General Public License.
 
 

+ 72 - 6
NEWS

@@ -2,12 +2,81 @@
                      ngIRCd - Next Generation IRC Server
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
                            http://ngircd.barton.de/
 
 
-               (c)2001-2011 Alexander Barton and Contributors.
+               (c)2001-2012 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
                    terms of the GNU General Public License.
 
 
                                   -- NEWS --
                                   -- NEWS --
 
 
+
+ngIRCd Release 19 (2012-02-29)
+
+  ngIRCd 19~rc1 (2012-02-12)
+  - Update preliminary ngIRCd protocol module for Anope 1.9.6, which now
+    is the only supported version.
+  - New numeric RPL_WHOISHOST_MSG(378), which returns the DNS hostname
+    (if available) and the IP address of a client in the WHOIS reply.
+    Only the user itself and local IRC operators get this numeric.
+  - Implement channel exception list (mode 'e'). This allows a channel
+    operator to define exception masks that allow users to join the
+    channel even when a "ban" would match and prevent them from joining:
+    the exception list (e) overrides the ban list (b).
+  - Implement user mode 'C': If the target user of a PRIVMSG or NOTICE
+    command has the user mode 'C' set, it is required that both sender
+    and receiver are on the same channel. This prevents private flooding
+    by completely unknown clients.
+  - New RPL_WHOISREGNICK_MSG(307) numeric in WHOIS command replies: it
+    indicates if a nick name is registered (if user mode 'R' set).
+  - Limit channel invite, ban, and exception lists to 50 entries and fix
+    duplicate check and error messages when adding already listed entries
+    or deleting no (longer) existing ones.
+  - Limit the number of list items in the reply of LIST (100), WHO (25),
+    WHOIS (10), and WHOWAS (25) commands.
+  - Limit the MODE command to handle a maximum number of 5 channel modes
+    that require an argument (+Ibkl) per call and report this number
+    in the ISUPPORT(005) numeric: "MODES=5".
+  - LINKS command: support <mask> parameter to limit the reply.
+  - Add 1 second penalty for every further target on PRIVMSG/NOTICE
+    commands: this reduces the possibility of flooding channels with
+    commands like "PRIVMSG/NOTICE #a,#n,#c,... :message" a little bit.
+    Problem noticed by Cahata, thanks!
+  - New configuration option "PAMIsOptional": when set, clients not
+    sending a password are still allowed to connect: they won't become
+    "identified" and keep the "~" character prepended to their supplied
+    user name. See "man 5 ngircd.conf" for details.
+  - Fixed handling of WHO commands. This fixes two bugs: "WHO <nick>"
+    returned nothing at all if the user was "+i" (reported by Cahata,
+    thanks) and "WHO <nick|nickmask>" returned channel names instead
+    of "*" when the user was member of a (visible) channel.
+  - LUSERS reply: only count channels that are visible to the requesting
+    client, so the existence of secret channels is no longer revealed by
+    using LUSERS. Reported by Cahata, thanks!
+  - Unknown user and channel modes no longer stop the mode parser, but
+    are simply ignored. Therefore modes after the unknown one are now
+    handled. This is how ircd2.10/ircd2.11/ircd-seven behave, at least.
+    Reported by Cahata, thanks!
+  - Implement IRC commands "GLINE" and "KLINE" to ban users. G-Lines are
+    synchronized between server on peering, K-Lines are local only.
+    If you use "*!<user>@<host>" or "*!*@<host>" masks, these connections
+    are blocked even before the user is fully logged in (before PASS,
+    NICK, and USER commands have been processed) and before the child
+    processes for authentication are forked, so resource usage is smaller.
+  - Added doc/Modes.txt: document modes supported by ngIRCd.
+  - Implement user mode "R": indicates that the nick name of this user
+    is "registered". This mode isn't handled by ngIRCd itself, but must
+    be set and unset by IRC services like Anope.
+  - Implement channel mode "R": only registered users (having the user
+    mode "R" set) are allowed to join this channel.
+  - Test suite: bind to loopback (127.0.0.1) interface only.
+  - Handle unknown user and channel modes: these modes are saved and
+    forwarded to other servers, but ignored otherwise.
+  - Handle channel user modes 'a', 'h', and 'q' from remote servers.
+    These channel user modes aren't used for anything at the moment,
+    but ngIRCd knows that these three modes are "channel user modes"
+    and not "channel modes", that is that these modes take an "nick name"
+    argument. Like unknown user and channel modes, these modes are saved
+    and forwarded to other servers, but ignored otherwise.
+
 ngIRCd Release 18 (2011-07-10)
 ngIRCd Release 18 (2011-07-10)
 
 
   - Add preliminary ngIRCd protocol module for Anope 1.9 to contrib/Anope/.
   - Add preliminary ngIRCd protocol module for Anope 1.9 to contrib/Anope/.
@@ -351,7 +420,7 @@ ngIRCd 0.6.0, 2002-12-24
     ausgehende Verbindung zu diesem auufzubauen. Dadurch kann nun auf beiden
     ausgehende Verbindung zu diesem auufzubauen. Dadurch kann nun auf beiden
     Servern in der Konfiguration ein Port fuer den Connect konfiguriert
     Servern in der Konfiguration ein Port fuer den Connect konfiguriert
     werden (beide Server versuchen sich dann gegenseitig zu connectieren).
     werden (beide Server versuchen sich dann gegenseitig zu connectieren).
-  - Server identifizieren sich nun mit asyncronen Passwoertern, d.h. das
+  - Server identifizieren sich nun mit asynchronen Passwoertern, d.h. das
     Passwort, welches A an B schickt, kann ein anderes sein als das, welches
     Passwort, welches A an B schickt, kann ein anderes sein als das, welches
     B als Antwort an A sendet. In der Konfig.-Datei, Abschnitt "Server",
     B als Antwort an A sendet. In der Konfig.-Datei, Abschnitt "Server",
     wurde "Password" dazu durch "MyPassword" und "PeerPassword" ersetzt.
     wurde "Password" dazu durch "MyPassword" und "PeerPassword" ersetzt.
@@ -377,7 +446,7 @@ ngIRCd 0.5.0, 20.09.2002
     Konfiguration "sample-ngircd.conf") und bleiben auch dann bestehen,
     Konfiguration "sample-ngircd.conf") und bleiben auch dann bestehen,
     wenn kein User mehr im Channel ist.
     wenn kein User mehr im Channel ist.
   - neue IRC-Befehle: KICK, INVITE, ADMIN, CHANINFO; LIST wurde erweitert.
   - neue IRC-Befehle: KICK, INVITE, ADMIN, CHANINFO; LIST wurde erweitert.
-    Mit dem neuen Befehl CHANINFO syncronisieren Server, die das IRC+-
+    Mit dem neuen Befehl CHANINFO synchronisieren Server, die das IRC+-
     Protokoll unterstuetzen, Channel-Modes und Topics. Fuer den ADMIN-Befehl
     Protokoll unterstuetzen, Channel-Modes und Topics. Fuer den ADMIN-Befehl
     gibt es neue Konfigurationsoptionen (Sektion "Global"): "AdminInfo1",
     gibt es neue Konfigurationsoptionen (Sektion "Global"): "AdminInfo1",
     "AdminInfo2" und "AdminEMail".
     "AdminInfo2" und "AdminEMail".
@@ -463,7 +532,3 @@ ngIRCd 0.0.2, 06.01.2002
 ngIRCd 0.0.1, 31.12.2001
 ngIRCd 0.0.1, 31.12.2001
 
 
   - erste oeffentliche Version von ngIRCd als "public preview" :-)
   - erste oeffentliche Version von ngIRCd als "public preview" :-)
-
-
-$Id: NEWS,v 1.88 2008/02/26 22:05:42 fw Exp $

+ 21 - 17
README

@@ -2,7 +2,7 @@
                      ngIRCd - Next Generation IRC Server
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
                            http://ngircd.barton.de/
 
 
-               (c)2001-2011 Alexander Barton and Contributors.
+               (c)2001-2012 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
                    terms of the GNU General Public License.
 
 
@@ -14,9 +14,10 @@ I. Introduction
 
 
 ngIRCd is an Open Source server for the Internet Relay Chat (IRC), which
 ngIRCd is an Open Source server for the Internet Relay Chat (IRC), which
 is developed and published under the terms of the GNU General Public
 is developed and published under the terms of the GNU General Public
-Licence (URL: http://www.gnu.org/licenses/gpl.html). ngIRCd means "next
-generation IRC daemon", it's written from scratch and not deduced from the
-"grandfather of IRC daemons", the daemon of the IRCNet.
+Licence, see the file COPYING for details. ngIRCd means "next generation
+IRC daemon" (which is a little bit exaggerated, "lightweight Internet Relay
+Chat server" would be better), it's written from scratch and not deduced
+from the "grandfather of IRC daemons", the daemon of the IRCNet.
 
 
 Please see the INSTALL document for installation and upgrade information!
 Please see the INSTALL document for installation and upgrade information!
 
 
@@ -33,22 +34,24 @@ used in real IRC networks.
 
 
 Implemented IRC-commands are:
 Implemented IRC-commands are:
 
 
-ADMIN, AWAY, CHANINFO, CONNECT, DIE, DISCONNECT, ERROR, HELP, INFO, INVITE,
-ISON, JOIN, KICK, KILL, LINKS, LIST, LUSERS, MODE, MOTD, NAMES, NICK, NJOIN,
-NOTICE, OPER, PART, PASS, PING, PONG, PRIVMSG, QUIT, REHASH, RESTART, SERVER,
-SERVICE, SERVLIST, SQUERY, SQUIT,  STATS, SUMMON, TIME, TOPIC, TRACE, USER,
-USERHOST, USERS, VERSION, WALLOPS, WEBIRC, WHO, WHOIS, WHOWAS.
-
+ADMIN, AWAY, CHANINFO, CONNECT, DIE, DISCONNECT, ERROR, GLINE, HELP, INFO,
+INVITE, ISON, JOIN, KICK, KILL, KLINE, LINKS, LIST, LUSERS, MODE, MOTD,
+NAMES, NICK, NJOIN, NOTICE, OPER, PART, PASS, PING, PONG, PRIVMSG, QUIT,
+REHASH, RESTART, SERVER, SERVICE, SERVLIST, SQUERY, SQUIT, STATS, SUMMON,
+TIME, TOPIC, TRACE, USER, USERHOST, USERS, VERSION, WALLOPS, WEBIRC, WHO,
+WHOIS, WHOWAS.
 
 
 III. Features (or: why use ngIRCd?)
 III. Features (or: why use ngIRCd?)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 
-- no problems with servers which have dynamic IP addresses
-- simple, easy understandable configuration file,
-- freely published open-source C source code,
-- ngIRCd will be developed on in the future.
+- well arranged (lean) configuration file
+- simple to build/install, configure and maintain
+- supports IPv6 and SSL
+- no problems with servers that have dynamic IP addresses
+- freely available, modern, portable and tidy C-source
 - wide field of supported platforms, including AIX, A/UX, FreeBSD, HP-UX,
 - wide field of supported platforms, including AIX, A/UX, FreeBSD, HP-UX,
   IRIX, Linux, Mac OS X, NetBSD, OpenBSD, Solaris, and Windows with Cygwin.
   IRIX, Linux, Mac OS X, NetBSD, OpenBSD, Solaris, and Windows with Cygwin.
+- ngIRCd is being actively developed since 2001.
 
 
 
 
 IV. Documentation
 IV. Documentation
@@ -68,7 +71,7 @@ releases there.
 If you are interested in the latest development versions (which are not
 If you are interested in the latest development versions (which are not
 always stable), then please read the section about "GIT" on the homepage and
 always stable), then please read the section about "GIT" on the homepage and
 the file "doc/GIT.txt" which describes the use of GIT, the version control
 the file "doc/GIT.txt" which describes the use of GIT, the version control
-system used by ngIRCd (homepage: http://git.or.cz/).
+system used by ngIRCd (homepage: http://git-scm.com/).
 
 
 
 
 VI. Bugs
 VI. Bugs
@@ -82,5 +85,6 @@ them at the following URL:
 There you can read about known bugs and limitations, too.
 There you can read about known bugs and limitations, too.
 
 
 If you have critics, patches or something else, please feel free to post a
 If you have critics, patches or something else, please feel free to post a
-mail to the ngIRCd mailing list: <ngircd-ml@arthur.ath.cx> (please see
-<http://ngircd.barton.de/support.php#ml> for details).
+mail to the ngIRCd mailing list: <ngircd-ml@arthur.barton.de> (please see
+<http://ngircd.barton.de/support.php#ml> for details) or join the ngIRCd
+IRC channel: <irc://irc.barton.de/ngircd>.

+ 247 - 243
config.guess

@@ -1,10 +1,10 @@
 #! /bin/sh
 #! /bin/sh
 # Attempt to guess a canonical system name.
 # Attempt to guess a canonical system name.
 #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
 #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
-#   Free Software Foundation, Inc.
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+#   2011, 2012 Free Software Foundation, Inc.
 
 
-timestamp='2008-01-23'
+timestamp='2012-02-10'
 
 
 # This file is free software; you can redistribute it and/or modify it
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
 # under the terms of the GNU General Public License as published by
@@ -17,9 +17,7 @@ timestamp='2008-01-23'
 # General Public License for more details.
 # General Public License for more details.
 #
 #
 # You should have received a copy of the GNU General Public License
 # 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., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
 #
 #
 # As a special exception to the GNU General Public License, if you
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
 # distribute this file as part of a program that contains a
@@ -27,16 +25,16 @@ timestamp='2008-01-23'
 # the same distribution terms that you use for the rest of that program.
 # the same distribution terms that you use for the rest of that program.
 
 
 
 
-# Originally written by Per Bothner <per@bothner.com>.
-# Please send patches to <config-patches@gnu.org>.  Submit a context
-# diff and a properly formatted ChangeLog entry.
+# Originally written by Per Bothner.  Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
 #
 #
 # This script attempts to guess a canonical system name similar to
 # This script attempts to guess a canonical system name similar to
 # config.sub.  If it succeeds, it prints the system name on stdout, and
 # config.sub.  If it succeeds, it prints the system name on stdout, and
 # exits with 0.  Otherwise, it exits with 1.
 # exits with 0.  Otherwise, it exits with 1.
 #
 #
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
 
 
 me=`echo "$0" | sed -e 's,.*/,,'`
 me=`echo "$0" | sed -e 's,.*/,,'`
 
 
@@ -56,8 +54,9 @@ version="\
 GNU config.guess ($timestamp)
 GNU config.guess ($timestamp)
 
 
 Originally written by Per Bothner.
 Originally written by Per Bothner.
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+Free Software Foundation, Inc.
 
 
 This is free software; see the source for copying conditions.  There is NO
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -144,7 +143,7 @@ UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
 case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
     *:NetBSD:*:*)
     *:NetBSD:*:*)
 	# NetBSD (nbsd) targets should (where applicable) match one or
 	# NetBSD (nbsd) targets should (where applicable) match one or
-	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
 	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
 	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
 	# switched to ELF, *-*-netbsd* would select the old
 	# switched to ELF, *-*-netbsd* would select the old
 	# object file format.  This provides both forward
 	# object file format.  This provides both forward
@@ -170,7 +169,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
 	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
 		eval $set_cc_for_build
 		eval $set_cc_for_build
 		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
 		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
-			| grep __ELF__ >/dev/null
+			| grep -q __ELF__
 		then
 		then
 		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
 		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
 		    # Return netbsd for either.  FIX?
 		    # Return netbsd for either.  FIX?
@@ -180,7 +179,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 		fi
 		fi
 		;;
 		;;
 	    *)
 	    *)
-	        os=netbsd
+		os=netbsd
 		;;
 		;;
 	esac
 	esac
 	# The OS release
 	# The OS release
@@ -223,7 +222,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
 		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
 		;;
 		;;
 	*5.*)
 	*5.*)
-	        UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
 		;;
 		;;
 	esac
 	esac
 	# According to Compaq, /usr/sbin/psrinfo has been available on
 	# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -269,7 +268,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 	# A Xn.n version is an unreleased experimental baselevel.
 	# A Xn.n version is an unreleased experimental baselevel.
 	# 1.2 uses "1.2" for uname -r.
 	# 1.2 uses "1.2" for uname -r.
 	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
 	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-	exit ;;
+	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+	exitcode=$?
+	trap '' 0
+	exit $exitcode ;;
     Alpha\ *:Windows_NT*:*)
     Alpha\ *:Windows_NT*:*)
 	# How do we know it's Interix rather than the generic POSIX subsystem?
 	# How do we know it's Interix rather than the generic POSIX subsystem?
 	# Should we change UNAME_MACHINE based on the output of uname instead
 	# Should we change UNAME_MACHINE based on the output of uname instead
@@ -295,7 +297,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 	echo s390-ibm-zvmoe
 	echo s390-ibm-zvmoe
 	exit ;;
 	exit ;;
     *:OS400:*:*)
     *:OS400:*:*)
-        echo powerpc-ibm-os400
+	echo powerpc-ibm-os400
 	exit ;;
 	exit ;;
     arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
     arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
 	echo arm-acorn-riscix${UNAME_RELEASE}
 	echo arm-acorn-riscix${UNAME_RELEASE}
@@ -324,14 +326,33 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
 	case `/usr/bin/uname -p` in
 	case `/usr/bin/uname -p` in
 	    sparc) echo sparc-icl-nx7; exit ;;
 	    sparc) echo sparc-icl-nx7; exit ;;
 	esac ;;
 	esac ;;
+    s390x:SunOS:*:*)
+	echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit ;;
     sun4H:SunOS:5.*:*)
     sun4H:SunOS:5.*:*)
 	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
 	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
 	exit ;;
 	exit ;;
     sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
     sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
 	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
 	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
 	exit ;;
 	exit ;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+	echo i386-pc-auroraux${UNAME_RELEASE}
+	exit ;;
     i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
     i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
-	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	eval $set_cc_for_build
+	SUN_ARCH="i386"
+	# If there is a compiler, see if it is configured for 64-bit objects.
+	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+	# This test works for both compilers.
+	if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+		(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		grep IS_64BIT_ARCH >/dev/null
+	    then
+		SUN_ARCH="x86_64"
+	    fi
+	fi
+	echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
 	exit ;;
 	exit ;;
     sun4*:SunOS:6*:*)
     sun4*:SunOS:6*:*)
 	# According to config.sub, this is the proper way to canonicalize
 	# According to config.sub, this is the proper way to canonicalize
@@ -375,23 +396,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
     # MiNT.  But MiNT is downward compatible to TOS, so this should
     # MiNT.  But MiNT is downward compatible to TOS, so this should
     # be no problem.
     # be no problem.
     atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
     atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
-        echo m68k-atari-mint${UNAME_RELEASE}
+	echo m68k-atari-mint${UNAME_RELEASE}
 	exit ;;
 	exit ;;
     atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
     atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
 	echo m68k-atari-mint${UNAME_RELEASE}
 	echo m68k-atari-mint${UNAME_RELEASE}
-        exit ;;
+	exit ;;
     *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
     *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
-        echo m68k-atari-mint${UNAME_RELEASE}
+	echo m68k-atari-mint${UNAME_RELEASE}
 	exit ;;
 	exit ;;
     milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
     milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
-        echo m68k-milan-mint${UNAME_RELEASE}
-        exit ;;
+	echo m68k-milan-mint${UNAME_RELEASE}
+	exit ;;
     hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
     hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
-        echo m68k-hades-mint${UNAME_RELEASE}
-        exit ;;
+	echo m68k-hades-mint${UNAME_RELEASE}
+	exit ;;
     *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
     *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
-        echo m68k-unknown-mint${UNAME_RELEASE}
-        exit ;;
+	echo m68k-unknown-mint${UNAME_RELEASE}
+	exit ;;
     m68k:machten:*:*)
     m68k:machten:*:*)
 	echo m68k-apple-machten${UNAME_RELEASE}
 	echo m68k-apple-machten${UNAME_RELEASE}
 	exit ;;
 	exit ;;
@@ -461,8 +482,8 @@ EOF
 	echo m88k-motorola-sysv3
 	echo m88k-motorola-sysv3
 	exit ;;
 	exit ;;
     AViiON:dgux:*:*)
     AViiON:dgux:*:*)
-        # DG/UX returns AViiON for all architectures
-        UNAME_PROCESSOR=`/usr/bin/uname -p`
+	# DG/UX returns AViiON for all architectures
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
 	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
 	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
 	then
 	then
 	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
 	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
@@ -475,7 +496,7 @@ EOF
 	else
 	else
 	    echo i586-dg-dgux${UNAME_RELEASE}
 	    echo i586-dg-dgux${UNAME_RELEASE}
 	fi
 	fi
- 	exit ;;
+	exit ;;
     M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
     M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
 	echo m88k-dolphin-sysv3
 	echo m88k-dolphin-sysv3
 	exit ;;
 	exit ;;
@@ -532,7 +553,7 @@ EOF
 		echo rs6000-ibm-aix3.2
 		echo rs6000-ibm-aix3.2
 	fi
 	fi
 	exit ;;
 	exit ;;
-    *:AIX:*:[456])
+    *:AIX:*:[4567])
 	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
 	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
 	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
 	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
 		IBM_ARCH=rs6000
 		IBM_ARCH=rs6000
@@ -575,52 +596,52 @@ EOF
 	    9000/[678][0-9][0-9])
 	    9000/[678][0-9][0-9])
 		if [ -x /usr/bin/getconf ]; then
 		if [ -x /usr/bin/getconf ]; then
 		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
 		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
-                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
-                    case "${sc_cpu_version}" in
-                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
-                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
-                      532)                      # CPU_PA_RISC2_0
-                        case "${sc_kernel_bits}" in
-                          32) HP_ARCH="hppa2.0n" ;;
-                          64) HP_ARCH="hppa2.0w" ;;
+		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+		    case "${sc_cpu_version}" in
+		      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+		      532)                      # CPU_PA_RISC2_0
+			case "${sc_kernel_bits}" in
+			  32) HP_ARCH="hppa2.0n" ;;
+			  64) HP_ARCH="hppa2.0w" ;;
 			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
 			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
-                        esac ;;
-                    esac
+			esac ;;
+		    esac
 		fi
 		fi
 		if [ "${HP_ARCH}" = "" ]; then
 		if [ "${HP_ARCH}" = "" ]; then
 		    eval $set_cc_for_build
 		    eval $set_cc_for_build
-		    sed 's/^              //' << EOF >$dummy.c
+		    sed 's/^		//' << EOF >$dummy.c
 
 
-              #define _HPUX_SOURCE
-              #include <stdlib.h>
-              #include <unistd.h>
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
 
 
-              int main ()
-              {
-              #if defined(_SC_KERNEL_BITS)
-                  long bits = sysconf(_SC_KERNEL_BITS);
-              #endif
-                  long cpu  = sysconf (_SC_CPU_VERSION);
+		int main ()
+		{
+		#if defined(_SC_KERNEL_BITS)
+		    long bits = sysconf(_SC_KERNEL_BITS);
+		#endif
+		    long cpu  = sysconf (_SC_CPU_VERSION);
 
 
-                  switch (cpu)
-              	{
-              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
-              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
-              	case CPU_PA_RISC2_0:
-              #if defined(_SC_KERNEL_BITS)
-              	    switch (bits)
-              		{
-              		case 64: puts ("hppa2.0w"); break;
-              		case 32: puts ("hppa2.0n"); break;
-              		default: puts ("hppa2.0"); break;
-              		} break;
-              #else  /* !defined(_SC_KERNEL_BITS) */
-              	    puts ("hppa2.0"); break;
-              #endif
-              	default: puts ("hppa1.0"); break;
-              	}
-                  exit (0);
-              }
+		    switch (cpu)
+			{
+			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+			case CPU_PA_RISC2_0:
+		#if defined(_SC_KERNEL_BITS)
+			    switch (bits)
+				{
+				case 64: puts ("hppa2.0w"); break;
+				case 32: puts ("hppa2.0n"); break;
+				default: puts ("hppa2.0"); break;
+				} break;
+		#else  /* !defined(_SC_KERNEL_BITS) */
+			    puts ("hppa2.0"); break;
+		#endif
+			default: puts ("hppa1.0"); break;
+			}
+		    exit (0);
+		}
 EOF
 EOF
 		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
 		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
 		    test -z "$HP_ARCH" && HP_ARCH=hppa
 		    test -z "$HP_ARCH" && HP_ARCH=hppa
@@ -640,7 +661,7 @@ EOF
 	    # => hppa64-hp-hpux11.23
 	    # => hppa64-hp-hpux11.23
 
 
 	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
 	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
-		grep __LP64__ >/dev/null
+		grep -q __LP64__
 	    then
 	    then
 		HP_ARCH="hppa2.0w"
 		HP_ARCH="hppa2.0w"
 	    else
 	    else
@@ -711,22 +732,22 @@ EOF
 	exit ;;
 	exit ;;
     C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
     C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
 	echo c1-convex-bsd
 	echo c1-convex-bsd
-        exit ;;
+	exit ;;
     C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
     C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
 	if getsysinfo -f scalar_acc
 	if getsysinfo -f scalar_acc
 	then echo c32-convex-bsd
 	then echo c32-convex-bsd
 	else echo c2-convex-bsd
 	else echo c2-convex-bsd
 	fi
 	fi
-        exit ;;
+	exit ;;
     C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
     C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
 	echo c34-convex-bsd
 	echo c34-convex-bsd
-        exit ;;
+	exit ;;
     C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
     C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
 	echo c38-convex-bsd
 	echo c38-convex-bsd
-        exit ;;
+	exit ;;
     C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
     C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
 	echo c4-convex-bsd
 	echo c4-convex-bsd
-        exit ;;
+	exit ;;
     CRAY*Y-MP:*:*:*)
     CRAY*Y-MP:*:*:*)
 	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
 	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
 	exit ;;
 	exit ;;
@@ -750,14 +771,14 @@ EOF
 	exit ;;
 	exit ;;
     F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
     F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
 	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
 	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
-        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
-        exit ;;
+	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+	FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+	echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	exit ;;
     5000:UNIX_System_V:4.*:*)
     5000:UNIX_System_V:4.*:*)
-        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
-        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+	FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+	FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+	echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
 	exit ;;
 	exit ;;
     i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
     i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
 	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
 	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
@@ -769,13 +790,12 @@ EOF
 	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
 	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
 	exit ;;
 	exit ;;
     *:FreeBSD:*:*)
     *:FreeBSD:*:*)
-	case ${UNAME_MACHINE} in
-	    pc98)
-		echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	case ${UNAME_PROCESSOR} in
 	    amd64)
 	    amd64)
 		echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
 		echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
 	    *)
 	    *)
-		echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+		echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
 	esac
 	esac
 	exit ;;
 	exit ;;
     i*:CYGWIN*:*)
     i*:CYGWIN*:*)
@@ -784,19 +804,22 @@ EOF
     *:MINGW*:*)
     *:MINGW*:*)
 	echo ${UNAME_MACHINE}-pc-mingw32
 	echo ${UNAME_MACHINE}-pc-mingw32
 	exit ;;
 	exit ;;
+    i*:MSYS*:*)
+	echo ${UNAME_MACHINE}-pc-msys
+	exit ;;
     i*:windows32*:*)
     i*:windows32*:*)
-    	# uname -m includes "-pc" on this system.
-    	echo ${UNAME_MACHINE}-mingw32
+	# uname -m includes "-pc" on this system.
+	echo ${UNAME_MACHINE}-mingw32
 	exit ;;
 	exit ;;
     i*:PW*:*)
     i*:PW*:*)
 	echo ${UNAME_MACHINE}-pc-pw32
 	echo ${UNAME_MACHINE}-pc-pw32
 	exit ;;
 	exit ;;
-    *:Interix*:[3456]*)
-    	case ${UNAME_MACHINE} in
+    *:Interix*:*)
+	case ${UNAME_MACHINE} in
 	    x86)
 	    x86)
 		echo i586-pc-interix${UNAME_RELEASE}
 		echo i586-pc-interix${UNAME_RELEASE}
 		exit ;;
 		exit ;;
-	    EM64T | authenticamd)
+	    authenticamd | genuineintel | EM64T)
 		echo x86_64-unknown-interix${UNAME_RELEASE}
 		echo x86_64-unknown-interix${UNAME_RELEASE}
 		exit ;;
 		exit ;;
 	    IA64)
 	    IA64)
@@ -806,6 +829,9 @@ EOF
     [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
     [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
 	echo i${UNAME_MACHINE}-pc-mks
 	echo i${UNAME_MACHINE}-pc-mks
 	exit ;;
 	exit ;;
+    8664:Windows_NT:*)
+	echo x86_64-pc-mks
+	exit ;;
     i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
     i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
 	# How do we know it's Interix rather than the generic POSIX subsystem?
 	# How do we know it's Interix rather than the generic POSIX subsystem?
 	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
 	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
@@ -835,6 +861,27 @@ EOF
     i*86:Minix:*:*)
     i*86:Minix:*:*)
 	echo ${UNAME_MACHINE}-pc-minix
 	echo ${UNAME_MACHINE}-pc-minix
 	exit ;;
 	exit ;;
+    aarch64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    aarch64_be:Linux:*:*)
+	UNAME_MACHINE=aarch64_be
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+	esac
+	objdump --private-headers /bin/sh | grep -q ld.so.1
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit ;;
     arm*:Linux:*:*)
     arm*:Linux:*:*)
 	eval $set_cc_for_build
 	eval $set_cc_for_build
 	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
 	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
@@ -842,20 +889,40 @@ EOF
 	then
 	then
 	    echo ${UNAME_MACHINE}-unknown-linux-gnu
 	    echo ${UNAME_MACHINE}-unknown-linux-gnu
 	else
 	else
-	    echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+		| grep -q __ARM_PCS_VFP
+	    then
+		echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+	    else
+		echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
+	    fi
 	fi
 	fi
 	exit ;;
 	exit ;;
     avr32*:Linux:*:*)
     avr32*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	exit ;;
 	exit ;;
     cris:Linux:*:*)
     cris:Linux:*:*)
-	echo cris-axis-linux-gnu
+	echo ${UNAME_MACHINE}-axis-linux-gnu
 	exit ;;
 	exit ;;
     crisv32:Linux:*:*)
     crisv32:Linux:*:*)
-	echo crisv32-axis-linux-gnu
+	echo ${UNAME_MACHINE}-axis-linux-gnu
 	exit ;;
 	exit ;;
     frv:Linux:*:*)
     frv:Linux:*:*)
-    	echo frv-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    hexagon:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
+    i*86:Linux:*:*)
+	LIBC=gnu
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#ifdef __dietlibc__
+	LIBC=dietlibc
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+	echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
 	exit ;;
 	exit ;;
     ia64:Linux:*:*)
     ia64:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -866,74 +933,33 @@ EOF
     m68*:Linux:*:*)
     m68*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	exit ;;
 	exit ;;
-    mips:Linux:*:*)
+    mips:Linux:*:* | mips64:Linux:*:*)
 	eval $set_cc_for_build
 	eval $set_cc_for_build
 	sed 's/^	//' << EOF >$dummy.c
 	sed 's/^	//' << EOF >$dummy.c
 	#undef CPU
 	#undef CPU
-	#undef mips
-	#undef mipsel
+	#undef ${UNAME_MACHINE}
+	#undef ${UNAME_MACHINE}el
 	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
 	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
-	CPU=mipsel
+	CPU=${UNAME_MACHINE}el
 	#else
 	#else
 	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
 	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
-	CPU=mips
+	CPU=${UNAME_MACHINE}
 	#else
 	#else
 	CPU=
 	CPU=
 	#endif
 	#endif
 	#endif
 	#endif
 EOF
 EOF
-	eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
-	    /^CPU/{
-		s: ::g
-		p
-	    }'`"
-	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
-	;;
-    mips64:Linux:*:*)
-	eval $set_cc_for_build
-	sed 's/^	//' << EOF >$dummy.c
-	#undef CPU
-	#undef mips64
-	#undef mips64el
-	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
-	CPU=mips64el
-	#else
-	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
-	CPU=mips64
-	#else
-	CPU=
-	#endif
-	#endif
-EOF
-	eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
-	    /^CPU/{
-		s: ::g
-		p
-	    }'`"
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
 	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
 	test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
 	;;
 	;;
     or32:Linux:*:*)
     or32:Linux:*:*)
-	echo or32-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	exit ;;
 	exit ;;
-    ppc:Linux:*:*)
-	echo powerpc-unknown-linux-gnu
+    padre:Linux:*:*)
+	echo sparc-unknown-linux-gnu
 	exit ;;
 	exit ;;
-    ppc64:Linux:*:*)
-	echo powerpc64-unknown-linux-gnu
-	exit ;;
-    alpha:Linux:*:*)
-	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
-	  EV5)   UNAME_MACHINE=alphaev5 ;;
-	  EV56)  UNAME_MACHINE=alphaev56 ;;
-	  PCA56) UNAME_MACHINE=alphapca56 ;;
-	  PCA57) UNAME_MACHINE=alphapca56 ;;
-	  EV6)   UNAME_MACHINE=alphaev6 ;;
-	  EV67)  UNAME_MACHINE=alphaev67 ;;
-	  EV68*) UNAME_MACHINE=alphaev68 ;;
-        esac
-	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
-	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
-	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
 	exit ;;
 	exit ;;
     parisc:Linux:*:* | hppa:Linux:*:*)
     parisc:Linux:*:* | hppa:Linux:*:*)
 	# Look for CPU level
 	# Look for CPU level
@@ -943,14 +969,17 @@ EOF
 	  *)    echo hppa-unknown-linux-gnu ;;
 	  *)    echo hppa-unknown-linux-gnu ;;
 	esac
 	esac
 	exit ;;
 	exit ;;
-    parisc64:Linux:*:* | hppa64:Linux:*:*)
-	echo hppa64-unknown-linux-gnu
+    ppc64:Linux:*:*)
+	echo powerpc64-unknown-linux-gnu
+	exit ;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
 	exit ;;
 	exit ;;
     s390:Linux:*:* | s390x:Linux:*:*)
     s390:Linux:*:* | s390x:Linux:*:*)
 	echo ${UNAME_MACHINE}-ibm-linux
 	echo ${UNAME_MACHINE}-ibm-linux
 	exit ;;
 	exit ;;
     sh64*:Linux:*:*)
     sh64*:Linux:*:*)
-    	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	exit ;;
 	exit ;;
     sh*:Linux:*:*)
     sh*:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -958,78 +987,18 @@ EOF
     sparc:Linux:*:* | sparc64:Linux:*:*)
     sparc:Linux:*:* | sparc64:Linux:*:*)
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	exit ;;
 	exit ;;
+    tile*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit ;;
     vax:Linux:*:*)
     vax:Linux:*:*)
 	echo ${UNAME_MACHINE}-dec-linux-gnu
 	echo ${UNAME_MACHINE}-dec-linux-gnu
 	exit ;;
 	exit ;;
     x86_64:Linux:*:*)
     x86_64:Linux:*:*)
-	echo x86_64-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	exit ;;
 	exit ;;
     xtensa*:Linux:*:*)
     xtensa*:Linux:*:*)
-    	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
 	exit ;;
 	exit ;;
-    i*86:Linux:*:*)
-	# The BFD linker knows what the default object file format is, so
-	# first see if it will tell us. cd to the root directory to prevent
-	# problems with other programs or directories called `ld' in the path.
-	# Set LC_ALL=C to ensure ld outputs messages in English.
-	ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
-			 | sed -ne '/supported targets:/!d
-				    s/[ 	][ 	]*/ /g
-				    s/.*supported targets: *//
-				    s/ .*//
-				    p'`
-        case "$ld_supported_targets" in
-	  elf32-i386)
-		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
-		;;
-	  a.out-i386-linux)
-		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
-		exit ;;
-	  coff-i386)
-		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
-		exit ;;
-	  "")
-		# Either a pre-BFD a.out linker (linux-gnuoldld) or
-		# one that does not give us useful --help.
-		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
-		exit ;;
-	esac
-	# Determine whether the default compiler is a.out or elf
-	eval $set_cc_for_build
-	sed 's/^	//' << EOF >$dummy.c
-	#include <features.h>
-	#ifdef __ELF__
-	# ifdef __GLIBC__
-	#  if __GLIBC__ >= 2
-	LIBC=gnu
-	#  else
-	LIBC=gnulibc1
-	#  endif
-	# else
-	LIBC=gnulibc1
-	# endif
-	#else
-	#if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-	LIBC=gnu
-	#else
-	LIBC=gnuaout
-	#endif
-	#endif
-	#ifdef __dietlibc__
-	LIBC=dietlibc
-	#endif
-EOF
-	eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
-	    /^LIBC/{
-		s: ::g
-		p
-	    }'`"
-	test x"${LIBC}" != x && {
-		echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
-		exit
-	}
-	test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
-	;;
     i*86:DYNIX/ptx:4*:*)
     i*86:DYNIX/ptx:4*:*)
 	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
 	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
 	# earlier versions are messed up and put the nodename in both
 	# earlier versions are messed up and put the nodename in both
@@ -1037,11 +1006,11 @@ EOF
 	echo i386-sequent-sysv4
 	echo i386-sequent-sysv4
 	exit ;;
 	exit ;;
     i*86:UNIX_SV:4.2MP:2.*)
     i*86:UNIX_SV:4.2MP:2.*)
-        # Unixware is an offshoot of SVR4, but it has its own version
-        # number series starting with 2...
-        # I am not positive that other SVR4 systems won't match this,
+	# Unixware is an offshoot of SVR4, but it has its own version
+	# number series starting with 2...
+	# I am not positive that other SVR4 systems won't match this,
 	# I just have to hope.  -- rms.
 	# I just have to hope.  -- rms.
-        # Use sysv4.2uw... so that sysv4* matches it.
+	# Use sysv4.2uw... so that sysv4* matches it.
 	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
 	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
 	exit ;;
 	exit ;;
     i*86:OS/2:*:*)
     i*86:OS/2:*:*)
@@ -1058,7 +1027,7 @@ EOF
     i*86:syllable:*:*)
     i*86:syllable:*:*)
 	echo ${UNAME_MACHINE}-pc-syllable
 	echo ${UNAME_MACHINE}-pc-syllable
 	exit ;;
 	exit ;;
-    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
 	echo i386-unknown-lynxos${UNAME_RELEASE}
 	echo i386-unknown-lynxos${UNAME_RELEASE}
 	exit ;;
 	exit ;;
     i*86:*DOS:*:*)
     i*86:*DOS:*:*)
@@ -1073,7 +1042,7 @@ EOF
 	fi
 	fi
 	exit ;;
 	exit ;;
     i*86:*:5:[678]*)
     i*86:*:5:[678]*)
-    	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	# UnixWare 7.x, OpenUNIX and OpenServer 6.
 	case `/bin/uname -X | grep "^Machine"` in
 	case `/bin/uname -X | grep "^Machine"` in
 	    *486*)	     UNAME_MACHINE=i486 ;;
 	    *486*)	     UNAME_MACHINE=i486 ;;
 	    *Pentium)	     UNAME_MACHINE=i586 ;;
 	    *Pentium)	     UNAME_MACHINE=i586 ;;
@@ -1101,10 +1070,13 @@ EOF
 	exit ;;
 	exit ;;
     pc:*:*:*)
     pc:*:*:*)
 	# Left here for compatibility:
 	# Left here for compatibility:
-        # uname -m prints for DJGPP always 'pc', but it prints nothing about
-        # the processor, so we play safe by assuming i386.
-	echo i386-pc-msdosdjgpp
-        exit ;;
+	# uname -m prints for DJGPP always 'pc', but it prints nothing about
+	# the processor, so we play safe by assuming i586.
+	# Note: whatever this is, it MUST be the same as what config.sub
+	# prints for the "djgpp" host, or else GDB configury will decide that
+	# this is a cross-build.
+	echo i586-pc-msdosdjgpp
+	exit ;;
     Intel:Mach:3*:*)
     Intel:Mach:3*:*)
 	echo i386-pc-mach3
 	echo i386-pc-mach3
 	exit ;;
 	exit ;;
@@ -1139,8 +1111,18 @@ EOF
 	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
 	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
 	  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
 	  && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
     3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
     3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
-        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-          && { echo i486-ncr-sysv4; exit; } ;;
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+	OS_REL='.3'
+	test -r /etc/.relid \
+	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	    && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+	    && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
     m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
     m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
 	echo m68k-unknown-lynxos${UNAME_RELEASE}
 	echo m68k-unknown-lynxos${UNAME_RELEASE}
 	exit ;;
 	exit ;;
@@ -1153,7 +1135,7 @@ EOF
     rs6000:LynxOS:2.*:*)
     rs6000:LynxOS:2.*:*)
 	echo rs6000-unknown-lynxos${UNAME_RELEASE}
 	echo rs6000-unknown-lynxos${UNAME_RELEASE}
 	exit ;;
 	exit ;;
-    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
 	echo powerpc-unknown-lynxos${UNAME_RELEASE}
 	echo powerpc-unknown-lynxos${UNAME_RELEASE}
 	exit ;;
 	exit ;;
     SM[BE]S:UNIX_SV:*:*)
     SM[BE]S:UNIX_SV:*:*)
@@ -1173,10 +1155,10 @@ EOF
 		echo ns32k-sni-sysv
 		echo ns32k-sni-sysv
 	fi
 	fi
 	exit ;;
 	exit ;;
-    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
-                      # says <Richard.M.Bartel@ccMail.Census.GOV>
-        echo i586-unisys-sysv4
-        exit ;;
+    PENTIUM:*:4.0*:*)	# Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	echo i586-unisys-sysv4
+	exit ;;
     *:UNIX_System_V:4*:FTX*)
     *:UNIX_System_V:4*:FTX*)
 	# From Gerald Hewes <hewes@openmarket.com>.
 	# From Gerald Hewes <hewes@openmarket.com>.
 	# How about differentiating between stratus architectures? -djm
 	# How about differentiating between stratus architectures? -djm
@@ -1202,11 +1184,11 @@ EOF
 	exit ;;
 	exit ;;
     R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
     R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
 	if [ -d /usr/nec ]; then
 	if [ -d /usr/nec ]; then
-	        echo mips-nec-sysv${UNAME_RELEASE}
+		echo mips-nec-sysv${UNAME_RELEASE}
 	else
 	else
-	        echo mips-unknown-sysv${UNAME_RELEASE}
+		echo mips-unknown-sysv${UNAME_RELEASE}
 	fi
 	fi
-        exit ;;
+	exit ;;
     BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
     BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
 	echo powerpc-be-beos
 	echo powerpc-be-beos
 	exit ;;
 	exit ;;
@@ -1216,6 +1198,9 @@ EOF
     BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
     BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
 	echo i586-pc-beos
 	echo i586-pc-beos
 	exit ;;
 	exit ;;
+    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+	echo i586-pc-haiku
+	exit ;;
     SX-4:SUPER-UX:*:*)
     SX-4:SUPER-UX:*:*)
 	echo sx4-nec-superux${UNAME_RELEASE}
 	echo sx4-nec-superux${UNAME_RELEASE}
 	exit ;;
 	exit ;;
@@ -1243,6 +1228,16 @@ EOF
     *:Darwin:*:*)
     *:Darwin:*:*)
 	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
 	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
 	case $UNAME_PROCESSOR in
 	case $UNAME_PROCESSOR in
+	    i386)
+		eval $set_cc_for_build
+		if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+		  if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		      (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+		      grep IS_64BIT_ARCH >/dev/null
+		  then
+		      UNAME_PROCESSOR="x86_64"
+		  fi
+		fi ;;
 	    unknown) UNAME_PROCESSOR=powerpc ;;
 	    unknown) UNAME_PROCESSOR=powerpc ;;
 	esac
 	esac
 	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
 	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
@@ -1258,6 +1253,9 @@ EOF
     *:QNX:*:4*)
     *:QNX:*:4*)
 	echo i386-pc-qnx
 	echo i386-pc-qnx
 	exit ;;
 	exit ;;
+    NEO-?:NONSTOP_KERNEL:*:*)
+	echo neo-tandem-nsk${UNAME_RELEASE}
+	exit ;;
     NSE-?:NONSTOP_KERNEL:*:*)
     NSE-?:NONSTOP_KERNEL:*:*)
 	echo nse-tandem-nsk${UNAME_RELEASE}
 	echo nse-tandem-nsk${UNAME_RELEASE}
 	exit ;;
 	exit ;;
@@ -1303,13 +1301,13 @@ EOF
 	echo pdp10-unknown-its
 	echo pdp10-unknown-its
 	exit ;;
 	exit ;;
     SEI:*:*:SEIUX)
     SEI:*:*:SEIUX)
-        echo mips-sei-seiux${UNAME_RELEASE}
+	echo mips-sei-seiux${UNAME_RELEASE}
 	exit ;;
 	exit ;;
     *:DragonFly:*:*)
     *:DragonFly:*:*)
 	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
 	echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
 	exit ;;
 	exit ;;
     *:*VMS:*:*)
     *:*VMS:*:*)
-    	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	UNAME_MACHINE=`(uname -p) 2>/dev/null`
 	case "${UNAME_MACHINE}" in
 	case "${UNAME_MACHINE}" in
 	    A*) echo alpha-dec-vms ; exit ;;
 	    A*) echo alpha-dec-vms ; exit ;;
 	    I*) echo ia64-dec-vms ; exit ;;
 	    I*) echo ia64-dec-vms ; exit ;;
@@ -1324,6 +1322,12 @@ EOF
     i*86:rdos:*:*)
     i*86:rdos:*:*)
 	echo ${UNAME_MACHINE}-pc-rdos
 	echo ${UNAME_MACHINE}-pc-rdos
 	exit ;;
 	exit ;;
+    i*86:AROS:*:*)
+	echo ${UNAME_MACHINE}-pc-aros
+	exit ;;
+    x86_64:VMkernel:*:*)
+	echo ${UNAME_MACHINE}-unknown-esx
+	exit ;;
 esac
 esac
 
 
 #echo '(No uname command or uname output not recognized.)' 1>&2
 #echo '(No uname command or uname output not recognized.)' 1>&2
@@ -1346,11 +1350,11 @@ main ()
 #include <sys/param.h>
 #include <sys/param.h>
   printf ("m68k-sony-newsos%s\n",
   printf ("m68k-sony-newsos%s\n",
 #ifdef NEWSOS4
 #ifdef NEWSOS4
-          "4"
+	"4"
 #else
 #else
-	  ""
+	""
 #endif
 #endif
-         ); exit (0);
+	); exit (0);
 #endif
 #endif
 #endif
 #endif
 
 

+ 192 - 77
config.sub

@@ -1,10 +1,10 @@
 #! /bin/sh
 #! /bin/sh
 # Configuration validation subroutine script.
 # Configuration validation subroutine script.
 #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
 #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
-#   Free Software Foundation, Inc.
+#   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+#   2011, 2012 Free Software Foundation, Inc.
 
 
-timestamp='2008-01-16'
+timestamp='2012-02-10'
 
 
 # This file is (in principle) common to ALL GNU software.
 # This file is (in principle) common to ALL GNU software.
 # The presence of a machine in this file suggests that SOME GNU software
 # The presence of a machine in this file suggests that SOME GNU software
@@ -21,9 +21,7 @@ timestamp='2008-01-16'
 # GNU General Public License for more details.
 # GNU General Public License for more details.
 #
 #
 # You should have received a copy of the GNU General Public License
 # 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., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
 #
 #
 # As a special exception to the GNU General Public License, if you
 # As a special exception to the GNU General Public License, if you
 # distribute this file as part of a program that contains a
 # distribute this file as part of a program that contains a
@@ -32,13 +30,16 @@ timestamp='2008-01-16'
 
 
 
 
 # Please send patches to <config-patches@gnu.org>.  Submit a context
 # Please send patches to <config-patches@gnu.org>.  Submit a context
-# diff and a properly formatted ChangeLog entry.
+# diff and a properly formatted GNU ChangeLog entry.
 #
 #
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Configuration subroutine to validate and canonicalize a configuration type.
 # Supply the specified configuration type as an argument.
 # Supply the specified configuration type as an argument.
 # If it is invalid, we print an error message on stderr and exit with code 1.
 # If it is invalid, we print an error message on stderr and exit with code 1.
 # Otherwise, we print the canonical config type on stdout and succeed.
 # Otherwise, we print the canonical config type on stdout and succeed.
 
 
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
 # This file is supposed to be the same for all GNU packages
 # This file is supposed to be the same for all GNU packages
 # and recognize all the CPU types, system types and aliases
 # and recognize all the CPU types, system types and aliases
 # that are meaningful with *any* GNU software.
 # that are meaningful with *any* GNU software.
@@ -72,8 +73,9 @@ Report bugs and patches to <config-patches@gnu.org>."
 version="\
 version="\
 GNU config.sub ($timestamp)
 GNU config.sub ($timestamp)
 
 
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+Free Software Foundation, Inc.
 
 
 This is free software; see the source for copying conditions.  There is NO
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -120,12 +122,18 @@ esac
 # Here we must recognize all the valid KERNEL-OS combinations.
 # Here we must recognize all the valid KERNEL-OS combinations.
 maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
 case $maybe_os in
 case $maybe_os in
-  nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
-  uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+  nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+  linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+  knetbsd*-gnu* | netbsd*-gnu* | \
+  kopensolaris*-gnu* | \
   storm-chaos* | os2-emx* | rtmk-nova*)
   storm-chaos* | os2-emx* | rtmk-nova*)
     os=-$maybe_os
     os=-$maybe_os
     basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
     basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
     ;;
     ;;
+  android-linux)
+    os=-linux-android
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+    ;;
   *)
   *)
     basic_machine=`echo $1 | sed 's/-[^-]*$//'`
     basic_machine=`echo $1 | sed 's/-[^-]*$//'`
     if [ $basic_machine != $1 ]
     if [ $basic_machine != $1 ]
@@ -148,10 +156,13 @@ case $os in
 	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
 	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
 	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
 	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
 	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
 	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
-	-apple | -axis | -knuth | -cray)
+	-apple | -axis | -knuth | -cray | -microblaze)
 		os=
 		os=
 		basic_machine=$1
 		basic_machine=$1
 		;;
 		;;
+	-bluegene*)
+		os=-cnk
+		;;
 	-sim | -cisco | -oki | -wec | -winbond)
 	-sim | -cisco | -oki | -wec | -winbond)
 		os=
 		os=
 		basic_machine=$1
 		basic_machine=$1
@@ -166,10 +177,10 @@ case $os in
 		os=-chorusos
 		os=-chorusos
 		basic_machine=$1
 		basic_machine=$1
 		;;
 		;;
- 	-chorusrdb)
- 		os=-chorusrdb
+	-chorusrdb)
+		os=-chorusrdb
 		basic_machine=$1
 		basic_machine=$1
- 		;;
+		;;
 	-hiux*)
 	-hiux*)
 		os=-hiuxwe2
 		os=-hiuxwe2
 		;;
 		;;
@@ -238,24 +249,32 @@ case $basic_machine in
 	# Some are omitted here because they have special meanings below.
 	# Some are omitted here because they have special meanings below.
 	1750a | 580 \
 	1750a | 580 \
 	| a29k \
 	| a29k \
+	| aarch64 | aarch64_be \
 	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
 	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
 	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
 	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
 	| am33_2.0 \
 	| am33_2.0 \
 	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
 	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+        | be32 | be64 \
 	| bfin \
 	| bfin \
 	| c4x | clipper \
 	| c4x | clipper \
 	| d10v | d30v | dlx | dsp16xx \
 	| d10v | d30v | dlx | dsp16xx \
+	| epiphany \
 	| fido | fr30 | frv \
 	| fido | fr30 | frv \
 	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
 	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+	| hexagon \
 	| i370 | i860 | i960 | ia64 \
 	| i370 | i860 | i960 | ia64 \
 	| ip2k | iq2000 \
 	| ip2k | iq2000 \
+	| le32 | le64 \
+	| lm32 \
 	| m32c | m32r | m32rle | m68000 | m68k | m88k \
 	| m32c | m32r | m32rle | m68000 | m68k | m88k \
-	| maxq | mb | microblaze | mcore | mep \
+	| maxq | mb | microblaze | mcore | mep | metag \
 	| mips | mipsbe | mipseb | mipsel | mipsle \
 	| mips | mipsbe | mipseb | mipsel | mipsle \
 	| mips16 \
 	| mips16 \
 	| mips64 | mips64el \
 	| mips64 | mips64el \
-	| mips64vr | mips64vrel \
+	| mips64octeon | mips64octeonel \
 	| mips64orion | mips64orionel \
 	| mips64orion | mips64orionel \
+	| mips64r5900 | mips64r5900el \
+	| mips64vr | mips64vrel \
 	| mips64vr4100 | mips64vr4100el \
 	| mips64vr4100 | mips64vr4100el \
 	| mips64vr4300 | mips64vr4300el \
 	| mips64vr4300 | mips64vr4300el \
 	| mips64vr5000 | mips64vr5000el \
 	| mips64vr5000 | mips64vr5000el \
@@ -268,29 +287,42 @@ case $basic_machine in
 	| mipsisa64sr71k | mipsisa64sr71kel \
 	| mipsisa64sr71k | mipsisa64sr71kel \
 	| mipstx39 | mipstx39el \
 	| mipstx39 | mipstx39el \
 	| mn10200 | mn10300 \
 	| mn10200 | mn10300 \
+	| moxie \
 	| mt \
 	| mt \
 	| msp430 \
 	| msp430 \
+	| nds32 | nds32le | nds32be \
 	| nios | nios2 \
 	| nios | nios2 \
 	| ns16k | ns32k \
 	| ns16k | ns32k \
+	| open8 \
 	| or32 \
 	| or32 \
 	| pdp10 | pdp11 | pj | pjl \
 	| pdp10 | pdp11 | pj | pjl \
-	| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+	| powerpc | powerpc64 | powerpc64le | powerpcle \
 	| pyramid \
 	| pyramid \
+	| rl78 | rx \
 	| score \
 	| score \
-	| sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+	| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
 	| sh64 | sh64le \
 	| sh64 | sh64le \
 	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
 	| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
 	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
 	| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
-	| spu | strongarm \
-	| tahoe | thumb | tic4x | tic80 | tron \
-	| v850 | v850e \
+	| spu \
+	| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+	| ubicom32 \
+	| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
 	| we32k \
 	| we32k \
-	| x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
-	| z8k)
+	| x86 | xc16x | xstormy16 | xtensa \
+	| z8k | z80)
 		basic_machine=$basic_machine-unknown
 		basic_machine=$basic_machine-unknown
 		;;
 		;;
-	m6811 | m68hc11 | m6812 | m68hc12)
-		# Motorola 68HC11/12.
+	c54x)
+		basic_machine=tic54x-unknown
+		;;
+	c55x)
+		basic_machine=tic55x-unknown
+		;;
+	c6x)
+		basic_machine=tic6x-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
 		basic_machine=$basic_machine-unknown
 		basic_machine=$basic_machine-unknown
 		os=-none
 		os=-none
 		;;
 		;;
@@ -300,6 +332,21 @@ case $basic_machine in
 		basic_machine=mt-unknown
 		basic_machine=mt-unknown
 		;;
 		;;
 
 
+	strongarm | thumb | xscale)
+		basic_machine=arm-unknown
+		;;
+	xgate)
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	xscaleeb)
+		basic_machine=armeb-unknown
+		;;
+
+	xscaleel)
+		basic_machine=armel-unknown
+		;;
+
 	# We use `pc' rather than `unknown'
 	# We use `pc' rather than `unknown'
 	# because (1) that's what they normally are, and
 	# because (1) that's what they normally are, and
 	# (2) the word "unknown" tends to confuse beginning users.
 	# (2) the word "unknown" tends to confuse beginning users.
@@ -314,29 +361,36 @@ case $basic_machine in
 	# Recognize the basic CPU types with company name.
 	# Recognize the basic CPU types with company name.
 	580-* \
 	580-* \
 	| a29k-* \
 	| a29k-* \
+	| aarch64-* | aarch64_be-* \
 	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
 	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
 	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
 	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
 	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
 	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
 	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
 	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
 	| avr-* | avr32-* \
 	| avr-* | avr32-* \
+	| be32-* | be64-* \
 	| bfin-* | bs2000-* \
 	| bfin-* | bs2000-* \
-	| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+	| c[123]* | c30-* | [cjt]90-* | c4x-* \
 	| clipper-* | craynv-* | cydra-* \
 	| clipper-* | craynv-* | cydra-* \
 	| d10v-* | d30v-* | dlx-* \
 	| d10v-* | d30v-* | dlx-* \
 	| elxsi-* \
 	| elxsi-* \
 	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
 	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
 	| h8300-* | h8500-* \
 	| h8300-* | h8500-* \
 	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
 	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+	| hexagon-* \
 	| i*86-* | i860-* | i960-* | ia64-* \
 	| i*86-* | i860-* | i960-* | ia64-* \
 	| ip2k-* | iq2000-* \
 	| ip2k-* | iq2000-* \
+	| le32-* | le64-* \
+	| lm32-* \
 	| m32c-* | m32r-* | m32rle-* \
 	| m32c-* | m32r-* | m32rle-* \
 	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
 	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
-	| m88110-* | m88k-* | maxq-* | mcore-* \
+	| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
 	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
 	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
 	| mips16-* \
 	| mips16-* \
 	| mips64-* | mips64el-* \
 	| mips64-* | mips64el-* \
-	| mips64vr-* | mips64vrel-* \
+	| mips64octeon-* | mips64octeonel-* \
 	| mips64orion-* | mips64orionel-* \
 	| mips64orion-* | mips64orionel-* \
+	| mips64r5900-* | mips64r5900el-* \
+	| mips64vr-* | mips64vrel-* \
 	| mips64vr4100-* | mips64vr4100el-* \
 	| mips64vr4100-* | mips64vr4100el-* \
 	| mips64vr4300-* | mips64vr4300el-* \
 	| mips64vr4300-* | mips64vr4300el-* \
 	| mips64vr5000-* | mips64vr5000el-* \
 	| mips64vr5000-* | mips64vr5000el-* \
@@ -351,27 +405,32 @@ case $basic_machine in
 	| mmix-* \
 	| mmix-* \
 	| mt-* \
 	| mt-* \
 	| msp430-* \
 	| msp430-* \
+	| nds32-* | nds32le-* | nds32be-* \
 	| nios-* | nios2-* \
 	| nios-* | nios2-* \
 	| none-* | np1-* | ns16k-* | ns32k-* \
 	| none-* | np1-* | ns16k-* | ns32k-* \
+	| open8-* \
 	| orion-* \
 	| orion-* \
 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
-	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
 	| pyramid-* \
 	| pyramid-* \
-	| romp-* | rs6000-* \
-	| sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+	| rl78-* | romp-* | rs6000-* | rx-* \
+	| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
 	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
 	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
 	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
 	| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
 	| sparclite-* \
 	| sparclite-* \
-	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
-	| tahoe-* | thumb-* \
+	| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
+	| tahoe-* \
 	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
 	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+	| tile*-* \
 	| tron-* \
 	| tron-* \
-	| v850-* | v850e-* | vax-* \
+	| ubicom32-* \
+	| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+	| vax-* \
 	| we32k-* \
 	| we32k-* \
-	| x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+	| x86-* | x86_64-* | xc16x-* | xps100-* \
 	| xstormy16-* | xtensa*-* \
 	| xstormy16-* | xtensa*-* \
 	| ymp-* \
 	| ymp-* \
-	| z8k-*)
+	| z8k-* | z80-*)
 		;;
 		;;
 	# Recognize the basic CPU types without company name, with glob match.
 	# Recognize the basic CPU types without company name, with glob match.
 	xtensa*)
 	xtensa*)
@@ -393,7 +452,7 @@ case $basic_machine in
 		basic_machine=a29k-amd
 		basic_machine=a29k-amd
 		os=-udi
 		os=-udi
 		;;
 		;;
-    	abacus)
+	abacus)
 		basic_machine=abacus-unknown
 		basic_machine=abacus-unknown
 		;;
 		;;
 	adobe68k)
 	adobe68k)
@@ -439,6 +498,10 @@ case $basic_machine in
 		basic_machine=m68k-apollo
 		basic_machine=m68k-apollo
 		os=-bsd
 		os=-bsd
 		;;
 		;;
+	aros)
+		basic_machine=i386-pc
+		os=-aros
+		;;
 	aux)
 	aux)
 		basic_machine=m68k-apple
 		basic_machine=m68k-apple
 		os=-aux
 		os=-aux
@@ -455,10 +518,27 @@ case $basic_machine in
 		basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
 		basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
 		os=-linux
 		os=-linux
 		;;
 		;;
+	bluegene*)
+		basic_machine=powerpc-ibm
+		os=-cnk
+		;;
+	c54x-*)
+		basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	c55x-*)
+		basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	c6x-*)
+		basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
 	c90)
 	c90)
 		basic_machine=c90-cray
 		basic_machine=c90-cray
 		os=-unicos
 		os=-unicos
 		;;
 		;;
+	cegcc)
+		basic_machine=arm-unknown
+		os=-cegcc
+		;;
 	convex-c1)
 	convex-c1)
 		basic_machine=c1-convex
 		basic_machine=c1-convex
 		os=-bsd
 		os=-bsd
@@ -487,7 +567,7 @@ case $basic_machine in
 		basic_machine=craynv-cray
 		basic_machine=craynv-cray
 		os=-unicosmp
 		os=-unicosmp
 		;;
 		;;
-	cr16)
+	cr16 | cr16-*)
 		basic_machine=cr16-unknown
 		basic_machine=cr16-unknown
 		os=-elf
 		os=-elf
 		;;
 		;;
@@ -526,6 +606,10 @@ case $basic_machine in
 		basic_machine=m88k-motorola
 		basic_machine=m88k-motorola
 		os=-sysv3
 		os=-sysv3
 		;;
 		;;
+	dicos)
+		basic_machine=i686-pc
+		os=-dicos
+		;;
 	djgpp)
 	djgpp)
 		basic_machine=i586-pc
 		basic_machine=i586-pc
 		os=-msdosdjgpp
 		os=-msdosdjgpp
@@ -641,7 +725,6 @@ case $basic_machine in
 	i370-ibm* | ibm*)
 	i370-ibm* | ibm*)
 		basic_machine=i370-ibm
 		basic_machine=i370-ibm
 		;;
 		;;
-# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
 	i*86v32)
 	i*86v32)
 		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
 		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
 		os=-sysv32
 		os=-sysv32
@@ -699,6 +782,9 @@ case $basic_machine in
 		basic_machine=ns32k-utek
 		basic_machine=ns32k-utek
 		os=-sysv
 		os=-sysv
 		;;
 		;;
+	microblaze)
+		basic_machine=microblaze-xilinx
+		;;
 	mingw32)
 	mingw32)
 		basic_machine=i386-pc
 		basic_machine=i386-pc
 		os=-mingw32
 		os=-mingw32
@@ -735,10 +821,18 @@ case $basic_machine in
 	ms1-*)
 	ms1-*)
 		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
 		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
 		;;
 		;;
+	msys)
+		basic_machine=i386-pc
+		os=-msys
+		;;
 	mvs)
 	mvs)
 		basic_machine=i370-ibm
 		basic_machine=i370-ibm
 		os=-mvs
 		os=-mvs
 		;;
 		;;
+	nacl)
+		basic_machine=le32-unknown
+		os=-nacl
+		;;
 	ncr3000)
 	ncr3000)
 		basic_machine=i486-ncr
 		basic_machine=i486-ncr
 		os=-sysv4
 		os=-sysv4
@@ -803,6 +897,12 @@ case $basic_machine in
 	np1)
 	np1)
 		basic_machine=np1-gould
 		basic_machine=np1-gould
 		;;
 		;;
+	neo-tandem)
+		basic_machine=neo-tandem
+		;;
+	nse-tandem)
+		basic_machine=nse-tandem
+		;;
 	nsr-tandem)
 	nsr-tandem)
 		basic_machine=nsr-tandem
 		basic_machine=nsr-tandem
 		;;
 		;;
@@ -885,9 +985,10 @@ case $basic_machine in
 		;;
 		;;
 	power)	basic_machine=power-ibm
 	power)	basic_machine=power-ibm
 		;;
 		;;
-	ppc)	basic_machine=powerpc-unknown
+	ppc | ppcbe)	basic_machine=powerpc-unknown
 		;;
 		;;
-	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+	ppc-* | ppcbe-*)
+		basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
 		;;
 		;;
 	ppcle | powerpclittle | ppc-le | powerpc-little)
 	ppcle | powerpclittle | ppc-le | powerpc-little)
 		basic_machine=powerpcle-unknown
 		basic_machine=powerpcle-unknown
@@ -981,6 +1082,9 @@ case $basic_machine in
 		basic_machine=i860-stratus
 		basic_machine=i860-stratus
 		os=-sysv4
 		os=-sysv4
 		;;
 		;;
+	strongarm-* | thumb-*)
+		basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
 	sun2)
 	sun2)
 		basic_machine=m68000-sun
 		basic_machine=m68000-sun
 		;;
 		;;
@@ -1037,20 +1141,8 @@ case $basic_machine in
 		basic_machine=t90-cray
 		basic_machine=t90-cray
 		os=-unicos
 		os=-unicos
 		;;
 		;;
-	tic54x | c54x*)
-		basic_machine=tic54x-unknown
-		os=-coff
-		;;
-	tic55x | c55x*)
-		basic_machine=tic55x-unknown
-		os=-coff
-		;;
-	tic6x | c6x*)
-		basic_machine=tic6x-unknown
-		os=-coff
-		;;
 	tile*)
 	tile*)
-		basic_machine=tile-unknown
+		basic_machine=$basic_machine-unknown
 		os=-linux-gnu
 		os=-linux-gnu
 		;;
 		;;
 	tx39)
 	tx39)
@@ -1120,6 +1212,9 @@ case $basic_machine in
 	xps | xps100)
 	xps | xps100)
 		basic_machine=xps100-honeywell
 		basic_machine=xps100-honeywell
 		;;
 		;;
+	xscale-* | xscalee[bl]-*)
+		basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+		;;
 	ymp)
 	ymp)
 		basic_machine=ymp-cray
 		basic_machine=ymp-cray
 		os=-unicos
 		os=-unicos
@@ -1128,6 +1223,10 @@ case $basic_machine in
 		basic_machine=z8k-unknown
 		basic_machine=z8k-unknown
 		os=-sim
 		os=-sim
 		;;
 		;;
+	z80-*-coff)
+		basic_machine=z80-unknown
+		os=-sim
+		;;
 	none)
 	none)
 		basic_machine=none-none
 		basic_machine=none-none
 		os=-none
 		os=-none
@@ -1166,7 +1265,7 @@ case $basic_machine in
 	we32k)
 	we32k)
 		basic_machine=we32k-att
 		basic_machine=we32k-att
 		;;
 		;;
-	sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
+	sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
 		basic_machine=sh-unknown
 		basic_machine=sh-unknown
 		;;
 		;;
 	sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
 	sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
@@ -1213,9 +1312,12 @@ esac
 if [ x"$os" != x"" ]
 if [ x"$os" != x"" ]
 then
 then
 case $os in
 case $os in
-        # First match some system type aliases
-        # that might get confused with valid system types.
+	# First match some system type aliases
+	# that might get confused with valid system types.
 	# -solaris* is a basic system type, with this one exception.
 	# -solaris* is a basic system type, with this one exception.
+	-auroraux)
+		os=-auroraux
+		;;
 	-solaris1 | -solaris1.*)
 	-solaris1 | -solaris1.*)
 		os=`echo $os | sed -e 's|solaris1|sunos4|'`
 		os=`echo $os | sed -e 's|solaris1|sunos4|'`
 		;;
 		;;
@@ -1236,10 +1338,11 @@ case $os in
 	# Each alternative MUST END IN A *, to match a version number.
 	# Each alternative MUST END IN A *, to match a version number.
 	# -sysv* is not here because it comes later, after sysvr4.
 	# -sysv* is not here because it comes later, after sysvr4.
 	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
 	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
-	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
-	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+	      | -sym* | -kopensolaris* \
 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
-	      | -aos* \
+	      | -aos* | -aros* \
 	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
 	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
 	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
 	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
 	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
 	      | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
@@ -1248,9 +1351,10 @@ case $os in
 	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
 	      | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
 	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
 	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
 	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
 	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
-	      | -chorusos* | -chorusrdb* \
-	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
-	      | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+	      | -chorusos* | -chorusrdb* | -cegcc* \
+	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -linux-gnu* | -linux-android* \
+	      | -linux-newlib* | -linux-uclibc* \
 	      | -uxpv* | -beos* | -mpeix* | -udk* \
 	      | -uxpv* | -beos* | -mpeix* | -udk* \
 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
@@ -1258,7 +1362,7 @@ case $os in
 	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
 	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
 	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
 	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
 	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
 	      | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
-	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops*)
+	      | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
 	# Remember, each alternative MUST END IN *, to match a version number.
 	# Remember, each alternative MUST END IN *, to match a version number.
 		;;
 		;;
 	-qnx*)
 	-qnx*)
@@ -1297,7 +1401,7 @@ case $os in
 	-opened*)
 	-opened*)
 		os=-openedition
 		os=-openedition
 		;;
 		;;
-        -os400*)
+	-os400*)
 		os=-os400
 		os=-os400
 		;;
 		;;
 	-wince*)
 	-wince*)
@@ -1346,7 +1450,7 @@ case $os in
 	-sinix*)
 	-sinix*)
 		os=-sysv4
 		os=-sysv4
 		;;
 		;;
-        -tpf*)
+	-tpf*)
 		os=-tpf
 		os=-tpf
 		;;
 		;;
 	-triton*)
 	-triton*)
@@ -1388,6 +1492,11 @@ case $os in
 	-zvmoe)
 	-zvmoe)
 		os=-zvmoe
 		os=-zvmoe
 		;;
 		;;
+	-dicos*)
+		os=-dicos
+		;;
+	-nacl*)
+		;;
 	-none)
 	-none)
 		;;
 		;;
 	*)
 	*)
@@ -1410,10 +1519,10 @@ else
 # system, and we'll never get to this point.
 # system, and we'll never get to this point.
 
 
 case $basic_machine in
 case $basic_machine in
-        score-*)
+	score-*)
 		os=-elf
 		os=-elf
 		;;
 		;;
-        spu-*)
+	spu-*)
 		os=-elf
 		os=-elf
 		;;
 		;;
 	*-acorn)
 	*-acorn)
@@ -1425,8 +1534,17 @@ case $basic_machine in
 	arm*-semi)
 	arm*-semi)
 		os=-aout
 		os=-aout
 		;;
 		;;
-        c4x-* | tic4x-*)
-        	os=-coff
+	c4x-* | tic4x-*)
+		os=-coff
+		;;
+	tic54x-*)
+		os=-coff
+		;;
+	tic55x-*)
+		os=-coff
+		;;
+	tic6x-*)
+		os=-coff
 		;;
 		;;
 	# This must come before the *-dec entry.
 	# This must come before the *-dec entry.
 	pdp10-*)
 	pdp10-*)
@@ -1446,14 +1564,11 @@ case $basic_machine in
 		;;
 		;;
 	m68000-sun)
 	m68000-sun)
 		os=-sunos3
 		os=-sunos3
-		# This also exists in the configure program, but was not the
-		# default.
-		# os=-sunos4
 		;;
 		;;
 	m68*-cisco)
 	m68*-cisco)
 		os=-aout
 		os=-aout
 		;;
 		;;
-        mep-*)
+	mep-*)
 		os=-elf
 		os=-elf
 		;;
 		;;
 	mips*-cisco)
 	mips*-cisco)
@@ -1480,7 +1595,7 @@ case $basic_machine in
 	*-ibm)
 	*-ibm)
 		os=-aix
 		os=-aix
 		;;
 		;;
-    	*-knuth)
+	*-knuth)
 		os=-mmixware
 		os=-mmixware
 		;;
 		;;
 	*-wec)
 	*-wec)
@@ -1585,7 +1700,7 @@ case $basic_machine in
 			-sunos*)
 			-sunos*)
 				vendor=sun
 				vendor=sun
 				;;
 				;;
-			-aix*)
+			-cnk*|-aix*)
 				vendor=ibm
 				vendor=ibm
 				;;
 				;;
 			-beos*)
 			-beos*)

+ 44 - 13
configure

@@ -1,6 +1,6 @@
 #! /bin/sh
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.67 for ngircd 18.
+# Generated by GNU Autoconf 2.67 for ngircd 19.
 #
 #
 #
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -549,8 +549,8 @@ MAKEFLAGS=
 # Identity of this package.
 # Identity of this package.
 PACKAGE_NAME='ngircd'
 PACKAGE_NAME='ngircd'
 PACKAGE_TARNAME='ngircd'
 PACKAGE_TARNAME='ngircd'
-PACKAGE_VERSION='18'
-PACKAGE_STRING='ngircd 18'
+PACKAGE_VERSION='19'
+PACKAGE_STRING='ngircd 19'
 PACKAGE_BUGREPORT=''
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 PACKAGE_URL=''
 
 
@@ -1267,7 +1267,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # 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.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
   cat <<_ACEOF
-\`configure' configures ngircd 18 to adapt to many kinds of systems.
+\`configure' configures ngircd 19 to adapt to many kinds of systems.
 
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
 
@@ -1338,7 +1338,7 @@ fi
 
 
 if test -n "$ac_init_help"; then
 if test -n "$ac_init_help"; then
   case $ac_init_help in
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ngircd 18:";;
+     short | recursive ) echo "Configuration of ngircd 19:";;
    esac
    esac
   cat <<\_ACEOF
   cat <<\_ACEOF
 
 
@@ -1448,7 +1448,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
 if $ac_init_version; then
   cat <<\_ACEOF
   cat <<\_ACEOF
-ngircd configure 18
+ngircd configure 19
 generated by GNU Autoconf 2.67
 generated by GNU Autoconf 2.67
 
 
 Copyright (C) 2010 Free Software Foundation, Inc.
 Copyright (C) 2010 Free Software Foundation, Inc.
@@ -1924,7 +1924,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 running configure, to aid debugging if configure makes a mistake.
 
 
-It was created by ngircd $as_me 18, which was
+It was created by ngircd $as_me 19, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
 
   $ $0 $@
   $ $0 $@
@@ -2850,7 +2850,7 @@ fi
 
 
 # Define the identity of the package.
 # Define the identity of the package.
  PACKAGE='ngircd'
  PACKAGE='ngircd'
- VERSION='18'
+ VERSION='19'
 
 
 
 
 cat >>confdefs.h <<_ACEOF
 cat >>confdefs.h <<_ACEOF
@@ -5773,8 +5773,9 @@ fi
 done
 done
 
 
 
 
-for ac_func in getaddrinfo getnameinfo inet_aton sigaction sigprocmask snprintf \
- vsnprintf strdup strlcpy strlcat strtok_r
+for ac_func in  \
+	gai_strerror getaddrinfo getnameinfo inet_aton sigaction \
+	sigprocmask snprintf vsnprintf strdup strlcpy strlcat strtok_r
 do :
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -6159,11 +6160,28 @@ if test "x$ac_cv_func_poll" = x""yes; then :
   cat >>confdefs.h <<_ACEOF
   cat >>confdefs.h <<_ACEOF
 #define HAVE_POLL 1
 #define HAVE_POLL 1
 _ACEOF
 _ACEOF
+
+				for ac_header in poll.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default"
+if test "x$ac_cv_header_poll_h" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_POLL_H 1
+_ACEOF
  x_io_backend=poll\(\)
  x_io_backend=poll\(\)
 else
 else
   as_fn_error $? "Can't enable poll IO support!" "$LINENO" 5
   as_fn_error $? "Can't enable poll IO support!" "$LINENO" 5
 
 
 fi
 fi
+
+done
+
+
+else
+
+				as_fn_error $? "Can't enable poll IO support!" "$LINENO" 5
+
+fi
 done
 done
 
 
 		fi
 		fi
@@ -6177,8 +6195,21 @@ if test "x$ac_cv_func_poll" = x""yes; then :
   cat >>confdefs.h <<_ACEOF
   cat >>confdefs.h <<_ACEOF
 #define HAVE_POLL 1
 #define HAVE_POLL 1
 _ACEOF
 _ACEOF
+
+			for ac_header in poll.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default"
+if test "x$ac_cv_header_poll_h" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_POLL_H 1
+_ACEOF
  x_io_backend=poll\(\)
  x_io_backend=poll\(\)
 fi
 fi
+
+done
+
+
+fi
 done
 done
 
 
 
 
@@ -6334,7 +6365,7 @@ else
 fi
 fi
 
 
 if test "$x_io_backend" = "none"; then
 if test "$x_io_backend" = "none"; then
-	as_fn_error $? "No useabe IO API activated/found!?" "$LINENO" 5
+	as_fn_error $? "No useable IO API activated/found!?" "$LINENO" 5
 fi
 fi
 
 
 # use SSL?
 # use SSL?
@@ -7444,7 +7475,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 # values after options handling.
 ac_log="
 ac_log="
-This file was extended by ngircd $as_me 18, which was
+This file was extended by ngircd $as_me 19, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
 
   CONFIG_FILES    = $CONFIG_FILES
   CONFIG_FILES    = $CONFIG_FILES
@@ -7510,7 +7541,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
 ac_cs_version="\\
-ngircd config.status 18
+ngircd config.status 19
 configured by $0, generated by GNU Autoconf 2.67,
 configured by $0, generated by GNU Autoconf 2.67,
   with options \\"\$ac_cs_config\\"
   with options \\"\$ac_cs_config\\"
 
 

+ 17 - 7
configure.in

@@ -160,10 +160,12 @@ AC_FUNC_STRFTIME
 
 
 AC_CHECK_FUNCS([ \
 AC_CHECK_FUNCS([ \
 	bind gethostbyaddr gethostbyname gethostname inet_ntoa \
 	bind gethostbyaddr gethostbyname gethostname inet_ntoa \
-	setsid setsockopt socket strcasecmp waitpid],,AC_MSG_ERROR([required function missing!]))
+	setsid setsockopt socket strcasecmp waitpid],,
+	AC_MSG_ERROR([required function missing!]))
 
 
-AC_CHECK_FUNCS(getaddrinfo getnameinfo inet_aton sigaction sigprocmask snprintf \
- vsnprintf strdup strlcpy strlcat strtok_r)
+AC_CHECK_FUNCS([ \
+	gai_strerror getaddrinfo getnameinfo inet_aton sigaction \
+	sigprocmask snprintf vsnprintf strdup strlcpy strlcat strtok_r])
 
 
 # -- Configuration options --
 # -- Configuration options --
 
 
@@ -250,13 +252,21 @@ AC_ARG_WITH(poll,
 				CPPFLAGS="-I$withval/include $CPPFLAGS"
 				CPPFLAGS="-I$withval/include $CPPFLAGS"
 				LDFLAGS="-L$withval/lib $LDFLAGS"
 				LDFLAGS="-L$withval/lib $LDFLAGS"
 			fi
 			fi
-			AC_CHECK_FUNCS(poll, x_io_backend=poll\(\),
+			AC_CHECK_FUNCS(poll, [
+				AC_CHECK_HEADERS(poll.h,
+					x_io_backend=poll\(\),
+					AC_MSG_ERROR(
+					     [Can't enable poll IO support!])
+				)
+			], [
 				AC_MSG_ERROR([Can't enable poll IO support!])
 				AC_MSG_ERROR([Can't enable poll IO support!])
-			)
+			])
 		fi
 		fi
 	],
 	],
 	[
 	[
-		AC_CHECK_FUNCS(poll, x_io_backend=poll\(\))
+		AC_CHECK_FUNCS(poll, [
+			AC_CHECK_HEADERS(poll.h, x_io_backend=poll\(\))
+		])
 	]
 	]
 )
 )
 
 
@@ -330,7 +340,7 @@ else
 fi
 fi
 
 
 if test "$x_io_backend" = "none"; then
 if test "$x_io_backend" = "none"; then
-	AC_MSG_ERROR([No useabe IO API activated/found!?])
+	AC_MSG_ERROR([No useable IO API activated/found!?])
 fi
 fi
 
 
 # use SSL?
 # use SSL?

+ 6 - 7
contrib/Anope/README

@@ -2,7 +2,7 @@
                      ngIRCd - Next Generation IRC Server
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
                            http://ngircd.barton.de/
 
 
-               (c)2001-2011 Alexander Barton and Contributors.
+               (c)2001-2012 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
                    terms of the GNU General Public License.
 
 
@@ -11,22 +11,21 @@
 
 
 This directory contains two preliminary patches that (re-) add a ngIRCd
 This directory contains two preliminary patches that (re-) add a ngIRCd
 protocol module to the Anope 1.9 development branch. It has been tested
 protocol module to the Anope 1.9 development branch. It has been tested
-with Anope 1.9.4, there is no guarantee that it will work with other
+with Anope 1.9.6, there is no guarantee that it will work with other
 versions as Anope 1.9.x is under heavy development ...
 versions as Anope 1.9.x is under heavy development ...
 
 
 To build this Anope protocol module, you have to
 To build this Anope protocol module, you have to
 
 
- - Download the Anope 1.9.x sources (tested with 1.9.4),
+ - Download the Anope 1.9.x sources (only tested with 1.9.6!),
  - Patch in the ngIRCd protocol module,
  - Patch in the ngIRCd protocol module,
  - Build and install Anope as usual,
  - Build and install Anope as usual,
  - Configure Anope as usual, use "ngircd" as protocol module.
  - Configure Anope as usual, use "ngircd" as protocol module.
 
 
 So the command sequence can be something like this:
 So the command sequence can be something like this:
 
 
- $ tar xzf anope-1.9.4-source.tar.gz
- $ cd anope-1.9.4-source
- $ patch -p1 < .../ngircd/contrib/Anope/0001-Revert-Removed-ngircd.patch
- $ patch -p1 < .../ngircd/contrib/Anope/0002-ngircd-whitespace-fixes.patch
+ $ tar xzf anope-1.9.6-source.tar.gz
+ $ cd anope-1.9.6-source
+ $ for p in .../ngircd/contrib/Anope/*.patch ; do patch -p1 < $p ; done
  $ ./Config
  $ ./Config
  $ cd build
  $ cd build
  $ make
  $ make

+ 12 - 0
contrib/Debian/changelog

@@ -1,3 +1,15 @@
+ngircd (19-0ab1) unstable; urgency=low
+
+  * New "upstream" release: ngIRCd 19.
+
+ -- Alexander Barton <alex@barton.de>  Wed, 29 Feb 2012 17:34:08 +0100
+
+ngircd (19~rc1-0ab1) unstable; urgency=low
+
+  * New "upstream" release candidate 1 for ngIRCd Release 19.
+
+ -- Alexander Barton <alex@barton.de>  Sun, 12 Feb 2012 17:47:51 +0100
+
 ngircd (18-0ab1) unstable; urgency=low
 ngircd (18-0ab1) unstable; urgency=low
 
 
   * New "upstream" release: ngIRCd 18.
   * New "upstream" release: ngIRCd 18.

+ 27 - 40
contrib/Debian/control

@@ -2,64 +2,60 @@ Source: ngircd
 Section: net
 Section: net
 Priority: optional
 Priority: optional
 Maintainer: Alexander Barton <alex@barton.de>
 Maintainer: Alexander Barton <alex@barton.de>
-Build-Depends: debhelper (>> 4.0.0), libz-dev, libwrap0-dev, libident-dev, libgnutls-dev, libpam0g-dev
+Build-Depends: debhelper (>> 4.0.0),
+    autotools-dev,
+    expect,
+    libz-dev,
+    libwrap0-dev,
+    libident-dev,
+    libgnutls-dev,
+    libpam0g-dev,
+    telnet,
 Standards-Version: 3.9.1
 Standards-Version: 3.9.1
 
 
 Package: ngircd
 Package: ngircd
 Architecture: any
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}
 Depends: ${shlibs:Depends}, ${misc:Depends}
 Provides: ircd
 Provides: ircd
-Description: A lightweight daemon for the Internet Relay Chat (IRC)
- ngIRCd is a free open source daemon for the Internet Relay Chat (IRC)
- network. It is written from scratch and is not based upon the original
- IRCd like many others.
+Description: lightweight Internet Relay Chat server
+ This package provides ngIRCd, a lightweight Internet Relay Chat
+ server for small or private networks. It is simple to configure, can
+ cope with dynamic IP addresses, and supports IPv6 as well as SSL. It
+ is written from scratch, not based on the original IRCd and quite
+ portable.
  .
  .
  This package contains the "standard distribution", including support for
  This package contains the "standard distribution", including support for
  syslog logging and compressed server-links using zlib. Please have a look
  syslog logging and compressed server-links using zlib. Please have a look
  at the "ngircd-full" package if you need advanced functionality like support
  at the "ngircd-full" package if you need advanced functionality like support
  for IPv6 or SSL.
  for IPv6 or SSL.
- .
- Advantages of ngIRCd:
-  - no problems with servers using changing/non-static IP addresses.
-  - small and lean configuration file.
-  - free, modern and open source C code.
-  - still under active development.
- .
- ngIRCd is compatible to the "original" ircd 2.10.3p3, so you can run
- mixed networks.
 
 
 Package: ngircd-full
 Package: ngircd-full
 Architecture: any
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}
 Depends: ${shlibs:Depends}, ${misc:Depends}
 Provides: ircd
 Provides: ircd
 Conflicts: ngircd, ngircd-dbg
 Conflicts: ngircd, ngircd-dbg
-Description: A lightweight daemon for the Internet Relay Chat (IRC)
- ngIRCd is a free open source daemon for the Internet Relay Chat (IRC)
- network. It is written from scratch and is not based upon the original
- IRCd like many others.
+Description: lightweight Internet Relay Chat server
+ This package provides ngIRCd, a lightweight Internet Relay Chat
+ server for small or private networks. It is simple to configure, can
+ cope with dynamic IP addresses, and supports IPv6 as well as SSL. It
+ is written from scratch, not based on the original IRCd and quite
+ portable.
  .
  .
  In addition to the features of the "standard package", this package
  In addition to the features of the "standard package", this package
  includes support for TCP wrappers, IDENT requests, the IPv6 protocol and
  includes support for TCP wrappers, IDENT requests, the IPv6 protocol and
  SSL encrypted client and server links.
  SSL encrypted client and server links.
- .
- Advantages of ngIRCd:
-  - no problems with servers using changing/non-static IP addresses.
-  - small and lean configuration file.
-  - free, modern and open source C code.
-  - still under active development.
- .
- ngIRCd is compatible to the "original" ircd 2.10.3p3, so you can run
- mixed networks.
 
 
 Package: ngircd-full-dbg
 Package: ngircd-full-dbg
 Architecture: any
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}
 Depends: ${shlibs:Depends}, ${misc:Depends}
 Provides: ircd
 Provides: ircd
 Conflicts: ngircd, ngircd-full
 Conflicts: ngircd, ngircd-full
-Description: A lightweight daemon for the Internet Relay Chat (IRC)
- ngIRCd is a free open source daemon for the Internet Relay Chat (IRC)
- network. It is written from scratch and is not based upon the original
- IRCd like many others.
+Description: lightweight Internet Relay Chat server
+ This package provides ngIRCd, a lightweight Internet Relay Chat
+ server for small or private networks. It is simple to configure, can
+ cope with dynamic IP addresses, and supports IPv6 as well as SSL. It
+ is written from scratch, not based on the original IRCd and quite
+ portable.
  .
  .
  In addition to the features of the "standard package", this package
  In addition to the features of the "standard package", this package
  includes support for TCP wrappers, IDENT requests, the IPv6 protocol and
  includes support for TCP wrappers, IDENT requests, the IPv6 protocol and
@@ -67,12 +63,3 @@ Description: A lightweight daemon for the Internet Relay Chat (IRC)
  .
  .
  And in addition to the "full" variant, the binaries contained in this
  And in addition to the "full" variant, the binaries contained in this
  package are build with debug code and contain debug symbols.
  package are build with debug code and contain debug symbols.
- .
- Advantages of ngIRCd:
-  - no problems with servers using changing/non-static IP addresses.
-  - small and lean configuration file.
-  - free, modern and open source C code.
-  - still under active development.
- .
- ngIRCd is compatible to the "original" ircd 2.10.3p3, so you can run
- mixed networks.

+ 8 - 1
contrib/MacOSX/config.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de).
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -74,6 +74,8 @@
 /* Define to 1 if you have the <netinet/ip.h> header file. */
 /* Define to 1 if you have the <netinet/ip.h> header file. */
 #define HAVE_NETINET_IP_H 1
 #define HAVE_NETINET_IP_H 1
 
 
+/* Define to 1 if you have the `gai_strerror' function. */
+#define HAVE_GAI_STRERROR 1
 /* Define to 1 if you have the `kqueue' function. */
 /* Define to 1 if you have the `kqueue' function. */
 #define HAVE_KQUEUE 1
 #define HAVE_KQUEUE 1
 /* Define to 1 if you have the `inet_ntoa' function. */
 /* Define to 1 if you have the `inet_ntoa' function. */
@@ -103,10 +105,15 @@
 #ifdef PAM
 #ifdef PAM
 /* Define to 1 if you have the `pam_authenticate' function. */
 /* Define to 1 if you have the `pam_authenticate' function. */
 #define HAVE_PAM_AUTHENTICATE 1
 #define HAVE_PAM_AUTHENTICATE 1
+#if (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1060)
 /* Define to 1 if you have the <pam/pam_appl.h> header file. */
 /* Define to 1 if you have the <pam/pam_appl.h> header file. */
 #define HAVE_PAM_PAM_APPL_H 1
 #define HAVE_PAM_PAM_APPL_H 1
 /* Mac OS X <10.6 doesn't have pam_fail_delay() */
 /* Mac OS X <10.6 doesn't have pam_fail_delay() */
 #define NO_PAM_FAIL_DELAY 1
 #define NO_PAM_FAIL_DELAY 1
+#else
+/* Define to 1 if you have the <security/pam_appl.h> header file. */
+#define HAVE_SECURITY_PAM_APPL_H 1
+#endif
 #endif
 #endif
 
 
 /* -eof- */
 /* -eof- */

+ 29 - 15
contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj

@@ -3,7 +3,7 @@
 	archiveVersion = 1;
 	archiveVersion = 1;
 	classes = {
 	classes = {
 	};
 	};
-	objectVersion = 44;
+	objectVersion = 46;
 	objects = {
 	objects = {
 
 
 /* Begin PBXBuildFile section */
 /* Begin PBXBuildFile section */
@@ -40,6 +40,7 @@
 		FA99428C10E82A27007F27ED /* proc.c in Sources */ = {isa = PBXBuildFile; fileRef = FA99428B10E82A27007F27ED /* proc.c */; };
 		FA99428C10E82A27007F27ED /* proc.c in Sources */ = {isa = PBXBuildFile; fileRef = FA99428B10E82A27007F27ED /* proc.c */; };
 		FAA3D27B0F139CDC00B2447E /* conn-ssl.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA3D2790F139CDC00B2447E /* conn-ssl.c */; };
 		FAA3D27B0F139CDC00B2447E /* conn-ssl.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA3D2790F139CDC00B2447E /* conn-ssl.c */; };
 		FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA97C55124A271400D5BBA9 /* sighandlers.c */; };
 		FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA97C55124A271400D5BBA9 /* sighandlers.c */; };
+		FAACD5F514A6099C006ED74F /* class.c in Sources */ = {isa = PBXBuildFile; fileRef = FAACD5F314A6099C006ED74F /* class.c */; };
 		FAE5CC2E0CF2308A007D69B6 /* numeric.c in Sources */ = {isa = PBXBuildFile; fileRef = FAE5CC2D0CF2308A007D69B6 /* numeric.c */; };
 		FAE5CC2E0CF2308A007D69B6 /* numeric.c in Sources */ = {isa = PBXBuildFile; fileRef = FAE5CC2D0CF2308A007D69B6 /* numeric.c */; };
 /* End PBXBuildFile section */
 /* End PBXBuildFile section */
 
 
@@ -170,7 +171,6 @@
 		FA322D8E0CEF7523001761B3 /* ngIRCd.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = ngIRCd.xcodeproj; sourceTree = "<group>"; };
 		FA322D8E0CEF7523001761B3 /* ngIRCd.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = ngIRCd.xcodeproj; sourceTree = "<group>"; };
 		FA322D910CEF7523001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		FA322D910CEF7523001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		FA322D920CEF7523001761B3 /* ngindent */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngindent; sourceTree = "<group>"; };
 		FA322D920CEF7523001761B3 /* ngindent */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngindent; sourceTree = "<group>"; };
-		FA322D930CEF7523001761B3 /* ngircd.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = ngircd.sh; sourceTree = "<group>"; };
 		FA322D940CEF7523001761B3 /* ngircd.spec */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngircd.spec; sourceTree = "<group>"; };
 		FA322D940CEF7523001761B3 /* ngircd.spec */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngircd.spec; sourceTree = "<group>"; };
 		FA322D950CEF7523001761B3 /* README */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
 		FA322D950CEF7523001761B3 /* README */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
 		FA322D960CEF7523001761B3 /* systrace.policy */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = systrace.policy; sourceTree = "<group>"; };
 		FA322D960CEF7523001761B3 /* systrace.policy */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = systrace.policy; sourceTree = "<group>"; };
@@ -196,6 +196,10 @@
 		FA407F2C0DB159F400271AF1 /* ng_ipaddr.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; name = ng_ipaddr.c; path = ipaddr/ng_ipaddr.c; sourceTree = "<group>"; };
 		FA407F2C0DB159F400271AF1 /* ng_ipaddr.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; name = ng_ipaddr.c; path = ipaddr/ng_ipaddr.c; sourceTree = "<group>"; };
 		FA407F2D0DB159F400271AF1 /* ng_ipaddr.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; name = ng_ipaddr.h; path = ipaddr/ng_ipaddr.h; sourceTree = "<group>"; };
 		FA407F2D0DB159F400271AF1 /* ng_ipaddr.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; name = ng_ipaddr.h; path = ipaddr/ng_ipaddr.h; sourceTree = "<group>"; };
 		FA407F380DB15AC700271AF1 /* GIT.txt */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = GIT.txt; sourceTree = "<group>"; };
 		FA407F380DB15AC700271AF1 /* GIT.txt */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = GIT.txt; sourceTree = "<group>"; };
+		FA4B08E513E7F8FB00765BA3 /* ngircd-bsd.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "ngircd-bsd.sh"; sourceTree = "<group>"; };
+		FA4B08E613E7F91700765BA3 /* ngIRCd-Logo.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = "ngIRCd-Logo.gif"; sourceTree = "<group>"; };
+		FA4B08E713E7F91700765BA3 /* ngircd-redhat.init */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "ngircd-redhat.init"; sourceTree = "<group>"; };
+		FA4B08E813E7F91C00765BA3 /* platformtest.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = platformtest.sh; sourceTree = "<group>"; };
 		FA77849A133FB9FF00740057 /* sample-ngircd.conf.tmpl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "sample-ngircd.conf.tmpl"; sourceTree = "<group>"; };
 		FA77849A133FB9FF00740057 /* sample-ngircd.conf.tmpl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "sample-ngircd.conf.tmpl"; sourceTree = "<group>"; };
 		FA85178A0FA061EC006A1F5A /* op.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = op.h; sourceTree = "<group>"; };
 		FA85178A0FA061EC006A1F5A /* op.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = op.h; sourceTree = "<group>"; };
 		FA85178B0FA061EC006A1F5A /* op.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = op.c; sourceTree = "<group>"; };
 		FA85178B0FA061EC006A1F5A /* op.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = op.c; sourceTree = "<group>"; };
@@ -225,6 +229,8 @@
 		FAA3D28B0F139D2E00B2447E /* preinstall.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = preinstall.sh; sourceTree = "<group>"; };
 		FAA3D28B0F139D2E00B2447E /* preinstall.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = preinstall.sh; sourceTree = "<group>"; };
 		FAA97C55124A271400D5BBA9 /* sighandlers.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = sighandlers.c; sourceTree = "<group>"; };
 		FAA97C55124A271400D5BBA9 /* sighandlers.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = sighandlers.c; sourceTree = "<group>"; };
 		FAA97C56124A271400D5BBA9 /* sighandlers.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = sighandlers.h; sourceTree = "<group>"; };
 		FAA97C56124A271400D5BBA9 /* sighandlers.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = sighandlers.h; sourceTree = "<group>"; };
+		FAACD5F314A6099C006ED74F /* class.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = class.c; sourceTree = "<group>"; };
+		FAACD5F414A6099C006ED74F /* class.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = class.h; sourceTree = "<group>"; };
 		FAE5CC2C0CF2308A007D69B6 /* numeric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = numeric.h; sourceTree = "<group>"; };
 		FAE5CC2C0CF2308A007D69B6 /* numeric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = numeric.h; sourceTree = "<group>"; };
 		FAE5CC2D0CF2308A007D69B6 /* numeric.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = numeric.c; sourceTree = "<group>"; };
 		FAE5CC2D0CF2308A007D69B6 /* numeric.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = numeric.c; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 /* End PBXFileReference section */
@@ -292,17 +298,19 @@
 		FA322CD70CEF74B1001761B3 /* ngircd */ = {
 		FA322CD70CEF74B1001761B3 /* ngircd */ = {
 			isa = PBXGroup;
 			isa = PBXGroup;
 			children = (
 			children = (
-				FAA3D2780F139CDC00B2447E /* conf-ssl.h */,
 				FAA3D2790F139CDC00B2447E /* conn-ssl.c */,
 				FAA3D2790F139CDC00B2447E /* conn-ssl.c */,
 				FAA3D27A0F139CDC00B2447E /* conn-ssl.h */,
 				FAA3D27A0F139CDC00B2447E /* conn-ssl.h */,
 				FA322CD90CEF74B1001761B3 /* array.c */,
 				FA322CD90CEF74B1001761B3 /* array.c */,
 				FA322CDA0CEF74B1001761B3 /* array.h */,
 				FA322CDA0CEF74B1001761B3 /* array.h */,
 				FA322CDB0CEF74B1001761B3 /* channel.c */,
 				FA322CDB0CEF74B1001761B3 /* channel.c */,
 				FA322CDC0CEF74B1001761B3 /* channel.h */,
 				FA322CDC0CEF74B1001761B3 /* channel.h */,
+				FAACD5F314A6099C006ED74F /* class.c */,
+				FAACD5F414A6099C006ED74F /* class.h */,
 				FA322CDD0CEF74B1001761B3 /* client.c */,
 				FA322CDD0CEF74B1001761B3 /* client.c */,
 				FA322CDE0CEF74B1001761B3 /* client.h */,
 				FA322CDE0CEF74B1001761B3 /* client.h */,
 				FA322CDF0CEF74B1001761B3 /* conf.c */,
 				FA322CDF0CEF74B1001761B3 /* conf.c */,
 				FA322CE00CEF74B1001761B3 /* conf.h */,
 				FA322CE00CEF74B1001761B3 /* conf.h */,
+				FAA3D2780F139CDC00B2447E /* conf-ssl.h */,
 				FA322CE10CEF74B1001761B3 /* conn-func.c */,
 				FA322CE10CEF74B1001761B3 /* conn-func.c */,
 				FA322CE20CEF74B1001761B3 /* conn-func.h */,
 				FA322CE20CEF74B1001761B3 /* conn-func.h */,
 				FA322CE30CEF74B1001761B3 /* conn-zip.c */,
 				FA322CE30CEF74B1001761B3 /* conn-zip.c */,
@@ -346,6 +354,8 @@
 				FAE5CC2C0CF2308A007D69B6 /* numeric.h */,
 				FAE5CC2C0CF2308A007D69B6 /* numeric.h */,
 				FA85178B0FA061EC006A1F5A /* op.c */,
 				FA85178B0FA061EC006A1F5A /* op.c */,
 				FA85178A0FA061EC006A1F5A /* op.h */,
 				FA85178A0FA061EC006A1F5A /* op.h */,
+				FA2D564911EA158B00D37A35 /* pam.c */,
+				FA2D564811EA158B00D37A35 /* pam.h */,
 				FA322D080CEF74B1001761B3 /* parse.c */,
 				FA322D080CEF74B1001761B3 /* parse.c */,
 				FA322D090CEF74B1001761B3 /* parse.h */,
 				FA322D090CEF74B1001761B3 /* parse.h */,
 				FA99428B10E82A27007F27ED /* proc.c */,
 				FA99428B10E82A27007F27ED /* proc.c */,
@@ -354,8 +364,6 @@
 				FA322D0D0CEF74B1001761B3 /* resolve.h */,
 				FA322D0D0CEF74B1001761B3 /* resolve.h */,
 				FAA97C55124A271400D5BBA9 /* sighandlers.c */,
 				FAA97C55124A271400D5BBA9 /* sighandlers.c */,
 				FAA97C56124A271400D5BBA9 /* sighandlers.h */,
 				FAA97C56124A271400D5BBA9 /* sighandlers.h */,
-				FA2D564811EA158B00D37A35 /* pam.h */,
-				FA2D564911EA158B00D37A35 /* pam.c */,
 			);
 			);
 			path = ngircd;
 			path = ngircd;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -432,8 +440,11 @@
 				FA322D730CEF7523001761B3 /* MacOSX */,
 				FA322D730CEF7523001761B3 /* MacOSX */,
 				FA322D910CEF7523001761B3 /* Makefile.am */,
 				FA322D910CEF7523001761B3 /* Makefile.am */,
 				FA322D920CEF7523001761B3 /* ngindent */,
 				FA322D920CEF7523001761B3 /* ngindent */,
-				FA322D930CEF7523001761B3 /* ngircd.sh */,
+				FA4B08E513E7F8FB00765BA3 /* ngircd-bsd.sh */,
+				FA4B08E613E7F91700765BA3 /* ngIRCd-Logo.gif */,
+				FA4B08E713E7F91700765BA3 /* ngircd-redhat.init */,
 				FA322D940CEF7523001761B3 /* ngircd.spec */,
 				FA322D940CEF7523001761B3 /* ngircd.spec */,
+				FA4B08E813E7F91C00765BA3 /* platformtest.sh */,
 				FA322D950CEF7523001761B3 /* README */,
 				FA322D950CEF7523001761B3 /* README */,
 				FA322D960CEF7523001761B3 /* systrace.policy */,
 				FA322D960CEF7523001761B3 /* systrace.policy */,
 			);
 			);
@@ -641,8 +652,11 @@
 /* Begin PBXProject section */
 /* Begin PBXProject section */
 		08FB7793FE84155DC02AAC07 /* Project object */ = {
 		08FB7793FE84155DC02AAC07 /* Project object */ = {
 			isa = PBXProject;
 			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0420;
+			};
 			buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "ngIRCd" */;
 			buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "ngIRCd" */;
-			compatibilityVersion = "Xcode 3.0";
+			compatibilityVersion = "Xcode 3.2";
 			developmentRegion = English;
 			developmentRegion = English;
 			hasScannedForEncodings = 1;
 			hasScannedForEncodings = 1;
 			knownRegions = (
 			knownRegions = (
@@ -703,6 +717,7 @@
 				FA99428C10E82A27007F27ED /* proc.c in Sources */,
 				FA99428C10E82A27007F27ED /* proc.c in Sources */,
 				FA2D564A11EA158B00D37A35 /* pam.c in Sources */,
 				FA2D564A11EA158B00D37A35 /* pam.c in Sources */,
 				FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */,
 				FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */,
+				FAACD5F514A6099C006ED74F /* class.c in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};
@@ -713,6 +728,7 @@
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
 				GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
 				GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
@@ -738,13 +754,11 @@
 		1DEB928B08733DD80010E9CD /* Default */ = {
 		1DEB928B08733DD80010E9CD /* Default */ = {
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
-				ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)";
-				ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc";
-				GCC_VERSION = 4.0;
+				ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+				GCC_VERSION = 4.2;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				PREBINDING = NO;
-				SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk";
+				SDKROOT = macosx10.6;
 			};
 			};
 			name = Default;
 			name = Default;
 		};
 		};
@@ -754,12 +768,11 @@
 				ARCHS = "$(NATIVE_ARCH_ACTUAL)";
 				ARCHS = "$(NATIVE_ARCH_ACTUAL)";
 				GCC_DEBUGGING_SYMBOLS = full;
 				GCC_DEBUGGING_SYMBOLS = full;
 				GCC_OPTIMIZATION_LEVEL = 0;
 				GCC_OPTIMIZATION_LEVEL = 0;
-				GCC_VERSION = 4.0;
+				GCC_VERSION = 4.2;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				ONLY_ACTIVE_ARCH = YES;
-				PREBINDING = NO;
-				SDKROOT = "";
+				SDKROOT = macosx;
 			};
 			};
 			name = Debug;
 			name = Debug;
 		};
 		};
@@ -767,6 +780,7 @@
 			isa = XCBuildConfiguration;
 			isa = XCBuildConfiguration;
 			buildSettings = {
 			buildSettings = {
 				GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
 				GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;

+ 1 - 0
contrib/ngindent

@@ -4,6 +4,7 @@ INDENTARGS="-kr -i8 -ts8 -l80 -c3 -cd41 -ss -ncs -psl"
 
 
 # check if indent(1) is available
 # check if indent(1) is available
 type indent >/dev/null 2>&1 && INDENT="indent"
 type indent >/dev/null 2>&1 && INDENT="indent"
+type gindent >/dev/null 2>&1 && INDENT="gindent"
 type gnuindent >/dev/null 2>&1 && INDENT="gnuindent"
 type gnuindent >/dev/null 2>&1 && INDENT="gnuindent"
 
 
 if [ -z "$INDENT" ]; then
 if [ -z "$INDENT" ]; then

+ 12 - 11
contrib/ngircd.spec

@@ -1,5 +1,5 @@
 %define name    ngircd
 %define name    ngircd
-%define version 18
+%define version 19
 %define release 1
 %define release 1
 %define prefix  %{_prefix}
 %define prefix  %{_prefix}
 
 
@@ -15,18 +15,19 @@ BuildRoot:    %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 BuildRequires:  zlib-devel, openssl-devel
 BuildRequires:  zlib-devel, openssl-devel
 
 
 %description
 %description
-ngIRCd is a free open source daemon for the Internet Relay Chat (IRC),
-developed under the GNU General Public License (GPL). It's written from
-scratch and is not based upon the original IRCd like many others.
+This package provides ngIRCd, a lightweight Internet Relay Chat
+server for small or private networks. It is simple to configure, can
+cope with dynamic IP addresses, and supports IPv6 as well as SSL. It
+is written from scratch, not based on the original IRCd and quite
+portable.
 
 
 Advantages:
 Advantages:
- - no problems with servers using changing/non-static IP addresses.
- - small and lean configuration file.
- - free, modern and open source C code.
- - still under active development.
-
-ngIRCd is compatible to the "original" ircd 2.10.3p3, so you can run
-mixed networks.
+ - well arranged (lean) configuration file
+ - simple to build/install, configure and maintain
+ - supports IPv6 and SSL
+ - no problems with servers that have dynamic IP addresses
+ - freely available, modern, portable and tidy C-source
+ - ngIRCd is being actively developed since 11 years.
 
 
 %prep
 %prep
 %setup -q
 %setup -q

+ 24 - 13
doc/GIT.txt

@@ -9,13 +9,23 @@
                                  -- GIT.txt --
                                  -- GIT.txt --
 
 
 
 
-The source code of ngIRCd is maintained using git, the stupid content
-tracker.
+The source code of ngIRCd is maintained using GIT, an distributed version
+control system. Homepage including documentation: <http://git-scm.com/>.
 
 
 
 
-I. Getting the source code
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-To access the source tree anonymously, run:
+I. Viewing the source code online
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ngIRCd "GITweb" interface allows you to browse the GIT repository and
+to see all individual files, tags, branches, commits etc.:
+
+ <http://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd.git>
+
+
+II. Getting the source code
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To access (copy, clone) the source tree repository anonymously, run:
 
 
  $ git clone git://ngircd.barton.de/ngircd.git
  $ git clone git://ngircd.barton.de/ngircd.git
 
 
@@ -23,23 +33,23 @@ Thereby a new folder "ngircd" will be created containing all the individual
 source files.
 source files.
 
 
 The newly created directory ("ngircd") is the "working directory", all
 The newly created directory ("ngircd") is the "working directory", all
-git commands will be executed from within this directory in the future.
+GIT commands will be executed from within this directory in the future.
 
 
-Please note: When checking out a fresh copy of ngIRCd using git, the
+Please note: When checking out a fresh copy of ngIRCd using GIT, the
 configure script doesn't exist; you have to run the autogen.sh shell script
 configure script doesn't exist; you have to run the autogen.sh shell script
 (which is included in the source tree) to generate it. This requires you to
 (which is included in the source tree) to generate it. This requires you to
 have GNU automake and GNU autoconf installed on your system. Please see the
 have GNU automake and GNU autoconf installed on your system. Please see the
 file INSTALL for details!
 file INSTALL for details!
 
 
-To update the git tree:
+To update the local GIT repository:
 
 
  $ git pull
  $ git pull
 
 
 This retrieves all changes and merges them into the current branch.
 This retrieves all changes and merges them into the current branch.
 
 
 
 
-II. Contributing
-~~~~~~~~~~~~~~~~
+III. Contributing
+~~~~~~~~~~~~~~~~~
 
 
 Patches should be sent to the ngircd mailing list. List homepage:
 Patches should be sent to the ngircd mailing list. List homepage:
 http://arthur.barton.de/mailman/listinfo/ngircd-ml
 http://arthur.barton.de/mailman/listinfo/ngircd-ml
@@ -48,7 +58,8 @@ If you do not want to send them to the list, you can also mail them
 to Alex Barton, <alex@barton.de>.
 to Alex Barton, <alex@barton.de>.
 
 
 
 
-III. Write Access
-~~~~~~~~~~~~~~~~~
-If you want to contribute a couple of patches and write access to the git
+IV. Write Access
+~~~~~~~~~~~~~~~~
+
+If you want to contribute a couple of patches and write access to the GIT
 repository would be handy, please contact Alex Barton, <alex@barton.de>.
 repository would be handy, please contact Alex Barton, <alex@barton.de>.

+ 9 - 10
doc/Makefile.am

@@ -1,13 +1,12 @@
 #
 #
 # ngIRCd -- The Next Generation IRC Daemon
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+# Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors
 #
 #
-# Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
-# der GNU General Public License (GPL), wie von der Free Software Foundation
-# herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
-# der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
-# Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
-# der an ngIRCd beteiligten Autoren finden Sie in der Datei 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 2 of the License, or
+# (at your option) any later version.
+# Please read the file COPYING, README and AUTHORS for more information.
 #
 #
 
 
 .tmpl:
 .tmpl:
@@ -17,9 +16,9 @@
 
 
 SUFFIXES = .tmpl
 SUFFIXES = .tmpl
 
 
-static_docs = Bopm.txt FAQ.txt GIT.txt HowToRelease.txt PAM.txt Platforms.txt \
-	Protocol.txt README-AUX.txt README-BeOS.txt README-Interix.txt RFC.txt \
-	SSL.txt Services.txt
+static_docs = Bopm.txt FAQ.txt GIT.txt HowToRelease.txt Modes.txt PAM.txt \
+	Platforms.txt Protocol.txt README-AUX.txt README-BeOS.txt \
+	README-Interix.txt RFC.txt SSL.txt Services.txt
 
 
 doc_templates = sample-ngircd.conf.tmpl
 doc_templates = sample-ngircd.conf.tmpl
 
 

+ 9 - 10
doc/Makefile.in

@@ -17,14 +17,13 @@
 
 
 #
 #
 # ngIRCd -- The Next Generation IRC Daemon
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+# Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors
 #
 #
-# Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
-# der GNU General Public License (GPL), wie von der Free Software Foundation
-# herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
-# der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
-# Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
-# der an ngIRCd beteiligten Autoren finden Sie in der Datei 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 2 of the License, or
+# (at your option) any later version.
+# Please read the file COPYING, README and AUTHORS for more information.
 #
 #
 VPATH = @srcdir@
 VPATH = @srcdir@
 pkgdatadir = $(datadir)/@PACKAGE@
 pkgdatadir = $(datadir)/@PACKAGE@
@@ -208,9 +207,9 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 top_srcdir = @top_srcdir@
 SUFFIXES = .tmpl
 SUFFIXES = .tmpl
-static_docs = Bopm.txt FAQ.txt GIT.txt HowToRelease.txt PAM.txt Platforms.txt \
-	Protocol.txt README-AUX.txt README-BeOS.txt README-Interix.txt RFC.txt \
-	SSL.txt Services.txt
+static_docs = Bopm.txt FAQ.txt GIT.txt HowToRelease.txt Modes.txt PAM.txt \
+	Platforms.txt Protocol.txt README-AUX.txt README-BeOS.txt \
+	README-Interix.txt RFC.txt SSL.txt Services.txt
 
 
 doc_templates = sample-ngircd.conf.tmpl
 doc_templates = sample-ngircd.conf.tmpl
 generated_docs = sample-ngircd.conf
 generated_docs = sample-ngircd.conf

+ 76 - 0
doc/Modes.txt

@@ -0,0 +1,76 @@
+
+                     ngIRCd - Next Generation IRC Server
+                           http://ngircd.barton.de/
+
+               (c)2001-2011 Alexander Barton and Contributors.
+               ngIRCd is free software and published under the
+                   terms of the GNU General Public License.
+
+                               -- Modes.txt --
+
+
+This document lists the different user modes, channel modes, and channel
+user modes that ngIRCd supports.
+
+
+I. User Modes
+~~~~~~~~~~~~~
+
+User modes are attributes a user has in the network, regardless of the
+channels he is using at the moment.
+
+  mode	since	description
+
+  a	0.3.0	User is away.
+  c	17	IRC operator wants to receive connect/disconnect NOTICEs.
+  C	19	Only users that share a channel are allowed to send messages.
+  i	0.0.1	User is "invisible".
+  o	0.0.1	User is IRC operator.
+  r	0.0.1	User is restricted.
+  R (1)	19	User is registered (e.g. by NickServ).
+  s	0.4.0	User wants to receive server notices.
+  w	0.11.0	User wants to receive WALLOPS messages.
+  x	17	Hostname of this user is "cloaked".
+
+II. Channel Modes
+~~~~~~~~~~~~~~~~~
+
+Channel modes are attributes of specific channels which are valid for all
+users joined (or trying to join) to this channel. Some modes add and remove
+users to lists (e.g. "invite list", "ban list"), others have parameters
+(like "channel key"), most are simple flags (like "moderated").
+
+  mode	since	description
+
+  b	0.5.0	Add/remove a host mask to the ban list.
+  i	0.5.0	Channel is "invite only".
+  I	0.5.0	Add/remove a host mask to the invite list.
+  k	0.6.0	Channel has a "key" (a password).
+  l	0.6.0	Channel has a user limit.
+  m	0.3.0	Channel is moderated, only "voiced" users can send messages.
+  n	0.3.0	Channel doesn't allow messages of users not being members.
+  O	18	Only IRC operators are allowed to join this channel.
+  P	0.5.0	Channel is "persistent".
+  r (1)	19	Channel is "registered" (e.g. by ChanServ).
+  R	19	Only registered users are allowed to join this channel.
+  s	0.9.0	Channel is "secret".
+  t	0.3.0	Only ChanOps are allowed to modify the channel topic.
+  z	16	Only users connected via SSL are allowed to join the channel.
+
+III. Channel User Modes
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Channel user modes are attributes that a particular user has in a specific
+channel of which he is a member.
+
+  mode	since	description
+
+  o	0.2.0	User is channel operator and can op/kick/... other members.
+  v	0.2.0	User is "voiced" and can speak even if channel is moderated.
+
+
+Notes
+~~~~~
+
+(1) This mode is not set by ngIRCd itself but by services. ngIRCd handles
+    the mode transparently and possibly adjusts its behaviour.

+ 19 - 16
doc/Platforms.txt

@@ -2,7 +2,7 @@
                      ngIRCd - Next Generation IRC Server
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
                            http://ngircd.barton.de/
 
 
-               (c)2001-2011 Alexander Barton and Contributors.
+               (c)2001-2012 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
                    terms of the GNU General Public License.
 
 
@@ -31,22 +31,23 @@ hppa1.1/unknown/linux-gnu   gcc 3.3.3    0.8.0      04-05-30 alex   Y Y Y Y
 hppa2.0/unknown/linux-gnu   gcc 3.3.5    13~rc1     08-12-02 alex   Y Y Y Y
 hppa2.0/unknown/linux-gnu   gcc 3.3.5    13~rc1     08-12-02 alex   Y Y Y Y
 hppa2.0w-hp-hpux11.11       gcc 4.2.3    14.1       09-07-22 goetz  Y Y Y Y
 hppa2.0w-hp-hpux11.11       gcc 4.2.3    14.1       09-07-22 goetz  Y Y Y Y
 i386/apple/darwin9.7.0      gcc 4.0.1    14.1       09-08-04 alex   Y Y Y Y (3)
 i386/apple/darwin9.7.0      gcc 4.0.1    14.1       09-08-04 alex   Y Y Y Y (3)
-i386/apple/darwin10.7.0     gcc 4.2.1    18         11-07-05 alex   Y Y Y Y (3)
-i386/apple/darwin11.0.0     gcc 4.2.1    18         11-07-02 alex   Y Y Y Y (3)
+i386/apple/darwin10.8.0     gcc 4.2.1    19         12-02-26 alex   Y Y Y Y (3)
+i386/apple/darwin11.3.0     gcc 4.2.1    19         12-02-26 alex   Y Y Y Y (3)
 i386/pc/solaris2.9          gcc 3.2.2    CVSHEAD    04-02-24 alex   Y Y Y Y
 i386/pc/solaris2.9          gcc 3.2.2    CVSHEAD    04-02-24 alex   Y Y Y Y
-i386/pc/solaris2.11         gcc 3.4.3    18         11-07-10 alex   Y Y N Y (4)
+i386/pc/solaris2.11         gcc 3.4.3    19         12-02-26 alex   Y Y N Y (4)
+i386/pc/solaris2.11         gcc 4.2.3    18         11-08-17 goetz  Y Y Y Y (4)
 i386/unknown/freebsd5.2.1   gcc 3.3.3    0.8.0      04-05-30 alex   Y Y Y Y
 i386/unknown/freebsd5.2.1   gcc 3.3.3    0.8.0      04-05-30 alex   Y Y Y Y
-i386/unknown/freebsd6.2     gcc 3.4.6    18         11-07-10 alex   Y Y Y Y (3)
-i386/unknown/freebsd7.3     gcc 4.2.1    18         11-07-1ß alex   Y Y Y Y (3)
-i686/unknown/gnu0.3         gcc 4.4.5    18         11-07-10 alex   Y Y Y Y
+i386/unknown/freebsd6.2     gcc 3.4.6    19         12-02-26 alex   Y Y Y Y (3)
+i386/unknown/freebsd7.3     gcc 4.2.1    19         12-02-26 alex   Y Y Y Y (3)
+i686/unknown/gnu0.3         gcc 4.4.5    19         12-02-29 alex   Y Y Y Y
 i686/unkn./kfreebsd7.2-gnu  gcc 4.3.4    15         09-12-02 alex   Y Y Y Y (3)
 i686/unkn./kfreebsd7.2-gnu  gcc 4.3.4    15         09-12-02 alex   Y Y Y Y (3)
 i386/unknown/netbsdelf1.6.2 gcc 2.95.3   18         11-07-10 goetz  Y Y Y Y
 i386/unknown/netbsdelf1.6.2 gcc 2.95.3   18         11-07-10 goetz  Y Y Y Y
 i386/unknown/netbsdelf3.0.1 gcc 3.3.3    0.10.0-p1  06-08-30 alex   Y Y Y Y (3)
 i386/unknown/netbsdelf3.0.1 gcc 3.3.3    0.10.0-p1  06-08-30 alex   Y Y Y Y (3)
-i386/unknown/netbsdelf4.0   gcc 4.1.2    18         11-07-10 alex   Y Y Y Y (3)
-i386/unknown/netbsdelf5.0.2 gcc 4.1.3    18         11-07-10 alex   Y Y Y Y (3)
+i386/unknown/netbsdelf4.0   gcc 4.1.2    19         12-02-29 alex   Y Y Y Y (3)
+i386/unknown/netbsdelf5.0.2 gcc 4.1.3    19         12-02-26 alex   Y Y Y Y (3)
 i386/unknown/openbsd3.9     gcc 3.3.5    0.10.0-p1  06-08-30 alex   Y Y Y Y (3)
 i386/unknown/openbsd3.9     gcc 3.3.5    0.10.0-p1  06-08-30 alex   Y Y Y Y (3)
 i386/unknown/openbsd4.1     gcc 3.3.5    16         10-04-11 alex   Y Y Y Y (3)
 i386/unknown/openbsd4.1     gcc 3.3.5    16         10-04-11 alex   Y Y Y Y (3)
-i586/pc/interix3.5          gcc 3.3      18         11-07-10 alex   Y Y N Y
+i586/pc/interix3.5          gcc 3.3      19         12-02-29 alex   Y Y N Y
 i686/pc/cygwin              gcc 3.3.1    0.8.0      04-05-30 alex   Y Y N Y
 i686/pc/cygwin              gcc 3.3.1    0.8.0      04-05-30 alex   Y Y N Y
 i686/pc/linux-gnu           gcc 2.95.4   0.8.0      04-05-30 alex   Y Y Y Y (1)
 i686/pc/linux-gnu           gcc 2.95.4   0.8.0      04-05-30 alex   Y Y Y Y (1)
 i686/pc/linux-gnu           gcc 3.3.5    14.1       09-08-04 alex   Y Y Y Y (1)
 i686/pc/linux-gnu           gcc 3.3.5    14.1       09-08-04 alex   Y Y Y Y (1)
@@ -54,11 +55,12 @@ i386/pc/linux-gnu           gcc 4.1.2    13~rc1     08-12-05 alex   Y Y Y Y (1)
 i686/pc/linux-gnu           gcc 4.3.2    14.1       09-08-04 alex   Y Y Y Y (1)
 i686/pc/linux-gnu           gcc 4.3.2    14.1       09-08-04 alex   Y Y Y Y (1)
 m68k/apple/aux3.0.1         gcc 2.7.2    17         10-11-07 alex   Y Y N Y
 m68k/apple/aux3.0.1         gcc 2.7.2    17         10-11-07 alex   Y Y N Y
 m68k/apple/aux3.0.1         Orig. A/UX   17         10-11-07 alex   Y Y N Y (2)
 m68k/apple/aux3.0.1         Orig. A/UX   17         10-11-07 alex   Y Y N Y (2)
-m68k/apple/aux3.1.1         gcc 2.7.2    18         11-07-02 alex   Y Y N Y
-m68k/apple/aux3.1.1         Orig. A/UX   18         11-07-02 alex   Y Y N Y (2)
+m68k/apple/aux3.1.1         gcc 2.7.2    19         12-02-26 alex   Y Y N Y
+m68k/apple/aux3.1.1         Orig. A/UX   19         12-02-26 alex   Y Y N Y (2)
 m68k/hp/hp-ux9.10           Orig. HPUX   0.7.x-CVS  03-04-30 goetz  Y Y Y Y
 m68k/hp/hp-ux9.10           Orig. HPUX   0.7.x-CVS  03-04-30 goetz  Y Y Y Y
 m88k/dg/dgux5.4R3.10        gcc 2.5.8    CVSHEAD    04-03-15 alex   Y Y ? ?
 m88k/dg/dgux5.4R3.10        gcc 2.5.8    CVSHEAD    04-03-15 alex   Y Y ? ?
 mipsel/unknown/linux-gnu    gcc 4.1.2    18         11-07-05 goetz  Y Y N Y (1)
 mipsel/unknown/linux-gnu    gcc 4.1.2    18         11-07-05 goetz  Y Y N Y (1)
+mipsel/unknown/linux-gnu    gcc 4.4.5    18         11-07-30 goetz  Y Y Y Y (1)
 powerpc/apple/darwin6.5     gcc 3.1      0.7.x-CVS  03-04-23 alex   Y Y Y Y
 powerpc/apple/darwin6.5     gcc 3.1      0.7.x-CVS  03-04-23 alex   Y Y Y Y
 powerpc/apple/darwin7.9.0   gcc 3.3      CVSHEAD    06-05-07 fw     Y Y Y Y (3)
 powerpc/apple/darwin7.9.0   gcc 3.3      CVSHEAD    06-05-07 fw     Y Y Y Y (3)
 powerpc/apple/darwin8.11.0  gcc 4.0.1    18         11-07-02 goetz  Y Y Y Y (3)
 powerpc/apple/darwin8.11.0  gcc 4.0.1    18         11-07-02 goetz  Y Y Y Y (3)
@@ -67,15 +69,16 @@ powerpc/unknown/openbsd3.6  gcc 2.95.3   0.10.0     06-10-08 alex   Y Y N Y
 sparc/sun/solaris2.6        gcc 2.95.3   0.7.x-CVS  03-04-22 alex   Y Y Y Y
 sparc/sun/solaris2.6        gcc 2.95.3   0.7.x-CVS  03-04-22 alex   Y Y Y Y
 sparc/sun/solaris2.7        gcc 3.3      0.8.0      04-05-30 alex   Y Y Y Y
 sparc/sun/solaris2.7        gcc 3.3      0.8.0      04-05-30 alex   Y Y Y Y
 sparc/unkn./netbsdelf1.6.1  gcc 2.95.3   0.8.0      04-05-30 alex   Y Y Y Y
 sparc/unkn./netbsdelf1.6.1  gcc 2.95.3   0.8.0      04-05-30 alex   Y Y Y Y
-x86_64/unknown/freebsd8.1   gcc 4.2.1    18         11-07-10 alex   Y Y Y Y (3)
-x86_64/unknown/linux-gnu    gcc 4.4.5    18         11-07-02 alex   Y Y Y Y (1)
-x86_64/unknown/openbsd4.7   gcc 3.3.5    18         11-07-10 alex   Y Y Y Y (3)
+x86_64/unknown/freebsd8.1   gcc 4.2.1    19         12-02-26 alex   Y Y Y Y (3)
+x86_64/unkn./freebsd8.1-gnu gcc 4.4.5    19         12-02-26 alex   Y Y Y Y (3)
+x86_64/unknown/linux-gnu    gcc 4.4.5    19         12-02-26 alex   Y Y Y Y (1)
+x86_64/unknown/openbsd4.7   gcc 3.3.5    19         12-02-26 alex   Y Y Y Y (3)
 
 
 
 
 Notes
 Notes
 ~~~~~
 ~~~~~
 
 
-(1) i686/pc/linux-gnu & x86_64/unknown/linux-gnu:
+(1) */*/linux-gnu (Linux platforms):
     ngIRCd has been tested with various Linux distributions, such as SuSE,
     ngIRCd has been tested with various Linux distributions, such as SuSE,
     RedHat, Debian, and Gentoo using Kernels 2.2.x, 2.4.x and 2.6.x with
     RedHat, Debian, and Gentoo using Kernels 2.2.x, 2.4.x and 2.6.x with
     various versions of the GNU C compiler (starting with 2.95.x and up to
     various versions of the GNU C compiler (starting with 2.95.x and up to

+ 17 - 10
doc/README-Interix.txt

@@ -1,12 +1,10 @@
 
 
-                    ngIRCd - Next Generation IRC Server
-
-                      (c)2001-2010 Alexander Barton,
-                   alex@barton.de, http://www.barton.de/
+                     ngIRCd - Next Generation IRC Server
+                           http://ngircd.barton.de/
 
 
+               (c)2001-2012 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                ngIRCd is free software and published under the
-                  terms of the GNU General Public License.
-
+                   terms of the GNU General Public License.
 
 
                          -- README-Interix.txt --
                          -- README-Interix.txt --
 
 
@@ -20,10 +18,13 @@ Windows Server 2003. SUA is supported on Windows Server 2003 R2, Windows
 Server 2008 & 2008 R2, Windows Vista, and Windows 7 -- so ngIRCd should be
 Server 2008 & 2008 R2, Windows Vista, and Windows 7 -- so ngIRCd should be
 able to run on all of these platforms.
 able to run on all of these platforms.
 
 
-But please note that the poll() API function is not fully implemented by
-SFU/SUA and therefore can't be used by ngIRCd -- which normally would be
-the default. Please see <http://www.suacommunity.com/faqs.aspx> section
-4.25 for details:
+But please note that two things:
+
+1. Don't use the poll() IO API
+
+The poll() API function is not fully implemented by SFU/SUA and therefore
+can't be used by ngIRCd -- which normally would be the default. Please see
+<http://www.suacommunity.com/faqs.aspx> section 4.25 for details:
 
 
   "If you do try to use the poll() API your program will block on the
   "If you do try to use the poll() API your program will block on the
   API call forever. You must direct your program to build using the
   API call forever. You must direct your program to build using the
@@ -35,3 +36,9 @@ So when running the ./configure script, you HAVE TO DISABLE poll() support:
 
 
 ngIRCd then defaults to using the select() API function which works fine.
 ngIRCd then defaults to using the select() API function which works fine.
 
 
+2. Use GNU make(1)
+
+Starting with ngIRCd 18, our build system doesn't work with the default
+make(1) binary of Interix, you should use GNU make instead (tested with
+version 3.82 built from source).
+

+ 18 - 1
doc/sample-ngircd.conf.tmpl

@@ -140,6 +140,8 @@
 	;DNS = yes
 	;DNS = yes
 
 
 	# Do IDENT lookups if ngIRCd has been compiled with support for it.
 	# Do IDENT lookups if ngIRCd has been compiled with support for it.
+	# Users identified using IDENT are registered without the "~" character
+	# prepended to their user name.
 	;Ident = yes
 	;Ident = yes
 
 
 	# Enhance user privacy slightly (useful for IRC server on TOR or I2P)
 	# Enhance user privacy slightly (useful for IRC server on TOR or I2P)
@@ -160,7 +162,22 @@
 	;OperServerMode = no
 	;OperServerMode = no
 
 
 	# Use PAM if ngIRCd has been compiled with support for it.
 	# Use PAM if ngIRCd has been compiled with support for it.
-	;PAM = no
+	# Users identified using PAM are registered without the "~" character
+	# prepended to their user name.
+	;PAM = yes
+
+	# When PAM is enabled, all clients are required to be authenticated
+	# using PAM; connecting to the server without successful PAM
+	# authentication isn't possible.
+	# If this option is set, clients not sending a password are still
+	# allowed to connect: they won't become "identified" and keep the "~"
+	# character prepended to their supplied user name.
+	# Please note: To make some use of this behavior, it most probably
+	# isn't useful to enable "Ident", "PAM" and "PAMIsOptional" at the
+	# same time, because you wouldn't be able to distinguish between
+	# Ident'ified and PAM-authenticated users: both don't have a "~"
+	# character prepended to their respective user names!
+	;PAMIsOptional = no
 
 
 	# Allow Pre-Defined Channels only (see Section [Channels])
 	# Allow Pre-Defined Channels only (see Section [Channels])
 	;PredefChannelsOnly = no
 	;PredefChannelsOnly = no

+ 19 - 3
man/ngircd.conf.5.tmpl

@@ -132,9 +132,8 @@ the pidfile resides in must be writable by the ngIRCd user and exist in the
 chroot directory (if configured, see above).
 chroot directory (if configured, see above).
 .TP
 .TP
 \fBPorts\fR (list of numbers)
 \fBPorts\fR (list of numbers)
-Ports on which the server should listen. There may be more than one port,
-separated with commas (","). Default: 6667, unless \fBSSL_Ports\fR are also
-specified.
+Ports on which the server should listen for unencrypted connections. There
+may be more than one port, separated with commas (","). Default: 6667.
 .TP
 .TP
 \fBServerGID\fR (string or number)
 \fBServerGID\fR (string or number)
 Group ID under which the ngIRCd should run; you can use the name of the
 Group ID under which the ngIRCd should run; you can use the name of the
@@ -244,6 +243,8 @@ Default: yes.
 \fBIdent\fR (boolean)
 \fBIdent\fR (boolean)
 If ngIRCd is compiled with IDENT support this can be used to disable IDENT
 If ngIRCd is compiled with IDENT support this can be used to disable IDENT
 lookups at run time.
 lookups at run time.
+Users identified using IDENT are registered without the "~" character
+prepended to their user name.
 Default: yes.
 Default: yes.
 .TP
 .TP
 \fBMorePrivacy\fR (boolean)
 \fBMorePrivacy\fR (boolean)
@@ -274,8 +275,23 @@ only enable it if you have ircd-irc2 servers in your IRC network.
 If ngIRCd is compiled with PAM support this can be used to disable all calls
 If ngIRCd is compiled with PAM support this can be used to disable all calls
 to the PAM library at runtime; all users connecting without password are
 to the PAM library at runtime; all users connecting without password are
 allowed to connect, all passwords given will fail.
 allowed to connect, all passwords given will fail.
+Users identified using PAM are registered without the "~" character
+prepended to their user name.
 Default: yes.
 Default: yes.
 .TP
 .TP
+\fBPAMIsOptional\fR (boolean)
+When PAM is enabled, all clients are required to be authenticated using PAM;
+connecting to the server without successful PAM authentication isn't possible.
+If this option is set, clients not sending a password are still allowed to
+connect: they won't become "identified" and keep the "~" character prepended
+to their supplied user name.
+Please note:
+To make some use of this behavior, it most probably isn't useful to enable
+"Ident", "PAM" and "PAMIsOptional" at the same time, because you wouldn't be
+able to distinguish between Ident'ified and PAM-authenticated users: both
+don't have a "~" character prepended to their respective user names!
+Default: no.
+.TP
 \fBPredefChannelsOnly\fR (boolean)
 \fBPredefChannelsOnly\fR (boolean)
 If enabled, no new channels can be created. Useful if you do not want to have
 If enabled, no new channels can be created. Useful if you do not want to have
 other channels than those defined in [Channel] sections in the configuration
 other channels than those defined in [Channel] sections in the configuration

+ 6 - 0
src/config.h.in

@@ -30,6 +30,9 @@
 /* Define to 1 if you have the `fork' function. */
 /* Define to 1 if you have the `fork' function. */
 #undef HAVE_FORK
 #undef HAVE_FORK
 
 
+/* Define to 1 if you have the `gai_strerror' function. */
+#undef HAVE_GAI_STRERROR
+
 /* Define to 1 if you have the `getaddrinfo' function. */
 /* Define to 1 if you have the `getaddrinfo' function. */
 #undef HAVE_GETADDRINFO
 #undef HAVE_GETADDRINFO
 
 
@@ -123,6 +126,9 @@
 /* Define to 1 if you have the `poll' function. */
 /* Define to 1 if you have the `poll' function. */
 #undef HAVE_POLL
 #undef HAVE_POLL
 
 
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
 /* Define to 1 if you have the <security/pam_appl.h> header file. */
 /* Define to 1 if you have the <security/pam_appl.h> header file. */
 #undef HAVE_SECURITY_PAM_APPL_H
 #undef HAVE_SECURITY_PAM_APPL_H
 
 

+ 2 - 0
src/ipaddr/ng_ipaddr.c

@@ -32,7 +32,9 @@ ng_ipaddr_init(ng_ipaddr_t *addr, const char *ip_str, UINT16 port)
 	assert(ip_str);
 	assert(ip_str);
 
 
 	memset(&hints, 0, sizeof(hints));
 	memset(&hints, 0, sizeof(hints));
+#ifdef AI_NUMERICHOST
 	hints.ai_flags = AI_NUMERICHOST;
 	hints.ai_flags = AI_NUMERICHOST;
+#endif
 #ifndef WANT_IPV6	/* do not convert ipv6 addresses */
 #ifndef WANT_IPV6	/* do not convert ipv6 addresses */
 	hints.ai_family = AF_INET;
 	hints.ai_family = AF_INET;
 #endif
 #endif

+ 10 - 9
src/ngircd/Makefile.am

@@ -18,20 +18,21 @@ LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \
 
 
 sbin_PROGRAMS = ngircd
 sbin_PROGRAMS = ngircd
 
 
-ngircd_SOURCES = ngircd.c array.c channel.c client.c conf.c conn.c conn-func.c \
-	conn-ssl.c conn-zip.c hash.c io.c irc.c irc-channel.c irc-info.c irc-login.c \
-	irc-mode.c irc-op.c irc-oper.c irc-server.c irc-write.c lists.c log.c \
-	match.c op.c numeric.c pam.c parse.c proc.c resolve.c sighandlers.c
+ngircd_SOURCES = ngircd.c array.c channel.c class.c client.c conf.c conn.c \
+	conn-func.c conn-ssl.c conn-zip.c hash.c io.c irc.c irc-channel.c \
+	irc-info.c irc-login.c irc-mode.c irc-op.c irc-oper.c irc-server.c \
+	irc-write.c lists.c log.c match.c op.c numeric.c pam.c parse.c \
+	proc.c resolve.c sighandlers.c
 
 
 ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr
 ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr
 
 
 ngircd_LDADD = -lngportab -lngtool -lngipaddr
 ngircd_LDADD = -lngportab -lngtool -lngipaddr
 
 
-noinst_HEADERS = ngircd.h array.h channel.h client.h conf.h conf-ssl.h conn.h \
-	conn-func.h conn-ssl.h conn-zip.h hash.h io.h irc.h irc-channel.h \
-	irc-info.h irc-login.h irc-mode.h irc-op.h irc-oper.h irc-server.h \
-	irc-write.h lists.h log.h match.h numeric.h op.h pam.h parse.h proc.h \
-	resolve.h sighandlers.h defines.h messages.h
+noinst_HEADERS = ngircd.h array.h channel.h class.h client.h conf.h \
+	conf-ssl.h conn.h conn-func.h conn-ssl.h conn-zip.h hash.h io.h \
+	irc.h irc-channel.h irc-info.h irc-login.h irc-mode.h irc-op.h \
+	irc-oper.h irc-server.h irc-write.h lists.h log.h match.h numeric.h \
+	op.h pam.h parse.h proc.h resolve.h sighandlers.h defines.h messages.h
 
 
 clean-local:
 clean-local:
 	rm -f check-version check-help lint.out
 	rm -f check-version check-help lint.out

+ 33 - 28
src/ngircd/Makefile.in

@@ -63,16 +63,16 @@ CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(sbindir)"
 am__installdirs = "$(DESTDIR)$(sbindir)"
 PROGRAMS = $(sbin_PROGRAMS)
 PROGRAMS = $(sbin_PROGRAMS)
 am_ngircd_OBJECTS = ngircd$U.$(OBJEXT) array$U.$(OBJEXT) \
 am_ngircd_OBJECTS = ngircd$U.$(OBJEXT) array$U.$(OBJEXT) \
-	channel$U.$(OBJEXT) client$U.$(OBJEXT) conf$U.$(OBJEXT) \
-	conn$U.$(OBJEXT) conn-func$U.$(OBJEXT) conn-ssl$U.$(OBJEXT) \
-	conn-zip$U.$(OBJEXT) hash$U.$(OBJEXT) io$U.$(OBJEXT) \
-	irc$U.$(OBJEXT) irc-channel$U.$(OBJEXT) irc-info$U.$(OBJEXT) \
-	irc-login$U.$(OBJEXT) irc-mode$U.$(OBJEXT) irc-op$U.$(OBJEXT) \
-	irc-oper$U.$(OBJEXT) irc-server$U.$(OBJEXT) \
-	irc-write$U.$(OBJEXT) lists$U.$(OBJEXT) log$U.$(OBJEXT) \
-	match$U.$(OBJEXT) op$U.$(OBJEXT) numeric$U.$(OBJEXT) \
-	pam$U.$(OBJEXT) parse$U.$(OBJEXT) proc$U.$(OBJEXT) \
-	resolve$U.$(OBJEXT) sighandlers$U.$(OBJEXT)
+	channel$U.$(OBJEXT) class$U.$(OBJEXT) client$U.$(OBJEXT) \
+	conf$U.$(OBJEXT) conn$U.$(OBJEXT) conn-func$U.$(OBJEXT) \
+	conn-ssl$U.$(OBJEXT) conn-zip$U.$(OBJEXT) hash$U.$(OBJEXT) \
+	io$U.$(OBJEXT) irc$U.$(OBJEXT) irc-channel$U.$(OBJEXT) \
+	irc-info$U.$(OBJEXT) irc-login$U.$(OBJEXT) \
+	irc-mode$U.$(OBJEXT) irc-op$U.$(OBJEXT) irc-oper$U.$(OBJEXT) \
+	irc-server$U.$(OBJEXT) irc-write$U.$(OBJEXT) lists$U.$(OBJEXT) \
+	log$U.$(OBJEXT) match$U.$(OBJEXT) op$U.$(OBJEXT) \
+	numeric$U.$(OBJEXT) pam$U.$(OBJEXT) parse$U.$(OBJEXT) \
+	proc$U.$(OBJEXT) resolve$U.$(OBJEXT) sighandlers$U.$(OBJEXT)
 ngircd_OBJECTS = $(am_ngircd_OBJECTS)
 ngircd_OBJECTS = $(am_ngircd_OBJECTS)
 ngircd_DEPENDENCIES =
 ngircd_DEPENDENCIES =
 ngircd_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(ngircd_LDFLAGS) \
 ngircd_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(ngircd_LDFLAGS) \
@@ -212,18 +212,19 @@ INCLUDES = -I$(srcdir)/../portab -I$(srcdir)/../tool -I$(srcdir)/../ipaddr
 LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \
 LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \
  -varuse -retvalother -emptyret -unrecog
  -varuse -retvalother -emptyret -unrecog
 
 
-ngircd_SOURCES = ngircd.c array.c channel.c client.c conf.c conn.c conn-func.c \
-	conn-ssl.c conn-zip.c hash.c io.c irc.c irc-channel.c irc-info.c irc-login.c \
-	irc-mode.c irc-op.c irc-oper.c irc-server.c irc-write.c lists.c log.c \
-	match.c op.c numeric.c pam.c parse.c proc.c resolve.c sighandlers.c
+ngircd_SOURCES = ngircd.c array.c channel.c class.c client.c conf.c conn.c \
+	conn-func.c conn-ssl.c conn-zip.c hash.c io.c irc.c irc-channel.c \
+	irc-info.c irc-login.c irc-mode.c irc-op.c irc-oper.c irc-server.c \
+	irc-write.c lists.c log.c match.c op.c numeric.c pam.c parse.c \
+	proc.c resolve.c sighandlers.c
 
 
 ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr
 ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr
 ngircd_LDADD = -lngportab -lngtool -lngipaddr
 ngircd_LDADD = -lngportab -lngtool -lngipaddr
-noinst_HEADERS = ngircd.h array.h channel.h client.h conf.h conf-ssl.h conn.h \
-	conn-func.h conn-ssl.h conn-zip.h hash.h io.h irc.h irc-channel.h \
-	irc-info.h irc-login.h irc-mode.h irc-op.h irc-oper.h irc-server.h \
-	irc-write.h lists.h log.h match.h numeric.h op.h pam.h parse.h proc.h \
-	resolve.h sighandlers.h defines.h messages.h
+noinst_HEADERS = ngircd.h array.h channel.h class.h client.h conf.h \
+	conf-ssl.h conn.h conn-func.h conn-ssl.h conn-zip.h hash.h io.h \
+	irc.h irc-channel.h irc-info.h irc-login.h irc-mode.h irc-op.h \
+	irc-oper.h irc-server.h irc-write.h lists.h log.h match.h numeric.h \
+	op.h pam.h parse.h proc.h resolve.h sighandlers.h defines.h messages.h
 
 
 TESTS = check-version check-help
 TESTS = check-version check-help
 all: all-am
 all: all-am
@@ -314,6 +315,7 @@ mostlyclean-kr:
 
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/class$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn$U.Po@am__quote@
@@ -362,6 +364,8 @@ array_.c: array.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/array.c; then echo $(srcdir)/array.c; else echo array.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/array.c; then echo $(srcdir)/array.c; else echo array.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 channel_.c: channel.c $(ANSI2KNR)
 channel_.c: channel.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/channel.c; then echo $(srcdir)/channel.c; else echo channel.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/channel.c; then echo $(srcdir)/channel.c; else echo channel.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+class_.c: class.c $(ANSI2KNR)
+	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/class.c; then echo $(srcdir)/class.c; else echo class.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 client_.c: client.c $(ANSI2KNR)
 client_.c: client.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/client.c; then echo $(srcdir)/client.c; else echo client.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/client.c; then echo $(srcdir)/client.c; else echo client.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 conf_.c: conf.c $(ANSI2KNR)
 conf_.c: conf.c $(ANSI2KNR)
@@ -418,15 +422,16 @@ resolve_.c: resolve.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/resolve.c; then echo $(srcdir)/resolve.c; else echo resolve.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/resolve.c; then echo $(srcdir)/resolve.c; else echo resolve.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 sighandlers_.c: sighandlers.c $(ANSI2KNR)
 sighandlers_.c: sighandlers.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/sighandlers.c; then echo $(srcdir)/sighandlers.c; else echo sighandlers.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/sighandlers.c; then echo $(srcdir)/sighandlers.c; else echo sighandlers.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
-array_.$(OBJEXT) channel_.$(OBJEXT) client_.$(OBJEXT) conf_.$(OBJEXT) \
-conn_.$(OBJEXT) conn-func_.$(OBJEXT) conn-ssl_.$(OBJEXT) \
-conn-zip_.$(OBJEXT) hash_.$(OBJEXT) io_.$(OBJEXT) irc_.$(OBJEXT) \
-irc-channel_.$(OBJEXT) irc-info_.$(OBJEXT) irc-login_.$(OBJEXT) \
-irc-mode_.$(OBJEXT) irc-op_.$(OBJEXT) irc-oper_.$(OBJEXT) \
-irc-server_.$(OBJEXT) irc-write_.$(OBJEXT) lists_.$(OBJEXT) \
-log_.$(OBJEXT) match_.$(OBJEXT) ngircd_.$(OBJEXT) numeric_.$(OBJEXT) \
-op_.$(OBJEXT) pam_.$(OBJEXT) parse_.$(OBJEXT) proc_.$(OBJEXT) \
-resolve_.$(OBJEXT) sighandlers_.$(OBJEXT) : $(ANSI2KNR)
+array_.$(OBJEXT) channel_.$(OBJEXT) class_.$(OBJEXT) client_.$(OBJEXT) \
+conf_.$(OBJEXT) conn_.$(OBJEXT) conn-func_.$(OBJEXT) \
+conn-ssl_.$(OBJEXT) conn-zip_.$(OBJEXT) hash_.$(OBJEXT) io_.$(OBJEXT) \
+irc_.$(OBJEXT) irc-channel_.$(OBJEXT) irc-info_.$(OBJEXT) \
+irc-login_.$(OBJEXT) irc-mode_.$(OBJEXT) irc-op_.$(OBJEXT) \
+irc-oper_.$(OBJEXT) irc-server_.$(OBJEXT) irc-write_.$(OBJEXT) \
+lists_.$(OBJEXT) log_.$(OBJEXT) match_.$(OBJEXT) ngircd_.$(OBJEXT) \
+numeric_.$(OBJEXT) op_.$(OBJEXT) pam_.$(OBJEXT) parse_.$(OBJEXT) \
+proc_.$(OBJEXT) resolve_.$(OBJEXT) sighandlers_.$(OBJEXT) : \
+$(ANSI2KNR)
 
 
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
 	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
 	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \

+ 1 - 1
src/ngircd/array.h

@@ -84,7 +84,7 @@ extern void* array_get PARAMS((array* a, size_t membersize, size_t pos));
 /* free the contents of this array. */
 /* free the contents of this array. */
 extern void array_free PARAMS((array* a));
 extern void array_free PARAMS((array* a));
 
 
-/* overwrite array with zeroes before free */
+/* overwrite array with zeros before free */
 extern void array_free_wipe PARAMS((array* a));
 extern void array_free_wipe PARAMS((array* a));
 
 
 /* return pointer to first element in this array */
 /* return pointer to first element in this array */

+ 82 - 24
src/ngircd/channel.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -88,6 +88,14 @@ Channel_GetListBans(CHANNEL *c)
 
 
 
 
 GLOBAL struct list_head *
 GLOBAL struct list_head *
+Channel_GetListExcepts(CHANNEL *c)
+{
+	assert(c != NULL);
+	return &c->list_excepts;
+}
+
+
+GLOBAL struct list_head *
 Channel_GetListInvites(CHANNEL *c)
 Channel_GetListInvites(CHANNEL *c)
 {
 {
 	assert(c != NULL);
 	assert(c != NULL);
@@ -110,9 +118,12 @@ Channel_InitPredefined( void )
 	assert(channel_count == 0 || conf_chan != NULL);
 	assert(channel_count == 0 || conf_chan != NULL);
 
 
 	for (i = 0; i < channel_count; i++, conf_chan++) {
 	for (i = 0; i < channel_count; i++, conf_chan++) {
-		if (!conf_chan->name[0] || !Channel_IsValidName(conf_chan->name)) {
-			Log(LOG_ERR, "Can't create pre-defined channel: invalid name: \"%s\"",
-									conf_chan->name);
+		if (!conf_chan->name[0])
+			continue;
+		if (!Channel_IsValidName(conf_chan->name)) {
+			Log(LOG_ERR,
+			    "Can't create pre-defined channel: invalid name: \"%s\"",
+			    conf_chan->name);
 			continue;
 			continue;
 		}
 		}
 
 
@@ -158,6 +169,7 @@ Free_Channel(CHANNEL *chan)
 	array_free(&chan->topic);
 	array_free(&chan->topic);
 	array_free(&chan->keyfile);
 	array_free(&chan->keyfile);
 	Lists_Free(&chan->list_bans);
 	Lists_Free(&chan->list_bans);
+	Lists_Free(&chan->list_excepts);
 	Lists_Free(&chan->list_invites);
 	Lists_Free(&chan->list_invites);
 
 
 	free(chan);
 	free(chan);
@@ -349,20 +361,31 @@ Channel_Quit( CLIENT *Client, const char *Reason )
 } /* Channel_Quit */
 } /* Channel_Quit */
 
 
 
 
+/**
+ * Get number of channels this server knows and that are "visible" to
+ * the given client. If no client is given, all channels will be counted.
+ *
+ * @param Client The client to check or NULL.
+ * @return Number of channels visible to the client.
+ */
 GLOBAL unsigned long
 GLOBAL unsigned long
-Channel_Count( void )
+Channel_CountVisible (CLIENT *Client)
 {
 {
 	CHANNEL *c;
 	CHANNEL *c;
 	unsigned long count = 0;
 	unsigned long count = 0;
 
 
 	c = My_Channels;
 	c = My_Channels;
-	while( c )
-	{
-		count++;
+	while(c) {
+		if (Client) {
+			if (!strchr(Channel_Modes(c), 's')
+			    || Channel_IsMemberOf(c, Client))
+				count++;
+		} else
+			count++;
 		c = c->next;
 		c = c->next;
 	}
 	}
 	return count;
 	return count;
-} /* Channel_Count */
+}
 
 
 
 
 GLOBAL unsigned long
 GLOBAL unsigned long
@@ -774,6 +797,13 @@ Channel_SetMaxUsers(CHANNEL *Chan, unsigned long Count)
 } /* Channel_SetMaxUsers */
 } /* Channel_SetMaxUsers */
 
 
 
 
+/**
+ * Check if a client is allowed to send to a specific channel.
+ *
+ * @param Chan The channel to check.
+ * @param From The client that wants to send.
+ * @return true if the client is allowed to send, false otherwise.
+ */
 static bool
 static bool
 Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 {
 {
@@ -808,6 +838,9 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 	if (strchr(Channel_Modes(Chan), 'm'))
 	if (strchr(Channel_Modes(Chan), 'm'))
 		return false;
 		return false;
 
 
+	if (Lists_Check(&Chan->list_excepts, From))
+		return true;
+
 	return !Lists_Check(&Chan->list_bans, From);
 	return !Lists_Check(&Chan->list_bans, From);
 }
 }
 
 
@@ -999,8 +1032,17 @@ GLOBAL bool
 Channel_AddBan(CHANNEL *c, const char *mask )
 Channel_AddBan(CHANNEL *c, const char *mask )
 {
 {
 	struct list_head *h = Channel_GetListBans(c);
 	struct list_head *h = Channel_GetListBans(c);
-	LogDebug("Adding \"%s\" to \"%s\" %s list", mask, Channel_Name(c), "ban");
-	return Lists_Add(h, mask, false);
+	LogDebug("Adding \"%s\" to \"%s\" ban list", mask, Channel_Name(c));
+	return Lists_Add(h, mask, false, NULL);
+}
+
+
+GLOBAL bool
+Channel_AddExcept(CHANNEL *c, const char *mask )
+{
+	struct list_head *h = Channel_GetListExcepts(c);
+	LogDebug("Adding \"%s\" to \"%s\" exception list", mask, Channel_Name(c));
+	return Lists_Add(h, mask, false, NULL);
 }
 }
 
 
 
 
@@ -1008,30 +1050,31 @@ GLOBAL bool
 Channel_AddInvite(CHANNEL *c, const char *mask, bool onlyonce)
 Channel_AddInvite(CHANNEL *c, const char *mask, bool onlyonce)
 {
 {
 	struct list_head *h = Channel_GetListInvites(c);
 	struct list_head *h = Channel_GetListInvites(c);
-	LogDebug("Adding \"%s\" to \"%s\" %s list", mask, Channel_Name(c), "invite");
-	return Lists_Add(h, mask, onlyonce);
+	LogDebug("Adding \"%s\" to \"%s\" invite list", mask, Channel_Name(c));
+	return Lists_Add(h, mask, onlyonce, NULL);
 }
 }
 
 
 
 
 static bool
 static bool
-ShowInvitesBans(struct list_head *head, CLIENT *Client, CHANNEL *Channel, bool invite)
+ShowChannelList(struct list_head *head, CLIENT *Client, CHANNEL *Channel,
+		char *msg, char *msg_end)
 {
 {
 	struct list_elem *e;
 	struct list_elem *e;
-	char *msg = invite ? RPL_INVITELIST_MSG : RPL_BANLIST_MSG;
-	char *msg_end;
 
 
-	assert( Client != NULL );
-	assert( Channel != NULL );
+	assert (Client != NULL);
+	assert (Channel != NULL);
 
 
 	e = Lists_GetFirst(head);
 	e = Lists_GetFirst(head);
 	while (e) {
 	while (e) {
-		if( ! IRC_WriteStrClient( Client, msg, Client_ID( Client ),
-				Channel_Name( Channel ), Lists_GetMask(e) )) return DISCONNECTED;
+		if (!IRC_WriteStrClient(Client, msg, Client_ID(Client),
+					Channel_Name(Channel),
+					Lists_GetMask(e)))
+			return DISCONNECTED;
 		e = Lists_GetNext(e);
 		e = Lists_GetNext(e);
 	}
 	}
 
 
-	msg_end = invite ? RPL_ENDOFINVITELIST_MSG : RPL_ENDOFBANLIST_MSG;
-	return IRC_WriteStrClient( Client, msg_end, Client_ID( Client ), Channel_Name( Channel ));
+	return IRC_WriteStrClient(Client, msg_end, Client_ID(Client),
+				  Channel_Name(Channel));
 }
 }
 
 
 
 
@@ -1043,7 +1086,21 @@ Channel_ShowBans( CLIENT *Client, CHANNEL *Channel )
 	assert( Channel != NULL );
 	assert( Channel != NULL );
 
 
 	h = Channel_GetListBans(Channel);
 	h = Channel_GetListBans(Channel);
-	return ShowInvitesBans(h, Client, Channel, false);
+	return ShowChannelList(h, Client, Channel, RPL_BANLIST_MSG,
+			       RPL_ENDOFBANLIST_MSG);
+}
+
+
+GLOBAL bool
+Channel_ShowExcepts( CLIENT *Client, CHANNEL *Channel )
+{
+	struct list_head *h;
+
+	assert( Channel != NULL );
+
+	h = Channel_GetListExcepts(Channel);
+	return ShowChannelList(h, Client, Channel, RPL_EXCEPTLIST_MSG,
+			       RPL_ENDOFEXCEPTLIST_MSG);
 }
 }
 
 
 
 
@@ -1055,7 +1112,8 @@ Channel_ShowInvites( CLIENT *Client, CHANNEL *Channel )
 	assert( Channel != NULL );
 	assert( Channel != NULL );
 
 
 	h = Channel_GetListInvites(Channel);
 	h = Channel_GetListInvites(Channel);
-	return ShowInvitesBans(h, Client, Channel, true);
+	return ShowChannelList(h, Client, Channel, RPL_INVITELIST_MSG,
+			       RPL_ENDOFINVITELIST_MSG);
 }
 }
 
 
 
 

+ 9 - 4
src/ngircd/channel.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@ typedef struct _CHANNEL
 	char key[CLIENT_PASS_LEN];	/* Channel key ("password", mode "k" ) */
 	char key[CLIENT_PASS_LEN];	/* Channel key ("password", mode "k" ) */
 	unsigned long maxusers;		/* Maximum number of members (mode "l") */
 	unsigned long maxusers;		/* Maximum number of members (mode "l") */
 	struct list_head list_bans;	/* list head of banned users */
 	struct list_head list_bans;	/* list head of banned users */
+	struct list_head list_excepts;	/* list head of (ban) exception list */
 	struct list_head list_invites;	/* list head of invited users */
 	struct list_head list_invites;	/* list head of invited users */
 	array keyfile;			/* Name of the channel key file */
 	array keyfile;			/* Name of the channel key file */
 } CHANNEL;
 } CHANNEL;
@@ -58,6 +59,7 @@ typedef POINTER CL2CHAN;
 #endif
 #endif
 
 
 GLOBAL struct list_head *Channel_GetListBans PARAMS((CHANNEL *c));
 GLOBAL struct list_head *Channel_GetListBans PARAMS((CHANNEL *c));
+GLOBAL struct list_head *Channel_GetListExcepts PARAMS((CHANNEL *c));
 GLOBAL struct list_head *Channel_GetListInvites PARAMS((CHANNEL *c));
 GLOBAL struct list_head *Channel_GetListInvites PARAMS((CHANNEL *c));
 
 
 GLOBAL void Channel_Init PARAMS(( void ));
 GLOBAL void Channel_Init PARAMS(( void ));
@@ -72,7 +74,7 @@ GLOBAL void Channel_Quit PARAMS(( CLIENT *Client, const char *Reason ));
 GLOBAL void Channel_Kick PARAMS((CLIENT *Peer, CLIENT *Target, CLIENT *Origin,
 GLOBAL void Channel_Kick PARAMS((CLIENT *Peer, CLIENT *Target, CLIENT *Origin,
 				 const char *Name, const char *Reason));
 				 const char *Name, const char *Reason));
 
 
-GLOBAL unsigned long Channel_Count PARAMS(( void ));
+GLOBAL unsigned long Channel_CountVisible PARAMS((CLIENT *Client));
 GLOBAL unsigned long Channel_MemberCount PARAMS(( CHANNEL *Chan ));
 GLOBAL unsigned long Channel_MemberCount PARAMS(( CHANNEL *Chan ));
 GLOBAL int Channel_CountForUser PARAMS(( CLIENT *Client ));
 GLOBAL int Channel_CountForUser PARAMS(( CLIENT *Client ));
 
 
@@ -123,10 +125,13 @@ GLOBAL char *Channel_TopicWho PARAMS(( CHANNEL *Chan ));
 GLOBAL unsigned int Channel_CreationTime PARAMS(( CHANNEL *Chan ));
 GLOBAL unsigned int Channel_CreationTime PARAMS(( CHANNEL *Chan ));
 #endif
 #endif
 
 
-GLOBAL bool Channel_AddInvite PARAMS((CHANNEL *c, const char *Mask, bool OnlyOnce ));
-GLOBAL bool Channel_AddBan PARAMS((CHANNEL *c, const char *Mask ));
+GLOBAL bool Channel_AddBan PARAMS((CHANNEL *c, const char *Mask));
+GLOBAL bool Channel_AddExcept PARAMS((CHANNEL *c, const char *Mask));
+GLOBAL bool Channel_AddInvite PARAMS((CHANNEL *c, const char *Mask,
+				      bool OnlyOnce));
 
 
 GLOBAL bool Channel_ShowBans PARAMS((CLIENT *client, CHANNEL *c));
 GLOBAL bool Channel_ShowBans PARAMS((CLIENT *client, CHANNEL *c));
+GLOBAL bool Channel_ShowExcepts PARAMS((CLIENT *client, CHANNEL *c));
 GLOBAL bool Channel_ShowInvites PARAMS((CLIENT *client, CHANNEL *c));
 GLOBAL bool Channel_ShowInvites PARAMS((CLIENT *client, CHANNEL *c));
 
 
 GLOBAL void Channel_LogServer PARAMS((const char *msg));
 GLOBAL void Channel_LogServer PARAMS((const char *msg));

+ 143 - 0
src/ngircd/class.c

@@ -0,0 +1,143 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * 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 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#include "portab.h"
+
+/**
+ * @file
+ * User class management.
+ */
+
+#include "imp.h"
+#include <assert.h>
+#include <string.h>
+
+#include "defines.h"
+#include "array.h"
+#include "conn.h"
+#include "client.h"
+#include "lists.h"
+#include "match.h"
+#include "stdio.h"
+
+#include "exp.h"
+#include "class.h"
+
+struct list_head My_Classes[CLASS_COUNT];
+
+char Reject_Reason[COMMAND_LEN];
+
+GLOBAL void
+Class_Init(void)
+{
+	memset(My_Classes, 0, sizeof(My_Classes));
+}
+
+GLOBAL void
+Class_Exit(void)
+{
+	int i;
+
+	for (i = 0; i < CLASS_COUNT; Lists_Free(&My_Classes[i++]));
+}
+
+GLOBAL char *
+Class_GetMemberReason(const int Class, CLIENT *Client)
+{
+	char *reason;
+
+	assert(Class < CLASS_COUNT);
+	assert(Client != NULL);
+
+	reason = Lists_CheckReason(&My_Classes[Class], Client);
+	if (!reason)
+		return NULL;
+
+	if (!*reason)
+		reason = "listed";
+
+	switch(Class) {
+		case CLASS_GLINE:
+			snprintf(Reject_Reason, sizeof(Reject_Reason),
+				 "\"%s\" (G-Line)", reason);
+			return Reject_Reason;
+		case CLASS_KLINE:
+			snprintf(Reject_Reason, sizeof(Reject_Reason),
+				 "\"%s\" (K-Line)", reason);
+			return Reject_Reason;
+	}
+	return reason;
+}
+
+/**
+ * Check if a client is banned from this server: GLINE, KLINE.
+ *
+ * If a client isn't allowed to connect, it will be disconnected again.
+ *
+ * @param Client The client to check.
+ * @return CONNECTED if client is allowed to join, DISCONNECTED if not.
+ */
+GLOBAL bool
+Class_HandleServerBans(CLIENT *Client)
+{
+	char *rejectptr;
+
+	assert(Client != NULL);
+
+	rejectptr = Class_GetMemberReason(CLASS_GLINE, Client);
+	if (!rejectptr)
+		rejectptr = Class_GetMemberReason(CLASS_KLINE, Client);
+	if (rejectptr) {
+		Client_Reject(Client, rejectptr, true);
+		return DISCONNECTED;
+	}
+
+	return CONNECTED;
+}
+
+
+GLOBAL bool
+Class_AddMask(const int Class, const char *Mask, time_t ValidUntil,
+	      const char *Reason)
+{
+	assert(Class < CLASS_COUNT);
+	assert(Mask != NULL);
+	assert(Reason != NULL);
+
+	return Lists_Add(&My_Classes[Class], Lists_MakeMask(Mask),
+			 ValidUntil, Reason);
+}
+
+GLOBAL void
+Class_DeleteMask(const int Class, const char *Mask)
+{
+	assert(Class < CLASS_COUNT);
+	assert(Mask != NULL);
+
+	Lists_Del(&My_Classes[Class], Lists_MakeMask(Mask));
+}
+
+GLOBAL struct list_head *
+Class_GetList(const int Class)
+{
+	assert(Class < CLASS_COUNT);
+
+	return &My_Classes[Class];
+}
+
+GLOBAL void
+Class_Expire(void)
+{
+	Lists_Expire(&My_Classes[CLASS_GLINE], "G-Line");
+	Lists_Expire(&My_Classes[CLASS_KLINE], "K-Line");
+}
+
+/* -eof- */

+ 41 - 0
src/ngircd/class.h

@@ -0,0 +1,41 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * 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 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#ifndef __class_h__
+#define __class_h__
+
+/**
+ * @file
+ * User class management.
+ */
+
+#define CLASS_KLINE 0
+#define CLASS_GLINE 1
+
+#define CLASS_COUNT 2
+
+GLOBAL void Class_Init PARAMS((void));
+GLOBAL void Class_Exit PARAMS((void));
+
+GLOBAL bool Class_AddMask PARAMS((const int Class, const char *Mask,
+				  const time_t ValidUntil, const char *Reason));
+GLOBAL void Class_DeleteMask PARAMS((const int Class, const char *Mask));
+
+GLOBAL char *Class_GetMemberReason PARAMS((const int Class, CLIENT *Client));
+GLOBAL bool Class_HandleServerBans PARAMS((CLIENT *Client));
+
+GLOBAL struct list_head *Class_GetList PARAMS((const int Class));
+
+GLOBAL void Class_Expire PARAMS((void));
+
+#endif /* __class_h__ */
+
+/* -eof- */

+ 95 - 28
src/ngircd/client.c

@@ -186,7 +186,6 @@ Init_New_Client(CONN_ID Idx, CLIENT *Introducer, CLIENT *TopServer,
 
 
 	assert(Idx >= NONE);
 	assert(Idx >= NONE);
 	assert(Introducer != NULL);
 	assert(Introducer != NULL);
-	assert(Hostname != NULL);
 
 
 	client = New_Client_Struct();
 	client = New_Client_Struct();
 	if (!client)
 	if (!client)
@@ -313,16 +312,29 @@ Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool Sen
 } /* Client_Destroy */
 } /* Client_Destroy */
 
 
 
 
+/**
+ * Set client hostname.
+ *
+ * If global hostname cloaking is in effect, don't set the real hostname
+ * but the configured one.
+ *
+ * @param Client The client of which the hostname should be set.
+ * @param Hostname The new hostname.
+ */
 GLOBAL void
 GLOBAL void
 Client_SetHostname( CLIENT *Client, const char *Hostname )
 Client_SetHostname( CLIENT *Client, const char *Hostname )
 {
 {
-	assert( Client != NULL );
-	assert( Hostname != NULL );
+	assert(Client != NULL);
+	assert(Hostname != NULL);
 
 
 	if (strlen(Conf_CloakHost)) {
 	if (strlen(Conf_CloakHost)) {
-		strlcpy( Client->host, Conf_CloakHost, sizeof( Client->host ));
+		LogDebug("Updating hostname of \"%s\": \"%s\" -> \"%s\"",
+			 Client_ID(Client), Client->host, Conf_CloakHost);
+		strlcpy(Client->host, Conf_CloakHost, sizeof(Client->host));
 	} else {
 	} else {
-		strlcpy( Client->host, Hostname, sizeof( Client->host ));
+		LogDebug("Updating hostname of \"%s\": \"%s\" -> \"%s\"",
+			 Client_ID(Client), Client->host, Hostname);
+		strlcpy(Client->host, Hostname, sizeof(Client->host));
 	}
 	}
 } /* Client_SetHostname */
 } /* Client_SetHostname */
 
 
@@ -768,7 +780,7 @@ Client_NextHop( CLIENT *Client )
  * Return ID of a client: "client!user@host"
  * Return ID of a client: "client!user@host"
  * This client ID is used for IRC prefixes, for example.
  * This client ID is used for IRC prefixes, for example.
  * Please note that this function uses a global static buffer, so you can't
  * Please note that this function uses a global static buffer, so you can't
- * nest invocations without overwriting erlier results!
+ * nest invocations without overwriting earlier results!
  * @param Client Pointer to client structure
  * @param Client Pointer to client structure
  * @return Pointer to global buffer containing the client ID
  * @return Pointer to global buffer containing the client ID
  */
  */
@@ -793,7 +805,7 @@ Client_Mask( CLIENT *Client )
  * Return ID of a client with cloaked hostname: "client!user@server-name"
  * Return ID of a client with cloaked hostname: "client!user@server-name"
  * This client ID is used for IRC prefixes, for example.
  * This client ID is used for IRC prefixes, for example.
  * Please note that this function uses a global static buffer, so you can't
  * Please note that this function uses a global static buffer, so you can't
- * nest invocations without overwriting erlier results!
+ * nest invocations without overwriting earlier results!
  * If the client has not enabled cloaking, the real hostname is used.
  * If the client has not enabled cloaking, the real hostname is used.
  * @param Client Pointer to client structure
  * @param Client Pointer to client structure
  * @return Pointer to global buffer containing the client ID
  * @return Pointer to global buffer containing the client ID
@@ -847,23 +859,37 @@ Client_Away( CLIENT *Client )
 } /* Client_Away */
 } /* Client_Away */
 
 
 
 
+/**
+ * Make sure that a given nickname is valid.
+ *
+ * If the nickname is not valid for the given client, this function sends back
+ * the appropriate error messages.
+ *
+ * @param	Client Client that wants to change the nickname.
+ * @param	Nick New nick name.
+ * @returns	true if nickname is valid, false otherwise.
+ */
 GLOBAL bool
 GLOBAL bool
-Client_CheckNick( CLIENT *Client, char *Nick )
+Client_CheckNick(CLIENT *Client, char *Nick)
 {
 {
-	assert( Client != NULL );
-	assert( Nick != NULL );
-
-	if (! Client_IsValidNick( Nick ))
-	{
-		IRC_WriteStrClient( Client, ERR_ERRONEUSNICKNAME_MSG, Client_ID( Client ), Nick );
+	assert(Client != NULL);
+	assert(Nick != NULL);
+
+	if (!Client_IsValidNick(Nick)) {
+		if (strlen(Nick ) >= Conf_MaxNickLength)
+			IRC_WriteStrClient(Client, ERR_NICKNAMETOOLONG_MSG,
+					   Client_ID(Client), Nick,
+					   Conf_MaxNickLength - 1);
+		else
+			IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
+					   Client_ID(Client), Nick);
 		return false;
 		return false;
 	}
 	}
 
 
-	/* Nick bereits vergeben? */
-	if( Client_Search( Nick ))
-	{
-		/* den Nick gibt es bereits */
-		IRC_WriteStrClient( Client, ERR_NICKNAMEINUSE_MSG, Client_ID( Client ), Nick );
+	/* Nickname already registered? */
+	if (Client_Search(Nick)) {
+		IRC_WriteStrClient(Client, ERR_NICKNAMEINUSE_MSG,
+			Client_ID(Client), Nick);
 		return false;
 		return false;
 	}
 	}
 
 
@@ -1019,23 +1045,31 @@ Client_MyMaxUserCount( void )
 } /* Client_MyMaxUserCount */
 } /* Client_MyMaxUserCount */
 
 
 
 
+/**
+ * Check that a given nickname is valid.
+ *
+ * @param	Nick the nickname to check.
+ * @returns	true if nickname is valid, false otherwise.
+ */
 GLOBAL bool
 GLOBAL bool
-Client_IsValidNick( const char *Nick )
+Client_IsValidNick(const char *Nick)
 {
 {
 	const char *ptr;
 	const char *ptr;
 	static const char goodchars[] = ";0123456789-";
 	static const char goodchars[] = ";0123456789-";
 
 
-	assert( Nick != NULL );
+	assert (Nick != NULL);
 
 
-	if( Nick[0] == '#' ) return false;
-	if( strchr( goodchars, Nick[0] )) return false;
-	if( strlen( Nick ) >= Conf_MaxNickLength) return false;
+	if (strchr(goodchars, Nick[0]))
+		return false;
+	if (strlen(Nick ) >= Conf_MaxNickLength)
+		return false;
 
 
 	ptr = Nick;
 	ptr = Nick;
-	while( *ptr )
-	{
-		if (( *ptr < 'A' ) && ( ! strchr( goodchars, *ptr ))) return false;
-		if ( *ptr > '}' ) return false;
+	while (*ptr) {
+		if (*ptr < 'A' && !strchr(goodchars, *ptr ))
+			return false;
+		if (*ptr > '}')
+			return false;
 		ptr++;
 		ptr++;
 	}
 	}
 
 
@@ -1075,6 +1109,39 @@ Client_StartTime(CLIENT *Client)
 } /* Client_Uptime */
 } /* Client_Uptime */
 
 
 
 
+/**
+ * Reject a client when logging in.
+ *
+ * This function is called when a client isn't allowed to connect to this
+ * server. Possible reasons are bad server password, bad PAM password,
+ * or that the client is G/K-Line'd.
+ *
+ * After calling this function, the client isn't connected any more.
+ *
+ * @param Client The client to reject.
+ * @param Reason The reason why the client has been rejected.
+ * @param InformClient If true, send the exact reason to the client.
+ */
+GLOBAL void
+Client_Reject(CLIENT *Client, const char *Reason, bool InformClient)
+{
+	char info[COMMAND_LEN];
+
+	assert(Client != NULL);
+	assert(Reason != NULL);
+
+	if (InformClient)
+		snprintf(info, sizeof(info), "Access denied: %s", Reason);
+	else
+		strcpy(info, "Access denied: Bad password?");
+
+	Log(LOG_ERR,
+	    "User \"%s\" rejected (connection %d): %s!",
+	    Client_Mask(Client), Client_Conn(Client), Reason);
+	Conn_Close(Client_Conn(Client), Reason, info, true);
+}
+
+
 static unsigned long
 static unsigned long
 Count( CLIENT_TYPE Type )
 Count( CLIENT_TYPE Type )
 {
 {

+ 3 - 0
src/ngircd/client.h

@@ -163,6 +163,9 @@ GLOBAL void Client_RegisterWhowas PARAMS(( CLIENT *Client ));
 
 
 GLOBAL const char *Client_TypeText PARAMS((CLIENT *Client));
 GLOBAL const char *Client_TypeText PARAMS((CLIENT *Client));
 
 
+GLOBAL void Client_Reject PARAMS((CLIENT *Client, const char *Reason,
+				  bool InformClient));
+
 #ifdef DEBUG
 #ifdef DEBUG
 GLOBAL void Client_DebugDump PARAMS((void));
 GLOBAL void Client_DebugDump PARAMS((void));
 #endif
 #endif

+ 67 - 41
src/ngircd/conf.c

@@ -55,8 +55,6 @@ static bool Use_Log = true, Using_MotdFile = true;
 static CONF_SERVER New_Server;
 static CONF_SERVER New_Server;
 static int New_Server_Idx;
 static int New_Server_Idx;
 
 
-static size_t Conf_Oper_Count;
-static size_t Conf_Channel_Count;
 static char Conf_MotdFile[FNAME_LEN];
 static char Conf_MotdFile[FNAME_LEN];
 
 
 static void Set_Defaults PARAMS(( bool InitServers ));
 static void Set_Defaults PARAMS(( bool InitServers ));
@@ -265,18 +263,18 @@ static void
 opers_puts(void)
 opers_puts(void)
 {
 {
 	struct Conf_Oper *op;
 	struct Conf_Oper *op;
-	size_t len;
+	size_t count, i;
 
 
-	len = array_length(&Conf_Opers, sizeof(*op));
+	count = array_length(&Conf_Opers, sizeof(*op));
 	op = array_start(&Conf_Opers);
 	op = array_start(&Conf_Opers);
-	while (len--) {
-		assert(op->name[0]);
+	for (i = 0; i < count; i++, op++) {
+		if (!op->name[0])
+			continue;
 
 
 		puts("[OPERATOR]");
 		puts("[OPERATOR]");
 		printf("  Name = %s\n", op->name);
 		printf("  Name = %s\n", op->name);
 		printf("  Password = %s\n", op->pwd);
 		printf("  Password = %s\n", op->pwd);
 		printf("  Mask = %s\n\n", op->mask ? op->mask : "");
 		printf("  Mask = %s\n\n", op->mask ? op->mask : "");
-		op++;
 	}
 	}
 }
 }
 
 
@@ -375,6 +373,7 @@ Conf_Test( void )
 	printf("  OperServerMode = %s\n", yesno_to_str(Conf_OperServerMode));
 	printf("  OperServerMode = %s\n", yesno_to_str(Conf_OperServerMode));
 #ifdef PAM
 #ifdef PAM
 	printf("  PAM = %s\n", yesno_to_str(Conf_PAM));
 	printf("  PAM = %s\n", yesno_to_str(Conf_PAM));
+	printf("  PAMIsOptional = %s\n", yesno_to_str(Conf_PAMIsOptional));
 #endif
 #endif
 	printf("  PredefChannelsOnly = %s\n", yesno_to_str(Conf_PredefChannelsOnly));
 	printf("  PredefChannelsOnly = %s\n", yesno_to_str(Conf_PredefChannelsOnly));
 #ifndef STRICT_RFC
 #ifndef STRICT_RFC
@@ -699,6 +698,7 @@ Set_Defaults(bool InitServers)
 #else
 #else
 	Conf_PAM = false;
 	Conf_PAM = false;
 #endif
 #endif
+	Conf_PAMIsOptional = false;
 	Conf_PredefChannelsOnly = false;
 	Conf_PredefChannelsOnly = false;
 #ifdef SYSLOG
 #ifdef SYSLOG
 	Conf_ScrubCTCP = false;
 	Conf_ScrubCTCP = false;
@@ -709,10 +709,6 @@ Set_Defaults(bool InitServers)
 #endif
 #endif
 #endif
 #endif
 
 
-	/* Initialize IRC operators and channels */
-	Conf_Oper_Count = 0;
-	Conf_Channel_Count = 0;
-
 	/* Initialize server configuration structures */
 	/* Initialize server configuration structures */
 	if (InitServers) {
 	if (InitServers) {
 		for (i = 0; i < MAX_SERVERS;
 		for (i = 0; i < MAX_SERVERS;
@@ -787,6 +783,7 @@ Read_Config( bool ngircd_starting )
 	char section[LINE_LEN], str[LINE_LEN], *var, *arg, *ptr;
 	char section[LINE_LEN], str[LINE_LEN], *var, *arg, *ptr;
 	const UINT16 defaultport = 6667;
 	const UINT16 defaultport = 6667;
 	int line, i, n;
 	int line, i, n;
+	size_t count;
 	FILE *fd;
 	FILE *fd;
 
 
 	/* Open configuration file */
 	/* Open configuration file */
@@ -857,10 +854,13 @@ Read_Config( bool ngircd_starting )
 		/* Is this the beginning of a new section? */
 		/* Is this the beginning of a new section? */
 		if(( str[0] == '[' ) && ( str[strlen( str ) - 1] == ']' )) {
 		if(( str[0] == '[' ) && ( str[strlen( str ) - 1] == ']' )) {
 			strlcpy( section, str, sizeof( section ));
 			strlcpy( section, str, sizeof( section ));
-			if (strcasecmp(section, "[GLOBAL]") == 0 ||
-			    strcasecmp(section, "[LIMITS]") == 0 ||
-			    strcasecmp(section, "[OPTIONS]") == 0 ||
-			    strcasecmp(section, "[SSL]") == 0)
+			if (strcasecmp(section, "[GLOBAL]") == 0
+			    || strcasecmp(section, "[LIMITS]") == 0
+			    || strcasecmp(section, "[OPTIONS]") == 0
+#ifdef SSL_SUPPORT
+			    || strcasecmp(section, "[SSL]") == 0
+#endif
+			    )
 				continue;
 				continue;
 
 
 			if( strcasecmp( section, "[SERVER]" ) == 0 ) {
 			if( strcasecmp( section, "[SERVER]" ) == 0 ) {
@@ -887,12 +887,30 @@ Read_Config( bool ngircd_starting )
 				else New_Server_Idx = i;
 				else New_Server_Idx = i;
 				continue;
 				continue;
 			}
 			}
+
 			if (strcasecmp(section, "[CHANNEL]") == 0) {
 			if (strcasecmp(section, "[CHANNEL]") == 0) {
-				Conf_Channel_Count++;
+				count = array_length(&Conf_Channels,
+						     sizeof(struct Conf_Channel));
+				if (!array_alloc(&Conf_Channels,
+						 sizeof(struct Conf_Channel),
+						 count)) {
+					Config_Error(LOG_ERR,
+						     "Could not allocate memory for new operator (line %d)",
+						     line);
+				}
 				continue;
 				continue;
 			}
 			}
+
 			if (strcasecmp(section, "[OPERATOR]") == 0) {
 			if (strcasecmp(section, "[OPERATOR]") == 0) {
-				Conf_Oper_Count++;
+				count = array_length(&Conf_Opers,
+						     sizeof(struct Conf_Oper));
+				if (!array_alloc(&Conf_Opers,
+						 sizeof(struct Conf_Oper),
+						 count)) {
+					Config_Error(LOG_ERR,
+						     "Could not allocate memory for new channel (line &d)",
+						     line);
+				}
 				continue;
 				continue;
 			}
 			}
 
 
@@ -978,17 +996,21 @@ Read_Config( bool ngircd_starting )
 }
 }
 
 
 /**
 /**
- * Check whether an string argument is true or false.
+ * Check whether a string argument is "true" or "false".
  *
  *
  * @param Arg	Input string.
  * @param Arg	Input string.
- * @returns	true if string has been parsed as "yes"/"true"/"on".
+ * @returns	true if the input string has been parsed as "yes", "true"
+ *		(case insensitive) or a non-zero integer value.
  */
  */
 static bool
 static bool
-Check_ArgIsTrue( const char *Arg )
+Check_ArgIsTrue(const char *Arg)
 {
 {
-	if( strcasecmp( Arg, "yes" ) == 0 ) return true;
-	if( strcasecmp( Arg, "true" ) == 0 ) return true;
-	if( atoi( Arg ) != 0 ) return true;
+	if (strcasecmp(Arg, "yes") == 0)
+		return true;
+	if (strcasecmp(Arg, "true") == 0)
+		return true;
+	if (atoi(Arg) != 0)
+		return true;
 
 
 	return false;
 	return false;
 }
 }
@@ -1289,7 +1311,9 @@ Handle_GLOBAL( int Line, char *Var, char *Arg )
 		else {
 		else {
 			Conf_GID = (unsigned int)atoi(Arg);
 			Conf_GID = (unsigned int)atoi(Arg);
 			if (!Conf_GID && strcmp(Arg, "0"))
 			if (!Conf_GID && strcmp(Arg, "0"))
-				Config_Error_NaN(Line, Var);
+				Config_Error(LOG_WARNING,
+					     "%s, line %d: Value of \"%s\" is not a valid group name or ID!",
+					     NGIRCd_ConfFile, Line, Var);
 		}
 		}
 		return;
 		return;
 	}
 	}
@@ -1300,7 +1324,9 @@ Handle_GLOBAL( int Line, char *Var, char *Arg )
 		else {
 		else {
 			Conf_UID = (unsigned int)atoi(Arg);
 			Conf_UID = (unsigned int)atoi(Arg);
 			if (!Conf_UID && strcmp(Arg, "0"))
 			if (!Conf_UID && strcmp(Arg, "0"))
-				Config_Error_NaN(Line, Var);
+				Config_Error(LOG_WARNING,
+					     "%s, line %d: Value of \"%s\" is not a valid user name or ID!",
+					     NGIRCd_ConfFile, Line, Var);
 		}
 		}
 		return;
 		return;
 	}
 	}
@@ -1483,6 +1509,10 @@ Handle_OPTIONS(int Line, char *Var, char *Arg)
 		WarnPAM(Line);
 		WarnPAM(Line);
 		return;
 		return;
 	}
 	}
+	if (strcasecmp(Var, "PAMIsOptional") == 0 ) {
+		Conf_PAMIsOptional = Check_ArgIsTrue(Arg);
+		return;
+	}
 	if (strcasecmp(Var, "PredefChannelsOnly") == 0) {
 	if (strcasecmp(Var, "PredefChannelsOnly") == 0) {
 		Conf_PredefChannelsOnly = Check_ArgIsTrue(Arg);
 		Conf_PredefChannelsOnly = Check_ArgIsTrue(Arg);
 		return;
 		return;
@@ -1580,13 +1610,11 @@ Handle_OPERATOR( int Line, char *Var, char *Arg )
 	assert( Line > 0 );
 	assert( Line > 0 );
 	assert( Var != NULL );
 	assert( Var != NULL );
 	assert( Arg != NULL );
 	assert( Arg != NULL );
-	assert( Conf_Oper_Count > 0 );
 
 
-	op = array_alloc(&Conf_Opers, sizeof(*op), Conf_Oper_Count - 1);
-	if (!op) {
-		Config_Error(LOG_ERR, "Could not allocate memory for operator (%d:%s = %s)", Line, Var, Arg);
+	op = array_get(&Conf_Opers, sizeof(*op),
+			 array_length(&Conf_Opers, sizeof(*op)) - 1);
+	if (!op)
 		return;
 		return;
-	}
 
 
 	if (strcasecmp(Var, "Name") == 0) {
 	if (strcasecmp(Var, "Name") == 0) {
 		/* Name of IRC operator */
 		/* Name of IRC operator */
@@ -1752,21 +1780,17 @@ static void
 Handle_CHANNEL(int Line, char *Var, char *Arg)
 Handle_CHANNEL(int Line, char *Var, char *Arg)
 {
 {
 	size_t len;
 	size_t len;
-	size_t chancount;
 	struct Conf_Channel *chan;
 	struct Conf_Channel *chan;
 
 
 	assert( Line > 0 );
 	assert( Line > 0 );
 	assert( Var != NULL );
 	assert( Var != NULL );
 	assert( Arg != NULL );
 	assert( Arg != NULL );
-	assert(Conf_Channel_Count > 0);
-
-	chancount = Conf_Channel_Count - 1;
 
 
-	chan = array_alloc(&Conf_Channels, sizeof(*chan), chancount);
-	if (!chan) {
-		Config_Error(LOG_ERR, "Could not allocate memory for predefined channel (%d:%s = %s)", Line, Var, Arg);
+	chan = array_get(&Conf_Channels, sizeof(*chan),
+			 array_length(&Conf_Channels, sizeof(*chan)) - 1);
+	if (!chan)
 		return;
 		return;
-	}
+
 	if (strcasecmp(Var, "Name") == 0) {
 	if (strcasecmp(Var, "Name") == 0) {
 		if (!Handle_Channelname(chan, Arg))
 		if (!Handle_Channelname(chan, Arg))
 			Config_Error_TooLong(Line, Var);
 			Config_Error_TooLong(Line, Var);
@@ -1913,8 +1937,10 @@ Validate_Config(bool Configtest, bool Rehash)
 		}
 		}
 	}
 	}
 	Log(LOG_DEBUG,
 	Log(LOG_DEBUG,
-	    "Configuration: Operators=%d, Servers=%d[%d], Channels=%d",
-	    Conf_Oper_Count, servers, servers_once, Conf_Channel_Count);
+	    "Configuration: Operators=%ld, Servers=%d[%d], Channels=%ld",
+	    array_length(&Conf_Opers, sizeof(struct Conf_Oper)),
+	    servers, servers_once,
+	    array_length(&Conf_Channels, sizeof(struct Conf_Channel)));
 #endif
 #endif
 
 
 	return config_valid;
 	return config_valid;
@@ -2044,7 +2070,7 @@ Init_Server_Struct( CONF_SERVER *Server )
 
 
 	Proc_InitStruct(&Server->res_stat);
 	Proc_InitStruct(&Server->res_stat);
 	Server->conn_id = NONE;
 	Server->conn_id = NONE;
-	memset(&Server->bind_addr, 0, sizeof(&Server->bind_addr));
+	memset(&Server->bind_addr, 0, sizeof(Server->bind_addr));
 }
 }
 
 
 /* -eof- */
 /* -eof- */

+ 4 - 1
src/ngircd/conf.h

@@ -154,7 +154,7 @@ GLOBAL bool Conf_OperCanMode;
 /**
 /**
  * If true, mask channel MODE commands of IRC operators to the server.
  * If true, mask channel MODE commands of IRC operators to the server.
  * Background: ircd2 will ignore channel MODE commands if an IRC operator
  * Background: ircd2 will ignore channel MODE commands if an IRC operator
- * gives chanel operator privileges to someone without being a channel operator
+ * gives channel operator privileges to someone without being a channel operator
  * himself. This enables a workaround: it masks the MODE command as coming
  * himself. This enables a workaround: it masks the MODE command as coming
  * from the IRC server and not the IRC operator.
  * from the IRC server and not the IRC operator.
  */
  */
@@ -184,6 +184,9 @@ GLOBAL bool Conf_NoticeAuth;
 /** Enable all usage of PAM, even when compiled with support for it */
 /** Enable all usage of PAM, even when compiled with support for it */
 GLOBAL bool Conf_PAM;
 GLOBAL bool Conf_PAM;
 
 
+/** Don't require all clients to send a password an to be PAM authenticated */
+GLOBAL bool Conf_PAMIsOptional;
+
 /** Disable all CTCP commands except for /me ? */
 /** Disable all CTCP commands except for /me ? */
 GLOBAL bool Conf_ScrubCTCP;
 GLOBAL bool Conf_ScrubCTCP;
 
 

+ 56 - 18
src/ngircd/conn-func.c

@@ -30,13 +30,30 @@
 #include "conn-func.h"
 #include "conn-func.h"
 
 
 
 
+/**
+ * Update "idle timestamp", the time of the last visible user action
+ * (e. g. like sending messages, joining or leaving channels).
+ *
+ * @param Idx Connection index.
+ */
 GLOBAL void
 GLOBAL void
-Conn_UpdateIdle( CONN_ID Idx )
+Conn_UpdateIdle(CONN_ID Idx)
 {
 {
-	assert( Idx > NONE );
-	My_Connections[Idx].lastprivmsg = time( NULL );
+	assert(Idx > NONE);
+	My_Connections[Idx].lastprivmsg = time(NULL);
 }
 }
 
 
+/**
+ * Update "ping timestamp", the time of the last outgoing PING request.
+ *
+ * @param Idx Connection index.
+ */
+GLOBAL void
+Conn_UpdatePing(CONN_ID Idx)
+{
+	assert(Idx > NONE);
+	My_Connections[Idx].lastping = time(NULL);
+}
 
 
 /*
 /*
  * Get signon time of a connection.
  * Get signon time of a connection.
@@ -65,35 +82,56 @@ Conn_LastPing( CONN_ID Idx )
 } /* Conn_LastPing */
 } /* Conn_LastPing */
 
 
 
 
+/**
+ * Add "penalty time" for a connection.
+ *
+ * During the "penalty time" the socket is ignored completely, no new data
+ * is read. This function only increases the penalty, it is not possible to
+ * decrease the penalty time.
+ *
+ * @param Idex Connection index.
+ * @param Seconds Seconds to add.
+ * @see Conn_ResetPenalty
+ */
 GLOBAL void
 GLOBAL void
-Conn_SetPenalty( CONN_ID Idx, time_t Seconds )
+Conn_SetPenalty(CONN_ID Idx, time_t Seconds)
 {
 {
-	/* set Penalty-Delay for a socket.
-	 * during the penalty, the socket is ignored completely, no new
-	 * data is read. This function only increases the penalty, it is
-	 * not possible to decrease the penalty time.
-	 */
 	time_t t;
 	time_t t;
-	
-	assert( Idx > NONE );
-	assert( Seconds >= 0 );
 
 
-	t = time( NULL ) + Seconds;
-	if (t > My_Connections[Idx].delaytime)
+	assert(Idx > NONE);
+	assert(Seconds >= 0);
+
+	t = time(NULL);
+	if (My_Connections[Idx].delaytime < t)
 		My_Connections[Idx].delaytime = t;
 		My_Connections[Idx].delaytime = t;
 
 
+	My_Connections[Idx].delaytime += Seconds;
+
 #ifdef DEBUG
 #ifdef DEBUG
-	Log(LOG_DEBUG, "Add penalty time on connection %d: %ld second(s).",
-			Idx, (long)Seconds);
+	Log(LOG_DEBUG,
+	    "Add penalty time on connection %d: %ld second%s, total %ld second%s.",
+	    Idx, (long)Seconds, Seconds != 1 ? "s" : "",
+	    My_Connections[Idx].delaytime - t,
+	    My_Connections[Idx].delaytime - t != 1 ? "s" : "");
 #endif
 #endif
 } /* Conn_SetPenalty */
 } /* Conn_SetPenalty */
 
 
 
 
+/**
+ * Reset the "penalty time" for one connection.
+ *
+ * @param Idx Connection index.
+ * @see Conn_SetPenalty
+ */
 GLOBAL void
 GLOBAL void
-Conn_ResetPenalty( CONN_ID Idx )
+Conn_ResetPenalty(CONN_ID Idx)
 {
 {
-	assert( Idx > NONE );
+	assert(Idx > NONE);
+
 	My_Connections[Idx].delaytime = 0;
 	My_Connections[Idx].delaytime = 0;
+#ifdef DEBUG
+	Log(LOG_DEBUG, "Penalty time on connection %d has been reset.");
+#endif
 } /* Conn_ResetPenalty */
 } /* Conn_ResetPenalty */
 
 
 
 

+ 3 - 1
src/ngircd/conn-func.h

@@ -29,7 +29,9 @@
 #endif
 #endif
 
 
 
 
-GLOBAL void Conn_UpdateIdle PARAMS(( CONN_ID Idx ));
+GLOBAL void Conn_UpdateIdle PARAMS((CONN_ID Idx));
+GLOBAL void Conn_UpdatePing PARAMS((CONN_ID Idx));
+
 GLOBAL time_t Conn_GetSignon PARAMS((CONN_ID Idx));
 GLOBAL time_t Conn_GetSignon PARAMS((CONN_ID Idx));
 GLOBAL time_t Conn_GetIdle PARAMS(( CONN_ID Idx ));
 GLOBAL time_t Conn_GetIdle PARAMS(( CONN_ID Idx ));
 GLOBAL time_t Conn_LastPing PARAMS(( CONN_ID Idx ));
 GLOBAL time_t Conn_LastPing PARAMS(( CONN_ID Idx ));

+ 64 - 29
src/ngircd/conn.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -9,6 +9,8 @@
  * Please read the file COPYING, README and AUTHORS for more information.
  * Please read the file COPYING, README and AUTHORS for more information.
  */
  */
 
 
+#undef DEBUG_BUFFER
+
 #define CONN_MODULE
 #define CONN_MODULE
 
 
 #include "portab.h"
 #include "portab.h"
@@ -63,6 +65,7 @@
 #include "ngircd.h"
 #include "ngircd.h"
 #include "array.h"
 #include "array.h"
 #include "client.h"
 #include "client.h"
+#include "class.h"
 #include "conf.h"
 #include "conf.h"
 #include "conn-ssl.h"
 #include "conn-ssl.h"
 #include "conn-zip.h"
 #include "conn-zip.h"
@@ -79,8 +82,8 @@
 #define SERVER_WAIT (NONE - 1)
 #define SERVER_WAIT (NONE - 1)
 
 
 #define MAX_COMMANDS 3
 #define MAX_COMMANDS 3
-#define MAX_COMMANDS_SERVER 10
-#define MAX_COMMANDS_SERVICE MAX_COMMANDS_SERVER
+#define MAX_COMMANDS_SERVER_MIN 10
+#define MAX_COMMANDS_SERVICE 10
 
 
 
 
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
@@ -367,7 +370,7 @@ cb_clientserver_ssl(int sock, short what)
 
 
 
 
 /**
 /**
- * Initialite connecion module.
+ * Initialize connecion module.
  */
  */
 GLOBAL void
 GLOBAL void
 Conn_Init( void )
 Conn_Init( void )
@@ -433,12 +436,13 @@ Conn_Exit( void )
  * they don't hold connections open that the main process wants to close.
  * they don't hold connections open that the main process wants to close.
  */
  */
 GLOBAL void
 GLOBAL void
-Conn_CloseAllSockets(void)
+Conn_CloseAllSockets(int ExceptOf)
 {
 {
 	CONN_ID idx;
 	CONN_ID idx;
 
 
 	for(idx = 0; idx < Pool_Size; idx++) {
 	for(idx = 0; idx < Pool_Size; idx++) {
-		if(My_Connections[idx].sock > NONE)
+		if(My_Connections[idx].sock > NONE &&
+		   My_Connections[idx].sock != ExceptOf)
 			close(My_Connections[idx].sock);
 			close(My_Connections[idx].sock);
 	}
 	}
 }
 }
@@ -739,6 +743,9 @@ Conn_Handler(void)
 		Check_Servers();
 		Check_Servers();
 		Check_Connections();
 		Check_Connections();
 
 
+		/* Expire outdated class/list items */
+		Class_Expire();
+
 		/* Look for non-empty read buffers ... */
 		/* Look for non-empty read buffers ... */
 		for (i = 0; i < Pool_Size; i++) {
 		for (i = 0; i < Pool_Size; i++) {
 			if ((My_Connections[i].sock > NONE)
 			if ((My_Connections[i].sock > NONE)
@@ -929,22 +936,25 @@ Conn_Write( CONN_ID Idx, char *Data, size_t Len )
 	assert( Data != NULL );
 	assert( Data != NULL );
 	assert( Len > 0 );
 	assert( Len > 0 );
 
 
-	c = Conn_GetClient(Idx);
-	assert( c != NULL);
-
-	/* Servers do get special write buffer limits, so they can generate
-	 * all the messages that are required while peering. */
-	if (Client_Type(c) == CLIENT_SERVER)
-		writebuf_limit = WRITEBUFFER_SLINK_LEN;
-
 	/* Is the socket still open? A previous call to Conn_Write()
 	/* Is the socket still open? A previous call to Conn_Write()
 	 * may have closed the connection due to a fatal error.
 	 * may have closed the connection due to a fatal error.
 	 * In this case it is sufficient to return an error, as well. */
 	 * In this case it is sufficient to return an error, as well. */
-	if( My_Connections[Idx].sock <= NONE ) {
+	if (My_Connections[Idx].sock <= NONE) {
 		LogDebug("Skipped write on closed socket (connection %d).", Idx);
 		LogDebug("Skipped write on closed socket (connection %d).", Idx);
 		return false;
 		return false;
 	}
 	}
 
 
+	/* Make sure that there still exists a CLIENT structure associated
+	 * with this connection and check if this is a server or not: */
+	c = Conn_GetClient(Idx);
+	if (c) {
+		/* Servers do get special write buffer limits, so they can
+		 * generate all the messages that are required while peering. */
+		if (Client_Type(c) == CLIENT_SERVER)
+			writebuf_limit = WRITEBUFFER_SLINK_LEN;
+	} else
+		LogDebug("Write on socket without client (connection %d)!?", Idx);
+
 #ifdef ZLIB
 #ifdef ZLIB
 	if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_ZIP )) {
 	if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_ZIP )) {
 		/* Compressed link:
 		/* Compressed link:
@@ -1007,7 +1017,7 @@ Conn_Write( CONN_ID Idx, char *Data, size_t Len )
 GLOBAL void
 GLOBAL void
 Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClient )
 Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClient )
 {
 {
-	/* Close connection. Open pipes of asyncronous resolver
+	/* Close connection. Open pipes of asynchronous resolver
 	 * sub-processes are closed down. */
 	 * sub-processes are closed down. */
 
 
 	CLIENT *c;
 	CLIENT *c;
@@ -1217,6 +1227,20 @@ Conn_SyncServerStruct(void)
 
 
 
 
 /**
 /**
+ * Get IP address string of a connection.
+ *
+ * @param Idx Connection index.
+ * @return Pointer to a global buffer containing the IP address as string.
+ */
+GLOBAL const char *
+Conn_GetIPAInfo(CONN_ID Idx)
+{
+	assert(Idx > NONE);
+	return ng_ipaddr_tostr(&My_Connections[Idx].addr);
+}
+
+
+/**
  * Send out data of write buffer; connect new sockets.
  * Send out data of write buffer; connect new sockets.
  *
  *
  * @param Idx	Connection index.
  * @param Idx	Connection index.
@@ -1255,9 +1279,11 @@ Handle_Write( CONN_ID Idx )
 		return true;
 		return true;
 	}
 	}
 
 
+#ifdef DEBUG_BUFFER
 	LogDebug
 	LogDebug
 	    ("Handle_Write() called for connection %d, %ld bytes pending ...",
 	    ("Handle_Write() called for connection %d, %ld bytes pending ...",
 	     Idx, wdatalen);
 	     Idx, wdatalen);
+#endif
 
 
 #ifdef SSL_SUPPORT
 #ifdef SSL_SUPPORT
 	if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_SSL )) {
 	if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_SSL )) {
@@ -1326,6 +1352,8 @@ New_Connection(int Sock)
 
 
 	assert(Sock > NONE);
 	assert(Sock > NONE);
 
 
+	LogDebug("Accepting new connection on socket %d ...", Sock);
+
 	new_sock_len = (int)sizeof(new_addr);
 	new_sock_len = (int)sizeof(new_addr);
 	new_sock = accept(Sock, (struct sockaddr *)&new_addr,
 	new_sock = accept(Sock, (struct sockaddr *)&new_addr,
 			  (socklen_t *)&new_sock_len);
 			  (socklen_t *)&new_sock_len);
@@ -1410,7 +1438,7 @@ New_Connection(int Sock)
 		return -1;
 		return -1;
 	}
 	}
 
 
-	c = Client_NewLocal(new_sock, ip_str, CLIENT_UNKNOWN, false);
+	c = Client_NewLocal(new_sock, NULL, CLIENT_UNKNOWN, false);
 	if (!c) {
 	if (!c) {
 		Log(LOG_ALERT,
 		Log(LOG_ALERT,
 		    "Can't accept connection: can't create client structure!");
 		    "Can't accept connection: can't create client structure!");
@@ -1561,7 +1589,7 @@ Read_Request( CONN_ID Idx )
 		if (!array_catb(&My_Connections[Idx].zip.rbuf, readbuf,
 		if (!array_catb(&My_Connections[Idx].zip.rbuf, readbuf,
 				(size_t) len)) {
 				(size_t) len)) {
 			Log(LOG_ERR,
 			Log(LOG_ERR,
-			    "Could not append recieved data to zip input buffer (connn %d): %d bytes!",
+			    "Could not append recieved data to zip input buffer (connection %d): %d bytes!",
 			    Idx, len);
 			    Idx, len);
 			Conn_Close(Idx, "Receive buffer space exhausted", NULL,
 			Conn_Close(Idx, "Receive buffer space exhausted", NULL,
 				   false);
 				   false);
@@ -1571,7 +1599,9 @@ Read_Request( CONN_ID Idx )
 #endif
 #endif
 	{
 	{
 		if (!array_catb( &My_Connections[Idx].rbuf, readbuf, len)) {
 		if (!array_catb( &My_Connections[Idx].rbuf, readbuf, len)) {
-			Log( LOG_ERR, "Could not append recieved data to input buffer (connn %d): %d bytes!", Idx, len );
+			Log(LOG_ERR,
+			    "Could not append recieved data to input buffer (connection %d): %d bytes!",
+			    Idx, len);
 			Conn_Close(Idx, "Receive buffer space exhausted", NULL, false );
 			Conn_Close(Idx, "Receive buffer space exhausted", NULL, false );
 		}
 		}
 	}
 	}
@@ -1644,16 +1674,15 @@ Handle_Buffer(CONN_ID Idx)
 
 
 	assert(c != NULL);
 	assert(c != NULL);
 
 
-	/* Servers do get special command limits, so they can process
-	 * all the messages that are required while peering. */
+	/* Servers get special command limits that depend on the user count */
 	switch (Client_Type(c)) {
 	switch (Client_Type(c)) {
 	    case CLIENT_SERVER:
 	    case CLIENT_SERVER:
-		/* Allow servers to send more commands in the first 10 secods
+		maxcmd = (int)(Client_UserCount() / 5)
+		       + MAX_COMMANDS_SERVER_MIN;
+		/* Allow servers to handle even more commands while peering
 		 * to speed up server login and network synchronisation. */
 		 * to speed up server login and network synchronisation. */
-		if (starttime - Client_StartTime(c) < 10)
-			maxcmd = MAX_COMMANDS_SERVER * 5;
-		else
-			maxcmd = MAX_COMMANDS_SERVER;
+		if (Conn_LastPing(Idx) == 0)
+			maxcmd *= 5;
 		break;
 		break;
 	    case CLIENT_SERVICE:
 	    case CLIENT_SERVICE:
 		maxcmd = MAX_COMMANDS_SERVICE; break;
 		maxcmd = MAX_COMMANDS_SERVICE; break;
@@ -1753,8 +1782,10 @@ Handle_Buffer(CONN_ID Idx)
 			return 0; /* error -> connection has been closed */
 			return 0; /* error -> connection has been closed */
 
 
 		array_moveleft(&My_Connections[Idx].rbuf, 1, len);
 		array_moveleft(&My_Connections[Idx].rbuf, 1, len);
+#ifdef DEBUG_BUFFER
 		LogDebug("Connection %d: %d bytes left in read buffer.",
 		LogDebug("Connection %d: %d bytes left in read buffer.",
 			 Idx, array_bytes(&My_Connections[Idx].rbuf));
 			 Idx, array_bytes(&My_Connections[Idx].rbuf));
+#endif
 #ifdef ZLIB
 #ifdef ZLIB
 		if ((!old_z) && (My_Connections[Idx].options & CONN_ZIP) &&
 		if ((!old_z) && (My_Connections[Idx].options & CONN_ZIP) &&
 		    (array_bytes(&My_Connections[Idx].rbuf) > 0)) {
 		    (array_bytes(&My_Connections[Idx].rbuf) > 0)) {
@@ -1818,7 +1849,7 @@ Check_Connections(void)
 				   time(NULL) - Conf_PingTimeout) {
 				   time(NULL) - Conf_PingTimeout) {
 				/* We need to send a PING ... */
 				/* We need to send a PING ... */
 				LogDebug("Connection %d: sending PING ...", i);
 				LogDebug("Connection %d: sending PING ...", i);
-				My_Connections[i].lastping = time(NULL);
+				Conn_UpdatePing(i);
 				Conn_WriteStr(i, "PING :%s",
 				Conn_WriteStr(i, "PING :%s",
 					      Client_ID(Client_ThisServer()));
 					      Client_ID(Client_ThisServer()));
 			}
 			}
@@ -2051,13 +2082,14 @@ Init_Socket( int Sock )
 	/* Set type of service (TOS) */
 	/* Set type of service (TOS) */
 #if defined(IPPROTO_IP) && defined(IPTOS_LOWDELAY)
 #if defined(IPPROTO_IP) && defined(IPTOS_LOWDELAY)
 	value = IPTOS_LOWDELAY;
 	value = IPTOS_LOWDELAY;
-	LogDebug("Setting IP_TOS on socket %d to IPTOS_LOWDELAY.", Sock);
 	if (setsockopt(Sock, IPPROTO_IP, IP_TOS, &value,
 	if (setsockopt(Sock, IPPROTO_IP, IP_TOS, &value,
 		       (socklen_t) sizeof(value))) {
 		       (socklen_t) sizeof(value))) {
 		LogDebug("Can't set socket option IP_TOS: %s!",
 		LogDebug("Can't set socket option IP_TOS: %s!",
 			 strerror(errno));
 			 strerror(errno));
 		/* ignore this error */
 		/* ignore this error */
-	}
+	} else
+		LogDebug("IP_TOS on socket %d has been set to IPTOS_LOWDELAY.",
+			 Sock);
 #endif
 #endif
 
 
 	return true;
 	return true;
@@ -2098,6 +2130,7 @@ cb_Connect_to_Server(int fd, UNUSED short events)
 
 
 	/* Read result from pipe */
 	/* Read result from pipe */
 	len = Proc_Read(&Conf_Server[i].res_stat, dest_addrs, sizeof(dest_addrs));
 	len = Proc_Read(&Conf_Server[i].res_stat, dest_addrs, sizeof(dest_addrs));
+	Proc_Close(&Conf_Server[i].res_stat);
 	if (len == 0) {
 	if (len == 0) {
 		/* Error resolving hostname: reset server structure */
 		/* Error resolving hostname: reset server structure */
 		Conf_Server[i].conn_id = NONE;
 		Conf_Server[i].conn_id = NONE;
@@ -2157,6 +2190,7 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 
 
 	/* Read result from pipe */
 	/* Read result from pipe */
 	len = Proc_Read(&My_Connections[i].proc_stat, readbuf, sizeof readbuf -1);
 	len = Proc_Read(&My_Connections[i].proc_stat, readbuf, sizeof readbuf -1);
+	Proc_Close(&My_Connections[i].proc_stat);
 	if (len == 0)
 	if (len == 0)
 		return;
 		return;
 
 
@@ -2204,6 +2238,7 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 					"NOTICE AUTH :*** No ident response");
 					"NOTICE AUTH :*** No ident response");
 		}
 		}
 #endif
 #endif
+		Class_HandleServerBans(c);
 	}
 	}
 #ifdef DEBUG
 #ifdef DEBUG
 		else Log( LOG_DEBUG, "Resolver: discarding result for already registered connection %d.", i );
 		else Log( LOG_DEBUG, "Resolver: discarding result for already registered connection %d.", i );

+ 4 - 2
src/ngircd/conn.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -106,7 +106,7 @@ GLOBAL long WCounter;
 GLOBAL void Conn_Init PARAMS((void ));
 GLOBAL void Conn_Init PARAMS((void ));
 GLOBAL void Conn_Exit PARAMS(( void ));
 GLOBAL void Conn_Exit PARAMS(( void ));
 
 
-GLOBAL void Conn_CloseAllSockets PARAMS((void));
+GLOBAL void Conn_CloseAllSockets PARAMS((int ExceptOf));
 
 
 GLOBAL unsigned int Conn_InitListeners PARAMS(( void ));
 GLOBAL unsigned int Conn_InitListeners PARAMS(( void ));
 GLOBAL void Conn_ExitListeners PARAMS(( void ));
 GLOBAL void Conn_ExitListeners PARAMS(( void ));
@@ -131,6 +131,8 @@ Conn_UsesSSL(UNUSED CONN_ID Idx)
 { return false; }
 { return false; }
 #endif
 #endif
 
 
+GLOBAL const char *Conn_GetIPAInfo PARAMS((CONN_ID Idx));
+
 GLOBAL long Conn_Count PARAMS((void));
 GLOBAL long Conn_Count PARAMS((void));
 GLOBAL long Conn_CountMax PARAMS((void));
 GLOBAL long Conn_CountMax PARAMS((void));
 GLOBAL long Conn_CountAccepted PARAMS((void));
 GLOBAL long Conn_CountAccepted PARAMS((void));

+ 167 - 77
src/ngircd/defines.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -17,98 +17,188 @@
  * Global constants ("#defines") used by the ngIRCd.
  * Global constants ("#defines") used by the ngIRCd.
  */
  */
 
 
+
+/* Internal flags */
+
+/** Flag: there is no connection. */
 #define NONE -1
 #define NONE -1
 
 
-#define FNAME_LEN 256			/* Max. length of file name */
-
-#define LINE_LEN 256			/* Max. length of a line in the
-					   configuration file */
-
-#define HOST_LEN 256			/* Max. lenght of fully qualified host
-					   names (e. g. "abc.domain.tld") */
-
-#define MAX_SERVERS 16			/* Max. count of configurable servers */
-
-#define MAX_WHOWAS 64			/* Max. number of WHOWAS items */
-#define DEFAULT_WHOWAS 5		/* default count for WHOWAS command */
-
-#define CONNECTION_POOL 100		/* Size of default connection pool */
-
-#define CLIENT_ID_LEN 64		/* Max. length of an IRC ID; see RFC
-					   RFC 2812 section 1.1 and 1.2.1 */
-#define CLIENT_NICK_LEN_DEFAULT 10	/* Default nick length, see. RFC 2812
-					 * section 1.2.1 */
-#define CLIENT_NICK_LEN 32		/* Maximum nick name length */
-#define CLIENT_PASS_LEN 21		/* Max. password length */
-#define CLIENT_USER_LEN 10		/* Max. length of user name ("login")
-					   see RFC 2812, section 1.2.1 */
-#define CLIENT_NAME_LEN 32		/* Max. length of "real names" */
-#define CLIENT_HOST_LEN 64		/* Max. host name length */
-#define CLIENT_MODE_LEN 9		/* Max. lenth of all client modes */
-#define CLIENT_INFO_LEN 64		/* Max. length of server info texts */
-#define CLIENT_AWAY_LEN 128		/* Max. length of away messages */
-#define CLIENT_FLAGS_LEN 100		/* Max. length of client flags */
-
-#define CHANNEL_NAME_LEN 51		/* Max. length of a channel name, see
-					   RFC 2812 section 1.3 */
-#define CHANNEL_MODE_LEN 9		/* Max. length of channel modes */
-
-#define COMMAND_LEN 513			/* Max. IRC command length, see. RFC
-					   2812 section 3.2 */
-
-#define READBUFFER_LEN 2048		/* Size of the read buffer of a
-					   connection in bytes. */
-#define WRITEBUFFER_FLUSH_LEN 4096	/* Size of a write buffer that triggers
-					   buffer flushing if more space is
-					   needed for storing data. */
-#define WRITEBUFFER_MAX_LEN 32768	/* Maximum size of the write buffer of a
-					   connection in bytes. */
-#define WRITEBUFFER_SLINK_LEN 65536	/* Maximum size of the write buffer of a
-					   server link connection in bytes. */
-
-#define PROTOVER "0210"			/* Implemented IRC protocol version,
-					   see RFC 2813 section 4.1.1. */
-#define PROTOIRC "-IRC"			/* Protocol suffix, see RFC 2813
-					   section 4.1.1 */
-#define PROTOIRCPLUS "-IRC+"		/* Protocol suffix used by the IRC+
-					   protocol, see doc/Protocol.txt */
+/** Flag: connection is (still) established. */
+#define CONNECTED true
+
+/** Flag: connection isn't established (any more). */
+#define DISCONNECTED false
+
+/** Tag for outbound server links. */
+#define TOKEN_OUTBOUND -2
+
+
+/* Generic buffer sizes */
+
+/** Max. length of a line in the configuration file. */
+#define LINE_LEN 256
+
+/** Max. length of a log message. */
+#define MAX_LOG_MSG_LEN 256
+
+/** Max. length of file name. */
+#define FNAME_LEN 256
+
+/** Max. lenght of fully qualified host names (e. g. "abc.domain.tld"). */
+#define HOST_LEN 256
+
+
+/* Size of structures */
+
+/** Max. count of configurable servers. */
+#define MAX_SERVERS 16
+
+/** Max. number of WHOWAS list items that can be stored. */
+#define MAX_WHOWAS 64
+
+/** Size of default connection pool. */
+#define CONNECTION_POOL 100
+
+
+/* Hard-coded (default) options */
+
+/** Delay after startup before outgoing connections are initiated in seconds. */
+#define STARTUP_DELAY 1
+
+/** Time to delay re-connect attempts in seconds. */
+#define RECONNECT_DELAY 3
+
+/** Configuration file name. */
+#define CONFIG_FILE "/ngircd.conf"
+
+/** Name of the MOTD file. */
+#define MOTD_FILE "/ngircd.motd"
+
+/** Default chroot() directory. */
+#define CHROOT_DIR ""
+
+/** Default file for the process ID. */
+#define PID_FILE ""
+
+
+/* Sizes of "IRC elements": nicks, users, ... */
+
+/** Max. length of an IRC ID (incl. NULL); see RFC 2812 section 1.1 and 1.2.1. */
+#define CLIENT_ID_LEN 64
+
+/** Default nick length (including NULL), see. RFC 2812 section 1.2.1. */
+#define CLIENT_NICK_LEN_DEFAULT 10
+
+/** Maximum nick name length (including NULL). */
+#define CLIENT_NICK_LEN 32
+
+/** Max. password length (including NULL). */
+#define CLIENT_PASS_LEN 21
+
+/** Max. length of user name ("login"; incl. NULL), RFC 2812, section 1.2.1. */
+#define CLIENT_USER_LEN 10
+
+/** Max. length of "real names" (including NULL). */
+#define CLIENT_NAME_LEN 32
+
+/** Max. host name length (including NULL). */
+#define CLIENT_HOST_LEN 64
+
+/** Max. length of all client modes (including NULL). */
+#define CLIENT_MODE_LEN 16
+
+/** Max. length of server info texts (including NULL). */
+#define CLIENT_INFO_LEN 64
+
+/** Max. length of away messages (including NULL). */
+#define CLIENT_AWAY_LEN 128
+
+/** Max. length of client flags (including NULL). */
+#define CLIENT_FLAGS_LEN 16
+
+/** Max. length of a channel name (including NULL), see RFC 2812 section 1.3. */
+#define CHANNEL_NAME_LEN 51
+
+/** Max. length of channel modes (including NULL). */
+#define CHANNEL_MODE_LEN 9
+
+/** Max. IRC command length (including NULL), see. RFC 2812 section 3.2. */
+#define COMMAND_LEN 513
+
+
+/* Read and write buffer sizes */
+
+/** Size of the read buffer of a connection in bytes. */
+#define READBUFFER_LEN 2048
+
+/** Size that triggers write buffer flushing if more space is needed. */
+#define WRITEBUFFER_FLUSH_LEN 4096
+
+/** Maximum size of the write buffer of a connection in bytes. */
+#define WRITEBUFFER_MAX_LEN 32768
+
+/** Maximum size of the write buffer of a server link connection in bytes. */
+#define WRITEBUFFER_SLINK_LEN 65536
+
+
+/* IRC/IRC+ protocol */
+
+/** Implemented IRC protocol version, see RFC 2813 section 4.1.1. */
+#define PROTOVER "0210"
+
+/** Protocol suffix, see RFC 2813 section 4.1.1. */
+#define PROTOIRC "-IRC"
+
+/** Protocol suffix used by the IRC+ protocol, see <doc/Protocol.txt>. */
+#define PROTOIRCPLUS "-IRC+"
 
 
 #ifdef IRCPLUS
 #ifdef IRCPLUS
-# define IRCPLUSFLAGS "CHLS"		/* Standard IRC+ flags */
+/** Standard IRC+ flags. */
+# define IRCPLUSFLAGS "CHLS"
 #endif
 #endif
 
 
-#define STARTUP_DELAY 1			/* Delay outgoing connections n seconds
-					   after startup. */
-#define RECONNECT_DELAY 3		/* Time to delay re-connect attempts
-					   in seconds. */
+/** Supported user modes. */
+#define USERMODES "acCiorRswx"
 
 
-#define USERMODES "aciorswx"		/* Supported user modes. */
-#define CHANMODES "biIklmnoOPstvz"	/* Supported channel modes. */
+/** Supported channel modes. */
+#define CHANMODES "beiIklmnoOPRstvz"
 
 
-#define CONNECTED true			/* Internal status codes. */
-#define DISCONNECTED false
+/** Away message for users connected to linked servers. */
+#define DEFAULT_AWAY_MSG "Away"
+
+/** Default ID for "topic owner". */
+#define DEFAULT_TOPIC_ID "-Server-"
+
+/** Prefix for NOTICEs from the server to users. Some servers use '*'. */
+#define NOTICE_TXTPREFIX ""
+
+/** Suffix for oversized messages that have been shortened and cut off. */
+#define CUT_TXTSUFFIX "[CUT]"
+
+
+/* Defaults and limits for IRC commands */
 
 
-#define DEFAULT_AWAY_MSG "Away"		/* Away message for users connected to
-					   linked servers. */
+/** Max. number of LIST replies. */
+#define MAX_RPL_LIST 100
 
 
-#define DEFAULT_TOPIC_ID "-Server-"	/* Default ID for "topic owner". */
+/** Max. number of elemets allowed in channel invite and ban lists. */
+#define MAX_HNDL_CHANNEL_LISTS 50
 
 
-#define CONFIG_FILE "/ngircd.conf"	/* Configuration file name. */
-#define MOTD_FILE "/ngircd.motd"	/* Name of the MOTD file. */
-#define CHROOT_DIR ""			/* Default chroot() directory. */
-#define PID_FILE ""			/* Default file for the process ID. */
+/** Max. number of channel modes with arguments per MODE command. */
+#define MAX_HNDL_MODES_ARG 5
 
 
-#define ERROR_DIR "/tmp"		/* Error directory used in debug mode */
+/** Max. number of WHO replies. */
+#define MAX_RPL_WHO 25
 
 
-#define MAX_LOG_MSG_LEN 256		/* Max. length of a log message. */
+/** Max. number of WHOIS replies. */
+#define MAX_RPL_WHOIS 10
 
 
-#define TOKEN_OUTBOUND -2		/* Tag for outbound server links. */
+/** Default count of WHOWAS command replies. */
+#define DEF_RPL_WHOWAS 5
 
 
-#define NOTICE_TXTPREFIX ""		/* Prefix for NOTICEs from the server
-					   to users. Some servers use '*'. */
+/** Max count of WHOWAS command replies. */
+#define MAX_RPL_WHOWAS 25
 
 
-#define CUT_TXTSUFFIX "[CUT]"		/* Suffix for oversized messages that
-					   have been shortened and cut off. */
 
 
 #endif
 #endif
 
 

+ 73 - 92
src/ngircd/io.c

@@ -41,6 +41,7 @@ typedef struct {
 
 
 #define INIT_IOEVENT		{ NULL, -1, 0, NULL }
 #define INIT_IOEVENT		{ NULL, -1, 0, NULL }
 #define IO_ERROR		4
 #define IO_ERROR		4
+#define MAX_EVENTS		100
 
 
 #ifdef HAVE_EPOLL_CREATE
 #ifdef HAVE_EPOLL_CREATE
 #  define IO_USE_EPOLL		1
 #  define IO_USE_EPOLL		1
@@ -54,7 +55,7 @@ typedef struct {
 #    ifdef HAVE_SYS_DEVPOLL_H
 #    ifdef HAVE_SYS_DEVPOLL_H
 #      define IO_USE_DEVPOLL	1
 #      define IO_USE_DEVPOLL	1
 #    else
 #    else
-#      ifdef HAVE_POLL
+#      if defined(HAVE_POLL) && defined(HAVE_POLL_H)
 #        define IO_USE_POLL	1
 #        define IO_USE_POLL	1
 #      else
 #      else
 #        ifdef HAVE_SELECT
 #        ifdef HAVE_SELECT
@@ -160,39 +161,34 @@ io_dispatch_devpoll(struct timeval *tv)
 {
 {
 	struct dvpoll dvp;
 	struct dvpoll dvp;
 	time_t sec = tv->tv_sec * 1000;
 	time_t sec = tv->tv_sec * 1000;
-	int i, total, ret, timeout = tv->tv_usec + sec;
+	int i, ret, timeout = tv->tv_usec + sec;
 	short what;
 	short what;
-	struct pollfd p[100];
+	struct pollfd p[MAX_EVENTS];
 
 
 	if (timeout < 0)
 	if (timeout < 0)
 		timeout = 1000;
 		timeout = 1000;
 
 
-	total = 0;
-	do {
-		dvp.dp_timeout = timeout;
-		dvp.dp_nfds = 100;
-		dvp.dp_fds = p;
-		ret = ioctl(io_masterfd, DP_POLL, &dvp);
-		total += ret;
-		if (ret <= 0)
-			return total;
-		for (i=0; i < ret ; i++) {
-			what = 0;
-			if (p[i].revents & (POLLIN|POLLPRI))
-				what = IO_WANTREAD;
-
-			if (p[i].revents & POLLOUT)
-				what |= IO_WANTWRITE;
-
-			if (p[i].revents && !what) {
-				/* other flag is set, probably POLLERR */
-				what = IO_ERROR;
-			}
-			io_docallback(p[i].fd, what);
+	dvp.dp_timeout = timeout;
+	dvp.dp_nfds = MAX_EVENTS;
+	dvp.dp_fds = p;
+	ret = ioctl(io_masterfd, DP_POLL, &dvp);
+
+	for (i=0; i < ret ; i++) {
+		what = 0;
+		if (p[i].revents & (POLLIN|POLLPRI))
+			what = IO_WANTREAD;
+
+		if (p[i].revents & POLLOUT)
+			what |= IO_WANTWRITE;
+
+		if (p[i].revents && !what) {
+			/* other flag is set, probably POLLERR */
+			what = IO_ERROR;
 		}
 		}
-	} while (ret == 100);
+		io_docallback(p[i].fd, what);
+	}
 
 
-	return total;
+	return ret;
 }
 }
 
 
 
 
@@ -462,37 +458,30 @@ static int
 io_dispatch_epoll(struct timeval *tv)
 io_dispatch_epoll(struct timeval *tv)
 {
 {
 	time_t sec = tv->tv_sec * 1000;
 	time_t sec = tv->tv_sec * 1000;
-	int i, total = 0, ret, timeout = tv->tv_usec + sec;
-	struct epoll_event epoll_ev[100];
+	int i, ret, timeout = tv->tv_usec + sec;
+	struct epoll_event epoll_ev[MAX_EVENTS];
 	short type;
 	short type;
 
 
 	if (timeout < 0)
 	if (timeout < 0)
 		timeout = 1000;
 		timeout = 1000;
 
 
-	do {
-		ret = epoll_wait(io_masterfd, epoll_ev, 100, timeout);
-		total += ret;
-		if (ret <= 0)
-			return total;
-
-		for (i = 0; i < ret; i++) {
-			type = 0;
-			if (epoll_ev[i].events & (EPOLLERR | EPOLLHUP))
-				type = IO_ERROR;
+	ret = epoll_wait(io_masterfd, epoll_ev, MAX_EVENTS, timeout);
 
 
-			if (epoll_ev[i].events & (EPOLLIN | EPOLLPRI))
-				type |= IO_WANTREAD;
+	for (i = 0; i < ret; i++) {
+		type = 0;
+		if (epoll_ev[i].events & (EPOLLERR | EPOLLHUP))
+			type = IO_ERROR;
 
 
-			if (epoll_ev[i].events & EPOLLOUT)
-				type |= IO_WANTWRITE;
+		if (epoll_ev[i].events & (EPOLLIN | EPOLLPRI))
+			type |= IO_WANTREAD;
 
 
-			io_docallback(epoll_ev[i].data.fd, type);
-		}
+		if (epoll_ev[i].events & EPOLLOUT)
+			type |= IO_WANTWRITE;
 
 
-		timeout = 0;
-	} while (ret == 100);
+		io_docallback(epoll_ev[i].data.fd, type);
+	}
 
 
-	return total;
+	return ret;
 }
 }
 
 
 static void
 static void
@@ -576,58 +565,50 @@ io_event_change_kqueue(int fd, short what, const int action)
 static int
 static int
 io_dispatch_kqueue(struct timeval *tv)
 io_dispatch_kqueue(struct timeval *tv)
 {
 {
-	int i, total = 0, ret;
-	struct kevent kev[100];
+	int i, ret;
+	struct kevent kev[MAX_EVENTS];
 	struct kevent *newevents;
 	struct kevent *newevents;
 	struct timespec ts;
 	struct timespec ts;
 	int newevents_len;
 	int newevents_len;
 	ts.tv_sec = tv->tv_sec;
 	ts.tv_sec = tv->tv_sec;
 	ts.tv_nsec = tv->tv_usec * 1000;
 	ts.tv_nsec = tv->tv_usec * 1000;
 
 
-	do {
-		newevents_len = (int) array_length(&io_evcache, sizeof (struct kevent));
-		newevents = (newevents_len > 0) ? array_start(&io_evcache) : NULL;
-		assert(newevents_len >= 0);
-
-		ret = kevent(io_masterfd, newevents, newevents_len, kev, 100, &ts);
-		if (newevents && ret != -1)
-			array_trunc(&io_evcache);
-
-		total += ret;
-		if (ret <= 0)
-			return total;
-
-		for (i = 0; i < ret; i++) {
-			io_debug("dispatch_kqueue: fd, kev.flags", (int)kev[i].ident, kev[i].flags);
-			if (kev[i].flags & (EV_EOF|EV_ERROR)) {
-				if (kev[i].flags & EV_ERROR)
-					Log(LOG_ERR, "kevent fd %d: EV_ERROR (%s)",
-						(int)kev[i].ident, strerror((int)kev[i].data));
-				io_docallback((int)kev[i].ident, IO_ERROR);
-				continue;
-			}
-
-			switch (kev[i].filter) {
-			case EVFILT_READ:
-				io_docallback((int)kev[i].ident, IO_WANTREAD);
-				break;
-			case EVFILT_WRITE:
-				io_docallback((int)kev[i].ident, IO_WANTWRITE);
-				break;
-			default:
-				LogDebug("Unknown kev.filter number %d for fd %d",
-					kev[i].filter, kev[i].ident);
-				/* Fall through */
-			case EV_ERROR:
-				io_docallback((int)kev[i].ident, IO_ERROR);
-				break;
-			}
+	newevents_len = (int) array_length(&io_evcache, sizeof (struct kevent));
+	newevents = (newevents_len > 0) ? array_start(&io_evcache) : NULL;
+	assert(newevents_len >= 0);
+
+	ret = kevent(io_masterfd, newevents, newevents_len, kev, MAX_EVENTS, &ts);
+	if (newevents && ret != -1)
+		array_trunc(&io_evcache);
+
+	for (i = 0; i < ret; i++) {
+		io_debug("dispatch_kqueue: fd, kev.flags", (int)kev[i].ident, kev[i].flags);
+		if (kev[i].flags & (EV_EOF|EV_ERROR)) {
+			if (kev[i].flags & EV_ERROR)
+				Log(LOG_ERR, "kevent fd %d: EV_ERROR (%s)",
+					(int)kev[i].ident, strerror((int)kev[i].data));
+			io_docallback((int)kev[i].ident, IO_ERROR);
+			continue;
+		}
+
+		switch (kev[i].filter) {
+		case EVFILT_READ:
+			io_docallback((int)kev[i].ident, IO_WANTREAD);
+			break;
+		case EVFILT_WRITE:
+			io_docallback((int)kev[i].ident, IO_WANTWRITE);
+			break;
+		default:
+			LogDebug("Unknown kev.filter number %d for fd %d",
+				kev[i].filter, kev[i].ident);
+			/* Fall through */
+		case EV_ERROR:
+			io_docallback((int)kev[i].ident, IO_ERROR);
+			break;
 		}
 		}
-		ts.tv_sec = 0;
-		ts.tv_nsec = 0;
-	} while (ret == 100);
+	}
 
 
-	return total;
+	return ret;
 }
 }
 
 
 static void
 static void

+ 89 - 62
src/ngircd/irc-channel.c

@@ -31,6 +31,7 @@
 #include "match.h"
 #include "match.h"
 #include "messages.h"
 #include "messages.h"
 #include "parse.h"
 #include "parse.h"
+#include "irc.h"
 #include "irc-info.h"
 #include "irc-info.h"
 #include "irc-write.h"
 #include "irc-write.h"
 #include "conf.h"
 #include "conf.h"
@@ -81,7 +82,7 @@ static bool
 join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
 join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
 	     const char *key)
 	     const char *key)
 {
 {
-	bool is_invited, is_banned;
+	bool is_invited, is_banned, is_exception;
 	const char *channel_modes;
 	const char *channel_modes;
 
 
 	/* Allow IRC operators to overwrite channel limits */
 	/* Allow IRC operators to overwrite channel limits */
@@ -89,9 +90,10 @@ join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
 		return true;
 		return true;
 
 
 	is_banned = Lists_Check(Channel_GetListBans(chan), Client);
 	is_banned = Lists_Check(Channel_GetListBans(chan), Client);
+	is_exception = Lists_Check(Channel_GetListExcepts(chan), Client);
 	is_invited = Lists_Check(Channel_GetListInvites(chan), Client);
 	is_invited = Lists_Check(Channel_GetListInvites(chan), Client);
 
 
-	if (is_banned && !is_invited) {
+	if (is_banned && !is_invited && !is_exception) {
 		/* Client is banned from channel (and not on invite list) */
 		/* Client is banned from channel (and not on invite list) */
 		IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG,
 		IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG,
 				   Client_ID(Client), channame);
 				   Client_ID(Client), channame);
@@ -137,6 +139,13 @@ join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
 		return false;
 		return false;
 	}
 	}
 
 
+	if (strchr(channel_modes, 'R') && !strchr(Client_Modes(Client), 'R')) {
+		/* Only registered users are allowed! */
+		IRC_WriteStrClient(Client, ERR_REGONLYCHANNEL_MSG,
+				   Client_ID(Client), channame);
+		return false;
+	}
+
 	return true;
 	return true;
 } /* join_allowed */
 } /* join_allowed */
 
 
@@ -237,7 +246,7 @@ join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan,
 	IRC_WriteStrChannelPrefix(Client, chan, target, false,
 	IRC_WriteStrChannelPrefix(Client, chan, target, false,
 				  "JOIN :%s",  channame);
 				  "JOIN :%s",  channame);
 
 
-	/* syncronize channel modes */
+	/* synchronize channel modes */
 	if (modes[1]) {
 	if (modes[1]) {
 		IRC_WriteStrChannelPrefix(Client, chan, target, false,
 		IRC_WriteStrChannelPrefix(Client, chan, target, false,
 					  "MODE %s +%s %s", channame,
 					  "MODE %s +%s %s", channame,
@@ -294,9 +303,9 @@ join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan,
  *
  *
  * See RFC 2812, 3.2.1 "Join message"; RFC 2813, 4.2.1 "Join message".
  * See RFC 2812, 3.2.1 "Join message"; RFC 2813, 4.2.1 "Join message".
  *
  *
- * @param Client	The client from which this command has been received
- * @param Req		Request structure with prefix and all parameters
- * @returns		CONNECTED or DISCONNECTED
+ * @param Client The client from which this command has been received
+ * @param Req Request structure with prefix and all parameters
+ * @returns CONNECTED or DISCONNECTED
  */
  */
 GLOBAL bool
 GLOBAL bool
 IRC_JOIN( CLIENT *Client, REQUEST *Req )
 IRC_JOIN( CLIENT *Client, REQUEST *Req )
@@ -305,8 +314,8 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 	CLIENT *target;
 	CLIENT *target;
 	CHANNEL *chan;
 	CHANNEL *chan;
 
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert (Client != NULL);
+	assert (Req != NULL);
 
 
 	/* Bad number of arguments? */
 	/* Bad number of arguments? */
 	if (Req->argc < 1 || Req->argc > 2)
 	if (Req->argc < 1 || Req->argc > 2)
@@ -320,7 +329,8 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 		target = Client;
 		target = Client;
 
 
 	if (!target)
 	if (!target)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, Client_ID(Client), Req->prefix);
+		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+					  Client_ID(Client), Req->prefix);
 
 
 	/* Is argument "0"? */
 	/* Is argument "0"? */
 	if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
 	if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
@@ -352,24 +362,35 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 
 
 		chan = Channel_Search(channame);
 		chan = Channel_Search(channame);
 		if (!chan && Conf_PredefChannelsOnly) {
 		if (!chan && Conf_PredefChannelsOnly) {
-			 /* channel must be created, but server does not allow this */
-			IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG, Client_ID(Client), channame);
-			break;
+			 /* channel must be created, but forbidden by config */
+			IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG,
+					   Client_ID(Client), channame);
+			goto join_next;
 		}
 		}
 
 
 		/* Local client? */
 		/* Local client? */
 		if (Client_Type(Client) == CLIENT_USER) {
 		if (Client_Type(Client) == CLIENT_USER) {
+			if (chan) {
+				/* Already existing channel: already member? */
+				if (Channel_IsMemberOf(chan, Client))
+				    goto join_next;
+			}
+
 			/* Test if the user has reached the channel limit */
 			/* Test if the user has reached the channel limit */
 			if ((Conf_MaxJoins > 0) &&
 			if ((Conf_MaxJoins > 0) &&
-			    (Channel_CountForUser(Client) >= Conf_MaxJoins))
-				return IRC_WriteStrClient(Client,
+			    (Channel_CountForUser(Client) >= Conf_MaxJoins)) {
+				if (!IRC_WriteStrClient(Client,
 						ERR_TOOMANYCHANNELS_MSG,
 						ERR_TOOMANYCHANNELS_MSG,
-						Client_ID(Client), channame);
+						Client_ID(Client), channame))
+					return DISCONNECTED;
+				goto join_next;
+			}
+
 			if (chan) {
 			if (chan) {
 				/* Already existing channel: check if the
 				/* Already existing channel: check if the
 				 * client is allowed to join */
 				 * client is allowed to join */
 				if (!join_allowed(Client, chan, channame, key))
 				if (!join_allowed(Client, chan, channame, key))
-					break;
+					goto join_next;
 			} else {
 			} else {
 				/* New channel: first user will become channel
 				/* New channel: first user will become channel
 				 * operator unless this is a modeless channel */
 				 * operator unless this is a modeless channel */
@@ -392,7 +413,7 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 
 
 		/* Join channel (and create channel if it doesn't exist) */
 		/* Join channel (and create channel if it doesn't exist) */
 		if (!Channel_Join(target, channame))
 		if (!Channel_Join(target, channame))
-			break;
+			goto join_next;
 
 
 		if (!chan) { /* channel is new; it has been created above */
 		if (!chan) { /* channel is new; it has been created above */
 			chan = Channel_Search(channame);
 			chan = Channel_Search(channame);
@@ -411,6 +432,7 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 		if (!join_send_topic(Client, target, chan, channame))
 		if (!join_send_topic(Client, target, chan, channame))
 			break; /* write error */
 			break; /* write error */
 
 
+	join_next:
 		/* next channel? */
 		/* next channel? */
 		channame = strtok_r(NULL, ",", &lastchan);
 		channame = strtok_r(NULL, ",", &lastchan);
 		if (channame && key)
 		if (channame && key)
@@ -582,9 +604,9 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
  * This implementation handles the local case as well as the forwarding of the
  * This implementation handles the local case as well as the forwarding of the
  * LIST command to other servers in the IRC network.
  * LIST command to other servers in the IRC network.
  *
  *
- * @param Client	The client from which this command has been received
- * @param Req		Request structure with prefix and all parameters
- * @returns		CONNECTED or DISCONNECTED
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
  */
  */
 GLOBAL bool
 GLOBAL bool
 IRC_LIST( CLIENT *Client, REQUEST *Req )
 IRC_LIST( CLIENT *Client, REQUEST *Req )
@@ -592,79 +614,84 @@ IRC_LIST( CLIENT *Client, REQUEST *Req )
 	char *pattern;
 	char *pattern;
 	CHANNEL *chan;
 	CHANNEL *chan;
 	CLIENT *from, *target;
 	CLIENT *from, *target;
+	int count = 0;
 
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert(Client != NULL);
+	assert(Req != NULL);
 
 
 	/* Bad number of prameters? */
 	/* Bad number of prameters? */
-	if( Req->argc > 2 )
-		return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
-			Client_ID( Client ), Req->command );
+	if (Req->argc > 2)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
-	if( Req->argc > 0 )
-		pattern = strtok( Req->argv[0], "," );
+	if (Req->argc > 0)
+		pattern = strtok(Req->argv[0], ",");
 	else
 	else
 		pattern = "*";
 		pattern = "*";
 
 
 	/* Get sender from prefix, if any */
 	/* Get sender from prefix, if any */
-	if( Client_Type( Client ) == CLIENT_SERVER )
-		from = Client_Search( Req->prefix );
+	if (Client_Type(Client) == CLIENT_SERVER)
+		from = Client_Search(Req->prefix);
 	else
 	else
 		from = Client;
 		from = Client;
 
 
-	if( ! from )
-		return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG,
-				Client_ID( Client ), Req->prefix );
+	if (!from)
+		return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
+					  Client_ID(Client), Req->prefix);
 
 
-	if( Req->argc == 2 )
-	{
+	if (Req->argc == 2) {
 		/* Forward to other server? */
 		/* Forward to other server? */
-		target = Client_Search( Req->argv[1] );
-		if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER ))
-			return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG,
-					Client_ID( Client ), Req->argv[1] );
+		target = Client_Search(Req->argv[1]);
+		if (! target || Client_Type(target) != CLIENT_SERVER)
+			return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
+						  Client_ID(Client),
+						  Req->argv[1]);
 
 
-		if( target != Client_ThisServer( ))
-		{
+		if (target != Client_ThisServer()) {
 			/* Target is indeed an other server, forward it! */
 			/* Target is indeed an other server, forward it! */
-			return IRC_WriteStrClientPrefix( target, from,
-					"LIST %s :%s", Client_ID( from ),
-					Req->argv[1] );
+			return IRC_WriteStrClientPrefix(target, from,
+							"LIST %s :%s",
+							Req->argv[0],
+							Req->argv[1]);
 		}
 		}
 	}
 	}
 
 
-	while( pattern )
-	{
+	while (pattern) {
 		/* Loop through all the channels */
 		/* Loop through all the channels */
-		chan = Channel_First( );
-		while( chan )
-		{
+		if (Req->argc > 0)
+			ngt_LowerStr(pattern);
+		chan = Channel_First();
+		while (chan) {
 			/* Check search pattern */
 			/* Check search pattern */
-			if( Match( pattern, Channel_Name( chan )))
-			{
+			if (MatchCaseInsensitive(pattern, Channel_Name(chan))) {
 				/* Gotcha! */
 				/* Gotcha! */
-				if( ! strchr( Channel_Modes( chan ), 's' ) ||
-				    Channel_IsMemberOf( chan, from ))
-				{
-					if( ! IRC_WriteStrClient( from,
-					    RPL_LIST_MSG, Client_ID( from ),
-					    Channel_Name( chan ),
-					    Channel_MemberCount( chan ),
-					    Channel_Topic( chan )))
+				if (!strchr(Channel_Modes(chan), 's')
+				    || Channel_IsMemberOf(chan, from)) {
+					if (IRC_CheckListTooBig(from, count,
+								 MAX_RPL_LIST,
+								 "LIST"))
+						break;
+					if (!IRC_WriteStrClient(from,
+					     RPL_LIST_MSG, Client_ID(from),
+					     Channel_Name(chan),
+					     Channel_MemberCount(chan),
+					     Channel_Topic( chan )))
 						return DISCONNECTED;
 						return DISCONNECTED;
+					count++;
 				}
 				}
 			}
 			}
-			chan = Channel_Next( chan );
+			chan = Channel_Next(chan);
 		}
 		}
 
 
 		/* Get next name ... */
 		/* Get next name ... */
-		if( Req->argc > 0 )
-			pattern = strtok( NULL, "," );
+		if(Req->argc > 0)
+			pattern = strtok(NULL, ",");
 		else
 		else
 			pattern = NULL;
 			pattern = NULL;
 	}
 	}
 
 
-	return IRC_WriteStrClient( from, RPL_LISTEND_MSG, Client_ID( from ));
+	IRC_SetPenalty(from, 2);
+	return IRC_WriteStrClient(from, RPL_LISTEND_MSG, Client_ID(from));
 } /* IRC_LIST */
 } /* IRC_LIST */
 
 
 
 

+ 337 - 191
src/ngircd/irc-info.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -28,13 +28,16 @@
 #include "conn-func.h"
 #include "conn-func.h"
 #include "conn-zip.h"
 #include "conn-zip.h"
 #include "channel.h"
 #include "channel.h"
+#include "class.h"
 #include "conf.h"
 #include "conf.h"
 #include "defines.h"
 #include "defines.h"
+#include "lists.h"
 #include "log.h"
 #include "log.h"
 #include "messages.h"
 #include "messages.h"
 #include "match.h"
 #include "match.h"
 #include "tool.h"
 #include "tool.h"
 #include "parse.h"
 #include "parse.h"
+#include "irc.h"
 #include "irc-write.h"
 #include "irc-write.h"
 
 
 #include "exp.h"
 #include "exp.h"
@@ -152,6 +155,15 @@ IRC_INFO(CLIENT * Client, REQUEST * Req)
 } /* IRC_INFO */
 } /* IRC_INFO */
 
 
 
 
+/**
+ * Handler for the IRC "ISON" command.
+ *
+ * See RFC 2812, 4.9 "Ison message".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
 IRC_ISON( CLIENT *Client, REQUEST *Req )
 IRC_ISON( CLIENT *Client, REQUEST *Req )
 {
 {
@@ -160,80 +172,103 @@ IRC_ISON( CLIENT *Client, REQUEST *Req )
 	char *ptr;
 	char *ptr;
 	int i;
 	int i;
 
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert(Client != NULL);
+	assert(Req != NULL);
 
 
-	/* Falsche Anzahl Parameter? */
-	if(( Req->argc < 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	/* Bad number of arguments? */
+	if (Req->argc < 1)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
-	strlcpy( rpl, RPL_ISON_MSG, sizeof rpl );
-	for( i = 0; i < Req->argc; i++ )
-	{
-		ptr = strtok( Req->argv[i], " " );
-		while( ptr )
-		{
-			ngt_TrimStr( ptr );
-			c = Client_Search( ptr );
-			if( c && ( Client_Type( c ) == CLIENT_USER ))
-			{
-				/* Dieser Nick ist "online" */
-				strlcat( rpl, ptr, sizeof( rpl ));
-				strlcat( rpl, " ", sizeof( rpl ));
+	strlcpy(rpl, RPL_ISON_MSG, sizeof rpl);
+	for (i = 0; i < Req->argc; i++) {
+		/* "All" ircd even parse ":<x> <y> ..." arguments and split
+		 * them up; so we do the same ... */
+		ptr = strtok(Req->argv[i], " ");
+		while (ptr) {
+			ngt_TrimStr(ptr);
+			c = Client_Search(ptr);
+			if (c && Client_Type(c) == CLIENT_USER) {
+				strlcat(rpl, Client_ID(c), sizeof(rpl));
+				strlcat(rpl, " ", sizeof(rpl));
 			}
 			}
-			ptr = strtok( NULL, " " );
+			ptr = strtok(NULL, " ");
 		}
 		}
 	}
 	}
 	ngt_TrimLastChr(rpl, ' ');
 	ngt_TrimLastChr(rpl, ' ');
 
 
-	return IRC_WriteStrClient( Client, rpl, Client_ID( Client ) );
+	return IRC_WriteStrClient(Client, rpl, Client_ID(Client));
 } /* IRC_ISON */
 } /* IRC_ISON */
 
 
 
 
+/**
+ * Handler for the IRC "LINKS" command.
+ *
+ * See RFC 2812, 3.4.5 "Links message".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
-IRC_LINKS( CLIENT *Client, REQUEST *Req )
+IRC_LINKS(CLIENT *Client, REQUEST *Req)
 {
 {
 	CLIENT *target, *from, *c;
 	CLIENT *target, *from, *c;
 	char *mask;
 	char *mask;
 
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert(Client != NULL);
+	assert(Req != NULL);
 
 
-	if(( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	IRC_SetPenalty(Client, 1);
 
 
-	/* Server-Mask ermitteln */
-	if( Req->argc > 0 ) mask = Req->argv[Req->argc - 1];
-	else mask = "*";
+	if (Req->argc > 2)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
-	/* Absender ermitteln */
-	if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
-	else from = Client;
-	if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
+	/* Get pointer to server mask or "*", if none given */
+	if (Req->argc > 0)
+		mask = Req->argv[Req->argc - 1];
+	else
+		mask = "*";
 
 
-	/* An anderen Server forwarden? */
-	if( Req->argc == 2 )
-	{
-		target = Client_Search( Req->argv[0] );
-		if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[0] );
-		else if( target != Client_ThisServer( )) return IRC_WriteStrClientPrefix( target, from, "LINKS %s %s", Req->argv[0], Req->argv[1] );
-	}
+	if (Client_Type(Client) == CLIENT_SERVER)
+		from = Client_Search(Req->prefix);
+	else
+		from = Client;
+	if (!from)
+		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+					  Client_ID(Client), Req->prefix);
 
 
-	/* Wer ist der Absender? */
-	if( Client_Type( Client ) == CLIENT_SERVER ) target = Client_Search( Req->prefix );
-	else target = Client;
-	if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
+	/* Forward? */
+	if (Req->argc == 2) {
+		target = Client_Search(Req->argv[0]);
+		if (! target || Client_Type(target) != CLIENT_SERVER)
+			return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
+						  Client_ID(from),
+						  Req->argv[0] );
+		else
+			if (target != Client_ThisServer())
+				return IRC_WriteStrClientPrefix(target, from,
+						"LINKS %s %s", Req->argv[0],
+						Req->argv[1]);
+	}
 
 
-	c = Client_First( );
-	while( c )
-	{
-		if( Client_Type( c ) == CLIENT_SERVER )
-		{
-			if( ! IRC_WriteStrClient( target, RPL_LINKS_MSG, Client_ID( target ), Client_ID( c ), Client_ID( Client_TopServer( c ) ? Client_TopServer( c ) : Client_ThisServer( )), Client_Hops( c ), Client_Info( c ))) return DISCONNECTED;
+	c = Client_First();
+	while (c) {
+		if (Client_Type(c) == CLIENT_SERVER
+		    && MatchCaseInsensitive(mask, Client_ID(c))) {
+			if (!IRC_WriteStrClient(from, RPL_LINKS_MSG,
+					Client_ID(from), Client_ID(c),
+					Client_ID(Client_TopServer(c)
+						  ? Client_TopServer(c)
+						  : Client_ThisServer()),
+					Client_Hops(c), Client_Info(c)))
+				return DISCONNECTED;
 		}
 		}
-		c = Client_Next( c );
+		c = Client_Next(c);
 	}
 	}
-	
-	IRC_SetPenalty( target, 1 );
-	return IRC_WriteStrClient( target, RPL_ENDOFLINKS_MSG, Client_ID( target ), mask );
+	return IRC_WriteStrClient(from, RPL_ENDOFLINKS_MSG,
+				  Client_ID(from), mask);
 } /* IRC_LINKS */
 } /* IRC_LINKS */
 
 
 
 
@@ -478,6 +513,8 @@ IRC_STATS( CLIENT *Client, REQUEST *Req )
 	COMMAND *cmd;
 	COMMAND *cmd;
 	time_t time_now;
 	time_t time_now;
 	unsigned int days, hrs, mins;
 	unsigned int days, hrs, mins;
+	struct list_head *list;
+	struct list_elem *list_item;
 
 
 	assert(Client != NULL);
 	assert(Client != NULL);
 	assert(Req != NULL);
 	assert(Req != NULL);
@@ -516,6 +553,28 @@ IRC_STATS( CLIENT *Client, REQUEST *Req )
 		query = '*';
 		query = '*';
 
 
 	switch (query) {
 	switch (query) {
+	case 'g':	/* Network-wide bans ("G-Lines") */
+	case 'G':
+	case 'k':	/* Server-local bans ("K-Lines") */
+	case 'K':
+		if (!Client_HasMode(from, 'o'))
+		    return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
+					      Client_ID(from));
+		if (query == 'g' || query == 'G')
+			list = Class_GetList(CLASS_GLINE);
+		else
+			list = Class_GetList(CLASS_KLINE);
+			list_item = Lists_GetFirst(list);
+			while (list_item) {
+				if (!IRC_WriteStrClient(from, RPL_STATSXLINE_MSG,
+						Client_ID(from), query,
+						Lists_GetMask(list_item),
+						Lists_GetValidity(list_item),
+						Lists_GetReason(list_item)))
+					return DISCONNECTED;
+				list_item = Lists_GetNext(list_item);
+			}
+		break;
 	case 'l':	/* Link status (servers and own link) */
 	case 'l':	/* Link status (servers and own link) */
 	case 'L':
 	case 'L':
 		time_now = time(NULL);
 		time_now = time(NULL);
@@ -589,10 +648,10 @@ IRC_STATS( CLIENT *Client, REQUEST *Req )
  * therefore answers with ERR_SUMMONDISABLED.
  * therefore answers with ERR_SUMMONDISABLED.
  */
  */
 GLOBAL bool
 GLOBAL bool
-IRC_SUMMON(CLIENT * Client, REQUEST * Req)
+IRC_SUMMON(CLIENT * Client, UNUSED REQUEST * Req)
 {
 {
 	return IRC_WriteStrClient(Client, ERR_SUMMONDISABLED_MSG,
 	return IRC_WriteStrClient(Client, ERR_SUMMONDISABLED_MSG,
-				  Client_ID(Client), Req->command);
+				  Client_ID(Client));
 } /* IRC_SUMMON */
 } /* IRC_SUMMON */
 
 
 
 
@@ -682,10 +741,10 @@ IRC_USERHOST(CLIENT *Client, REQUEST *Req)
  * See RFC 2812 section 4.6. As suggested there the command is disabled.
  * See RFC 2812 section 4.6. As suggested there the command is disabled.
  */
  */
 GLOBAL bool
 GLOBAL bool
-IRC_USERS(CLIENT * Client, REQUEST * Req)
+IRC_USERS(CLIENT * Client, UNUSED REQUEST * Req)
 {
 {
 	return IRC_WriteStrClient(Client, ERR_USERSDISABLED_MSG,
 	return IRC_WriteStrClient(Client, ERR_USERSDISABLED_MSG,
-				  Client_ID(Client), Req->command);
+				  Client_ID(Client));
 } /* IRC_USERS */
 } /* IRC_USERS */
 
 
 
 
@@ -758,8 +817,16 @@ who_flags_qualifier(const char *chan_user_modes)
 }
 }
 
 
 
 
+/**
+ * Send WHO reply for a "channel target" ("WHO #channel").
+ *
+ * @param Client Client requesting the information.
+ * @param Chan Channel being requested.
+ * @param OnlyOps Only display IRC operators.
+ * @return CONNECTED or DISCONNECTED.
+ */
 static bool
 static bool
-IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
+IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 {
 {
 	bool is_visible, is_member, is_ircop;
 	bool is_visible, is_member, is_ircop;
 	CL2CHAN *cl2chan;
 	CL2CHAN *cl2chan;
@@ -767,6 +834,7 @@ IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 	const char *chan_user_modes;
 	const char *chan_user_modes;
 	char flags[8];
 	char flags[8];
 	CLIENT *c;
 	CLIENT *c;
+	int count = 0;
 
 
 	assert( Client != NULL );
 	assert( Client != NULL );
 	assert( Chan != NULL );
 	assert( Chan != NULL );
@@ -775,7 +843,8 @@ IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 
 
 	/* Secret channel? */
 	/* Secret channel? */
 	if (!is_member && strchr(Channel_Modes(Chan), 's'))
 	if (!is_member && strchr(Channel_Modes(Chan), 's'))
-		return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), Channel_Name(Chan));
+		return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG,
+					  Client_ID(Client), Channel_Name(Chan));
 
 
 	cl2chan = Channel_FirstMember(Chan);
 	cl2chan = Channel_FirstMember(Chan);
 	for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) {
 	for (; cl2chan ; cl2chan = Channel_NextMember(Chan, cl2chan)) {
@@ -788,141 +857,178 @@ IRC_Send_WHO(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 
 
 		is_visible = strchr(client_modes, 'i') == NULL;
 		is_visible = strchr(client_modes, 'i') == NULL;
 		if (is_member || is_visible) {
 		if (is_member || is_visible) {
+			if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO"))
+				break;
+
 			strcpy(flags, who_flags_status(client_modes));
 			strcpy(flags, who_flags_status(client_modes));
 			if (is_ircop)
 			if (is_ircop)
 				strlcat(flags, "*", sizeof(flags));
 				strlcat(flags, "*", sizeof(flags));
 
 
 			chan_user_modes = Channel_UserModes(Chan, c);
 			chan_user_modes = Channel_UserModes(Chan, c);
-			strlcat(flags, who_flags_qualifier(chan_user_modes), sizeof(flags));
+			strlcat(flags, who_flags_qualifier(chan_user_modes),
+				sizeof(flags));
 
 
-			if (!write_whoreply(Client, c, Channel_Name(Chan), flags))
+			if (!write_whoreply(Client, c, Channel_Name(Chan),
+					    flags))
 				return DISCONNECTED;
 				return DISCONNECTED;
+			count++;
 		}
 		}
 	}
 	}
-	return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), Channel_Name(Chan));
-} /* IRC_Send_WHO */
+	return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
+				  Channel_Name(Chan));
+}
 
 
 
 
-GLOBAL bool
-IRC_WHO( CLIENT *Client, REQUEST *Req )
+/**
+ * Send WHO reply for a "mask target" ("WHO m*sk").
+ *
+ * @param Client Client requesting the information.
+ * @param Mask Mask being requested or NULL for "all" clients.
+ * @param OnlyOps Only display IRC operators.
+ * @return CONNECTED or DISCONNECTED.
+ */
+static bool
+IRC_WHO_Mask(CLIENT *Client, char *Mask, bool OnlyOps)
 {
 {
-	bool only_ops, have_arg, client_match;
-	const char *channelname, *client_modes, *chan_user_modes;
-	char pattern[COMMAND_LEN];
-	char flags[4];
-	CL2CHAN *cl2chan;
-	CHANNEL *chan, *cn;
 	CLIENT *c;
 	CLIENT *c;
+	CL2CHAN *cl2chan;
+	CHANNEL *chan;
+	bool client_match, is_visible;
+	char flags[4];
+	int count = 0;
 
 
-	assert( Client != NULL );
-	assert( Req != NULL );
-
-	if (Req->argc > 2)
-		return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	assert (Client != NULL);
 
 
-	only_ops = false;
-	have_arg = false;
-
-	if (Req->argc == 2) {
-		if (strcmp(Req->argv[1], "o") == 0)
-			only_ops = true;
-#ifdef STRICT_RFC
-		else return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command);
-#endif
-	}
-
-	IRC_SetPenalty(Client, 1);
-	if (Req->argc >= 1) { /* Channel or Mask. */
-		chan = Channel_Search(Req->argv[0]);
-		if (chan)
-			return IRC_Send_WHO(Client, chan, only_ops);
-		if (strcmp(Req->argv[0], "0") != 0) { /* RFC stupidity, same as no arguments */
-			have_arg = true;
-			strlcpy(pattern, Req->argv[0], sizeof(pattern));
-			ngt_LowerStr(pattern);
-			IRC_SetPenalty(Client, 3);
-		}
-	}
+	if (Mask)
+		ngt_LowerStr(Mask);
 
 
 	for (c = Client_First(); c != NULL; c = Client_Next(c)) {
 	for (c = Client_First(); c != NULL; c = Client_Next(c)) {
 		if (Client_Type(c) != CLIENT_USER)
 		if (Client_Type(c) != CLIENT_USER)
 			continue;
 			continue;
-		 /*
-		  * RFC 2812, 3.6.1:
-		  * In the absence of the parameter, all visible (users who aren't
-		  * invisible (user mode +i) and who don't have a common channel
-		  * with the requesting client) are listed.
-		  *
-		  * The same result can be achieved by using a [sic] of "0"
-		  * or any wildcard which will end up matching every visible user.
-		  *
-		  * The [sic] passed to WHO is matched against users' host, server, real name and
-		  * nickname if the channel cannot be found.
-		  */
-		client_modes = Client_Modes(c);
-		if (strchr(client_modes, 'i'))
-			continue;
 
 
-		if (only_ops && !strchr(client_modes, 'o'))
+		if (OnlyOps && !Client_HasMode(c, 'o'))
 			continue;
 			continue;
 
 
-		if (have_arg) { /* match pattern against user host/server/name/nick */
-			client_match = MatchCaseInsensitive(pattern, Client_Hostname(c)); /* user's host */
+		if (Mask) {
+			/* Match pattern against user host/server/name/nick */
+			client_match = MatchCaseInsensitive(Mask,
+						Client_Hostname(c));
 			if (!client_match)
 			if (!client_match)
-				client_match = MatchCaseInsensitive(pattern, Client_ID(Client_Introducer(c))); /* server */
+				client_match = MatchCaseInsensitive(Mask,
+						Client_ID(Client_Introducer(c)));
 			if (!client_match)
 			if (!client_match)
-				client_match = Match(Req->argv[0], Client_Info(c)); /* realname */
+				client_match = MatchCaseInsensitive(Mask,
+						Client_Info(c));
 			if (!client_match)
 			if (!client_match)
-				client_match = MatchCaseInsensitive(pattern, Client_ID(c)); /* nick name */
-
-			if (!client_match) /* This isn't the client you're looking for */
-				continue;
+				client_match = MatchCaseInsensitive(Mask,
+						Client_ID(c));
+			if (!client_match)
+				continue;	/* no match: skip this client */
 		}
 		}
 
 
-		strcpy(flags, who_flags_status(client_modes));
+		is_visible = !Client_HasMode(c, 'i');
 
 
-		if (strchr(client_modes, 'o')) /* this client is an operator */
-			strlcat(flags, "*", sizeof(flags));
+		/* Target client is invisible, but mask matches exactly? */
+		if (!is_visible && Mask && strcasecmp(Client_ID(c), Mask) == 0)
+			is_visible = true;
 
 
-		/* Search suitable channel */
-		cl2chan = Channel_FirstChannelOf(c);
-		while (cl2chan) {
-			cn = Channel_GetChannel(cl2chan);
-			if (Channel_IsMemberOf(cn, Client) ||
-				    !strchr(Channel_Modes(cn), 's'))
-			{
-				channelname = Channel_Name(cn);
-				break;
+		/* Target still invisible, but are both on the same channel? */
+		if (!is_visible) {
+			cl2chan = Channel_FirstChannelOf(Client);
+			while (cl2chan && !is_visible) {
+				chan = Channel_GetChannel(cl2chan);
+				if (Channel_IsMemberOf(chan, c))
+					is_visible = true;
+				cl2chan = Channel_NextChannelOf(Client, cl2chan);
 			}
 			}
-			cl2chan = Channel_NextChannelOf(c, cl2chan);
 		}
 		}
-		if (cl2chan) {
-			chan = Channel_GetChannel(cl2chan);
-			chan_user_modes = Channel_UserModes(chan, c);
-			strlcat(flags, who_flags_qualifier(chan_user_modes), sizeof(flags));
-		} else
-			channelname = "*";
-
-		if (!write_whoreply(Client, c, channelname, flags))
+
+		if (!is_visible)	/* target user is not visible */
+			continue;
+
+		if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO"))
+			break;
+
+		strcpy(flags, who_flags_status(Client_Modes(c)));
+		if (strchr(Client_Modes(c), 'o'))
+			strlcat(flags, "*", sizeof(flags));
+
+		if (!write_whoreply(Client, c, "*", flags))
 			return DISCONNECTED;
 			return DISCONNECTED;
+		count++;
 	}
 	}
 
 
-	if (Req->argc > 0)
-		channelname = Req->argv[0];
-	else
-		channelname = "*";
+	return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
+				  Mask ? Mask : "*");
+}
+
+
+/**
+ * Handler for the IRC "WHO" command.
+ *
+ * See RFC 2812, 3.6.1 "Who query".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+IRC_WHO(CLIENT *Client, REQUEST *Req)
+{
+	bool only_ops;
+	CHANNEL *chan;
 
 
-	return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client), channelname);
+	assert (Client != NULL);
+	assert (Req != NULL);
+
+	if (Req->argc > 2)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
+
+	only_ops = false;
+
+	if (Req->argc == 2) {
+		if (strcmp(Req->argv[1], "o") == 0)
+			only_ops = true;
+#ifdef STRICT_RFC
+		else
+			return IRC_WriteStrClient(Client,
+						  ERR_NEEDMOREPARAMS_MSG,
+						  Client_ID(Client),
+						  Req->command);
+#endif
+	}
+
+	IRC_SetPenalty(Client, 1);
+	if (Req->argc >= 1) {
+		/* Channel or mask given */
+		chan = Channel_Search(Req->argv[0]);
+		if (chan) {
+			/* Members of a channel have been requested */
+			IRC_SetPenalty(Client, 1);
+			return IRC_WHO_Channel(Client, chan, only_ops);
+		}
+		if (strcmp(Req->argv[0], "0") != 0) {
+			/* A mask has been given. But please note this RFC
+			 * stupidity: "0" is same as no arguments ... */
+			IRC_SetPenalty(Client, 3);
+			return IRC_WHO_Mask(Client, Req->argv[0], only_ops);
+		}
+	}
+
+	/* No channel or (valid) mask given */
+	IRC_SetPenalty(Client, 2);
+	return IRC_WHO_Mask(Client, NULL, only_ops);
 } /* IRC_WHO */
 } /* IRC_WHO */
 
 
 
 
 /**
 /**
  * Generate WHOIS reply of one actual client.
  * Generate WHOIS reply of one actual client.
  *
  *
- * @param Client	The client from which this command has been received.
- * @param from		The client requesting the information ("originator").
- * @param c		The client of which information should be returned.
- * @returns		CONNECTED or DISCONNECTED.
+ * @param Client The client from which this command has been received.
+ * @param from The client requesting the information ("originator").
+ * @param c The client of which information should be returned.
+ * @return CONNECTED or DISCONNECTED.
  */
  */
 static bool
 static bool
 IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
 IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
@@ -931,6 +1037,10 @@ IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
 	CL2CHAN *cl2chan;
 	CL2CHAN *cl2chan;
 	CHANNEL *chan;
 	CHANNEL *chan;
 
 
+	assert(Client != NULL);
+	assert(from != NULL);
+	assert(c != NULL);
+
 	/* Nick, user, hostname and client info */
 	/* Nick, user, hostname and client info */
 	if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from),
 	if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from),
 				Client_ID(c), Client_User(c),
 				Client_ID(c), Client_User(c),
@@ -988,33 +1098,43 @@ IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
 
 
 	/* IRC-Operator? */
 	/* IRC-Operator? */
 	if (Client_HasMode(c, 'o') &&
 	if (Client_HasMode(c, 'o') &&
-		!IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG,
-				    Client_ID(from), Client_ID(c)))
-			return DISCONNECTED;
+	    !IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG,
+				Client_ID(from), Client_ID(c)))
+		return DISCONNECTED;
 
 
 	/* Connected using SSL? */
 	/* Connected using SSL? */
 	if (Conn_UsesSSL(Client_Conn(c)) &&
 	if (Conn_UsesSSL(Client_Conn(c)) &&
-		!IRC_WriteStrClient(from, RPL_WHOISSSL_MSG,
-				    Client_ID(from), Client_ID(c)))
-			return DISCONNECTED;
+	    !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from),
+				Client_ID(c)))
+		return DISCONNECTED;
+
+	/* Registered nick name? */
+	if (Client_HasMode(c, 'R') &&
+	    !IRC_WriteStrClient(from, RPL_WHOISREGNICK_MSG,
+				Client_ID(from), Client_ID(c)))
+		return DISCONNECTED;
+
+	if (Client_Conn(c) > NONE && (Client_OperByMe(from) || from == c) &&
+	    !IRC_WriteStrClient(from, RPL_WHOISHOST_MSG, Client_ID(from),
+				Client_ID(c), Client_Hostname(c),
+				Conn_GetIPAInfo(Client_Conn(c))))
+		return DISCONNECTED;
 
 
 	/* Idle and signon time (local clients only!) */
 	/* Idle and signon time (local clients only!) */
 	if (!Conf_MorePrivacy && Client_Conn(c) > NONE &&
 	if (!Conf_MorePrivacy && Client_Conn(c) > NONE &&
-		!IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
-				    Client_ID(from), Client_ID(c),
-				    (unsigned long)Conn_GetIdle(Client_Conn(c)),
-				    (unsigned long)Conn_GetSignon(Client_Conn(c))))
-			return DISCONNECTED;
+	    !IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
+				Client_ID(from), Client_ID(c),
+				(unsigned long)Conn_GetIdle(Client_Conn(c)),
+				(unsigned long)Conn_GetSignon(Client_Conn(c))))
+		return DISCONNECTED;
 
 
 	/* Away? */
 	/* Away? */
 	if (Client_HasMode(c, 'a') &&
 	if (Client_HasMode(c, 'a') &&
-		!IRC_WriteStrClient(from, RPL_AWAY_MSG,
-				    Client_ID(from), Client_ID(c),
-				    Client_Away(c)))
-			return DISCONNECTED;
+	    !IRC_WriteStrClient(from, RPL_AWAY_MSG,
+				Client_ID(from), Client_ID(c), Client_Away(c)))
+		return DISCONNECTED;
 
 
-	return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG,
-				  Client_ID(from), Client_ID(c));
+	return CONNECTED;
 } /* IRC_WHOIS_SendReply */
 } /* IRC_WHOIS_SendReply */
 
 
 
 
@@ -1034,7 +1154,7 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
 	unsigned int match_count = 0, found = 0;
 	unsigned int match_count = 0, found = 0;
 	bool has_wildcards, is_remote;
 	bool has_wildcards, is_remote;
 	bool got_wildcard = false;
 	bool got_wildcard = false;
-	const char *query;
+	char mask[COMMAND_LEN], *query;
 
 
 	assert( Client != NULL );
 	assert( Client != NULL );
 	assert( Req != NULL );
 	assert( Req != NULL );
@@ -1075,7 +1195,8 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
 						Req->argv[0], Req->argv[1]);
 						Req->argv[0], Req->argv[1]);
 
 
 	is_remote = Client_Conn(from) < 0;
 	is_remote = Client_Conn(from) < 0;
-	for (query = strtok(Req->argv[Req->argc - 1], ",");
+	strlcpy(mask, Req->argv[Req->argc - 1], sizeof(mask));
+	for (query = strtok(ngt_LowerStr(mask), ",");
 			query && found < 3;
 			query && found < 3;
 			query = strtok(NULL, ","), found++)
 			query = strtok(NULL, ","), found++)
 	{
 	{
@@ -1086,11 +1207,11 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
 		 *  - no wildcards for remote clients
 		 *  - no wildcards for remote clients
 		 *  - only one wildcard target per local client
 		 *  - only one wildcard target per local client
 		 *
 		 *
-		 *  also, at most ten matches are returned.
+		 *  Also, at most MAX_RPL_WHOIS matches are returned.
 		 */
 		 */
 		if (!has_wildcards || is_remote) {
 		if (!has_wildcards || is_remote) {
 			c = Client_Search(query);
 			c = Client_Search(query);
-			if (c) {
+			if (c && Client_Type(c) == CLIENT_USER) {
 				if (!IRC_WHOIS_SendReply(Client, from, c))
 				if (!IRC_WHOIS_SendReply(Client, from, c))
 					return DISCONNECTED;
 					return DISCONNECTED;
 			} else {
 			} else {
@@ -1112,21 +1233,28 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
 		got_wildcard = true;
 		got_wildcard = true;
 		IRC_SetPenalty(Client, 3);
 		IRC_SetPenalty(Client, 3);
 
 
-		for (c = Client_First(); c && match_count < 10; c = Client_Next(c)) {
+		for (c = Client_First(); c; c = Client_Next(c)) {
+			if (IRC_CheckListTooBig(Client, match_count,
+					    MAX_RPL_WHOIS, "WHOIS"))
+				break;
+
 			if (Client_Type(c) != CLIENT_USER)
 			if (Client_Type(c) != CLIENT_USER)
 				continue;
 				continue;
 			if (!MatchCaseInsensitive(query, Client_ID(c)))
 			if (!MatchCaseInsensitive(query, Client_ID(c)))
 				continue;
 				continue;
 			if (!IRC_WHOIS_SendReply(Client, from, c))
 			if (!IRC_WHOIS_SendReply(Client, from, c))
 				return DISCONNECTED;
 				return DISCONNECTED;
+
 			match_count++;
 			match_count++;
 		}
 		}
 
 
 		if (match_count == 0)
 		if (match_count == 0)
-			return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
-				Client_ID(Client), Req->argv[Req->argc - 1]);
+			IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+					   Client_ID(Client),
+					   Req->argv[Req->argc - 1]);
 	}
 	}
-	return CONNECTED;
+	return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG,
+				  Client_ID(from), Req->argv[Req->argc - 1]);
 } /* IRC_WHOIS */
 } /* IRC_WHOIS */
 
 
 
 
@@ -1208,11 +1336,11 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req )
 	if (last < 0)
 	if (last < 0)
 		last = 0;
 		last = 0;
 
 
-	max = DEFAULT_WHOWAS;
+	max = DEF_RPL_WHOWAS;
 	if (Req->argc > 1) {
 	if (Req->argc > 1) {
 		max = atoi(Req->argv[1]);
 		max = atoi(Req->argv[1]);
 		if (max < 1)
 		if (max < 1)
-			max = MAX_WHOWAS;
+			max = MAX_RPL_WHOWAS;
 	}
 	}
 
 
 	/*
 	/*
@@ -1251,38 +1379,55 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req )
 } /* IRC_WHOWAS */
 } /* IRC_WHOWAS */
 
 
 
 
+/**
+ * Send LUSERS reply to a client.
+ *
+ * @param Client The receipient of the information.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
-IRC_Send_LUSERS( CLIENT *Client )
+IRC_Send_LUSERS(CLIENT *Client)
 {
 {
 	unsigned long cnt;
 	unsigned long cnt;
 #ifndef STRICT_RFC
 #ifndef STRICT_RFC
 	unsigned long max;
 	unsigned long max;
 #endif
 #endif
 
 
-	assert( Client != NULL );
+	assert(Client != NULL);
 
 
 	/* Users, services and serevers in the network */
 	/* Users, services and serevers in the network */
-	if( ! IRC_WriteStrClient( Client, RPL_LUSERCLIENT_MSG, Client_ID( Client ), Client_UserCount( ), Client_ServiceCount( ), Client_ServerCount( ))) return DISCONNECTED;
+	if (!IRC_WriteStrClient(Client, RPL_LUSERCLIENT_MSG, Client_ID(Client),
+				Client_UserCount(), Client_ServiceCount(),
+				Client_ServerCount()))
+		return DISCONNECTED;
 
 
 	/* Number of IRC operators */
 	/* Number of IRC operators */
 	cnt = Client_OperCount( );
 	cnt = Client_OperCount( );
-	if( cnt > 0 )
-	{
-		if( ! IRC_WriteStrClient( Client, RPL_LUSEROP_MSG, Client_ID( Client ), cnt )) return DISCONNECTED;
+	if (cnt > 0) {
+		if (!IRC_WriteStrClient(Client, RPL_LUSEROP_MSG,
+					Client_ID(Client), cnt))
+			return DISCONNECTED;
 	}
 	}
 
 
 	/* Unknown connections */
 	/* Unknown connections */
 	cnt = Client_UnknownCount( );
 	cnt = Client_UnknownCount( );
-	if( cnt > 0 )
-	{
-		if( ! IRC_WriteStrClient( Client, RPL_LUSERUNKNOWN_MSG, Client_ID( Client ), cnt )) return DISCONNECTED;
+	if (cnt > 0) {
+		if (!IRC_WriteStrClient(Client, RPL_LUSERUNKNOWN_MSG,
+					Client_ID(Client), cnt))
+			return DISCONNECTED;
 	}
 	}
 
 
 	/* Number of created channels */
 	/* Number of created channels */
-	if( ! IRC_WriteStrClient( Client, RPL_LUSERCHANNELS_MSG, Client_ID( Client ), Channel_Count( ))) return DISCONNECTED;
+	if (!IRC_WriteStrClient(Client, RPL_LUSERCHANNELS_MSG,
+				Client_ID(Client),
+				Channel_CountVisible(Client)))
+		return DISCONNECTED;
 
 
 	/* Number of local users, services and servers */
 	/* Number of local users, services and servers */
-	if( ! IRC_WriteStrClient( Client, RPL_LUSERME_MSG, Client_ID( Client ), Client_MyUserCount( ), Client_MyServiceCount( ), Client_MyServerCount( ))) return DISCONNECTED;
+	if (!IRC_WriteStrClient(Client, RPL_LUSERME_MSG, Client_ID(Client),
+				Client_MyUserCount(), Client_MyServiceCount(),
+				Client_MyServerCount()))
+		return DISCONNECTED;
 
 
 #ifndef STRICT_RFC
 #ifndef STRICT_RFC
 	/* Maximum number of local users */
 	/* Maximum number of local users */
@@ -1453,7 +1598,8 @@ IRC_Send_ISUPPORT(CLIENT * Client)
 	return IRC_WriteStrClient(Client, RPL_ISUPPORT2_MSG, Client_ID(Client),
 	return IRC_WriteStrClient(Client, RPL_ISUPPORT2_MSG, Client_ID(Client),
 				  CHANNEL_NAME_LEN - 1, Conf_MaxNickLength - 1,
 				  CHANNEL_NAME_LEN - 1, Conf_MaxNickLength - 1,
 				  COMMAND_LEN - 23, CLIENT_AWAY_LEN - 1,
 				  COMMAND_LEN - 23, CLIENT_AWAY_LEN - 1,
-				  COMMAND_LEN - 113);
+				  COMMAND_LEN - 113, MAX_HNDL_MODES_ARG,
+				  MAX_HNDL_CHANNEL_LISTS);
 } /* IRC_Send_ISUPPORT */
 } /* IRC_Send_ISUPPORT */
 
 
 
 

+ 63 - 57
src/ngircd/irc-login.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
 
 
 #include "ngircd.h"
 #include "ngircd.h"
 #include "conn-func.h"
 #include "conn-func.h"
+#include "class.h"
 #include "conf.h"
 #include "conf.h"
 #include "channel.h"
 #include "channel.h"
 #include "io.h"
 #include "io.h"
@@ -46,7 +47,6 @@ static bool Hello_User PARAMS(( CLIENT *Client ));
 static bool Hello_User_PostAuth PARAMS(( CLIENT *Client ));
 static bool Hello_User_PostAuth PARAMS(( CLIENT *Client ));
 static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
 static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
 static void Introduce_Client PARAMS((CLIENT *To, CLIENT *Client, int Type));
 static void Introduce_Client PARAMS((CLIENT *To, CLIENT *Client, int Type));
-static void Reject_Client PARAMS((CLIENT *Client));
 
 
 static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix,
 static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix,
 				       void *i));
 				       void *i));
@@ -653,32 +653,37 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
 	CLIENT *target;
 	CLIENT *target;
 	char quitmsg[LINE_LEN];
 	char quitmsg[LINE_LEN];
 
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert(Client != NULL);
+	assert(Req != NULL);
 
 
 	/* Wrong number of arguments? */
 	/* Wrong number of arguments? */
-	if( Req->argc > 1 )
-		return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	if (Req->argc > 1)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
 	if (Req->argc == 1)
 	if (Req->argc == 1)
 		strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
 		strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
 
 
-	if ( Client_Type( Client ) == CLIENT_SERVER )
-	{
+	if (Client_Type(Client) == CLIENT_SERVER) {
 		/* Server */
 		/* Server */
-		target = Client_Search( Req->prefix );
-		if( ! target )
-		{
-			Log( LOG_WARNING, "Got QUIT from %s for unknown client!?", Client_ID( Client ));
+		target = Client_Search(Req->prefix);
+		if (!target) {
+			Log(LOG_WARNING,
+			    "Got QUIT from %s for unknown client!?",
+			    Client_ID(Client));
 			return CONNECTED;
 			return CONNECTED;
 		}
 		}
 
 
-		Client_Destroy( target, "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
-
-		return CONNECTED;
-	}
-	else
-	{
+		if (target != Client) {
+			Client_Destroy(target, "Got QUIT command.",
+				       Req->argc == 1 ? quitmsg : NULL, true);
+			return CONNECTED;
+		} else {
+			Conn_Close(Client_Conn(Client), "Got QUIT command.",
+				   Req->argc == 1 ? quitmsg : NULL, true);
+			return DISCONNECTED;
+		}
+	} else {
 		if (Req->argc == 1 && quitmsg[0] != '\"') {
 		if (Req->argc == 1 && quitmsg[0] != '\"') {
 			/* " " to avoid confusion */
 			/* " " to avoid confusion */
 			strlcpy(quitmsg, "\"", sizeof quitmsg);
 			strlcpy(quitmsg, "\"", sizeof quitmsg);
@@ -687,7 +692,8 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
 		}
 		}
 
 
 		/* User, Service, or not yet registered */
 		/* User, Service, or not yet registered */
-		Conn_Close( Client_Conn( Client ), "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
+		Conn_Close(Client_Conn(Client), "Got QUIT command.",
+			   Req->argc == 1 ? quitmsg : NULL, true);
 
 
 		return DISCONNECTED;
 		return DISCONNECTED;
 	}
 	}
@@ -883,15 +889,16 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 	}
 	}
 #endif
 #endif
 
 
-#ifdef DEBUG
-	if (conn > NONE)
-		Log(LOG_DEBUG,
-			"Connection %d: received PONG. Lag: %ld seconds.", conn,
-			time(NULL) - Conn_LastPing(Client_Conn(Client)));
-	else
-		 Log(LOG_DEBUG,
-			"Connection %d: received PONG.", conn);
-#endif
+	if (Client_Type(Client) == CLIENT_SERVER && Conn_LastPing(conn) == 0) {
+		Log(LOG_INFO,
+		    "Synchronization with \"%s\" done (connection %d): %ld seconds [%ld users, %ld channels]",
+		    Client_ID(Client), conn, time(NULL) - Conn_GetSignon(conn),
+		    Client_UserCount(), Channel_CountVisible(NULL));
+		Conn_UpdatePing(conn);
+	} else
+		LogDebug("Connection %d: received PONG. Lag: %ld seconds.",
+			 conn, time(NULL) - Conn_LastPing(conn));
+
 	return CONNECTED;
 	return CONNECTED;
 } /* IRC_PONG */
 } /* IRC_PONG */
 
 
@@ -938,10 +945,19 @@ Hello_User(CLIENT * Client)
 		 * passwords supplied are classified as "wrong". */
 		 * passwords supplied are classified as "wrong". */
 		if(Client_Password(Client)[0] == '\0')
 		if(Client_Password(Client)[0] == '\0')
 			return Hello_User_PostAuth(Client);
 			return Hello_User_PostAuth(Client);
-		Reject_Client(Client);
+		Client_Reject(Client, "Non-empty password", false);
 		return DISCONNECTED;
 		return DISCONNECTED;
 	}
 	}
 
 
+	if (Conf_PAMIsOptional && strcmp(Client_Password(Client), "") == 0) {
+		/* Clients are not required to send a password and to be PAM-
+		 * authenticated at all. If not, they won't become "identified"
+		 * and keep the "~" in their supplied user name.
+		 * Therefore it is sensible to either set Conf_PAMisOptional or
+		 * to enable IDENT lookups -- not both. */
+		return Hello_User_PostAuth(Client);
+	}
+
 	/* Fork child process for PAM authentication; and make sure that the
 	/* Fork child process for PAM authentication; and make sure that the
 	 * process timeout is set higher than the login timeout! */
 	 * process timeout is set higher than the login timeout! */
 	pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
 	pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
@@ -953,6 +969,7 @@ Hello_User(CLIENT * Client)
 	} else {
 	} else {
 		/* Sub process */
 		/* Sub process */
 		Log_Init_Subprocess("Auth");
 		Log_Init_Subprocess("Auth");
+		Conn_CloseAllSockets(NONE);
 		result = PAM_Authenticate(Client);
 		result = PAM_Authenticate(Client);
 		if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
 		if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
 			Log_Subprocess(LOG_ERR,
 			Log_Subprocess(LOG_ERR,
@@ -964,7 +981,7 @@ Hello_User(CLIENT * Client)
 	/* Check global server password ... */
 	/* Check global server password ... */
 	if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
 	if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
 		/* Bad password! */
 		/* Bad password! */
-		Reject_Client(Client);
+		Client_Reject(Client, "Bad server password", false);
 		return DISCONNECTED;
 		return DISCONNECTED;
 	}
 	}
 	return Hello_User_PostAuth(Client);
 	return Hello_User_PostAuth(Client);
@@ -1003,12 +1020,13 @@ cb_Read_Auth_Result(int r_fd, UNUSED short events)
 
 
 	/* Read result from pipe */
 	/* Read result from pipe */
 	len = Proc_Read(proc, &result, sizeof(result));
 	len = Proc_Read(proc, &result, sizeof(result));
+	Proc_Close(proc);
 	if (len == 0)
 	if (len == 0)
 		return;
 		return;
 
 
 	if (len != sizeof(result)) {
 	if (len != sizeof(result)) {
 		Log(LOG_CRIT, "Auth: Got malformed result!");
 		Log(LOG_CRIT, "Auth: Got malformed result!");
-		Reject_Client(client);
+		Client_Reject(client, "Internal error", false);
 		return;
 		return;
 	}
 	}
 
 
@@ -1016,32 +1034,13 @@ cb_Read_Auth_Result(int r_fd, UNUSED short events)
 		Client_SetUser(client, Client_OrigUser(client), true);
 		Client_SetUser(client, Client_OrigUser(client), true);
 		(void)Hello_User_PostAuth(client);
 		(void)Hello_User_PostAuth(client);
 	} else
 	} else
-		Reject_Client(client);
+		Client_Reject(client, "Bad password", false);
 }
 }
 
 
 #endif
 #endif
 
 
 
 
 /**
 /**
- * Reject a client because of wrong password.
- *
- * This function is called either when the global server password or a password
- * checked using PAM has been wrong.
- *
- * @param Client	The client to reject.
- */
-static void
-Reject_Client(CLIENT *Client)
-{
-	Log(LOG_ERR,
-	    "User \"%s\" rejected (connection %d): Access denied!",
-	    Client_Mask(Client), Client_Conn(Client));
-	Conn_Close(Client_Conn(Client), NULL,
-		   "Access denied! Bad password?", true);
-}
-
-
-/**
  * Finish client registration.
  * Finish client registration.
  *
  *
  * Introduce the new client to the network and send all "hello messages"
  * Introduce the new client to the network and send all "hello messages"
@@ -1053,6 +1052,11 @@ Reject_Client(CLIENT *Client)
 static bool
 static bool
 Hello_User_PostAuth(CLIENT *Client)
 Hello_User_PostAuth(CLIENT *Client)
 {
 {
+	assert(Client != NULL);
+
+	if (Class_HandleServerBans(Client) != CONNECTED)
+		return DISCONNECTED;
+
 	Introduce_Client(NULL, Client, CLIENT_USER);
 	Introduce_Client(NULL, Client, CLIENT_USER);
 
 
 	if (!IRC_WriteStrClient
 	if (!IRC_WriteStrClient
@@ -1096,20 +1100,22 @@ Hello_User_PostAuth(CLIENT *Client)
  * @param Reason	Reason for the KILL.
  * @param Reason	Reason for the KILL.
  */
  */
 static void
 static void
-Kill_Nick( char *Nick, char *Reason )
+Kill_Nick(char *Nick, char *Reason)
 {
 {
 	REQUEST r;
 	REQUEST r;
 
 
-	assert( Nick != NULL );
-	assert( Reason != NULL );
+	assert (Nick != NULL);
+	assert (Reason != NULL);
 
 
-	r.prefix = (char *)Client_ThisServer( );
+	r.prefix = NULL;
 	r.argv[0] = Nick;
 	r.argv[0] = Nick;
 	r.argv[1] = Reason;
 	r.argv[1] = Reason;
 	r.argc = 2;
 	r.argc = 2;
 
 
-	Log( LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s", Nick, Reason );
-	IRC_KILL( Client_ThisServer( ), &r );
+	Log(LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s",
+	    Nick, Reason);
+
+	IRC_KILL(Client_ThisServer(), &r);
 } /* Kill_Nick */
 } /* Kill_Nick */
 
 
 
 

+ 387 - 206
src/ngircd/irc-mode.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -36,39 +36,54 @@
 #include "irc-mode.h"
 #include "irc-mode.h"
 
 
 
 
-static bool Client_Mode PARAMS(( CLIENT *Client, REQUEST *Req, CLIENT *Origin,
-	CLIENT *Target ));
-static bool Channel_Mode PARAMS(( CLIENT *Client, REQUEST *Req, CLIENT *Origin,
-	CHANNEL *Channel ));
+static bool Client_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin,
+				CLIENT *Target));
+static bool Channel_Mode PARAMS((CLIENT *Client, REQUEST *Req, CLIENT *Origin,
+				 CHANNEL *Channel));
 
 
-static bool Add_Ban_Invite PARAMS((int what, CLIENT *Prefix, CLIENT *Client,
-	CHANNEL *Channel, const char *Pattern));
-static bool Del_Ban_Invite PARAMS((int what, CLIENT *Prefix, CLIENT *Client,
-	CHANNEL *Channel, const char *Pattern));
+static bool Add_To_List PARAMS((char what, CLIENT *Prefix, CLIENT *Client,
+				CHANNEL *Channel, const char *Pattern));
+static bool Del_From_List PARAMS((char what, CLIENT *Prefix, CLIENT *Client,
+				  CHANNEL *Channel, const char *Pattern));
 
 
-static bool Send_ListChange PARAMS((const char *Mode, CLIENT *Prefix,
-	CLIENT *Client, CHANNEL *Channel, const char *Mask));
+static bool Send_ListChange PARAMS((const bool IsAdd, const char ModeChar,
+				    CLIENT *Prefix, CLIENT *Client,
+				    CHANNEL *Channel, const char *Mask));
 
 
 
 
+/**
+ * Handler for the IRC "MODE" command.
+ *
+ * See RFC 2812 section 3.1.5 ("user mode message") and section 3.2.3
+ * ("channel mode message"), and RFC 2811 section 4 ("channel modes").
+ *
+ * @param Client	The client from which this command has been received.
+ * @param Req		Request structure with prefix and all parameters.
+ * @returns		CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
 IRC_MODE( CLIENT *Client, REQUEST *Req )
 IRC_MODE( CLIENT *Client, REQUEST *Req )
 {
 {
 	CLIENT *cl, *origin;
 	CLIENT *cl, *origin;
 	CHANNEL *chan;
 	CHANNEL *chan;
 
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert(Client != NULL);
+	assert(Req != NULL);
 
 
 	/* No parameters? */
 	/* No parameters? */
-	if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	if (Req->argc < 1)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
 	/* Origin for answers */
 	/* Origin for answers */
-	if( Client_Type( Client ) == CLIENT_SERVER )
-	{
-		origin = Client_Search( Req->prefix );
-		if( ! origin ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
-	}
-	else origin = Client;
+	if (Client_Type(Client) == CLIENT_SERVER) {
+		origin = Client_Search(Req->prefix);
+		if (!origin)
+			return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+						  Client_ID(Client),
+						  Req->prefix);
+	} else
+		origin = Client;
 
 
 	/* Channel or user mode? */
 	/* Channel or user mode? */
 	cl = NULL; chan = NULL;
 	cl = NULL; chan = NULL;
@@ -88,178 +103,242 @@ IRC_MODE( CLIENT *Client, REQUEST *Req )
 } /* IRC_MODE */
 } /* IRC_MODE */
 
 
 
 
+/**
+ * Check if the "mode limit" for a client has been reached.
+ *
+ * This limit doesn't apply for servers or services!
+ *
+ * @param Client The client to check.
+ * @param Count The number of modes already handled.
+ * @return true if the limit has been reached.
+ */
 static bool
 static bool
-Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
+Mode_Limit_Reached(CLIENT *Client, int Count)
 {
 {
-	/* Handle client mode requests */
+	if (Client_Type(Client) == CLIENT_SERVER
+	    || Client_Type(Client) == CLIENT_SERVICE)
+		return false;
+	if (Count < MAX_HNDL_MODES_ARG)
+		return false;
+	return true;
+}
+
 
 
+/**
+ * Handle client mode requests
+ *
+ * @param Client	The client from which this command has been received.
+ * @param Req		Request structure with prefix and all parameters.
+ * @param Origin	The originator of the MODE command (prefix).
+ * @param Target	The target (client) of this MODE command.
+ * @returns		CONNECTED or DISCONNECTED.
+ */
+static bool
+Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
+{
 	char the_modes[COMMAND_LEN], x[2], *mode_ptr;
 	char the_modes[COMMAND_LEN], x[2], *mode_ptr;
 	bool ok, set;
 	bool ok, set;
 	int mode_arg;
 	int mode_arg;
 	size_t len;
 	size_t len;
 
 
 	/* Is the client allowed to request or change the modes? */
 	/* Is the client allowed to request or change the modes? */
-	if( Client_Type( Client ) == CLIENT_USER )
-	{
+	if (Client_Type(Client) == CLIENT_USER) {
 		/* Users are only allowed to manipulate their own modes! */
 		/* Users are only allowed to manipulate their own modes! */
-		if( Target != Client ) return IRC_WriteStrClient( Client, ERR_USERSDONTMATCH_MSG, Client_ID( Client ));
+		if (Target != Client)
+			return IRC_WriteStrClient(Client,
+						  ERR_USERSDONTMATCH_MSG,
+						  Client_ID(Client));
 	}
 	}
 
 
 	/* Mode request: let's answer it :-) */
 	/* Mode request: let's answer it :-) */
-	if( Req->argc == 1 ) return IRC_WriteStrClient( Origin, RPL_UMODEIS_MSG, Client_ID( Origin ), Client_Modes( Target ));
+	if (Req->argc == 1)
+		return IRC_WriteStrClient(Origin, RPL_UMODEIS_MSG,
+					  Client_ID(Origin),
+					  Client_Modes(Target));
 
 
 	mode_arg = 1;
 	mode_arg = 1;
 	mode_ptr = Req->argv[mode_arg];
 	mode_ptr = Req->argv[mode_arg];
 
 
 	/* Initial state: set or unset modes? */
 	/* Initial state: set or unset modes? */
-	if( *mode_ptr == '+' ) set = true;
-	else if( *mode_ptr == '-' ) set = false;
-	else return IRC_WriteStrClient( Origin, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Origin ));
-
-	/* Prepare reply string */
-	if( set ) strcpy( the_modes, "+" );
-	else strcpy( the_modes, "-" );
+	if (*mode_ptr == '+') {
+		set = true;
+		strcpy(the_modes, "+");
+	} else if (*mode_ptr == '-') {
+		set = false;
+		strcpy(the_modes, "-");
+	} else
+		return IRC_WriteStrClient(Origin, ERR_UMODEUNKNOWNFLAG_MSG,
+					  Client_ID(Origin));
 
 
 	x[1] = '\0';
 	x[1] = '\0';
 	ok = CONNECTED;
 	ok = CONNECTED;
-	while( mode_ptr )
-	{
+	while (mode_ptr) {
 		mode_ptr++;
 		mode_ptr++;
-		if( ! *mode_ptr )
-		{
+		if (!*mode_ptr) {
 			/* Try next argument if there's any */
 			/* Try next argument if there's any */
 			mode_arg++;
 			mode_arg++;
-			if( mode_arg < Req->argc ) mode_ptr = Req->argv[mode_arg];
-			else break;
+			if (mode_arg < Req->argc)
+				mode_ptr = Req->argv[mode_arg];
+			else
+				break;
 		}
 		}
-		
-		switch( *mode_ptr )
-		{
-			case '+':
-			case '-':
-				if((( *mode_ptr == '+' ) && ( ! set )) || (( *mode_ptr == '-' ) && ( set )))
-				{
-					/* Action modifier ("+"/"-") must be changed ... */
-					len = strlen( the_modes ) - 1;
-					if(( the_modes[len] == '+' ) || ( the_modes[len] == '-' ))
-					{
-						/* Adjust last action modifier in result */
-						the_modes[len] = *mode_ptr;
-					}
-					else
-					{
-						/* Append modifier character to result string */
-						x[0] = *mode_ptr;
-						strlcat( the_modes, x, sizeof( the_modes ));
-					}
-					if( *mode_ptr == '+' ) set = true;
-					else set = false;
+
+		switch(*mode_ptr) {
+		  case '+':
+		  case '-':
+			if ((*mode_ptr == '+' && !set)
+			    || (*mode_ptr == '-' && set)) {
+				/* Action modifier ("+"/"-") must be changed */
+				len = strlen(the_modes) - 1;
+				if (the_modes[len] == '+'
+				    || the_modes[len] == '-') {
+					/* Last character in the "result
+					 * string" was an "action", so just
+					 * overwrite it with the new action */
+					the_modes[len] = *mode_ptr;
+				} else {
+					/* Append new modifier character to
+					 * the resulting mode string */
+					x[0] = *mode_ptr;
+					strlcat(the_modes, x,
+						sizeof(the_modes));
 				}
 				}
-				continue;
+				if (*mode_ptr == '+')
+					set = true;
+				else
+					set = false;
+			}
+			continue;
 		}
 		}
-		
+
 		/* Validate modes */
 		/* Validate modes */
 		x[0] = '\0';
 		x[0] = '\0';
-		switch( *mode_ptr )
-		{
-			case 'i': /* Invisible */
-			case 's': /* Server messages */
-			case 'w': /* Wallops messages */
-				x[0] = *mode_ptr;
-				break;
-
-			case 'a': /* Away */
-				if( Client_Type( Client ) == CLIENT_SERVER )
-				{
-					x[0] = 'a';
-					Client_SetAway( Origin, DEFAULT_AWAY_MSG );
-				}
-				else ok = IRC_WriteStrClient( Origin, ERR_NOPRIVILEGES_MSG, Client_ID( Origin ));
-				break;
-
-			case 'c': /* Receive connect notices
-				   * (only settable by IRC operators!) */
-				if(!set || Client_OperByMe(Origin)
-				   || Client_Type(Client) == CLIENT_SERVER)
-					x[0] = 'c';
-				else
-					ok = IRC_WriteStrClient(Origin,
+		switch (*mode_ptr) {
+		case 'C': /* Only messages from clients sharing a channel */
+		case 'i': /* Invisible */
+		case 's': /* Server messages */
+		case 'w': /* Wallops messages */
+			x[0] = *mode_ptr;
+			break;
+		case 'a': /* Away */
+			if (Client_Type(Client) == CLIENT_SERVER) {
+				x[0] = 'a';
+				Client_SetAway(Origin, DEFAULT_AWAY_MSG);
+			} else
+				ok = IRC_WriteStrClient(Origin,
 							ERR_NOPRIVILEGES_MSG,
 							ERR_NOPRIVILEGES_MSG,
 							Client_ID(Origin));
 							Client_ID(Origin));
-				break;
-
-			case 'o': /* IRC operator (only unsettable!) */
-				if(( ! set ) || ( Client_Type( Client ) == CLIENT_SERVER ))
-				{
-					Client_SetOperByMe( Target, false );
-					x[0] = 'o';
-				}
-				else ok = IRC_WriteStrClient( Origin, ERR_NOPRIVILEGES_MSG, Client_ID( Origin ));
-				break;
-
-			case 'r': /* Restricted (only settable) */
-				if(( set ) || ( Client_Type( Client ) == CLIENT_SERVER )) x[0] = 'r';
-				else ok = IRC_WriteStrClient( Origin, ERR_RESTRICTED_MSG, Client_ID( Origin ));
-				break;
-
-			case 'x': /* Cloak hostname */
-				if (Client_HasMode(Client, 'r'))
-					ok = IRC_WriteStrClient(Origin,
-							   ERR_RESTRICTED_MSG,
-							   Client_ID(Origin));
-				else
-					x[0] = 'x';
-				break;
-
-			default:
-				Log( LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\"!?", set ? '+' : '-', *mode_ptr, Client_ID( Origin ));
-				if( Client_Type( Client ) != CLIENT_SERVER ) ok = IRC_WriteStrClient( Origin, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Origin ), set ? '+' : '-', *mode_ptr );
+			break;
+		case 'c': /* Receive connect notices
+			   * (only settable by IRC operators!) */
+			if (!set || Client_Type(Client) == CLIENT_SERVER
+			    || Client_OperByMe(Origin))
+				x[0] = 'c';
+			else
+				ok = IRC_WriteStrClient(Origin,
+							ERR_NOPRIVILEGES_MSG,
+							Client_ID(Origin));
+			break;
+		case 'o': /* IRC operator (only unsettable!) */
+			if (!set || Client_Type(Client) == CLIENT_SERVER) {
+				Client_SetOperByMe(Target, false);
+				x[0] = 'o';
+			} else
+				ok = IRC_WriteStrClient(Origin,
+							ERR_NOPRIVILEGES_MSG,
+							Client_ID(Origin));
+			break;
+		case 'r': /* Restricted (only settable) */
+			if (set || Client_Type(Client) == CLIENT_SERVER)
+				x[0] = 'r';
+			else
+				ok = IRC_WriteStrClient(Origin,
+							ERR_RESTRICTED_MSG,
+							Client_ID(Origin));
+			break;
+		case 'x': /* Cloak hostname */
+			if (Client_HasMode(Client, 'r'))
+				ok = IRC_WriteStrClient(Origin,
+							ERR_RESTRICTED_MSG,
+							Client_ID(Origin));
+			else
+				x[0] = 'x';
+			break;
+		default:
+			if (Client_Type(Client) != CLIENT_SERVER) {
+				Log(LOG_DEBUG,
+				    "Unknown mode \"%c%c\" from \"%s\"!?",
+				    set ? '+' : '-', *mode_ptr,
+				    Client_ID(Origin));
+				ok = IRC_WriteStrClient(Origin,
+							ERR_UMODEUNKNOWNFLAG2_MSG,
+							Client_ID(Origin),
+							set ? '+' : '-',
+							*mode_ptr);
 				x[0] = '\0';
 				x[0] = '\0';
-				goto client_exit;
+			} else {
+				Log(LOG_DEBUG,
+				    "Handling unknown mode \"%c%c\" from \"%s\" for \"%s\" ...",
+				    set ? '+' : '-', *mode_ptr,
+				    Client_ID(Origin), Client_ID(Target));
+				x[0] = *mode_ptr;
+			}
 		}
 		}
-		if( ! ok ) break;
 
 
-		/* Is there a valid mode change? */
-		if( ! x[0] ) continue;
+		if (!ok)
+			break;
 
 
-		if( set )
-		{
-			/* Set mode */
-			if( Client_ModeAdd( Target, x[0] )) strlcat( the_modes, x, sizeof( the_modes ));
+		/* Is there a valid mode change? */
+		if (!x[0])
+			continue;
 
 
+		if (set) {
+			if (Client_ModeAdd(Target, x[0]))
+				strlcat(the_modes, x, sizeof(the_modes));
+		} else {
+			if (Client_ModeDel(Target, x[0]))
+				strlcat(the_modes, x, sizeof(the_modes));
 		}
 		}
-		else
-		{
-			/* Unset mode */
-			if( Client_ModeDel( Target, x[0] )) strlcat( the_modes, x, sizeof( the_modes ));
-		}		
 	}
 	}
-client_exit:
-	
+
 	/* Are there changed modes? */
 	/* Are there changed modes? */
-	if( the_modes[1] )
-	{
-		/* Remoce needless action modifier characters */
-		len = strlen( the_modes ) - 1;
-		if(( the_modes[len] == '+' ) || ( the_modes[len] == '-' )) the_modes[len] = '\0';
+	if (the_modes[1]) {
+		/* Remove needless action modifier characters */
+		len = strlen(the_modes) - 1;
+		if (the_modes[len] == '+' || the_modes[len] == '-')
+			the_modes[len] = '\0';
 
 
-		if( Client_Type( Client ) == CLIENT_SERVER )
-		{
+		if (Client_Type(Client) == CLIENT_SERVER) {
 			/* Forward modes to other servers */
 			/* Forward modes to other servers */
-			IRC_WriteStrServersPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
-		}
-		else
-		{
+			if (Client_Conn(Target) != NONE) {
+				/* Remote server (service?) changed modes
+				 * for one of our clients. Inform it! */
+				IRC_WriteStrClientPrefix(Target, Origin,
+							 "MODE %s :%s",
+							 Client_ID(Target),
+							 the_modes);
+			}
+			IRC_WriteStrServersPrefix(Client, Origin,
+						  "MODE %s :%s",
+						  Client_ID(Target),
+						  the_modes);
+		} else {
 			/* Send reply to client and inform other servers */
 			/* Send reply to client and inform other servers */
-			ok = IRC_WriteStrClientPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
-			IRC_WriteStrServersPrefix( Client, Origin, "MODE %s :%s", Client_ID( Target ), the_modes );
+			ok = IRC_WriteStrClientPrefix(Client, Origin,
+						      "MODE %s :%s",
+						      Client_ID(Target),
+						      the_modes);
+			IRC_WriteStrServersPrefix(Client, Origin,
+						  "MODE %s :%s",
+						  Client_ID(Target),
+						  the_modes);
 		}
 		}
 		LogDebug("%s \"%s\": Mode change, now \"%s\".",
 		LogDebug("%s \"%s\": Mode change, now \"%s\".",
 			 Client_TypeText(Target), Client_Mask(Target),
 			 Client_TypeText(Target), Client_Mask(Target),
 			 Client_Modes(Target));
 			 Client_Modes(Target));
 	}
 	}
-	
-	IRC_SetPenalty( Client, 1 );	
+
+	IRC_SetPenalty(Client, 1);
 	return ok;
 	return ok;
 } /* Client_Mode */
 } /* Client_Mode */
 
 
@@ -319,7 +398,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 	char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
 	char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
 	    argadd[CLIENT_PASS_LEN], *mode_ptr;
 	    argadd[CLIENT_PASS_LEN], *mode_ptr;
 	bool connected, set, skiponce, retval, onchannel, modeok, use_servermode;
 	bool connected, set, skiponce, retval, onchannel, modeok, use_servermode;
-	int mode_arg, arg_arg;
+	int mode_arg, arg_arg, mode_arg_count = 0;
 	CLIENT *client;
 	CLIENT *client;
 	long l;
 	long l;
 	size_t len;
 	size_t len;
@@ -372,6 +451,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 			mode_ptr++;
 			mode_ptr++;
 		if (!*mode_ptr) {
 		if (!*mode_ptr) {
 			/* Try next argument if there's any */
 			/* Try next argument if there's any */
+			if (arg_arg < 0)
+				break;
 			if (arg_arg > mode_arg)
 			if (arg_arg > mode_arg)
 				mode_arg = arg_arg;
 				mode_arg = arg_arg;
 			else
 			else
@@ -421,6 +502,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 		case 'i': /* Invite only */
 		case 'i': /* Invite only */
 		case 'm': /* Moderated */
 		case 'm': /* Moderated */
 		case 'n': /* Only members can write */
 		case 'n': /* Only members can write */
+		case 'R': /* Registered users only */
 		case 's': /* Secret channel */
 		case 's': /* Secret channel */
 		case 't': /* Topic locked */
 		case 't': /* Topic locked */
 		case 'z': /* Secure connections only */
 		case 'z': /* Secure connections only */
@@ -432,6 +514,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 					Client_ID(Origin), Channel_Name(Channel));
 					Client_ID(Origin), Channel_Name(Channel));
 			break;
 			break;
 		case 'k': /* Channel key */
 		case 'k': /* Channel key */
+			if (Mode_Limit_Reached(Client, mode_arg_count++))
+				goto chan_exit;
 			if (!set) {
 			if (!set) {
 				if (modeok)
 				if (modeok)
 					x[0] = *mode_ptr;
 					x[0] = *mode_ptr;
@@ -466,6 +550,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 			}
 			}
 			break;
 			break;
 		case 'l': /* Member limit */
 		case 'l': /* Member limit */
+			if (Mode_Limit_Reached(Client, mode_arg_count++))
+				goto chan_exit;
 			if (!set) {
 			if (!set) {
 				if (modeok)
 				if (modeok)
 					x[0] = *mode_ptr;
 					x[0] = *mode_ptr;
@@ -536,6 +622,16 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 					Channel_Name(Channel));
 					Channel_Name(Channel));
 			break;
 			break;
 		/* --- Channel user modes --- */
 		/* --- Channel user modes --- */
+		case 'a':
+		case 'h':
+		case 'q':
+			if (Client_Type(Client) != CLIENT_SERVER) {
+				connected = IRC_WriteStrClient(Origin,
+					ERR_CHANOPRIVSNEEDED_MSG,
+					Client_ID(Origin),
+					Channel_Name(Channel));
+				goto chan_exit;
+			}
 		case 'o': /* Channel operator */
 		case 'o': /* Channel operator */
 		case 'v': /* Voice */
 		case 'v': /* Voice */
 			if (arg_arg > mode_arg) {
 			if (arg_arg > mode_arg) {
@@ -566,14 +662,17 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 		/* --- Channel lists --- */
 		/* --- Channel lists --- */
 		case 'I': /* Invite lists */
 		case 'I': /* Invite lists */
 		case 'b': /* Ban lists */
 		case 'b': /* Ban lists */
+		case 'e': /* Channel exception lists */
+			if (Mode_Limit_Reached(Client, mode_arg_count++))
+				goto chan_exit;
 			if (arg_arg > mode_arg) {
 			if (arg_arg > mode_arg) {
 				/* modify list */
 				/* modify list */
 				if (modeok) {
 				if (modeok) {
 					connected = set
 					connected = set
-					   ? Add_Ban_Invite(*mode_ptr, Origin,
+					   ? Add_To_List(*mode_ptr, Origin,
 						Client, Channel,
 						Client, Channel,
 						Req->argv[arg_arg])
 						Req->argv[arg_arg])
-					   : Del_Ban_Invite(*mode_ptr, Origin,
+					   : Del_From_List(*mode_ptr, Origin,
 						Client, Channel,
 						Client, Channel,
 						Req->argv[arg_arg]);
 						Req->argv[arg_arg]);
 				} else {
 				} else {
@@ -585,25 +684,38 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				Req->argv[arg_arg][0] = '\0';
 				Req->argv[arg_arg][0] = '\0';
 				arg_arg++;
 				arg_arg++;
 			} else {
 			} else {
-				if (*mode_ptr == 'I')
+				switch (*mode_ptr) {
+				case 'I':
 					Channel_ShowInvites(Origin, Channel);
 					Channel_ShowInvites(Origin, Channel);
-				else
+					break;
+				case 'b':
 					Channel_ShowBans(Origin, Channel);
 					Channel_ShowBans(Origin, Channel);
+					break;
+				case 'e':
+					Channel_ShowExcepts(Origin, Channel);
+					break;
+				}
 			}
 			}
 			break;
 			break;
 		default:
 		default:
-			Log(LOG_DEBUG,
-			    "Unknown mode \"%c%c\" from \"%s\" on %s!?",
-			    set ? '+' : '-', *mode_ptr, Client_ID(Origin),
-			    Channel_Name(Channel));
-			if (Client_Type(Client) != CLIENT_SERVER)
+			if (Client_Type(Client) != CLIENT_SERVER) {
+				Log(LOG_DEBUG,
+				    "Unknown mode \"%c%c\" from \"%s\" on %s!?",
+				    set ? '+' : '-', *mode_ptr,
+				    Client_ID(Origin), Channel_Name(Channel));
 				connected = IRC_WriteStrClient(Origin,
 				connected = IRC_WriteStrClient(Origin,
-					ERR_UMODEUNKNOWNFLAG2_MSG,
-					Client_ID(Origin),
-					set ? '+' : '-', *mode_ptr);
-			x[0] = '\0';
-			goto chan_exit;
-		}	/* switch() */
+					ERR_UNKNOWNMODE_MSG,
+					Client_ID(Origin), *mode_ptr,
+					Channel_Name(Channel));
+				x[0] = '\0';
+			} else {
+				Log(LOG_DEBUG,
+				    "Handling unknown mode \"%c%c\" from \"%s\" on %s ...",
+				    set ? '+' : '-', *mode_ptr,
+				    Client_ID(Origin), Channel_Name(Channel));
+				x[0] = *mode_ptr;
+			}
+		}
 
 
 		if (!connected)
 		if (!connected)
 			break;
 			break;
@@ -729,82 +841,151 @@ IRC_AWAY( CLIENT *Client, REQUEST *Req )
 } /* IRC_AWAY */
 } /* IRC_AWAY */
 
 
 
 
+/**
+ * Add entries to channel invite, ban and exception lists.
+ *
+ * @param what Can be 'I' for invite, 'b' for ban, and 'e' for exception list.
+ * @param Prefix The originator of the command.
+ * @param Client The sender of the command.
+ * @param Channel The channel of which the list should be modified.
+ * @param Pattern The pattern to add to the list.
+ * @return CONNECTED or DISCONNECTED.
+ */
 static bool
 static bool
-Add_Ban_Invite(int what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, const char *Pattern)
+Add_To_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
+	    const char *Pattern)
 {
 {
 	const char *mask;
 	const char *mask;
-	bool already;
-	bool ret;
+	struct list_head *list;
+	long int current_count;
 
 
-	assert( Client != NULL );
-	assert( Channel != NULL );
-	assert( Pattern != NULL );
-	assert(what == 'I' || what == 'b');
+	assert(Client != NULL);
+	assert(Channel != NULL);
+	assert(Pattern != NULL);
+	assert(what == 'I' || what == 'b' || what == 'e');
 
 
 	mask = Lists_MakeMask(Pattern);
 	mask = Lists_MakeMask(Pattern);
+	current_count = Lists_Count(Channel_GetListInvites(Channel))
+			+ Lists_Count(Channel_GetListExcepts(Channel))
+			+ Lists_Count(Channel_GetListBans(Channel));
 
 
-	already = Lists_CheckDupeMask(Channel_GetListInvites(Channel), mask);
-	if (!already) {
-		if (what == 'I')
-			ret = Channel_AddInvite(Channel, mask, false);
-		else
-			ret = Channel_AddBan(Channel, mask);
-		if (!ret)
-			return CONNECTED;
+	switch(what) {
+		case 'I':
+			list = Channel_GetListInvites(Channel);
+			break;
+		case 'b':
+			list = Channel_GetListBans(Channel);
+			break;
+		case 'e':
+			list = Channel_GetListExcepts(Channel);
+			break;
 	}
 	}
-	if (already && (Client_Type(Prefix) == CLIENT_SERVER))
-		return CONNECTED;
 
 
-	if (what == 'I')
-		return Send_ListChange("+I", Prefix, Client, Channel, mask);
-	return Send_ListChange("+b", Prefix, Client, Channel, mask);
+	if (Lists_CheckDupeMask(list, mask))
+		return CONNECTED;
+	if (Client_Type(Client) == CLIENT_USER &&
+	    current_count >= MAX_HNDL_CHANNEL_LISTS)
+		return IRC_WriteStrClient(Client, ERR_LISTFULL_MSG,
+					  Client_ID(Client),
+					  Channel_Name(Channel), mask,
+					  MAX_HNDL_CHANNEL_LISTS);
+
+	switch (what) {
+		case 'I':
+			if (!Channel_AddInvite(Channel, mask, false))
+				return CONNECTED;
+			break;
+		case 'b':
+			if (!Channel_AddBan(Channel, mask))
+				return CONNECTED;
+			break;
+		case 'e':
+			if (!Channel_AddExcept(Channel, mask))
+				return CONNECTED;
+			break;
+	}
+	return Send_ListChange(true, what, Prefix, Client, Channel, mask);
 }
 }
 
 
 
 
+/**
+ * Delete entries from channel invite, ban and exeption lists.
+ *
+ * @param what Can be 'I' for invite, 'b' for ban, and 'e' for exception list.
+ * @param Prefix The originator of the command.
+ * @param Client The sender of the command.
+ * @param Channel The channel of which the list should be modified.
+ * @param Pattern The pattern to add to the list.
+ * @return CONNECTED or DISCONNECTED.
+ */
 static bool
 static bool
-Del_Ban_Invite(int what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, const char *Pattern)
+Del_From_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
+	      const char *Pattern)
 {
 {
 	const char *mask;
 	const char *mask;
 	struct list_head *list;
 	struct list_head *list;
 
 
-	assert( Client != NULL );
-	assert( Channel != NULL );
-	assert( Pattern != NULL );
-	assert(what == 'I' || what == 'b');
+	assert(Client != NULL);
+	assert(Channel != NULL);
+	assert(Pattern != NULL);
+	assert(what == 'I' || what == 'b' || what == 'e');
 
 
-	mask = Lists_MakeMask( Pattern );
+	mask = Lists_MakeMask(Pattern);
 
 
-	if (what == 'I')
-		list = Channel_GetListInvites(Channel);
-	else
-		list = Channel_GetListBans(Channel);
+	switch (what) {
+		case 'I':
+			list = Channel_GetListInvites(Channel);
+			break;
+		case 'b':
+			list = Channel_GetListBans(Channel);
+			break;
+		case 'e':
+			list = Channel_GetListExcepts(Channel);
+			break;
+	}
 
 
+	if (!Lists_CheckDupeMask(list, mask))
+		return CONNECTED;
 	Lists_Del(list, mask);
 	Lists_Del(list, mask);
-	if (what == 'I')
-		return Send_ListChange( "-I", Prefix, Client, Channel, mask );
-	return Send_ListChange( "-b", Prefix, Client, Channel, mask );
+
+	return Send_ListChange(false, what, Prefix, Client, Channel, mask);
 }
 }
 
 
 
 
+/**
+ * Send information about changed channel invite/ban/exception lists to clients.
+ *
+ * @param IsAdd true if the list item has been added, false otherwise.
+ * @param ModeChar The mode to use (e. g. 'b' or 'I')
+ * @param Prefix The originator of the mode list change.
+ * @param Client The sender of the command.
+ * @param Channel The channel of which the list has been modified.
+ * @param Mask The mask which has been added or removed.
+ * @return CONNECTED or DISCONNECTED.
+ */
 static bool
 static bool
-Send_ListChange(const char *Mode, CLIENT *Prefix, CLIENT *Client,
-		CHANNEL *Channel, const char *Mask)
+Send_ListChange(const bool IsAdd, const char ModeChar, CLIENT *Prefix,
+		CLIENT *Client, CHANNEL *Channel, const char *Mask)
 {
 {
-	bool ok;
+	bool ok = true;
 
 
-	if( Client_Type( Client ) == CLIENT_USER )
-	{
-		/* send confirmation to client */
-		ok = IRC_WriteStrClientPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
-	}
-	else ok = true;
+	/* Send confirmation to the client */
+	if (Client_Type(Client) == CLIENT_USER)
+		ok = IRC_WriteStrClientPrefix(Client, Prefix, "MODE %s %c%c %s",
+					      Channel_Name(Channel),
+					      IsAdd ? '+' : '-',
+					      ModeChar, Mask);
 
 
 	/* to other servers */
 	/* to other servers */
-	IRC_WriteStrServersPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
+	IRC_WriteStrServersPrefix(Client, Prefix, "MODE %s %c%c %s",
+				  Channel_Name(Channel), IsAdd ? '+' : '-',
+				  ModeChar, Mask);
 
 
 	/* and local users in channel */
 	/* and local users in channel */
-	IRC_WriteStrChannelPrefix( Client, Channel, Prefix, false, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
-	
+	IRC_WriteStrChannelPrefix(Client, Channel, Prefix, false,
+				  "MODE %s %c%c %s", Channel_Name(Channel),
+				  IsAdd ? '+' : '-', ModeChar, Mask );
+
 	return ok;
 	return ok;
 } /* Send_ListChange */
 } /* Send_ListChange */
 
 

+ 170 - 27
src/ngircd/irc-oper.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
 #include "conn-func.h"
 #include "conn-func.h"
 #include "conf.h"
 #include "conf.h"
 #include "channel.h"
 #include "channel.h"
+#include "class.h"
 #include "irc-write.h"
 #include "irc-write.h"
 #include "log.h"
 #include "log.h"
 #include "match.h"
 #include "match.h"
@@ -37,7 +38,6 @@
 #include <exp.h>
 #include <exp.h>
 #include "irc-oper.h"
 #include "irc-oper.h"
 
 
-
 /**
 /**
  * Handle invalid received OPER command.
  * Handle invalid received OPER command.
  * Log OPER attempt and send error message to client.
  * Log OPER attempt and send error message to client.
@@ -52,7 +52,15 @@ Bad_OperPass(CLIENT *Client, char *errtoken, char *errmsg)
 				  Client_ID(Client));
 				  Client_ID(Client));
 } /* Bad_OperPass */
 } /* Bad_OperPass */
 
 
-
+/**
+ * Handler for the IRC "OPER" command.
+ *
+ * See RFC 2812, 3.1.4 "Oper message".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
 IRC_OPER( CLIENT *Client, REQUEST *Req )
 IRC_OPER( CLIENT *Client, REQUEST *Req )
 {
 {
@@ -62,7 +70,9 @@ IRC_OPER( CLIENT *Client, REQUEST *Req )
 	assert( Client != NULL );
 	assert( Client != NULL );
 	assert( Req != NULL );
 	assert( Req != NULL );
 
 
-	if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	if (Req->argc != 2)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
 	len = array_length(&Conf_Opers, sizeof(*op));
 	len = array_length(&Conf_Opers, sizeof(*op));
 	op = array_start(&Conf_Opers);
 	op = array_start(&Conf_Opers);
@@ -77,20 +87,33 @@ IRC_OPER( CLIENT *Client, REQUEST *Req )
 	if (op[i].mask && (!Match(op[i].mask, Client_Mask(Client))))
 	if (op[i].mask && (!Match(op[i].mask, Client_Mask(Client))))
 		return Bad_OperPass(Client, op[i].mask, "hostmask check failed");
 		return Bad_OperPass(Client, op[i].mask, "hostmask check failed");
 
 
-	if( ! Client_HasMode( Client, 'o' ))
-	{
-		Client_ModeAdd( Client, 'o' );
-		if( ! IRC_WriteStrClient( Client, "MODE %s :+o", Client_ID( Client ))) return DISCONNECTED;
-		IRC_WriteStrServersPrefix( NULL, Client, "MODE %s :+o", Client_ID( Client ));
+	if (!Client_HasMode(Client, 'o')) {
+		Client_ModeAdd(Client, 'o');
+		if (!IRC_WriteStrClient(Client, "MODE %s :+o",
+					Client_ID(Client)))
+			return DISCONNECTED;
+		IRC_WriteStrServersPrefix(NULL, Client, "MODE %s :+o",
+					  Client_ID(Client));
 	}
 	}
 
 
-	if( ! Client_OperByMe( Client )) Log( LOG_NOTICE|LOG_snotice, "Got valid OPER from \"%s\", user is an IRC operator now.", Client_Mask( Client ));
+	if (!Client_OperByMe(Client))
+		Log(LOG_NOTICE|LOG_snotice,
+		    "Got valid OPER from \"%s\", user is an IRC operator now.",
+		    Client_Mask(Client));
 
 
-	Client_SetOperByMe( Client, true);
-	return IRC_WriteStrClient( Client, RPL_YOUREOPER_MSG, Client_ID( Client ));
+	Client_SetOperByMe(Client, true);
+	return IRC_WriteStrClient(Client, RPL_YOUREOPER_MSG, Client_ID(Client));
 } /* IRC_OPER */
 } /* IRC_OPER */
 
 
-
+/**
+ * Handler for the IRC "DIE" command.
+ *
+ * See RFC 2812, 4.3 "Die message".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
 IRC_DIE(CLIENT * Client, REQUEST * Req)
 IRC_DIE(CLIENT * Client, REQUEST * Req)
 {
 {
@@ -133,7 +156,15 @@ IRC_DIE(CLIENT * Client, REQUEST * Req)
 	return CONNECTED;
 	return CONNECTED;
 } /* IRC_DIE */
 } /* IRC_DIE */
 
 
-
+/**
+ * Handler for the IRC "REHASH" command.
+ *
+ * See RFC 2812, 4.2 "Rehash message".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
 IRC_REHASH( CLIENT *Client, REQUEST *Req )
 IRC_REHASH( CLIENT *Client, REQUEST *Req )
 {
 {
@@ -146,15 +177,26 @@ IRC_REHASH( CLIENT *Client, REQUEST *Req )
 		return Op_NoPrivileges(Client, Req);
 		return Op_NoPrivileges(Client, Req);
 
 
 	/* Bad number of parameters? */
 	/* Bad number of parameters? */
-	if( Req->argc != 0 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	if (Req->argc != 0)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command );
 
 
-	Log( LOG_NOTICE|LOG_snotice, "Got REHASH command from \"%s\" ...", Client_Mask( Client ));
+	Log(LOG_NOTICE|LOG_snotice, "Got REHASH command from \"%s\" ...",
+	    Client_Mask(Client));
 	raise(SIGHUP);
 	raise(SIGHUP);
 
 
 	return CONNECTED;
 	return CONNECTED;
 } /* IRC_REHASH */
 } /* IRC_REHASH */
 
 
-
+/**
+ * Handler for the IRC "RESTART" command.
+ *
+ * See RFC 2812, 4.4 "Restart message".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
 IRC_RESTART( CLIENT *Client, REQUEST *Req )
 IRC_RESTART( CLIENT *Client, REQUEST *Req )
 {
 {
@@ -167,16 +209,25 @@ IRC_RESTART( CLIENT *Client, REQUEST *Req )
 		return Op_NoPrivileges(Client, Req);
 		return Op_NoPrivileges(Client, Req);
 
 
 	/* Bad number of parameters? */
 	/* Bad number of parameters? */
-	if( Req->argc != 0 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	if (Req->argc != 0)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
-	Log( LOG_NOTICE|LOG_snotice, "Got RESTART command from \"%s\" ...", Client_Mask( Client ));
+	Log(LOG_NOTICE|LOG_snotice, "Got RESTART command from \"%s\" ...",
+	    Client_Mask(Client));
 	NGIRCd_SignalRestart = true;
 	NGIRCd_SignalRestart = true;
+
 	return CONNECTED;
 	return CONNECTED;
 } /* IRC_RESTART */
 } /* IRC_RESTART */
 
 
-
 /**
 /**
- * Connect configured or new server.
+ * Handler for the IRC "CONNECT" command.
+ *
+ * See RFC 2812, 3.4.7 "Connect message".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
  */
  */
 GLOBAL bool
 GLOBAL bool
 IRC_CONNECT(CLIENT * Client, REQUEST * Req)
 IRC_CONNECT(CLIENT * Client, REQUEST * Req)
@@ -272,9 +323,15 @@ IRC_CONNECT(CLIENT * Client, REQUEST * Req)
 	return CONNECTED;
 	return CONNECTED;
 } /* IRC_CONNECT */
 } /* IRC_CONNECT */
 
 
-
 /**
 /**
- * Disconnect (and disable) configured server.
+ * Handler for the IRC "DISCONNECT" command.
+ *
+ * This command is not specified in the IRC RFCs, it is an extension
+ * of ngIRCd: it shuts down and disables a configured server connection.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
  */
  */
 GLOBAL bool
 GLOBAL bool
 IRC_DISCONNECT(CLIENT * Client, REQUEST * Req)
 IRC_DISCONNECT(CLIENT * Client, REQUEST * Req)
@@ -315,7 +372,15 @@ IRC_DISCONNECT(CLIENT * Client, REQUEST * Req)
 		return DISCONNECTED;
 		return DISCONNECTED;
 } /* IRC_DISCONNECT */
 } /* IRC_DISCONNECT */
 
 
-
+/**
+ * Handler for the IRC "WALLOPS" command.
+ *
+ * See RFC 2812, 4.7 "Operwall message".
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
 IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
 IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
 {
 {
@@ -325,12 +390,14 @@ IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
 	assert( Req != NULL );
 	assert( Req != NULL );
 
 
 	if (Req->argc != 1)
 	if (Req->argc != 1)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command);
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
 	switch (Client_Type(Client)) {
 	switch (Client_Type(Client)) {
 	case CLIENT_USER:
 	case CLIENT_USER:
 		if (!Client_OperByMe(Client))
 		if (!Client_OperByMe(Client))
-			return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG, Client_ID(Client));
+			return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG,
+						  Client_ID(Client));
 		from = Client;
 		from = Client;
 		break;
 		break;
 	case CLIENT_SERVER:
 	case CLIENT_SERVER:
@@ -341,11 +408,87 @@ IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
 	}
 	}
 
 
 	if (!from)
 	if (!from)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, Client_ID(Client), Req->prefix);
+		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+					  Client_ID(Client), Req->prefix);
 
 
 	IRC_SendWallops(Client, from, "%s", Req->argv[0]);
 	IRC_SendWallops(Client, from, "%s", Req->argv[0]);
 	return CONNECTED;
 	return CONNECTED;
 } /* IRC_WALLOPS */
 } /* IRC_WALLOPS */
 
 
+/**
+ * Handle <?>LINE commands (GLINE, KLINE).
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+IRC_xLINE(CLIENT *Client, REQUEST *Req)
+{
+	CLIENT *from;
+	int class;
+	char class_c;
+
+	assert(Client != NULL);
+	assert(Req != NULL);
+
+	from = Op_Check(Client, Req);
+	if (!from)
+		return Op_NoPrivileges(Client, Req);
+
+	/* Bad number of parameters? */
+	if (Req->argc != 1 && Req->argc != 3)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
+
+	switch(Req->command[0]) {
+		case 'g':
+		case 'G':
+			class = CLASS_GLINE; class_c = 'G';
+			break;
+		case 'k':
+		case 'K':
+			class = CLASS_KLINE; class_c = 'K';
+			break;
+		default:
+			Log(LOG_CRIT,
+			    "IRC_xLINE() called for unknown line: %c!? Ignored.",
+			    Req->command[0]);
+			return CONNECTED;
+	}
+
+	if (Req->argc == 1) {
+		/* Delete mask from list */
+		Class_DeleteMask(class, Req->argv[0]);
+		Log(LOG_NOTICE|LOG_snotice,
+		    "\"%s\" deleted \"%s\" from %c-Line list.",
+		    Client_Mask(from), Req->argv[0], class_c);
+		if (class == CLASS_GLINE) {
+			/* Inform other servers */
+			IRC_WriteStrServersPrefix(Client, from, "%s %s",
+						  Req->command, Req->argv[0]);
+
+		}
+	} else {
+		/* Add new mask to list */
+		if (Class_AddMask(class, Req->argv[0],
+				  time(NULL) + atol(Req->argv[1]),
+				  Req->argv[2])) {
+			Log(LOG_NOTICE|LOG_snotice,
+			    "\"%s\" added \"%s\" to %c-Line list: \"%s\" (%ld seconds).",
+			    Client_Mask(from), Req->argv[0], class_c,
+			    Req->argv[2], atol(Req->argv[1]));
+			if (class == CLASS_GLINE) {
+				/* Inform other servers */
+				IRC_WriteStrServersPrefix(Client, from,
+						"%s %s %s :%s", Req->command,
+						Req->argv[0], Req->argv[1],
+						Req->argv[2]);
+			}
+		}
+	}
+
+	return CONNECTED;
+}
 
 
 /* -eof- */
 /* -eof- */

+ 3 - 1
src/ngircd/irc-oper.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -25,6 +25,8 @@ GLOBAL bool IRC_CONNECT PARAMS((CLIENT *Client, REQUEST *Req ));
 GLOBAL bool IRC_DISCONNECT PARAMS((CLIENT *Client, REQUEST *Req ));
 GLOBAL bool IRC_DISCONNECT PARAMS((CLIENT *Client, REQUEST *Req ));
 GLOBAL bool IRC_WALLOPS PARAMS(( CLIENT *Client, REQUEST *Req ));
 GLOBAL bool IRC_WALLOPS PARAMS(( CLIENT *Client, REQUEST *Req ));
 
 
+GLOBAL bool IRC_xLINE PARAMS((CLIENT *Client, REQUEST *Req));
+
 #endif
 #endif
 
 
 /* -eof- */
 /* -eof- */

+ 101 - 51
src/ngircd/irc.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2004 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -45,6 +45,35 @@ static bool Send_Message_Mask PARAMS((CLIENT *from, char *command,
 				      bool SendErrors));
 				      bool SendErrors));
 
 
 
 
+/**
+ * Check if a list limit is reached and inform client accordingly.
+ *
+ * @param From The client.
+ * @param Count Reply item count.
+ * @param Limit Reply limit.
+ * @param Name Name of the list.
+ * @return true if list limit has been reached; false otherwise.
+ */
+GLOBAL bool
+IRC_CheckListTooBig(CLIENT *From, const int Count, const int Limit,
+		    const char *Name)
+{
+	assert(From != NULL);
+	assert(Count >= 0);
+	assert(Limit > 0);
+	assert(Name != NULL);
+
+	if (Count < Limit)
+		return false;
+
+	(void)IRC_WriteStrClient(From,
+				 "NOTICE %s :%s list limit (%d) reached!",
+				 Client_ID(From), Name, Limit);
+	IRC_SetPenalty(From, 2);
+	return true;
+}
+
+
 GLOBAL bool
 GLOBAL bool
 IRC_ERROR( CLIENT *Client, REQUEST *Req )
 IRC_ERROR( CLIENT *Client, REQUEST *Req )
 {
 {
@@ -63,13 +92,21 @@ IRC_ERROR( CLIENT *Client, REQUEST *Req )
 
 
 
 
 /**
 /**
- * Kill client on request.
+ * Handler for the IRC "KILL" command.
+ *
  * This function implements the IRC command "KILL" wich is used to selectively
  * This function implements the IRC command "KILL" wich is used to selectively
  * disconnect clients. It can be used by IRC operators and servers, for example
  * disconnect clients. It can be used by IRC operators and servers, for example
- * to "solve" nick collisions after netsplits.
+ * to "solve" nick collisions after netsplits. See RFC 2812 section 3.7.1.
+ *
  * Please note that this function is also called internally, without a real
  * Please note that this function is also called internally, without a real
  * KILL command being received over the network! Client is Client_ThisServer()
  * KILL command being received over the network! Client is Client_ThisServer()
- * in this case. */
+ * in this case, and the prefix in Req is NULL.
+ *
+ * @param Client	The client from which this command has been received
+ *			or Client_ThisServer() when generated interanlly.
+ * @param Req		Request structure with prefix and all parameters.
+ * @returns		CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
 GLOBAL bool
 IRC_KILL( CLIENT *Client, REQUEST *Req )
 IRC_KILL( CLIENT *Client, REQUEST *Req )
 {
 {
@@ -77,55 +114,47 @@ IRC_KILL( CLIENT *Client, REQUEST *Req )
 	char reason[COMMAND_LEN], *msg;
 	char reason[COMMAND_LEN], *msg;
 	CONN_ID my_conn, conn;
 	CONN_ID my_conn, conn;
 
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert (Client != NULL);
+	assert (Req != NULL);
 
 
-	if(( Client_Type( Client ) != CLIENT_SERVER ) &&
-	   ( ! Client_OperByMe( Client )))
-	{
-		/* The originator of the KILL is neither an IRC operator of
-		 * this server nor a server. */
-		return IRC_WriteStrClient( Client, ERR_NOPRIVILEGES_MSG,
-					   Client_ID( Client ));
-	}
+	if (Client_Type(Client) != CLIENT_SERVER && !Client_OperByMe(Client))
+		return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG,
+					  Client_ID(Client));
 
 
-	if( Req->argc != 2 )
-	{
-		/* This command requires exactly 2 parameters! */
-		return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
-					   Client_ID( Client ), Req->command );
-	}
+	if (Req->argc != 2)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 
 
-	if( Req->prefix ) prefix = Client_Search( Req->prefix );
-	else prefix = Client;
-	if( ! prefix )
-	{
-		Log( LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!",
-		     Req->prefix );
-		prefix = Client_ThisServer( );
+	/* Get prefix (origin); use the client if no prefix is given. */
+	if (Req->prefix)
+		prefix = Client_Search(Req->prefix);
+	else
+		prefix = Client;
+
+	/* Log a warning message and use this server as origin when the
+	 * prefix (origin) is invalid. */
+	if (!prefix) {
+		Log(LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!",
+		    Req->prefix );
+		prefix = Client_ThisServer();
 	}
 	}
 
 
-	if( Client != Client_ThisServer( ))
-	{
-		/* This is a "real" KILL received from the network. */
-		Log( LOG_NOTICE|LOG_snotice, "Got KILL command from \"%s\" for \"%s\": %s",
-		     Client_Mask( prefix ), Req->argv[0], Req->argv[1] );
-	}
+	if (Client != Client_ThisServer())
+		Log(LOG_NOTICE|LOG_snotice,
+		    "Got KILL command from \"%s\" for \"%s\": %s",
+		    Client_Mask(prefix), Req->argv[0], Req->argv[1]);
 
 
-	/* Build reason string */
-	if( Client_Type( Client ) == CLIENT_USER )
-	{
-		/* Prefix the "reason" if the originator is a regular user,
-		 * so users can't spoof KILLs of servers. */
-		snprintf( reason, sizeof( reason ), "KILLed by %s: %s",
-			  Client_ID( Client ), Req->argv[1] );
-	}
+	/* Build reason string: Prefix the "reason" if the originator is a
+	 * regular user, so users can't spoof KILLs of servers. */
+	if (Client_Type(Client) == CLIENT_USER)
+		snprintf(reason, sizeof(reason), "KILLed by %s: %s",
+			 Client_ID(Client), Req->argv[1]);
 	else
 	else
-		strlcpy( reason, Req->argv[1], sizeof( reason ));
+		strlcpy(reason, Req->argv[1], sizeof(reason));
 
 
 	/* Inform other servers */
 	/* Inform other servers */
-	IRC_WriteStrServersPrefix( Client, prefix, "KILL %s :%s",
-				   Req->argv[0], reason );
+	IRC_WriteStrServersPrefix(Client, prefix, "KILL %s :%s",
+				  Req->argv[0], reason);
 
 
 	/* Save ID of this connection */
 	/* Save ID of this connection */
 	my_conn = Client_Conn( Client );
 	my_conn = Client_Conn( Client );
@@ -320,6 +349,7 @@ static bool
 Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 {
 {
 	CLIENT *cl, *from;
 	CLIENT *cl, *from;
+	CL2CHAN *cl2chan;
 	CHANNEL *chan;
 	CHANNEL *chan;
 	char *currentTarget = Req->argv[0];
 	char *currentTarget = Req->argv[0];
 	char *lastCurrentTarget = NULL;
 	char *lastCurrentTarget = NULL;
@@ -410,8 +440,8 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 				    Client_Type(cl) != CLIENT_SERVICE)
 				    Client_Type(cl) != CLIENT_SERVICE)
 					continue;
 					continue;
 				if (nick != NULL && host != NULL) {
 				if (nick != NULL && host != NULL) {
-					if (strcmp(nick, Client_ID(cl)) == 0 &&
-					    strcmp(user, Client_User(cl)) == 0 &&
+					if (strcasecmp(nick, Client_ID(cl)) == 0 &&
+					    strcasecmp(user, Client_User(cl)) == 0 &&
 					    strcasecmp(host, Client_HostnameCloaked(cl)) == 0)
 					    strcasecmp(host, Client_HostnameCloaked(cl)) == 0)
 						break;
 						break;
 					else
 					else
@@ -439,11 +469,11 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 #else
 #else
 			if (Client_Type(cl) != ForceType) {
 			if (Client_Type(cl) != ForceType) {
 #endif
 #endif
-				if (!SendErrors)
-					return CONNECTED;
-				return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
-							  Client_ID(from),
-							  currentTarget);
+				if (SendErrors && !IRC_WriteStrClient(
+				    from, ERR_NOSUCHNICK_MSG,Client_ID(from),
+				    currentTarget))
+					return DISCONNECTED;
+				goto send_next_target;
 			}
 			}
 
 
 #ifndef STRICT_RFC
 #ifndef STRICT_RFC
@@ -456,6 +486,23 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 			}
 			}
 #endif
 #endif
 
 
+			if (Client_HasMode(cl, 'C')) {
+				cl2chan = Channel_FirstChannelOf(cl);
+				while (cl2chan) {
+					chan = Channel_GetChannel(cl2chan);
+					if (Channel_IsMemberOf(chan, from))
+						break;
+					cl2chan = Channel_NextChannelOf(cl, cl2chan);
+				}
+				if (!cl2chan) {
+					if (SendErrors && !IRC_WriteStrClient(
+					    from, ERR_NOTONSAMECHANNEL_MSG,
+					    Client_ID(from), Client_ID(cl)))
+						return DISCONNECTED;
+					goto send_next_target;
+				}
+			}
+
 			if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
 			if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
 			    && strchr(Client_Modes(cl), 'a')) {
 			    && strchr(Client_Modes(cl), 'a')) {
 				/* Target is away */
 				/* Target is away */
@@ -493,7 +540,10 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 				return DISCONNECTED;
 				return DISCONNECTED;
 		}
 		}
 
 
+	send_next_target:
 		currentTarget = strtok_r(NULL, ",", &lastCurrentTarget);
 		currentTarget = strtok_r(NULL, ",", &lastCurrentTarget);
+		if (currentTarget)
+			Conn_SetPenalty(Client_Conn(Client), 1);
 	}
 	}
 
 
 	return CONNECTED;
 	return CONNECTED;

+ 4 - 1
src/ngircd/irc.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -17,6 +17,9 @@
  * IRC commands (header)
  * IRC commands (header)
  */
  */
 
 
+GLOBAL bool IRC_CheckListTooBig PARAMS((CLIENT *From, const int Count,
+					const int Limit, const char *Name));
+
 GLOBAL bool IRC_ERROR PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_ERROR PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_KILL PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_KILL PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_NOTICE PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_NOTICE PARAMS((CLIENT *Client, REQUEST *Req));

+ 260 - 75
src/ngircd/lists.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2005 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -37,89 +37,178 @@
 #define MASK_LEN	(2*CLIENT_HOST_LEN)
 #define MASK_LEN	(2*CLIENT_HOST_LEN)
 
 
 struct list_elem {
 struct list_elem {
-	struct list_elem *next;
-	char mask[MASK_LEN];
-	bool onlyonce;
+	struct list_elem *next;	/** pointer to next list element */
+	char mask[MASK_LEN];	/** IRC mask */
+	char *reason;		/** Optional "reason" text */
+	time_t valid_until;	/** 0: unlimited; 1: once; t(>1): until t */
 };
 };
 
 
-
+/**
+ * Get IRC mask stored in list element.
+ *
+ * @param list_elem List element.
+ * @return Pointer to IRC mask
+ */
 GLOBAL const char *
 GLOBAL const char *
 Lists_GetMask(const struct list_elem *e)
 Lists_GetMask(const struct list_elem *e)
 {
 {
+	assert(e != NULL);
 	return e->mask;
 	return e->mask;
 }
 }
 
 
+/**
+ * Get optional "reason" text stored in list element.
+ *
+ * @param list_elem List element.
+ * @return Pointer to "reason" text or empty string ("").
+ */
+GLOBAL const char *
+Lists_GetReason(const struct list_elem *e)
+{
+	assert(e != NULL);
+	return e->reason ? e->reason : "";
+}
 
 
+/**
+ * Get "validity" value stored in list element.
+ *
+ * @param list_elem List element.
+ * @return Validity: 0=unlimited, 1=once, >1 until this time stamp.
+ */
+GLOBAL time_t
+Lists_GetValidity(const struct list_elem *e)
+{
+	assert(e != NULL);
+	return e->valid_until;
+}
+
+/**
+ * Get first list element of a list.
+ *
+ * @param h List head.
+ * @return Pointer to first list element.
+ */
 GLOBAL struct list_elem*
 GLOBAL struct list_elem*
 Lists_GetFirst(const struct list_head *h)
 Lists_GetFirst(const struct list_head *h)
 {
 {
+	assert(h != NULL);
 	return h->first;
 	return h->first;
 }
 }
 
 
-
+/**
+ * Get next list element of a list.
+ *
+ * @param e Current list element.
+ * @return Pointer to next list element.
+ */
 GLOBAL struct list_elem*
 GLOBAL struct list_elem*
 Lists_GetNext(const struct list_elem *e)
 Lists_GetNext(const struct list_elem *e)
 {
 {
+	assert(e != NULL);
 	return e->next;
 	return e->next;
 }
 }
 
 
-
+/**
+ * Add a new mask to a list.
+ *
+ * @param h List head.
+ * @param Mask The IRC mask to add to the list.
+ * @param ValidUntil 0: unlimited, 1: only once, t>1: until given time_t.
+ * @param Reason Reason string or NULL, if no reason should be saved.
+ * @return true on success, false otherwise.
+ */
 bool
 bool
-Lists_Add(struct list_head *header, const char *Mask, bool OnlyOnce )
+Lists_Add(struct list_head *h, const char *Mask, time_t ValidUntil,
+	  const char *Reason)
 {
 {
 	struct list_elem *e, *newelem;
 	struct list_elem *e, *newelem;
 
 
-	assert( header != NULL );
-	assert( Mask != NULL );
+	assert(h != NULL);
+	assert(Mask != NULL);
 
 
-	if (Lists_CheckDupeMask(header, Mask )) return true;
+	e = Lists_CheckDupeMask(h, Mask);
+	if (e) {
+		e->valid_until = ValidUntil;
+		if (Reason) {
+			free(e->reason);
+			e->reason = strdup(Reason);
+		}
+		return true;
+	}
 
 
-	e = Lists_GetFirst(header);
+	e = Lists_GetFirst(h);
 
 
 	newelem = malloc(sizeof(struct list_elem));
 	newelem = malloc(sizeof(struct list_elem));
-	if( ! newelem ) {
-		Log( LOG_EMERG, "Can't allocate memory for new Ban/Invite entry!" );
+	if (!newelem) {
+		Log(LOG_EMERG,
+		    "Can't allocate memory for new list entry!");
 		return false;
 		return false;
 	}
 	}
 
 
-	strlcpy( newelem->mask, Mask, sizeof( newelem->mask ));
-	newelem->onlyonce = OnlyOnce;
+	strlcpy(newelem->mask, Mask, sizeof(newelem->mask));
+	if (Reason) {
+		newelem->reason = malloc(strlen(Reason) + 1);
+		if (newelem->reason)
+			strlcpy(newelem->reason, Reason, strlen(Reason) + 1);
+		else
+			Log(LOG_EMERG,
+			    "Can't allocate memory for new list reason text!");
+	}
+	else
+		newelem->reason = NULL;
+	newelem->valid_until = ValidUntil;
 	newelem->next = e;
 	newelem->next = e;
-	header->first = newelem;
+	h->first = newelem;
 
 
 	return true;
 	return true;
 }
 }
 
 
-
+/**
+ * Delete a list element from a list.
+ *
+ * @param h List head.
+ * @param p Pointer to previous list element or NULL, if there is none.
+ * @param victim List element to delete.
+ */
 static void
 static void
-Lists_Unlink(struct list_head *header, struct list_elem *p, struct list_elem *victim)
+Lists_Unlink(struct list_head *h, struct list_elem *p, struct list_elem *victim)
 {
 {
 	assert(victim != NULL);
 	assert(victim != NULL);
-	assert(header != NULL);
+	assert(h != NULL);
 
 
-	if (p) p->next = victim->next;
-	else header->first = victim->next;
+	if (p)
+		p->next = victim->next;
+	else
+		h->first = victim->next;
+
+	if (victim->reason)
+		free(victim->reason);
 
 
 	free(victim);
 	free(victim);
 }
 }
 
 
-
+/**
+ * Delete a given IRC mask from a list.
+ *
+ * @param h List head.
+ * @param Mask IRC mask to delete from the list.
+ */
 GLOBAL void
 GLOBAL void
-Lists_Del(struct list_head *header, const char *Mask)
+Lists_Del(struct list_head *h, const char *Mask)
 {
 {
 	struct list_elem *e, *last, *victim;
 	struct list_elem *e, *last, *victim;
 
 
-	assert( header != NULL );
-	assert( Mask != NULL );
+	assert(h != NULL);
+	assert(Mask != NULL);
 
 
 	last = NULL;
 	last = NULL;
-	e = Lists_GetFirst(header);
-	while( e ) {
-		if(strcasecmp( e->mask, Mask ) == 0 ) {
+	e = Lists_GetFirst(h);
+	while (e) {
+		if (strcasecmp(e->mask, Mask) == 0) {
 			LogDebug("Deleted \"%s\" from list", e->mask);
 			LogDebug("Deleted \"%s\" from list", e->mask);
 			victim = e;
 			victim = e;
 			e = victim->next;
 			e = victim->next;
-			Lists_Unlink(header, last, victim);
+			Lists_Unlink(h, last, victim);
 			continue;
 			continue;
 		}
 		}
 		last = e;
 		last = e;
@@ -127,7 +216,11 @@ Lists_Del(struct list_head *header, const char *Mask)
 	}
 	}
 }
 }
 
 
-
+/**
+ * Free a complete list.
+ *
+ * @param head List head.
+ */
 GLOBAL void
 GLOBAL void
 Lists_Free(struct list_head *head)
 Lists_Free(struct list_head *head)
 {
 {
@@ -138,101 +231,193 @@ Lists_Free(struct list_head *head)
 	e = head->first;
 	e = head->first;
 	head->first = NULL;
 	head->first = NULL;
 	while (e) {
 	while (e) {
-		LogDebug("Deleted \"%s\" from invite list" , e->mask);
+		LogDebug("Deleted \"%s\" from list" , e->mask);
 		victim = e;
 		victim = e;
 		e = e->next;
 		e = e->next;
-		free( victim );
+		if (victim->reason)
+			free(victim->reason);
+		free(victim);
 	}
 	}
 }
 }
 
 
-
-GLOBAL bool
+/**
+ * Check if an IRC mask is already contained in a list.
+ *
+ * @param h List head.
+ * @param Mask IRC mask to test.
+ * @return true if mask is already stored in the list, false otherwise.
+ */
+GLOBAL struct list_elem *
 Lists_CheckDupeMask(const struct list_head *h, const char *Mask )
 Lists_CheckDupeMask(const struct list_head *h, const char *Mask )
 {
 {
 	struct list_elem *e;
 	struct list_elem *e;
 	e = h->first;
 	e = h->first;
 	while (e) {
 	while (e) {
-		if (strcasecmp( e->mask, Mask ) == 0 )
-			return true;
+		if (strcasecmp(e->mask, Mask) == 0)
+			return e;
 		e = e->next;
 		e = e->next;
 	}
 	}
-	return false;
+	return NULL;
 }
 }
 
 
-
+/**
+ * Generate a valid IRC mask from "any" string given.
+ *
+ * Attention: This mask is only valid until the next call to Lists_MakeMask(),
+ * because a single global buffer ist used! You have to copy the generated
+ * mask to some sane location yourself!
+ *
+ * @param Pattern Source string to generate an IRC mask for.
+ * @return Pointer to global result buffer.
+ */
 GLOBAL const char *
 GLOBAL const char *
 Lists_MakeMask(const char *Pattern)
 Lists_MakeMask(const char *Pattern)
 {
 {
-	/* This function generats a valid IRC mask of "any" string. This
-	 * mask is only valid until the next call to Lists_MakeMask(),
-	 * because a single global buffer is used. You have to copy the
-	 * generated mask to some sane location yourself! */
-
 	static char TheMask[MASK_LEN];
 	static char TheMask[MASK_LEN];
 	char *excl, *at;
 	char *excl, *at;
 
 
-	assert( Pattern != NULL );
+	assert(Pattern != NULL);
 
 
-	excl = strchr( Pattern, '!' );
-	at = strchr( Pattern, '@' );
+	excl = strchr(Pattern, '!');
+	at = strchr(Pattern, '@');
 
 
-	if(( at ) && ( at < excl )) excl = NULL;
+	if (at && at < excl)
+		excl = NULL;
 
 
-	if(( ! at ) && ( ! excl ))
-	{
+	if (!at && !excl) {
 		/* Neither "!" nor "@" found: use string as nick name */
 		/* Neither "!" nor "@" found: use string as nick name */
-		strlcpy( TheMask, Pattern, sizeof( TheMask ) - 5 );
-		strlcat( TheMask, "!*@*", sizeof( TheMask ));
+		strlcpy(TheMask, Pattern, sizeof(TheMask) - 5);
+		strlcat(TheMask, "!*@*", sizeof(TheMask));
 		return TheMask;
 		return TheMask;
 	}
 	}
 
 
-	if(( ! at ) && ( excl ))
-	{
+	if (!at && excl) {
 		/* Domain part is missing */
 		/* Domain part is missing */
-		strlcpy( TheMask, Pattern, sizeof( TheMask ) - 3 );
-		strlcat( TheMask, "@*", sizeof( TheMask ));
+		strlcpy(TheMask, Pattern, sizeof(TheMask) - 3);
+		strlcat(TheMask, "@*", sizeof(TheMask));
 		return TheMask;
 		return TheMask;
 	}
 	}
 
 
-	if(( at ) && ( ! excl ))
-	{
+	if (at && !excl) {
 		/* User name is missing */
 		/* User name is missing */
 		*at = '\0'; at++;
 		*at = '\0'; at++;
-		strlcpy( TheMask, Pattern, sizeof( TheMask ) - 5 );
-		strlcat( TheMask, "!*@", sizeof( TheMask ));
-		strlcat( TheMask, at, sizeof( TheMask ));
+		strlcpy(TheMask, Pattern, sizeof(TheMask) - 5);
+		strlcat(TheMask, "!*@", sizeof(TheMask));
+		strlcat(TheMask, at, sizeof(TheMask));
 		return TheMask;
 		return TheMask;
 	}
 	}
 
 
 	/* All parts (nick, user and domain name) are given */
 	/* All parts (nick, user and domain name) are given */
-	strlcpy( TheMask, Pattern, sizeof( TheMask ));
+	strlcpy(TheMask, Pattern, sizeof(TheMask));
 	return TheMask;
 	return TheMask;
 } /* Lists_MakeMask */
 } /* Lists_MakeMask */
 
 
-
+/**
+ * Check if a client is listed in a list.
+ *
+ * @param h List head.
+ * @param Client Client to check.
+ * @return true if client is listed, false if not.
+ */
 bool
 bool
-Lists_Check( struct list_head *header, CLIENT *Client)
+Lists_Check(struct list_head *h, CLIENT *Client)
+{
+	return Lists_CheckReason(h, Client) != NULL;
+}
+
+/**
+ * Check if a client is listed in a list and return the "reason".
+ *
+ * @param h List head.
+ * @param Client Client to check.
+ * @return true if client is listed, false if not.
+ */
+char *
+Lists_CheckReason(struct list_head *h, CLIENT *Client)
 {
 {
-	struct list_elem *e, *last;
+	struct list_elem *e, *last, *next;
 
 
-	assert( header != NULL );
+	assert(h != NULL);
 
 
-	e = header->first;
+	e = h->first;
 	last = NULL;
 	last = NULL;
 
 
-	while( e ) {
-		if( Match( e->mask, Client_Mask( Client ))) {
-			if( e->onlyonce ) { /* delete entry */
-				LogDebug("Deleted \"%s\" from list", e->mask);
-				Lists_Unlink(header, last, e);
+	while (e) {
+		next = e->next;
+		if (Match(e->mask, Client_Mask(Client))) {
+			if (e->valid_until == 1) {
+				/* Entry is valid only once, delete it */
+				LogDebug("Deleted \"%s\" from list (used).",
+					 e->mask);
+				Lists_Unlink(h, last, e);
 			}
 			}
-			return true;
+			return e->reason ? e->reason : "";
 		}
 		}
 		last = e;
 		last = e;
-		e = e->next;
+		e = next;
+	}
+
+	return NULL;
+}
+
+/**
+ * Check list and purge expired entries.
+ *
+ * @param h List head.
+ */
+GLOBAL void
+Lists_Expire(struct list_head *h, const char *ListName)
+{
+	struct list_elem *e, *last, *next;
+	time_t now;
+
+	assert(h != NULL);
+
+	e = h->first;
+	last = NULL;
+	now = time(NULL);
+
+	while (e) {
+		next = e->next;
+		if (e->valid_until > 1 && e->valid_until < now) {
+			/* Entry is expired, delete it */
+			if (e->reason)
+				Log(LOG_INFO,
+				    "Deleted \"%s\" (\"%s\") from %s list (expired).",
+				    e->mask, e->reason, ListName);
+			else
+				Log(LOG_INFO,
+				    "Deleted \"%s\" from %s list (expired).",
+				    e->mask, ListName);
+			Lists_Unlink(h, last, e);
+			e = next;
+			continue;
+		}
+		last = e;
+		e = next;
 	}
 	}
+}
+
+/**
+ * Return the number of entries of a list.
+ *
+ * @param h List head.
+ * @return Number of items.
+ */
+GLOBAL unsigned long
+Lists_Count(struct list_head *h)
+{
+	struct list_elem *e;
+	unsigned long count = 0;
+
+	assert(h != NULL);
 
 
-	return false;
+	e = h->first;
+	while (e) {
+		count++;
+		e = e->next;
+	}
+	return count;
 }
 }
 
 
 /* -eof- */
 /* -eof- */

+ 15 - 9
src/ngircd/lists.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -29,18 +29,24 @@ struct list_head {
 GLOBAL struct list_elem *Lists_GetFirst PARAMS((const struct list_head *));
 GLOBAL struct list_elem *Lists_GetFirst PARAMS((const struct list_head *));
 GLOBAL struct list_elem *Lists_GetNext PARAMS((const struct list_elem *));
 GLOBAL struct list_elem *Lists_GetNext PARAMS((const struct list_elem *));
 
 
-GLOBAL bool Lists_Check PARAMS((struct list_head *head, CLIENT *client ));
-GLOBAL bool Lists_CheckDupeMask PARAMS((const struct list_head *head, const char *mask ));
+GLOBAL bool Lists_Check PARAMS((struct list_head *head, CLIENT *client));
+GLOBAL char *Lists_CheckReason PARAMS((struct list_head *head, CLIENT *client));
+GLOBAL struct list_elem *Lists_CheckDupeMask PARAMS((const struct list_head *head,
+					const char *mask));
 
 
-GLOBAL bool Lists_Add PARAMS((struct list_head *header, const char *Mask, bool OnlyOnce ));
-GLOBAL void Lists_Del PARAMS((struct list_head *head, const char *Mask ));
+GLOBAL bool Lists_Add PARAMS((struct list_head *h, const char *Mask,
+			      time_t ValidUntil, const char *Reason));
+GLOBAL void Lists_Del PARAMS((struct list_head *head, const char *Mask));
+GLOBAL unsigned long Lists_Count PARAMS((struct list_head *h));
 
 
-GLOBAL bool Lists_AlreadyRegistered PARAMS(( const struct list_head *head, const char *Mask));
-
-GLOBAL void Lists_Free PARAMS(( struct list_head *head ));
+GLOBAL void Lists_Free PARAMS((struct list_head *head));
 
 
 GLOBAL const char *Lists_MakeMask PARAMS((const char *Pattern));
 GLOBAL const char *Lists_MakeMask PARAMS((const char *Pattern));
-GLOBAL const char *Lists_GetMask PARAMS(( const struct list_elem *e ));
+GLOBAL const char *Lists_GetMask PARAMS((const struct list_elem *e));
+GLOBAL const char *Lists_GetReason PARAMS((const struct list_elem *e));
+GLOBAL time_t Lists_GetValidity PARAMS((const struct list_elem *e));
+
+GLOBAL void Lists_Expire PARAMS((struct list_head *h, const char *ListName));
 
 
 #endif
 #endif
 
 

+ 26 - 31
src/ngircd/log.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -44,7 +44,6 @@
 #include "log.h"
 #include "log.h"
 
 
 
 
-static char Init_Txt[127];
 static bool Is_Daemon;
 static bool Is_Daemon;
 
 
 
 
@@ -65,11 +64,17 @@ Log_Message(int Level, const char *msg)
 }
 }
 
 
 
 
+/**
+ * Initialitze logging.
+ * This function is called before the configuration file is read in.
+ *
+ * @param Daemon_Mode Set to true if ngIRCd is running as daemon.
+ */
 GLOBAL void
 GLOBAL void
-Log_Init( bool Daemon_Mode )
+Log_Init(bool Daemon_Mode)
 {
 {
 	Is_Daemon = Daemon_Mode;
 	Is_Daemon = Daemon_Mode;
-	
+
 #ifdef SYSLOG
 #ifdef SYSLOG
 #ifndef LOG_CONS     /* Kludge: mips-dec-ultrix4.5 has no LOG_CONS */
 #ifndef LOG_CONS     /* Kludge: mips-dec-ultrix4.5 has no LOG_CONS */
 #define LOG_CONS 0
 #define LOG_CONS 0
@@ -77,35 +82,25 @@ Log_Init( bool Daemon_Mode )
 	openlog(PACKAGE_NAME, LOG_CONS|LOG_PID, Conf_SyslogFacility);
 	openlog(PACKAGE_NAME, LOG_CONS|LOG_PID, Conf_SyslogFacility);
 #endif
 #endif
 
 
-	Log( LOG_NOTICE, "%s started.", NGIRCd_Version );
-	  
-	/* Information about "Operation Mode" */
-	Init_Txt[0] = '\0';
-#ifdef DEBUG
-	if( NGIRCd_Debug )
-	{
-		strlcpy( Init_Txt, "debug-mode", sizeof Init_Txt );
-	}
+	Log(LOG_NOTICE, "%s started.", NGIRCd_Version);
+} /* Log_Init */
+
+
+/**
+ * Re-init logging after reading the configuration file.
+ */
+GLOBAL void
+Log_ReInit(void)
+{
+#ifdef SYSLOG
+#ifndef LOG_CONS     /* Kludge: mips-dec-ultrix4.5 has no LOG_CONS */
+#define LOG_CONS 0
 #endif
 #endif
-	if( ! Is_Daemon )
-	{
-		if( Init_Txt[0] ) strlcat( Init_Txt, ", ", sizeof Init_Txt );
-		strlcat( Init_Txt, "no-daemon-mode", sizeof Init_Txt );
-	}
-	if( NGIRCd_Passive )
-	{
-		if( Init_Txt[0] ) strlcat( Init_Txt, ", ", sizeof Init_Txt );
-		strlcat( Init_Txt, "passive-mode", sizeof Init_Txt );
-	}
-#ifdef SNIFFER
-	if( NGIRCd_Sniffer )
-	{
-		if( Init_Txt[0] ) strlcat( Init_Txt, ", ", sizeof Init_Txt );
-		strlcat( Init_Txt, "network sniffer", sizeof Init_Txt );
-	}
+	closelog();
+	openlog(PACKAGE_NAME, LOG_CONS|LOG_PID, Conf_SyslogFacility);
 #endif
 #endif
-	if( Init_Txt[0] ) Log( LOG_INFO, "Activating: %s.", Init_Txt );
-} /* Log_Init */
+	Log(LOG_NOTICE, "%s started.", NGIRCd_Version);
+}
 
 
 
 
 GLOBAL void
 GLOBAL void

+ 2 - 1
src/ngircd/log.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -36,6 +36,7 @@ GLOBAL void Log_Init PARAMS(( bool Daemon_Mode ));
 GLOBAL void Log_Exit PARAMS(( void ));
 GLOBAL void Log_Exit PARAMS(( void ));
 
 
 GLOBAL void Log PARAMS(( int Level, const char *Format, ... ));
 GLOBAL void Log PARAMS(( int Level, const char *Format, ... ));
+GLOBAL void Log_ReInit PARAMS((void));
 
 
 GLOBAL void Log_ServerNotice PARAMS((char UserMode, const char *Format, ...));
 GLOBAL void Log_ServerNotice PARAMS((char UserMode, const char *Format, ...));
 
 

+ 15 - 6
src/ngircd/messages.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -21,14 +21,15 @@
 #define RPL_YOURHOST_MSG		"002 %s :Your host is %s, running version ngircd-%s (%s/%s/%s)"
 #define RPL_YOURHOST_MSG		"002 %s :Your host is %s, running version ngircd-%s (%s/%s/%s)"
 #define RPL_CREATED_MSG			"003 %s :This server has been started %s"
 #define RPL_CREATED_MSG			"003 %s :This server has been started %s"
 #define RPL_MYINFO_MSG			"004 %s %s ngircd-%s %s %s"
 #define RPL_MYINFO_MSG			"004 %s %s ngircd-%s %s %s"
-#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=bI,k,l,imnPst CHANLIMIT=#&+:%d :are supported on this server"
-#define RPL_ISUPPORT2_MSG		"005 %s CHANNELLEN=%d NICKLEN=%d TOPICLEN=%d AWAYLEN=%d KICKLEN=%d PENALTY :are supported on this server"
+#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=beI,k,l,imnOPRstz CHANLIMIT=#&+:%d :are supported on this server"
+#define RPL_ISUPPORT2_MSG		"005 %s CHANNELLEN=%d NICKLEN=%d TOPICLEN=%d AWAYLEN=%d KICKLEN=%d MODES=%d MAXLIST=beI:%d EXCEPTS=e INVEX=I PENALTY :are supported on this server"
 
 
 #define RPL_TRACELINK_MSG		"200 %s Link %s-%s %s %s V%s %ld %d %d"
 #define RPL_TRACELINK_MSG		"200 %s Link %s-%s %s %s V%s %ld %d %d"
 #define RPL_TRACEOPERATOR_MSG		"204 %s Oper 2 :%s"
 #define RPL_TRACEOPERATOR_MSG		"204 %s Oper 2 :%s"
 #define RPL_TRACESERVER_MSG		"206 %s Serv 1 0S 0C %s[%s@%s] *!*@%s :V%s"
 #define RPL_TRACESERVER_MSG		"206 %s Serv 1 0S 0C %s[%s@%s] *!*@%s :V%s"
 #define RPL_STATSLINKINFO_MSG		"211 %s %s %d %ld %ld %ld %ld :%ld"
 #define RPL_STATSLINKINFO_MSG		"211 %s %s %d %ld %ld %ld %ld :%ld"
 #define RPL_STATSCOMMANDS_MSG		"212 %s %s %ld %ld %ld"
 #define RPL_STATSCOMMANDS_MSG		"212 %s %s %ld %ld %ld"
+#define RPL_STATSXLINE_MSG		"216 %s %c %s %ld :%s"
 #define RPL_ENDOFSTATS_MSG		"219 %s %c :End of STATS report"
 #define RPL_ENDOFSTATS_MSG		"219 %s %c :End of STATS report"
 #define RPL_UMODEIS_MSG			"221 %s +%s"
 #define RPL_UMODEIS_MSG			"221 %s +%s"
 #define RPL_SERVLIST_MSG		"234 %s %s %s %s %d %d :%s"
 #define RPL_SERVLIST_MSG		"234 %s %s %s %s %d %d :%s"
@@ -55,6 +56,7 @@
 #define RPL_ISON_MSG			"303 %s :"
 #define RPL_ISON_MSG			"303 %s :"
 #define RPL_UNAWAY_MSG			"305 %s :You are no longer marked as being away"
 #define RPL_UNAWAY_MSG			"305 %s :You are no longer marked as being away"
 #define RPL_NOWAWAY_MSG			"306 %s :You have been marked as being away"
 #define RPL_NOWAWAY_MSG			"306 %s :You have been marked as being away"
+#define RPL_WHOISREGNICK_MSG		"307 %s %s :is a registered nick"
 #define RPL_WHOISUSER_MSG		"311 %s %s %s %s * :%s"
 #define RPL_WHOISUSER_MSG		"311 %s %s %s %s * :%s"
 #define RPL_WHOISSERVER_MSG		"312 %s %s %s :%s"
 #define RPL_WHOISSERVER_MSG		"312 %s %s %s :%s"
 #define RPL_WHOISOPERATOR_MSG		"313 %s %s :is an IRC operator"
 #define RPL_WHOISOPERATOR_MSG		"313 %s %s :is an IRC operator"
@@ -73,6 +75,8 @@
 #define RPL_INVITING_MSG		"341 %s %s %s%s"
 #define RPL_INVITING_MSG		"341 %s %s %s%s"
 #define RPL_INVITELIST_MSG		"346 %s %s %s"
 #define RPL_INVITELIST_MSG		"346 %s %s %s"
 #define RPL_ENDOFINVITELIST_MSG		"347 %s %s :End of channel invite list"
 #define RPL_ENDOFINVITELIST_MSG		"347 %s %s :End of channel invite list"
+#define RPL_EXCEPTLIST_MSG		"348 %s %s %s"
+#define RPL_ENDOFEXCEPTLIST_MSG		"349 %s %s :End of channel exception list"
 #define RPL_VERSION_MSG			"351 %s %s-%s.%s %s :%s"
 #define RPL_VERSION_MSG			"351 %s %s-%s.%s %s :%s"
 #define RPL_WHOREPLY_MSG		"352 %s %s %s %s %s %s %s :%d %s"
 #define RPL_WHOREPLY_MSG		"352 %s %s %s %s %s %s %s :%d %s"
 #define RPL_NAMREPLY_MSG		"353 %s %s %s :"
 #define RPL_NAMREPLY_MSG		"353 %s %s %s :"
@@ -87,6 +91,7 @@
 #define RPL_MOTD_MSG			"372 %s :- %s"
 #define RPL_MOTD_MSG			"372 %s :- %s"
 #define RPL_MOTDSTART_MSG		"375 %s :- %s message of the day"
 #define RPL_MOTDSTART_MSG		"375 %s :- %s message of the day"
 #define RPL_ENDOFMOTD_MSG		"376 %s :End of MOTD command"
 #define RPL_ENDOFMOTD_MSG		"376 %s :End of MOTD command"
+#define RPL_WHOISHOST_MSG		"378 %s %s :is connecting from *@%s %s"
 #define RPL_YOUREOPER_MSG		"381 %s :You are now an IRC Operator"
 #define RPL_YOUREOPER_MSG		"381 %s :You are now an IRC Operator"
 #define RPL_YOURESERVICE_MSG		"383 %s :You are service %s"
 #define RPL_YOURESERVICE_MSG		"383 %s :You are service %s"
 #define RPL_TIME_MSG			"391 %s %s :%s"
 #define RPL_TIME_MSG			"391 %s %s :%s"
@@ -105,12 +110,13 @@
 #define ERR_NOMOTD_MSG			"422 %s :MOTD file is missing"
 #define ERR_NOMOTD_MSG			"422 %s :MOTD file is missing"
 #define ERR_NONICKNAMEGIVEN_MSG		"431 %s :No nickname given"
 #define ERR_NONICKNAMEGIVEN_MSG		"431 %s :No nickname given"
 #define ERR_ERRONEUSNICKNAME_MSG	"432 %s %s :Erroneous nickname"
 #define ERR_ERRONEUSNICKNAME_MSG	"432 %s %s :Erroneous nickname"
+#define ERR_NICKNAMETOOLONG_MSG		"432 %s %s :Nickname too long, max. %u characters"
 #define ERR_NICKNAMEINUSE_MSG		"433 %s %s :Nickname already in use"
 #define ERR_NICKNAMEINUSE_MSG		"433 %s %s :Nickname already in use"
 #define ERR_USERNOTINCHANNEL_MSG	"441 %s %s %s :They aren't on that channel"
 #define ERR_USERNOTINCHANNEL_MSG	"441 %s %s %s :They aren't on that channel"
 #define ERR_NOTONCHANNEL_MSG		"442 %s %s :You are not on that channel"
 #define ERR_NOTONCHANNEL_MSG		"442 %s %s :You are not on that channel"
 #define ERR_USERONCHANNEL_MSG		"443 %s %s %s :is already on channel"
 #define ERR_USERONCHANNEL_MSG		"443 %s %s %s :is already on channel"
-#define ERR_SUMMONDISABLED_MSG		"445 %s %s :SUMMON has been disabled"
-#define ERR_USERSDISABLED_MSG		"446 %s %s :USERS has been disabled"
+#define ERR_SUMMONDISABLED_MSG		"445 %s :SUMMON has been disabled"
+#define ERR_USERSDISABLED_MSG		"446 %s :USERS has been disabled"
 #define ERR_NOTREGISTERED_MSG		"451 %s :Connection not registered"
 #define ERR_NOTREGISTERED_MSG		"451 %s :Connection not registered"
 #define ERR_NOTREGISTEREDSERVER_MSG	"451 %s :Connection not registered as server link"
 #define ERR_NOTREGISTEREDSERVER_MSG	"451 %s :Connection not registered as server link"
 #define ERR_NEEDMOREPARAMS_MSG		"461 %s %s :Syntax error"
 #define ERR_NEEDMOREPARAMS_MSG		"461 %s %s :Syntax error"
@@ -119,16 +125,19 @@
 #define ERR_CHANNELISFULL_MSG		"471 %s %s :Cannot join channel (+l)"
 #define ERR_CHANNELISFULL_MSG		"471 %s %s :Cannot join channel (+l)"
 #define ERR_SECURECHANNEL_MSG		"471 %s %s :Cannot join channel (+z)"
 #define ERR_SECURECHANNEL_MSG		"471 %s %s :Cannot join channel (+z)"
 #define ERR_OPONLYCHANNEL_MSG		"471 %s %s :Cannot join channel (+O)"
 #define ERR_OPONLYCHANNEL_MSG		"471 %s %s :Cannot join channel (+O)"
-#define ERR_UNKNOWNMODE_MSG		"472 %s: %c :is unknown mode char for %s"
+#define ERR_REGONLYCHANNEL_MSG		"471 %s %s :Cannot join channel (+R)"
+#define ERR_UNKNOWNMODE_MSG		"472 %s %c :is unknown mode char for %s"
 #define ERR_INVITEONLYCHAN_MSG		"473 %s %s :Cannot join channel (+i)"
 #define ERR_INVITEONLYCHAN_MSG		"473 %s %s :Cannot join channel (+i)"
 #define ERR_BANNEDFROMCHAN_MSG		"474 %s %s :Cannot join channel (+b)"
 #define ERR_BANNEDFROMCHAN_MSG		"474 %s %s :Cannot join channel (+b)"
 #define ERR_BADCHANNELKEY_MSG		"475 %s %s :Cannot join channel (+k)"
 #define ERR_BADCHANNELKEY_MSG		"475 %s %s :Cannot join channel (+k)"
 #define ERR_NOCHANMODES_MSG		"477 %s %s :Channel doesn't support modes"
 #define ERR_NOCHANMODES_MSG		"477 %s %s :Channel doesn't support modes"
+#define ERR_LISTFULL_MSG		"478 %s %s %s: Channel list is full (%d)"
 #define ERR_NOPRIVILEGES_MSG		"481 %s :Permission denied"
 #define ERR_NOPRIVILEGES_MSG		"481 %s :Permission denied"
 #define ERR_CHANOPRIVSNEEDED_MSG	"482 %s %s :You are not channel operator"
 #define ERR_CHANOPRIVSNEEDED_MSG	"482 %s %s :You are not channel operator"
 #define ERR_CANTKILLSERVER_MSG		"483 %s :You can't kill a server!"
 #define ERR_CANTKILLSERVER_MSG		"483 %s :You can't kill a server!"
 #define ERR_RESTRICTED_MSG		"484 %s :Your connection is restricted"
 #define ERR_RESTRICTED_MSG		"484 %s :Your connection is restricted"
 #define ERR_NOOPERHOST_MSG		"491 %s :Not configured for your host"
 #define ERR_NOOPERHOST_MSG		"491 %s :Not configured for your host"
+#define ERR_NOTONSAMECHANNEL_MSG	"493 %s :You must share a common channel with %s"
 
 
 #define ERR_UMODEUNKNOWNFLAG_MSG	"501 %s :Unknown mode"
 #define ERR_UMODEUNKNOWNFLAG_MSG	"501 %s :Unknown mode"
 #define ERR_UMODEUNKNOWNFLAG2_MSG	"501 %s :Unknown mode \"%c%c\""
 #define ERR_UMODEUNKNOWNFLAG2_MSG	"501 %s :Unknown mode \"%c%c\""

+ 175 - 151
src/ngircd/ngircd.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -38,6 +38,7 @@
 
 
 #include "defines.h"
 #include "defines.h"
 #include "conn.h"
 #include "conn.h"
+#include "class.h"
 #include "conf-ssl.h"
 #include "conf-ssl.h"
 #include "channel.h"
 #include "channel.h"
 #include "conf.h"
 #include "conf.h"
@@ -73,12 +74,12 @@ static bool NGIRCd_Init PARAMS(( bool ));
  * Here all starts: this function is called by the operating system loader,
  * Here all starts: this function is called by the operating system loader,
  * it is the first portion of code executed of ngIRCd.
  * it is the first portion of code executed of ngIRCd.
  *
  *
- * @param argc	The number of arguments passed to ngIRCd on the command line.
- * @param argv	An array containing all the arguments passed to ngIRCd.
- * @return	Global exit code of ngIRCd, zero on success.
+ * @param argc The number of arguments passed to ngIRCd on the command line.
+ * @param argv An array containing all the arguments passed to ngIRCd.
+ * @return Global exit code of ngIRCd, zero on success.
  */
  */
 GLOBAL int
 GLOBAL int
-main( int argc, const char *argv[] )
+main(int argc, const char *argv[])
 {
 {
 	bool ok, configtest = false;
 	bool ok, configtest = false;
 	bool NGIRCd_NoDaemon = false;
 	bool NGIRCd_NoDaemon = false;
@@ -91,7 +92,7 @@ main( int argc, const char *argv[] )
 	mtrace();
 	mtrace();
 #endif
 #endif
 
 
-	umask( 0077 );
+	umask(0077);
 
 
 	NGIRCd_SignalQuit = NGIRCd_SignalRestart = false;
 	NGIRCd_SignalQuit = NGIRCd_SignalRestart = false;
 	NGIRCd_Passive = false;
 	NGIRCd_Passive = false;
@@ -101,75 +102,62 @@ main( int argc, const char *argv[] )
 #ifdef SNIFFER
 #ifdef SNIFFER
 	NGIRCd_Sniffer = false;
 	NGIRCd_Sniffer = false;
 #endif
 #endif
-	strlcpy( NGIRCd_ConfFile, SYSCONFDIR, sizeof( NGIRCd_ConfFile ));
-	strlcat( NGIRCd_ConfFile, CONFIG_FILE, sizeof( NGIRCd_ConfFile ));
+	strlcpy(NGIRCd_ConfFile, SYSCONFDIR, sizeof(NGIRCd_ConfFile));
+	strlcat(NGIRCd_ConfFile, CONFIG_FILE, sizeof(NGIRCd_ConfFile));
 
 
-	Fill_Version( );
+	Fill_Version();
 
 
 	/* parse conmmand line */
 	/* parse conmmand line */
-	for( i = 1; i < argc; i++ )
-	{
+	for (i = 1; i < argc; i++) {
 		ok = false;
 		ok = false;
-		if(( argv[i][0] == '-' ) && ( argv[i][1] == '-' ))
-		{
+		if (argv[i][0] == '-' && argv[i][1] == '-') {
 			/* long option */
 			/* long option */
-			if( strcmp( argv[i], "--config" ) == 0 )
-			{
-				if( i + 1 < argc )
-				{
+			if (strcmp(argv[i], "--config") == 0) {
+				if (i + 1 < argc) {
 					/* Ok, there's an parameter left */
 					/* Ok, there's an parameter left */
-					strlcpy( NGIRCd_ConfFile, argv[i + 1], sizeof( NGIRCd_ConfFile ));
-
+					strlcpy(NGIRCd_ConfFile, argv[i+1],
+						sizeof(NGIRCd_ConfFile));
 					/* next parameter */
 					/* next parameter */
 					i++; ok = true;
 					i++; ok = true;
 				}
 				}
 			}
 			}
-			if( strcmp( argv[i], "--configtest" ) == 0 )
-			{
+			if (strcmp(argv[i], "--configtest") == 0) {
 				configtest = true;
 				configtest = true;
 				ok = true;
 				ok = true;
 			}
 			}
 #ifdef DEBUG
 #ifdef DEBUG
-			if( strcmp( argv[i], "--debug" ) == 0 )
-			{
+			if (strcmp(argv[i], "--debug") == 0) {
 				NGIRCd_Debug = true;
 				NGIRCd_Debug = true;
 				ok = true;
 				ok = true;
 			}
 			}
 #endif
 #endif
-			if( strcmp( argv[i], "--help" ) == 0 )
-			{
-				Show_Version( );
-				puts( "" ); Show_Help( ); puts( "" );
-				exit( 1 );
+			if (strcmp(argv[i], "--help") == 0) {
+				Show_Version();
+				puts(""); Show_Help( ); puts( "" );
+				exit(1);
 			}
 			}
-			if( strcmp( argv[i], "--nodaemon" ) == 0 )
-			{
+			if (strcmp(argv[i], "--nodaemon") == 0) {
 				NGIRCd_NoDaemon = true;
 				NGIRCd_NoDaemon = true;
 				ok = true;
 				ok = true;
 			}
 			}
-			if( strcmp( argv[i], "--passive" ) == 0 )
-			{
+			if (strcmp(argv[i], "--passive") == 0) {
 				NGIRCd_Passive = true;
 				NGIRCd_Passive = true;
 				ok = true;
 				ok = true;
 			}
 			}
 #ifdef SNIFFER
 #ifdef SNIFFER
-			if( strcmp( argv[i], "--sniffer" ) == 0 )
-			{
+			if (strcmp(argv[i], "--sniffer") == 0) {
 				NGIRCd_Sniffer = true;
 				NGIRCd_Sniffer = true;
 				ok = true;
 				ok = true;
 			}
 			}
 #endif
 #endif
-			if( strcmp( argv[i], "--version" ) == 0 )
-			{
-				Show_Version( );
-				exit( 1 );
+			if (strcmp(argv[i], "--version") == 0) {
+				Show_Version();
+				exit(1);
 			}
 			}
 		}
 		}
-		else if(( argv[i][0] == '-' ) && ( argv[i][1] != '-' ))
-		{
+		else if(argv[i][0] == '-' && argv[i][1] != '-') {
 			/* short option */
 			/* short option */
-			for( n = 1; n < strlen( argv[i] ); n++ )
-			{
+			for (n = 1; n < strlen(argv[i]); n++) {
 				ok = false;
 				ok = false;
 #ifdef DEBUG
 #ifdef DEBUG
 				if (argv[i][n] == 'd') {
 				if (argv[i][n] == 'd') {
@@ -178,14 +166,14 @@ main( int argc, const char *argv[] )
 				}
 				}
 #endif
 #endif
 				if (argv[i][n] == 'f') {
 				if (argv[i][n] == 'f') {
-					if(( ! argv[i][n + 1] ) && ( i + 1 < argc ))
-					{
+					if (!argv[i][n+1] && i+1 < argc) {
 						/* Ok, next character is a blank */
 						/* Ok, next character is a blank */
-						strlcpy( NGIRCd_ConfFile, argv[i + 1], sizeof( NGIRCd_ConfFile ));
+						strlcpy(NGIRCd_ConfFile, argv[i+1],
+							sizeof(NGIRCd_ConfFile));
 
 
 						/* go to the following parameter */
 						/* go to the following parameter */
 						i++;
 						i++;
-						n = strlen( argv[i] );
+						n = strlen(argv[i]);
 						ok = true;
 						ok = true;
 					}
 					}
 				}
 				}
@@ -220,55 +208,58 @@ main( int argc, const char *argv[] )
 					exit(1);
 					exit(1);
 				}
 				}
 
 
-				if (! ok) {
-					printf( "%s: invalid option \"-%c\"!\n", PACKAGE_NAME, argv[i][n] );
-					printf( "Try \"%s --help\" for more information.\n", PACKAGE_NAME );
-					exit( 1 );
+				if (!ok) {
+					printf("%s: invalid option \"-%c\"!\n",
+					       PACKAGE_NAME, argv[i][n]);
+					printf("Try \"%s --help\" for more information.\n",
+					       PACKAGE_NAME);
+					exit(1);
 				}
 				}
 			}
 			}
 
 
 		}
 		}
-		if( ! ok )
-		{
-			printf( "%s: invalid option \"%s\"!\n", PACKAGE_NAME, argv[i] );
-			printf( "Try \"%s --help\" for more information.\n", PACKAGE_NAME );
-			exit( 1 );
+		if (!ok) {
+			printf("%s: invalid option \"%s\"!\n",
+			       PACKAGE_NAME, argv[i]);
+			printf("Try \"%s --help\" for more information.\n",
+			       PACKAGE_NAME);
+			exit(1);
 		}
 		}
 	}
 	}
 
 
 	/* Debug level for "VERSION" command */
 	/* Debug level for "VERSION" command */
 	NGIRCd_DebugLevel[0] = '\0';
 	NGIRCd_DebugLevel[0] = '\0';
 #ifdef DEBUG
 #ifdef DEBUG
-	if( NGIRCd_Debug ) strcpy( NGIRCd_DebugLevel, "1" );
+	if (NGIRCd_Debug)
+		strcpy(NGIRCd_DebugLevel, "1");
 #endif
 #endif
 #ifdef SNIFFER
 #ifdef SNIFFER
-	if( NGIRCd_Sniffer )
-	{
+	if (NGIRCd_Sniffer) {
 		NGIRCd_Debug = true;
 		NGIRCd_Debug = true;
-		strcpy( NGIRCd_DebugLevel, "2" );
+		strcpy(NGIRCd_DebugLevel, "2");
 	}
 	}
 #endif
 #endif
 
 
-	if( configtest )
-	{
-		Show_Version( ); puts( "" );
-		exit( Conf_Test( ));
+	if (configtest) {
+		Show_Version(); puts("");
+		exit(Conf_Test());
 	}
 	}
-	
-	while( ! NGIRCd_SignalQuit )
-	{
+
+	while (!NGIRCd_SignalQuit) {
 		/* Initialize global variables */
 		/* Initialize global variables */
-		NGIRCd_Start = time( NULL );
-		(void)strftime( NGIRCd_StartStr, 64, "%a %b %d %Y at %H:%M:%S (%Z)", localtime( &NGIRCd_Start ));
+		NGIRCd_Start = time(NULL);
+		(void)strftime(NGIRCd_StartStr, 64,
+			       "%a %b %d %Y at %H:%M:%S (%Z)",
+			       localtime(&NGIRCd_Start));
 
 
 		NGIRCd_SignalRestart = false;
 		NGIRCd_SignalRestart = false;
 		NGIRCd_SignalQuit = false;
 		NGIRCd_SignalQuit = false;
 
 
-		Random_Init();
-
 		/* Initialize modules, part I */
 		/* Initialize modules, part I */
-		Log_Init( ! NGIRCd_NoDaemon );
-		Conf_Init( );
+		Log_Init(!NGIRCd_NoDaemon);
+		Random_Init();
+		Conf_Init();
+		Log_ReInit();
 
 
 		/* Initialize the "main program": chroot environment, user and
 		/* Initialize the "main program": chroot environment, user and
 		 * group ID, ... */
 		 * group ID, ... */
@@ -279,17 +270,22 @@ main( int argc, const char *argv[] )
 
 
 		/* Initialize modules, part II: these functions are eventually
 		/* Initialize modules, part II: these functions are eventually
 		 * called with already dropped privileges ... */
 		 * called with already dropped privileges ... */
-		Channel_Init( );
-		Client_Init( );
-		Conn_Init( );
+		Channel_Init();
+		Client_Init();
+		Conn_Init();
+		Class_Init();
 
 
 		if (!io_library_init(CONNECTION_POOL)) {
 		if (!io_library_init(CONNECTION_POOL)) {
-			Log(LOG_ALERT, "Fatal: Cannot initialize IO routines: %s", strerror(errno));
+			Log(LOG_ALERT,
+			    "Fatal: Could not initialize IO routines: %s",
+			    strerror(errno));
 			exit(1);
 			exit(1);
 		}
 		}
 
 
 		if (!Signals_Init()) {
 		if (!Signals_Init()) {
-			Log(LOG_ALERT, "Fatal: Could not set up signal handlers: %s", strerror(errno));
+			Log(LOG_ALERT,
+			    "Fatal: Could not set up signal handlers: %s",
+			    strerror(errno));
 			exit(1);
 			exit(1);
 		}
 		}
 
 
@@ -297,39 +293,45 @@ main( int argc, const char *argv[] )
 		 * used by ngIRCd in PASS commands and the known "extended
 		 * used by ngIRCd in PASS commands and the known "extended
 		 * flags" are described in doc/Protocol.txt. */
 		 * flags" are described in doc/Protocol.txt. */
 #ifdef IRCPLUS
 #ifdef IRCPLUS
-		snprintf( NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s:%s", PROTOVER, PROTOIRCPLUS, PACKAGE_NAME, PACKAGE_VERSION, IRCPLUSFLAGS );
+		snprintf(NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s:%s",
+			 PROTOVER, PROTOIRCPLUS, PACKAGE_NAME, PACKAGE_VERSION,
+			 IRCPLUSFLAGS);
 #ifdef ZLIB
 #ifdef ZLIB
-		strcat( NGIRCd_ProtoID, "Z" );
+		strcat(NGIRCd_ProtoID, "Z");
 #endif
 #endif
-		if( Conf_OperCanMode ) strcat( NGIRCd_ProtoID, "o" );
-#else
-		snprintf( NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s", PROTOVER, PROTOIRC, PACKAGE_NAME, PACKAGE_VERSION );
-#endif
-		strlcat( NGIRCd_ProtoID, " P", sizeof NGIRCd_ProtoID );
+		if (Conf_OperCanMode)
+			strcat(NGIRCd_ProtoID, "o");
+#else /* IRCPLUS */
+		snprintf(NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s",
+			 PROTOVER, PROTOIRC, PACKAGE_NAME, PACKAGE_VERSION);
+#endif /* IRCPLUS */
+		strlcat(NGIRCd_ProtoID, " P", sizeof NGIRCd_ProtoID);
 #ifdef ZLIB
 #ifdef ZLIB
-		strlcat( NGIRCd_ProtoID, "Z", sizeof NGIRCd_ProtoID );
+		strlcat(NGIRCd_ProtoID, "Z", sizeof NGIRCd_ProtoID);
 #endif
 #endif
 		LogDebug("Protocol and server ID is \"%s\".", NGIRCd_ProtoID);
 		LogDebug("Protocol and server ID is \"%s\".", NGIRCd_ProtoID);
 
 
-		Channel_InitPredefined( );
+		Channel_InitPredefined();
 
 
-		if( Conn_InitListeners( ) < 1 )
-		{
-			Log( LOG_ALERT, "Server isn't listening on a single port!" );
-			Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
-			Pidfile_Delete( );
-			exit( 1 );
+		if (Conn_InitListeners() < 1) {
+			Log(LOG_ALERT,
+			    "Server isn't listening on a single port!" );
+			Log(LOG_ALERT,
+			    "%s exiting due to fatal errors!", PACKAGE_NAME);
+			Pidfile_Delete();
+			exit(1);
 		}
 		}
 
 
 		/* Main Run Loop */
 		/* Main Run Loop */
-		Conn_Handler( );
+		Conn_Handler();
 
 
-		Conn_Exit( );
-		Client_Exit( );
-		Channel_Exit( );
-		Log_Exit( );
+		Conn_Exit();
+		Client_Exit();
+		Channel_Exit();
+		Class_Exit();
+		Log_Exit();
 	}
 	}
-	Pidfile_Delete( );
+	Pidfile_Delete();
 
 
 	return 0;
 	return 0;
 } /* main */
 } /* main */
@@ -421,7 +423,7 @@ static void
 Show_Version( void )
 Show_Version( void )
 {
 {
 	puts( NGIRCd_Version );
 	puts( NGIRCd_Version );
-	puts( "Copyright (c)2001-2011 Alexander Barton (<alex@barton.de>) and Contributors." );
+	puts( "Copyright (c)2001-2012 Alexander Barton (<alex@barton.de>) and Contributors." );
 	puts( "Homepage: <http://ngircd.barton.de/>\n" );
 	puts( "Homepage: <http://ngircd.barton.de/>\n" );
 	puts( "This is free software; see the source for copying conditions. There is NO" );
 	puts( "This is free software; see the source for copying conditions. There is NO" );
 	puts( "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." );
 	puts( "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." );
@@ -553,9 +555,10 @@ NGIRCd_getNobodyID(uid_t *uid, gid_t *gid )
 #endif
 #endif
 
 
 	pwd = getpwnam("nobody");
 	pwd = getpwnam("nobody");
-	if (!pwd) return false;
+	if (!pwd)
+		return false;
 
 
-	if ( !pwd->pw_uid || !pwd->pw_gid)
+	if (!pwd->pw_uid || !pwd->pw_gid)
 		return false;
 		return false;
 
 
 	*uid = pwd->pw_uid;
 	*uid = pwd->pw_uid;
@@ -593,19 +596,19 @@ Random_Init(void)
 		return;
 		return;
 	if (Random_Init_Kern("/dev/arandom"))
 	if (Random_Init_Kern("/dev/arandom"))
 		return;
 		return;
-	srand(rand() ^ getpid() ^ time(NULL));
+	srand(rand() ^ (unsigned)getpid() ^ (unsigned)time(NULL));
 }
 }
 
 
 
 
 /**
 /**
  * Initialize ngIRCd daemon.
  * Initialize ngIRCd daemon.
  *
  *
- * @param NGIRCd_NoDaemon	Set to true if ngIRCd should run in the
- *				foreground and not as a daemon.
- * @return			true on success.
+ * @param NGIRCd_NoDaemon Set to true if ngIRCd should run in the
+ *		foreground (and not as a daemon).
+ * @return true on success.
  */
  */
 static bool
 static bool
-NGIRCd_Init( bool NGIRCd_NoDaemon ) 
+NGIRCd_Init(bool NGIRCd_NoDaemon)
 {
 {
 	static bool initialized;
 	static bool initialized;
 	bool chrooted = false;
 	bool chrooted = false;
@@ -621,57 +624,74 @@ NGIRCd_Init( bool NGIRCd_NoDaemon )
 		/* open /dev/null before chroot() */
 		/* open /dev/null before chroot() */
 		fd = open( "/dev/null", O_RDWR);
 		fd = open( "/dev/null", O_RDWR);
 		if (fd < 0)
 		if (fd < 0)
-			Log(LOG_WARNING, "Could not open /dev/null: %s", strerror(errno));
+			Log(LOG_WARNING, "Could not open /dev/null: %s",
+			    strerror(errno));
 	}
 	}
 
 
+	/* SSL initialization */
 	if (!ConnSSL_InitLibrary())
 	if (!ConnSSL_InitLibrary())
 		Log(LOG_WARNING,
 		Log(LOG_WARNING,
 		    "Warning: Error during SSL initialization, continuing ...");
 		    "Warning: Error during SSL initialization, continuing ...");
 
 
-	if( Conf_Chroot[0] ) {
-		if( chdir( Conf_Chroot ) != 0 ) {
-			Log( LOG_ERR, "Can't chdir() in ChrootDir (%s): %s", Conf_Chroot, strerror( errno ));
+	/* Change root */
+	if (Conf_Chroot[0]) {
+		if (chdir(Conf_Chroot) != 0) {
+			Log(LOG_ERR, "Can't chdir() in ChrootDir (%s): %s",
+			    Conf_Chroot, strerror(errno));
 			goto out;
 			goto out;
 		}
 		}
 
 
-		if( chroot( Conf_Chroot ) != 0 ) {
+		if (chroot(Conf_Chroot) != 0) {
 			if (errno != EPERM) {
 			if (errno != EPERM) {
-				Log( LOG_ERR, "Can't change root directory to \"%s\": %s",
-								Conf_Chroot, strerror( errno ));
+				Log(LOG_ERR,
+				    "Can't change root directory to \"%s\": %s",
+				    Conf_Chroot, strerror(errno));
 				goto out;
 				goto out;
 			}
 			}
 		} else {
 		} else {
 			chrooted = true;
 			chrooted = true;
-			Log( LOG_INFO, "Changed root and working directory to \"%s\".", Conf_Chroot );
+			Log(LOG_INFO,
+			    "Changed root and working directory to \"%s\".",
+			    Conf_Chroot);
 		}
 		}
 	}
 	}
 
 
+	/* Check user ID */
 	if (Conf_UID == 0) {
 	if (Conf_UID == 0) {
-		Log(LOG_INFO, "ServerUID must not be 0, using \"nobody\" instead.", Conf_UID);
-
-  		if (! NGIRCd_getNobodyID(&Conf_UID, &Conf_GID)) {
-			Log(LOG_WARNING, "Could not get user/group ID of user \"nobody\": %s",
-					errno ? strerror(errno) : "not found" );
+		pwd = getpwuid(0);
+		Log(LOG_INFO,
+		    "ServerUID must not be %s(0), using \"nobody\" instead.",
+		    pwd ? pwd->pw_name : "?");
+		if (!NGIRCd_getNobodyID(&Conf_UID, &Conf_GID)) {
+			Log(LOG_WARNING,
+			    "Could not get user/group ID of user \"nobody\": %s",
+			    errno ? strerror(errno) : "not found" );
 			goto out;
 			goto out;
 		}
 		}
 	}
 	}
 
 
+	/* Change group ID */
 	if (getgid() != Conf_GID) {
 	if (getgid() != Conf_GID) {
-		/* Change group ID */
 		if (setgid(Conf_GID) != 0) {
 		if (setgid(Conf_GID) != 0) {
 			real_errno = errno;
 			real_errno = errno;
-			Log( LOG_ERR, "Can't change group ID to %u: %s", Conf_GID, strerror( errno ));
+			grp = getgrgid(Conf_GID);
+			Log(LOG_ERR, "Can't change group ID to %s(%u): %s",
+			    grp ? grp->gr_name : "?", Conf_GID,
+			    strerror(errno));
 			if (real_errno != EPERM) 
 			if (real_errno != EPERM) 
 				goto out;
 				goto out;
 		}
 		}
 	}
 	}
 
 
+	/* Change user ID */
 	if (getuid() != Conf_UID) {
 	if (getuid() != Conf_UID) {
-		/* Change user ID */
 		if (setuid(Conf_UID) != 0) {
 		if (setuid(Conf_UID) != 0) {
 			real_errno = errno;
 			real_errno = errno;
-			Log(LOG_ERR, "Can't change user ID to %u: %s", Conf_UID, strerror(errno));
-			if (real_errno != EPERM) 
+			pwd = getpwuid(Conf_UID);
+			Log(LOG_ERR, "Can't change user ID to %s(%u): %s",
+			    pwd ? pwd->pw_name : "?", Conf_UID,
+			    strerror(errno));
+			if (real_errno != EPERM)
 				goto out;
 				goto out;
 		}
 		}
 	}
 	}
@@ -681,26 +701,27 @@ NGIRCd_Init( bool NGIRCd_NoDaemon )
 	/* Normally a child process is forked which isn't any longer
 	/* Normally a child process is forked which isn't any longer
 	 * connected to ther controlling terminal. Use "--nodaemon"
 	 * connected to ther controlling terminal. Use "--nodaemon"
 	 * to disable this "daemon mode" (useful for debugging). */
 	 * to disable this "daemon mode" (useful for debugging). */
-	if ( ! NGIRCd_NoDaemon ) {
-		pid = fork( );
-		if( pid > 0 ) {
+	if (!NGIRCd_NoDaemon) {
+		pid = fork();
+		if (pid > 0) {
 			/* "Old" process: exit. */
 			/* "Old" process: exit. */
-			exit( 0 );
+			exit(0);
 		}
 		}
-		if( pid < 0 ) {
+		if (pid < 0) {
 			/* Error!? */
 			/* Error!? */
-			fprintf( stderr, "%s: Can't fork: %s!\nFatal error, exiting now ...\n",
-								PACKAGE_NAME, strerror( errno ));
-			exit( 1 );
+			fprintf(stderr,
+				"%s: Can't fork: %s!\nFatal error, exiting now ...\n",
+				PACKAGE_NAME, strerror(errno));
+			exit(1);
 		}
 		}
 
 
 		/* New child process */
 		/* New child process */
 #ifndef NeXT
 #ifndef NeXT
-		(void)setsid( );
+		(void)setsid();
 #else
 #else
 		setpgrp(0, getpid());
 		setpgrp(0, getpid());
 #endif
 #endif
-		if (chdir( "/" ) != 0)
+		if (chdir("/") != 0)
 			Log(LOG_ERR, "Can't change directory to '/': %s",
 			Log(LOG_ERR, "Can't change directory to '/': %s",
 				     strerror(errno));
 				     strerror(errno));
 
 
@@ -711,19 +732,19 @@ NGIRCd_Init( bool NGIRCd_NoDaemon )
 	}
 	}
 	pid = getpid();
 	pid = getpid();
 
 
-	Pidfile_Create( pid );
+	Pidfile_Create(pid);
 
 
 	/* Check UID/GID we are running as, can be different from values
 	/* Check UID/GID we are running as, can be different from values
 	 * configured (e. g. if we were already started with a UID>0. */
 	 * configured (e. g. if we were already started with a UID>0. */
 	Conf_UID = getuid();
 	Conf_UID = getuid();
 	Conf_GID = getgid();
 	Conf_GID = getgid();
 
 
-	pwd = getpwuid( Conf_UID );
-	grp = getgrgid( Conf_GID );
+	pwd = getpwuid(Conf_UID);
+	grp = getgrgid(Conf_GID);
 
 
 	Log(LOG_INFO, "Running as user %s(%ld), group %s(%ld), with PID %ld.",
 	Log(LOG_INFO, "Running as user %s(%ld), group %s(%ld), with PID %ld.",
-				pwd ? pwd->pw_name : "unknown", (long)Conf_UID,
-				grp ? grp->gr_name : "unknown", (long)Conf_GID, (long)pid);
+	    pwd ? pwd->pw_name : "unknown", (long)Conf_UID,
+	    grp ? grp->gr_name : "unknown", (long)Conf_GID, (long)pid);
 
 
 	if (chrooted) {
 	if (chrooted) {
 		Log(LOG_INFO, "Running with root directory \"%s\".",
 		Log(LOG_INFO, "Running with root directory \"%s\".",
@@ -732,20 +753,23 @@ NGIRCd_Init( bool NGIRCd_NoDaemon )
 	} else
 	} else
 		Log(LOG_INFO, "Not running with changed root directory.");
 		Log(LOG_INFO, "Not running with changed root directory.");
 
 
-	/* Change working directory to home directory of the user
-	 * we are running as (only when running in daemon mode and not in chroot) */
+	/* Change working directory to home directory of the user we are
+	 * running as (only when running in daemon mode and not in chroot) */
+
+	if (NGIRCd_NoDaemon)
+		return true;
 
 
 	if (pwd) {
 	if (pwd) {
-		if (!NGIRCd_NoDaemon ) {
-			if( chdir( pwd->pw_dir ) == 0 ) 
-				Log( LOG_DEBUG, "Changed working directory to \"%s\" ...", pwd->pw_dir );
-			else 
-				Log( LOG_INFO, "Notice: Can't change working directory to \"%s\": %s",
-								pwd->pw_dir, strerror( errno ));
-		}
-	} else {
-		Log( LOG_ERR, "Can't get user informaton for UID %d!?", Conf_UID );
-	}
+		if (chdir(pwd->pw_dir) == 0)
+			Log(LOG_DEBUG,
+			    "Changed working directory to \"%s\" ...",
+			    pwd->pw_dir);
+		else
+			Log(LOG_INFO,
+			    "Notice: Can't change working directory to \"%s\": %s",
+			    pwd->pw_dir, strerror(errno));
+	} else
+		Log(LOG_ERR, "Can't get user informaton for UID %d!?", Conf_UID);
 
 
 	return true;
 	return true;
  out:
  out:

+ 21 - 2
src/ngircd/numeric.c

@@ -28,6 +28,7 @@
 #include "conn.h"
 #include "conn.h"
 #include "conn-func.h"
 #include "conn-func.h"
 #include "channel.h"
 #include "channel.h"
+#include "class.h"
 #include "irc-write.h"
 #include "irc-write.h"
 #include "lists.h"
 #include "lists.h"
 #include "log.h"
 #include "log.h"
@@ -194,8 +195,10 @@ Announce_User(CLIENT * Client, CLIENT * User)
 #ifdef IRCPLUS
 #ifdef IRCPLUS
 
 
 /**
 /**
- * Synchronize invite and ban lists between servers
- * @param Client New server
+ * Synchronize invite, ban, G- and K-Line lists between servers.
+ *
+ * @param Client New server.
+ * @return CONNECTED or DISCONNECTED.
  */
  */
 static bool
 static bool
 Synchronize_Lists(CLIENT * Client)
 Synchronize_Lists(CLIENT * Client)
@@ -206,6 +209,18 @@ Synchronize_Lists(CLIENT * Client)
 
 
 	assert(Client != NULL);
 	assert(Client != NULL);
 
 
+	/* g-lines */
+	head = Class_GetList(CLASS_GLINE);
+	elem = Lists_GetFirst(head);
+	while (elem) {
+		if (!IRC_WriteStrClient(Client, "GLINE %s %ld :%s",
+					Lists_GetMask(elem),
+					Lists_GetValidity(elem) - time(NULL),
+					Lists_GetReason(elem)))
+			return DISCONNECTED;
+		elem = Lists_GetNext(elem);
+	}
+
 	c = Channel_First();
 	c = Channel_First();
 	while (c) {
 	while (c) {
 		/* ban list */
 		/* ban list */
@@ -369,6 +384,10 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 	}
 	}
 #endif
 #endif
 
 
+	if (!IRC_WriteStrClient(Client, "PING :%s",
+	    Client_ID(Client_ThisServer())))
+		return DISCONNECTED;
+
 	return CONNECTED;
 	return CONNECTED;
 } /* IRC_Num_ENDOFMOTD */
 } /* IRC_Num_ENDOFMOTD */
 
 

+ 17 - 6
src/ngircd/op.c

@@ -58,9 +58,15 @@ Op_NoPrivileges(CLIENT * Client, REQUEST * Req)
 
 
 
 
 /**
 /**
- * Check that the client is an IRC operator allowed to administer this server.
+ * Check that the originator of a request is an IRC operator and allowed
+ * to administer this server.
+ *
+ * @param CLient Client from which the command has been received.
+ * @param Req Request structure.
+ * @return CLIENT structure of the client that initiated the command or
+ *	   NULL if client is not allowed to execute operator commands.
  */
  */
-GLOBAL bool
+GLOBAL CLIENT *
 Op_Check(CLIENT * Client, REQUEST * Req)
 Op_Check(CLIENT * Client, REQUEST * Req)
 {
 {
 	CLIENT *c;
 	CLIENT *c;
@@ -72,15 +78,20 @@ Op_Check(CLIENT * Client, REQUEST * Req)
 		c = Client_Search(Req->prefix);
 		c = Client_Search(Req->prefix);
 	else
 	else
 		c = Client;
 		c = Client;
+
 	if (!c)
 	if (!c)
-		return false;
+		return NULL;
+	if (Client_Type(Client) == CLIENT_SERVER
+	    && Client_Type(c) == CLIENT_SERVER)
+		return c;
 	if (!Client_HasMode(c, 'o'))
 	if (!Client_HasMode(c, 'o'))
-		return false;
+		return NULL;
 	if (!Client_OperByMe(c) && !Conf_AllowRemoteOper)
 	if (!Client_OperByMe(c) && !Conf_AllowRemoteOper)
-		return false;
+		return NULL;
+
 	/* The client is an local IRC operator, or this server is configured
 	/* The client is an local IRC operator, or this server is configured
 	 * to trust remote operators. */
 	 * to trust remote operators. */
-	return true;
+	return c;
 } /* Op_Check */
 } /* Op_Check */
 
 
 
 

+ 2 - 2
src/ngircd/op.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2009 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
  */
  */
 
 
 GLOBAL bool Op_NoPrivileges PARAMS((CLIENT * Client, REQUEST * Req));
 GLOBAL bool Op_NoPrivileges PARAMS((CLIENT * Client, REQUEST * Req));
-GLOBAL bool Op_Check PARAMS((CLIENT * Client, REQUEST * Req));
+GLOBAL CLIENT *Op_Check PARAMS((CLIENT * Client, REQUEST * Req));
 
 
 #endif
 #endif
 
 

+ 1 - 1
src/ngircd/pam.c

@@ -103,7 +103,7 @@ PAM_Authenticate(CLIENT *Client) {
 	if (password)
 	if (password)
 		free(password);
 		free(password);
 	password = strdup(Client_Password(Client));
 	password = strdup(Client_Password(Client));
-	conv.appdata_ptr = password;
+	conv.appdata_ptr = Client_Password(Client);
 
 
 	/* Initialize PAM */
 	/* Initialize PAM */
 	retval = pam_start("ngircd", Client_OrigUser(Client), &conv, &pam);
 	retval = pam_start("ngircd", Client_OrigUser(Client), &conv, &pam);

+ 16 - 6
src/ngircd/parse.c

@@ -63,6 +63,7 @@ static COMMAND My_Commands[] =
 	{ "DIE", IRC_DIE, CLIENT_USER, 0, 0, 0 },
 	{ "DIE", IRC_DIE, CLIENT_USER, 0, 0, 0 },
 	{ "DISCONNECT", IRC_DISCONNECT, CLIENT_USER, 0, 0, 0 },
 	{ "DISCONNECT", IRC_DISCONNECT, CLIENT_USER, 0, 0, 0 },
 	{ "ERROR", IRC_ERROR, 0xFFFF, 0, 0, 0 },
 	{ "ERROR", IRC_ERROR, 0xFFFF, 0, 0, 0 },
+	{ "GLINE", IRC_xLINE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "HELP", IRC_HELP, CLIENT_USER, 0, 0, 0 },
 	{ "HELP", IRC_HELP, CLIENT_USER, 0, 0, 0 },
 	{ "INFO", IRC_INFO, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "INFO", IRC_INFO, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "INVITE", IRC_INVITE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "INVITE", IRC_INVITE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
@@ -70,6 +71,7 @@ static COMMAND My_Commands[] =
 	{ "JOIN", IRC_JOIN, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "JOIN", IRC_JOIN, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "KICK", IRC_KICK, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "KICK", IRC_KICK, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "KILL", IRC_KILL, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "KILL", IRC_KILL, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
+	{ "KLINE", IRC_xLINE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "LINKS", IRC_LINKS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "LINKS", IRC_LINKS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "LIST", IRC_LIST, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "LIST", IRC_LIST, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "LUSERS", IRC_LUSERS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "LUSERS", IRC_LUSERS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
@@ -325,13 +327,21 @@ Validate_Prefix( CONN_ID Idx, REQUEST *Req, bool *Closed )
 	/* check if the client named in the prefix is expected
 	/* check if the client named in the prefix is expected
 	 * to come from that direction */
 	 * to come from that direction */
 	if (Client_NextHop(c) != client) {
 	if (Client_NextHop(c) != client) {
-		Log(LOG_ERR,
-		    "Spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
-		    Req->prefix, Client_Mask(Conn_GetClient(Idx)), Idx,
-		    Req->command);
-		Conn_Close(Idx, NULL, "Spoofed prefix", true);
-		*Closed = true;
+		if (Client_Type(c) != CLIENT_SERVER) {
+			Log(LOG_ERR,
+			    "Spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
+			    Req->prefix, Client_Mask(Conn_GetClient(Idx)), Idx,
+			    Req->command);
+			Conn_Close(Idx, NULL, "Spoofed prefix", true);
+			*Closed = true;
+		} else {
+			Log(LOG_INFO,
+			    "Ignoring spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\").",
+			    Req->prefix, Client_Mask(Conn_GetClient(Idx)), Idx,
+			    Req->command);
+		}
 		return false;
 		return false;
+
 	}
 	}
 
 
 	return true;
 	return true;

+ 20 - 7
src/ngircd/proc.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -79,7 +79,6 @@ Proc_Fork(PROC_STAT *proc, int *pipefds, void (*cbfunc)(int, short), int timeout
 		signal(SIGALRM, Proc_GenericSignalHandler);
 		signal(SIGALRM, Proc_GenericSignalHandler);
 		close(pipefds[0]);
 		close(pipefds[0]);
 		alarm(timeout);
 		alarm(timeout);
-		Conn_CloseAllSockets();
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -138,14 +137,28 @@ Proc_Read(PROC_STAT *proc, void *buffer, size_t buflen)
 			return 0;
 			return 0;
 		Log(LOG_CRIT, "Can't read from child process %ld: %s",
 		Log(LOG_CRIT, "Can't read from child process %ld: %s",
 		    proc->pid, strerror(errno));
 		    proc->pid, strerror(errno));
+		Proc_Close(proc);
 		bytes_read = 0;
 		bytes_read = 0;
+	} else if (bytes_read == 0) {
+		/* EOF: clean up */
+		LogDebug("Child process %ld: EOF reached, closing pipe.",
+		         proc->pid);
+		Proc_Close(proc);
 	}
 	}
-#if DEBUG
-	else if (bytes_read == 0)
-		LogDebug("Can't read from child process %ld: EOF", proc->pid);
-#endif
-	Proc_InitStruct(proc);
 	return (size_t)bytes_read;
 	return (size_t)bytes_read;
 }
 }
 
 
+/**
+ * Close pipe to a forked child process.
+ */
+GLOBAL void
+Proc_Close(PROC_STAT *proc)
+{
+	/* Close socket, if it exists */
+	if (proc->pipe_fd >= 0)
+		io_close(proc->pipe_fd);
+
+	Proc_InitStruct(proc);
+}
+
 /* -eof- */
 /* -eof- */

+ 4 - 1
src/ngircd/proc.h

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -38,6 +38,9 @@ GLOBAL void Proc_GenericSignalHandler PARAMS((int Signal));
 
 
 GLOBAL size_t Proc_Read PARAMS((PROC_STAT *proc, void *buffer, size_t buflen));
 GLOBAL size_t Proc_Read PARAMS((PROC_STAT *proc, void *buffer, size_t buflen));
 
 
+GLOBAL void Proc_Close PARAMS((PROC_STAT *proc));
+
+
 #endif
 #endif
 
 
 /* -eof- */
 /* -eof- */

+ 4 - 2
src/ngircd/resolve.c

@@ -1,6 +1,6 @@
 /*
 /*
  * ngIRCd -- The Next Generation IRC Daemon
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2009 by Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License as published by
@@ -75,7 +75,8 @@ Resolve_Addr(PROC_STAT * s, const ng_ipaddr_t *Addr, int identsock,
 	} else if( pid == 0 ) {
 	} else if( pid == 0 ) {
 		/* Sub process */
 		/* Sub process */
 		Log_Init_Subprocess("Resolver");
 		Log_Init_Subprocess("Resolver");
-		Do_ResolveAddr( Addr, identsock, pipefd[1]);
+		Conn_CloseAllSockets(identsock);
+		Do_ResolveAddr(Addr, identsock, pipefd[1]);
 		Log_Exit_Subprocess("Resolver");
 		Log_Exit_Subprocess("Resolver");
 		exit(0);
 		exit(0);
 	}
 	}
@@ -104,6 +105,7 @@ Resolve_Name( PROC_STAT *s, const char *Host, void (*cbfunc)(int, short))
 	} else if( pid == 0 ) {
 	} else if( pid == 0 ) {
 		/* Sub process */
 		/* Sub process */
 		Log_Init_Subprocess("Resolver");
 		Log_Init_Subprocess("Resolver");
+		Conn_CloseAllSockets(NONE);
 		Do_ResolveName(Host, pipefd[1]);
 		Do_ResolveName(Host, pipefd[1]);
 		Log_Exit_Subprocess("Resolver");
 		Log_Exit_Subprocess("Resolver");
 		exit(0);
 		exit(0);

+ 4 - 0
src/portab/portab.h

@@ -164,6 +164,10 @@ extern char * strtok_r PARAMS((char *str, const char *delim, char **saveptr));
 extern int vsnprintf PARAMS(( char *str, size_t count, const char *fmt, va_list args ));
 extern int vsnprintf PARAMS(( char *str, size_t count, const char *fmt, va_list args ));
 #endif
 #endif
 
 
+#ifndef HAVE_GAI_STRERROR
+#define gai_strerror(r) "unknown error"
+#endif
+
 #ifndef PACKAGE_NAME
 #ifndef PACKAGE_NAME
 #define PACKAGE_NAME PACKAGE
 #define PACKAGE_NAME PACKAGE
 #endif
 #endif

+ 1 - 1
src/testsuite/getpid.sh

@@ -16,7 +16,7 @@ elif [ $UNAME = "GNU" ]; then
 elif [ $UNAME = "SunOS" ]; then
 elif [ $UNAME = "SunOS" ]; then
   PS_FLAGS="-af"; PS_PIDCOL=2; HEAD_FLAGS="-n 1"
   PS_FLAGS="-af"; PS_PIDCOL=2; HEAD_FLAGS="-n 1"
 else
 else
-  PS_FLAGS="-f"; PS_PIDCOL="2"; HEAD_FLAGS="-n 1"
+  PS_FLAGS="-af"; PS_PIDCOL="2"; HEAD_FLAGS="-n 1"
   ps $PS_FLAGS > /dev/null 2>&1
   ps $PS_FLAGS > /dev/null 2>&1
   if [ $? -ne 0 ]; then PS_FLAGS="a"; PS_PIDCOL="1"; fi
   if [ $? -ne 0 ]; then PS_FLAGS="a"; PS_PIDCOL="1"; fi
 fi
 fi

+ 3 - 3
src/testsuite/message-test.e

@@ -35,7 +35,7 @@ expect {
 	"@* PRIVMSG nick :test\r*@* PRIVMSG nick :test"
 	"@* PRIVMSG nick :test\r*@* PRIVMSG nick :test"
 }
 }
 
 
-send "privmsg nick,#testChannel,nick :test\r"
+send "privmsg Nick,#testChannel,nick :test\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
 	"@* PRIVMSG nick :test\r*401*@* PRIVMSG nick :test"
 	"@* PRIVMSG nick :test\r*401*@* PRIVMSG nick :test"
@@ -47,7 +47,7 @@ expect {
 	"401"
 	"401"
 }
 }
 
 
-send "privmsg ~user@ngircd.test.server :test\r"
+send "privmsg ~UsEr@ngIRCd.Test.Server :test\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
 	"@* PRIVMSG nick :test"
 	"@* PRIVMSG nick :test"
@@ -65,7 +65,7 @@ expect {
 #	"@* PRIVMSG nick :test"
 #	"@* PRIVMSG nick :test"
 #}
 #}
 #
 #
-#send "privmsg nick!~user@localhost :test\r"
+#send "privmsg Nick!~User@LocalHost :test\r"
 #expect {
 #expect {
 #	timeout { exit 1 }
 #	timeout { exit 1 }
 #	"@* PRIVMSG nick :test"
 #	"@* PRIVMSG nick :test"

+ 1 - 0
src/testsuite/ngircd-test1.conf

@@ -4,6 +4,7 @@
 [Global]
 [Global]
 	Name = ngircd.test.server
 	Name = ngircd.test.server
 	Info = ngIRCd Test-Server 1
 	Info = ngIRCd Test-Server 1
+	Listen = 127.0.0.1
 	Ports = 6789
 	Ports = 6789
 	MotdFile = ngircd-test1.motd
 	MotdFile = ngircd-test1.motd
 	AdminEMail = admin@irc.server
 	AdminEMail = admin@irc.server

+ 2 - 1
src/testsuite/ngircd-test2.conf

@@ -4,6 +4,7 @@
 [Global]
 [Global]
 	Name = ngircd.test.server2
 	Name = ngircd.test.server2
 	Info = ngIRCd Test-Server 2
 	Info = ngIRCd Test-Server 2
+	Listen = 127.0.0.1
 	Ports = 6790
 	Ports = 6790
 	MotdFile = ngircd-test2.motd
 	MotdFile = ngircd-test2.motd
 	AdminEMail = admin@irc.server2
 	AdminEMail = admin@irc.server2
@@ -23,7 +24,7 @@
 
 
 [Server]
 [Server]
 	Name = ngircd.test.server
 	Name = ngircd.test.server
-	Host = localhost
+	Host = 127.0.0.1
 	Port = 6789
 	Port = 6789
 	MyPassword = pwd2
 	MyPassword = pwd2
 	PeerPassword = pwd1
 	PeerPassword = pwd1

+ 48 - 18
src/testsuite/who-test.e

@@ -14,19 +14,13 @@ expect {
 send "who\r"
 send "who\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick \* * ngircd.test.server nick H :0 Real Name"
-}
-
-send "join #channel\r"
-expect {
-	timeout { exit 1 }
-	"@* JOIN :#channel"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick H :0 Real Name"
 }
 }
 
 
 send "who 0\r"
 send "who 0\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick #channel * ngircd.test.server nick H@ :0 Real Name"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick H :0 Real Name"
 }
 }
 
 
 send "away :testing\r"
 send "away :testing\r"
@@ -38,7 +32,19 @@ expect {
 send "who *\r"
 send "who *\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick #channel * ngircd.test.server nick G@ :0 Real Name"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick G :0 Real Name"
+}
+
+send "join #channel\r"
+expect {
+	timeout { exit 1 }
+	"@* JOIN :#channel"
+}
+
+send "who #channel\r"
+expect {
+	timeout { exit 1 }
+	":ngircd.test.server 352 nick #channel * * ngircd.test.server nick G@ :0 Real Name"
 }
 }
 
 
 send "mode #channel +v nick\r"
 send "mode #channel +v nick\r"
@@ -47,10 +53,16 @@ expect {
 	"@* MODE #channel +v nick\r"
 	"@* MODE #channel +v nick\r"
 }
 }
 
 
+send "who #channel\r"
+expect {
+	timeout { exit 1 }
+	":ngircd.test.server 352 nick #channel * * ngircd.test.server nick G@ :0 Real Name"
+}
+
 send "who localhos*\r"
 send "who localhos*\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick #channel * ngircd.test.server nick G@ :0 Real Name"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick G :0 Real Name"
 }
 }
 
 
 send "mode #channel -o nick\r"
 send "mode #channel -o nick\r"
@@ -59,10 +71,16 @@ expect {
 	"@* MODE #channel -o nick\r"
 	"@* MODE #channel -o nick\r"
 }
 }
 
 
+send "who #channel\r"
+expect {
+	timeout { exit 1 }
+	":ngircd.test.server 352 nick #channel * * ngircd.test.server nick G+ :0 Real Name"
+}
+
 send "who ngircd.test.server\r"
 send "who ngircd.test.server\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick #channel * ngircd.test.server nick G+ :0 Real Name"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick G :0 Real Name"
 }
 }
 
 
 send "part #channel\r"
 send "part #channel\r"
@@ -74,7 +92,7 @@ expect {
 send "who Real?Name\r"
 send "who Real?Name\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick \* * ngircd.test.server nick G :0 Real Name"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick G :0 Real Name"
 }
 }
 
 
 send "oper TestOp 123\r"
 send "oper TestOp 123\r"
@@ -90,7 +108,7 @@ expect {
 send "who 0 o\r"
 send "who 0 o\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick \* * ngircd.test.server nick G* :0 Real Name"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick G* :0 Real Name"
 }
 }
 
 
 send "away\r"
 send "away\r"
@@ -102,7 +120,7 @@ expect {
 send "who ??cal*ho*\r"
 send "who ??cal*ho*\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick \* * ngircd.test.server nick H* :0 Real Name"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick H* :0 Real Name"
 }
 }
 
 
 send "join #opers\r"
 send "join #opers\r"
@@ -114,7 +132,13 @@ expect {
 send "who #opers\r"
 send "who #opers\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick #opers * ngircd.test.server nick H*@ :0 Real Name"
+	":ngircd.test.server 352 nick #opers * * ngircd.test.server nick H*@ :0 Real Name"
+}
+
+send "who Re*me\r"
+expect {
+	timeout { exit 1 }
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick H* :0 Real Name"
 }
 }
 
 
 send "mode #opers -o nick\r"
 send "mode #opers -o nick\r"
@@ -123,10 +147,16 @@ expect {
 	"@* MODE #opers -o nick\r"
 	"@* MODE #opers -o nick\r"
 }
 }
 
 
+send "who #opers\r"
+expect {
+	timeout { exit 1 }
+	":ngircd.test.server 352 nick #opers * * ngircd.test.server nick H* :0 Real Name"
+}
+
 send "who *.server\r"
 send "who *.server\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick #opers * ngircd.test.server nick H* :0 Real Name"
+	":ngircd.test.server 352 nick \* * * ngircd.test.server nick H* :0 Real Name"
 }
 }
 
 
 send "mode #opers +v nick\r"
 send "mode #opers +v nick\r"
@@ -135,10 +165,10 @@ expect {
 	"@* MODE #opers +v nick\r"
 	"@* MODE #opers +v nick\r"
 }
 }
 
 
-send "who Real*me\r"
+send "who #opers\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	":ngircd.test.server 352 nick #opers * ngircd.test.server nick H*+ :0 Real Name"
+	":ngircd.test.server 352 nick #opers * * ngircd.test.server nick H*+ :0 Real Name"
 }
 }
 
 
 send "mode #opers +s\r"
 send "mode #opers +s\r"

+ 5 - 5
src/testsuite/whois-test.e

@@ -17,31 +17,31 @@ expect {
 send "whois nick\r"
 send "whois nick\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	"311 nick nick ~user localhost \* :Real Name\r"
+	"311 nick nick ~user localhost* \* :Real Name\r"
 }
 }
 
 
 send "whois *\r"
 send "whois *\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	"311 nick nick ~user localhost \* :Real Name\r"
+	"311 nick nick ~user localhost* \* :Real Name\r"
 }
 }
 
 
 send "whois n*\r"
 send "whois n*\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	"311 nick nick ~user localhost \* :Real Name\r"
+	"311 nick nick ~user localhost* \* :Real Name\r"
 }
 }
 
 
 send "whois ?ick\r"
 send "whois ?ick\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	"311 nick nick ~user localhost \* :Real Name\r"
+	"311 nick nick ~user localhost* \* :Real Name\r"
 }
 }
 
 
 send "whois ????,n?*k\r"
 send "whois ????,n?*k\r"
 expect {
 expect {
 	timeout { exit 1 }
 	timeout { exit 1 }
-	"311 nick nick ~user localhost \* :Real Name\r"
+	"311 nick nick ~user localhost* \* :Real Name\r"
 }
 }
 
 
 send "quit\r"
 send "quit\r"