Browse Source

Import upstream version 21

Alexander Barton 10 years ago
parent
commit
95204d08e8
100 changed files with 5543 additions and 3215 deletions
  1. 28 16
      AUTHORS
  2. 157 2
      ChangeLog
  3. 12 1
      INSTALL
  4. 11 7
      Makefile.am
  5. 11 7
      Makefile.in
  6. 85 1
      NEWS
  7. 22 7
      autogen.sh
  8. 82 16
      configure
  9. 16 10
      configure.ac
  10. 16 10
      configure.ng
  11. 31 0
      contrib/Debian/changelog
  12. 0 2
      contrib/Debian/ngircd.default
  13. 4 3
      contrib/Debian/ngircd.init
  14. 15 13
      contrib/Debian/rules
  15. 12 7
      contrib/MacOSX/config.h
  16. 1 1
      contrib/MacOSX/ngIRCd.pmdoc/01ngircd-contents.xml
  17. 1 1
      contrib/MacOSX/ngIRCd.pmdoc/01ngircd.xml
  18. 2 2
      contrib/MacOSX/ngIRCd.pmdoc/index.xml
  19. 67 129
      contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj
  20. 2 1
      contrib/Makefile.am
  21. 2 1
      contrib/Makefile.in
  22. 11 2
      contrib/README
  23. 2 2
      contrib/ngircd.service
  24. 11 0
      contrib/ngircd.socket
  25. 1 1
      contrib/ngircd.spec
  26. 41 18
      contrib/platformtest.sh
  27. 991 0
      doc/Commands.txt
  28. 3 1
      doc/Makefile.am
  29. 3 1
      doc/Makefile.in
  30. 12 3
      doc/PAM.txt
  31. 81 70
      doc/Platforms.txt
  32. 3 1
      doc/Protocol.txt
  33. 3 5
      doc/Services.txt
  34. 36 4
      doc/sample-ngircd.conf.tmpl
  35. 17 88
      doc/src/Doxyfile
  36. 1 1
      doc/src/footer.inc.html
  37. 8 6
      man/ngircd.8.tmpl
  38. 38 9
      man/ngircd.conf.5.tmpl
  39. 12 0
      src/config.h.in
  40. 8 0
      src/ipaddr/ng_ipaddr.h
  41. 1 0
      src/ngircd/Makefile.am
  42. 1 0
      src/ngircd/Makefile.in
  43. 1 0
      src/ngircd/Makefile.ng
  44. 71 68
      src/ngircd/channel.c
  45. 3 1
      src/ngircd/channel.h
  46. 29 30
      src/ngircd/class.c
  47. 4 3
      src/ngircd/class.h
  48. 288 100
      src/ngircd/client.c
  49. 25 17
      src/ngircd/client.h
  50. 1 0
      src/ngircd/conf-ssl.h
  51. 441 225
      src/ngircd/conf.c
  52. 13 3
      src/ngircd/conf.h
  53. 1 1
      src/ngircd/conn-encoding.c
  54. 0 8
      src/ngircd/conn-func.c
  55. 235 64
      src/ngircd/conn-ssl.c
  56. 3 0
      src/ngircd/conn-ssl.h
  57. 229 62
      src/ngircd/conn.c
  58. 4 5
      src/ngircd/conn.h
  59. 15 4
      src/ngircd/defines.h
  60. 1 1
      src/ngircd/hash.c
  61. 1 1
      src/ngircd/io.c
  62. 113 125
      src/ngircd/irc-cap.c
  63. 136 189
      src/ngircd/irc-channel.c
  64. 2 6
      src/ngircd/irc-encoding.c
  65. 730 823
      src/ngircd/irc-info.c
  66. 126 210
      src/ngircd/irc-login.c
  67. 113 0
      src/ngircd/irc-macros.h
  68. 9 9
      src/ngircd/irc-metadata.c
  69. 113 128
      src/ngircd/irc-mode.c
  70. 48 46
      src/ngircd/irc-op.c
  71. 56 80
      src/ngircd/irc-oper.c
  72. 74 43
      src/ngircd/irc-server.c
  73. 193 84
      src/ngircd/irc-write.c
  74. 6 4
      src/ngircd/irc-write.h
  75. 290 154
      src/ngircd/irc.c
  76. 4 1
      src/ngircd/irc.h
  77. 30 39
      src/ngircd/lists.c
  78. 3 2
      src/ngircd/lists.h
  79. 5 4
      src/ngircd/log.c
  80. 27 5
      src/ngircd/login.c
  81. 0 1
      src/ngircd/match.c
  82. 4 2
      src/ngircd/messages.h
  83. 38 20
      src/ngircd/ngircd.c
  84. 13 79
      src/ngircd/numeric.c
  85. 4 4
      src/ngircd/op.c
  86. 1 1
      src/ngircd/pam.c
  87. 1 1
      src/ngircd/pam.h
  88. 99 89
      src/ngircd/parse.c
  89. 2 0
      src/ngircd/parse.h
  90. 9 0
      src/ngircd/proc.c
  91. 12 16
      src/ngircd/sighandlers.c
  92. 1 1
      src/portab/Makefile.am
  93. 9 5
      src/portab/Makefile.in
  94. 1 1
      src/portab/Makefile.ng
  95. 4 0
      src/portab/portab.h
  96. 37 0
      src/portab/strndup.c
  97. 1 0
      src/testsuite/README
  98. 2 1
      src/testsuite/getpid.sh
  99. 1 0
      src/testsuite/ngircd-test1.conf
  100. 0 0
      src/testsuite/ngircd-test2.conf

+ 28 - 16
AUTHORS

@@ -18,29 +18,41 @@ Don't mail the people listed here directly, if possible!
 
 Main Authors
 ~~~~~~~~~~~~
-Alexander Barton, <alex@barton.de> (alex)
-Florian Westphal, <fw@strlen.de>
+Alexander Barton <alex@barton.de>
+Florian Westphal <fw@strlen.de>
 
 
 Contributors
 ~~~~~~~~~~~~
-Ali Shemiran, <ashemira@ucsd.edu>
-Ask Bjørn Hansen, <ask@develooper.com>
-Benjamin Pineau, <ben@zouh.org>
-Brandon Beresini, <beresini@google.com>
-Bryan Caldwell, <bcaldwel@ucsd.edu>
-Dana Dahlstrom, <dana+ngIRCd@cs.ucsd.edu>
-Eric Grunow, <egrunow@ucsd.edu>
-Goetz Hoffart, <goetz@hoffart.de>
-Ilja Osthoff, <i.osthoff@gmx.net>
-Jari Aalto, <jari.aalto@cante.net>
-Rolf Eike Beer, <eike@sf-mail.de>
-Scott Perry, <scperry@ucsd.edu>
-Sean Reifschneider, <jafo-rpms@tummy.com>
+Ali Shemiran <ashemira@ucsd.edu>
+Ask Bjørn Hansen <ask@develooper.com>
+Benjamin Pineau <ben@zouh.org>
+Brandon Beresini <beresini@google.com>
+Brett Smith <brett@w3.org>
+Brian Collins <bricollins@gmail.com>
+Bryan Caldwell <bcaldwel@ucsd.edu>
+Christoph Biedl <ngircd.anoy@manchmal.in-ulm.de>
+DNS <dns@rbose.org>
+Dana Dahlstrom <dana+ngIRCd@cs.ucsd.edu>
+David Kingston <deathking1337@aim.com>
+Eric Grunow <egrunow@ucsd.edu>
+Federico G. Schwindt <fgsch@lodoss.net>
+Gabor Adam Toth <tg@tgbit.net>
+Goetz Hoffart <goetz@hoffart.de>
+Ilja Osthoff <i.osthoff@gmx.net>
+Jari Aalto <jari.aalto@cante.net>
+Neale Pickett <neale@woozle.org>
+Rolf Eike Beer <eike@sf-mail.de>
+Scott Perry <scperry@ucsd.edu>
+Sean Reifschneider <jafo-rpms@tummy.com>
+Sebastian Köhler <sebkoehler@whoami.org.uk>
+Tassilo Schweyer <dev@welterde.de>
+William Pitcock <nenolod@dereferenced.org>
+xor <xorboy@gmail.com>
 
 
 Code snippets
 ~~~~~~~~~~~~~
 J. Kercheval: pattern matching functions
-Patrick Powell, <papowell@astart.com>: snprintf()-function
+Patrick Powell <papowell@astart.com>: snprintf()-function
 Andrew Tridgell & Martin Pool: strl{cpy|cat}()-functions

+ 157 - 2
ChangeLog

@@ -9,6 +9,161 @@
                                -- ChangeLog --
 
 
+ngIRCd 21 (2013-10-30)
+
+  - ./contrib/Debian/ngircd.init: Make sure no stale PID file is left over
+    when (re-)starting ngIRCd.
+  - Change ./contrib/platformtest.sh and update ./doc/Platforms.txt to
+    allow usernames up to 8 characters.
+  - Call arc4random_stir() in forked subprocesses, when available. This
+    is required by FreeBSD <10 and current NetBSD at least to correctly
+    initialize the "arc4" random number generator on these platforms.
+  - Update our own Debian package configuration and fix the default path
+    of the "HelpFile" of the "full" package variants.
+
+  ngIRCd 21~rc2 (2013-10-20)
+  - Report the correct configuration file name on configuration errors,
+    support longer configuration lines, and warn when lines are truncated.
+  - Use arc4random() function to generate "random" numbers, when available.
+  - platformtest.sh: Detect clang compiler, and clean up GIT source tree
+    before building (when possible).
+  - Update (date of) manual pages.
+  - Update "Upgrade Information" in INSTALL file, add more systems to
+    doc/Platforms.txt, and fix spelling in NEWS and ChangeLog files =:)
+  - Fix remaining compiler warnings on OpenBSD.
+
+  ngIRCd 21~rc1 (2013-10-05)
+  - Actually KILL clients on GLINE/KLINE. (Closes bug #156)
+  - Adjust log messages for invalid and spoofed prefixes, which cleans up
+    logging of commands related to already KILL'ed clients. And don't
+    forward KILL commands for (already) unknown clients any more to prevent
+    unnecessary duplicates.
+  - Add support to show all user links using the "STATS L" (uppercase)
+    command (restricted to IRC Operators).
+  - Fixed blocking of server reconnects in some error configurations.
+  - Don't ignore SSL-related errors during startup any more: abort startup
+    when SSL is requested by the configuration but can't be initialized and
+    don't continue only listening on plain text communication ports.
+    (Closes bug #163)
+  - Implement configurable SSL cipher list selection for GnuTLS and OpenSSL
+    using the new configuration option "CipherList". In addition, this
+    changes the defaults to more secure values: "HIGH:!aNULL:@STRENGTH" for
+    OpenSSL, and "SECURE128" for GnuTLS.
+  - Fix "TRACE": Correctly return ERR_NEEDMOREPARAMS(461) (which basically
+    is "syntax error") when there are too many parameters.
+  - Clean up lots of permission and parameter checks in functions handling
+    IRC commands; and more consistently add penalty times on errors.
+  - Fix error numeric of WHOIS when no nick name has been provided:
+    as per RFC it should be ERR_NONICKNAMEGIVEN(431).
+  - Only log "IDENT ... no result" messages when an IDENT looked took place
+    and didn't return any data, not when IDENT has been disabled.
+  - Show connection flag "s" (SSL) in RPL_TRACE{LINK|SERVER} messages: now
+    you can check if a server-to-server link is SSL-encrypted or not using
+    the IRC "TRACE" command.
+  - Correctly discard supplementary groups on server startup.
+  - Save client IP address text for "WebIRC" users and correctly display
+    it on WHOIS, for example. (Closes bug #159)
+  - Implement the new configuration option "DefaultUserModes" which lists
+    user modes that become automatically set on new local clients right
+    after login. Please note that only modes can be set that the client
+    could set on itself, so you can't set "a" (away) or "o" (IRC Op),
+    for example! User modes "i" (invisible) or "x" (cloaked) etc. are
+    "interesting", though. (Closes bug #160)
+  - Add support for the new METADATA "account" property, which allows
+    services to automatically identify users after netsplits and across
+    service restarts.
+  - Enforce "penalty times" on error conditions more consistently and in
+    more places. Now most error codes sent back from the IRC server to the
+    client should result in a 2 second "penalty".
+  - Implement a new configuration option "AllowedChannelTypes" that lists
+    all allowed channel types (channel prefixes) for newly created channels
+    on the local server. By default, all supported channel types are allowed.
+    If set to the empty string, local clients can't create new channels at
+    all, which equals the old "PredefChannelsOnly = yes" setting.
+    This change deprecates the "PredefChannelsOnly" variable, too, but it is
+    still supported and translated to the appropriate "AllowedChannelTypes"
+    setting. When the old "PredefChannelsOnly" variable is processed, a
+    warning message is logged. (Closes bug #152)
+  - Add support for "client certificate fingerprinting". When a client
+    passes an SSL certificate to the server, the "fingerprint" will be
+    forwarded in the network which enables IRC services to identify the
+    user using this certificate and not using passwords.
+  - IRC Operator names, as defined in ngircd.conf, are logged now when
+    handling successful OPER commands.
+  - Some error conditions while handling IRC commands, like "permission
+    denied" or "need more parameters", result in more penalty times.
+  - The numeric replies of some commands became split too early which
+    resulted in more numeric reply lines than necessary.
+  - Implement a new configuration option "IncludeDir" in the "[Options]"
+    section that can be used to specify a directory which can contain
+    further configuration files and configuration file snippets matching
+    the pattern "*.conf". These files are read in after the main server
+    configuration file ("ngircd.conf" by default) has been read in and
+    parsed.  The default is "$SYSCONFDIR/ngircd.conf.d", so that it is
+    possible to adjust the configuration only by placing additional files
+    into this directory. (Closes bug #157)
+  - Fix use-after-free in the Lists_CheckReason() function, which is used
+    to check if a client is a member of a particular ban/invite/... list.
+  - Xcode: fix detection of host OS, vendor, and CPU type, and update
+    project settings for Xcode 5.
+  - OS X PackageMaker: use relative path names in project files and package
+    with correct file permissions (requires root privileges on "make").
+  - Add Travis-CI configuration file (".travis.yml") to project.
+  - Look for possible cloaked Masks in Lists. Users with +x user mode can
+    be banned with their cloaked hostname now.
+  - Don't read SSL client data before DNS resolver is finished which could
+    have resulted in discarding the resolved client hostname and IDENT
+    reply afterwards, because in some situations (timing dependent) the
+    NICK and USER commands could have already been read in from the client,
+    stored in the buffer, and been processed.
+    Thanks to Julian Brost for reporting the issue and testing, and to
+    Federico G. Schwindt <fgsch@lodoss.net> for helping to debug it!
+  - Increase password length limit to 64 characters. (Closes bug #154)
+  - doc/Services.txt: Update Anope status and URL.
+  - Clean up Xcode project file, remove outdated files, add missing ones.
+  - Update Doxygen configuration file.
+  - configure: search for iconv_open as well as libiconv_open, because
+    on some installations iconv_open() is actually libiconv_open().
+    iconv_open() is the glibc version while libiconv_open() is the
+    libiconv version, now both variants are supported. (Closes bug #151)
+  - ngIRCd now accepts user names including "@" characters, saves the
+    unmodified name for authentication but stores only the part in front
+    of the "@" character as "IRC user name". And the latter is how
+    ircd2.11, Bahamut, and irc-seven behave as well. (Closes bug #155)
+  - Lots of IRC "information functions" like ADMIN, INFO, ... now accept
+    server masks and names of connected users (in addition to server names)
+    for specifying the target server of the command. (Closes bug #153)
+  - Implement a new configuration option "IdleTimeout" in the "[Limits]"
+    section of the configuration file which can be used to set a timeout
+    in seconds after which the whole daemon will shutdown when no more
+    connections are left active after handling at least one client.
+    The default is 0, "never".
+    This can be useful for testing or when ngIRCd is started using "socket
+    activation" with systemd(8), for example.
+  - Implement support for systemd(8) "socket activation".
+  - contrib/README: add description for more files.
+  - Enable WHOIS to display information about IRC Services using the new
+    numeric 310(RPL_WHOISSERVICE) This numeric is used for this purpose by
+    InspIRCd, for example -- but as usual, other numerics are in use, too,
+    like 613 in UltimateIRCd ...
+    Please note that neither the Operator (+o) not the "bot status" (+B)
+    of an IRC service is displayed in the output.
+  - Exit message: use singular & plural :-)
+  - autogen.sh: Check for autoconf/automake wrapper scripts
+  - Add missing punctuation marks in log messages, adjust some severity
+    levels, and make SSL-related messages more readable.
+  - AUTHORS file: Update list of contributors.
+  - Update systemd(8) example configuration files in ./contrib/ directory:
+    the "ngircd.service" file now uses the "forking" service type which
+    enhances the log messages shown by "systemctl status ngircd.service",
+    and the new "ngircd.socket" file configures a systemd socket that
+    configures a socket for ngIRCd and launches the daemon on demand.
+  - Enhance help system and the HELP command: now a "help text file" can be
+    set using the new configuration option "HelpFile" ("global" section),
+    which is read in and parsed on server startup and configuration reload,
+    and then is used to output individual help texts to specific topics.
+    Please see the file ./doc/Commands.txt for details.
+
 ngIRCd 20.3 (2013-08-23)
 
   - Security: Fix a denial of service bug (server crash) which could happen
@@ -43,7 +198,7 @@ ngIRCd 20.2 (2013-02-15)
   - autogen.sh: Enforce serial test harness on GNU automake >=1.13. The
     new parallel test harness which is enabled by default starting with
     automake 1.13 isn't compatible with our test suite.
-    And don't use "egrep -o", insetead use "sed", because it isn't portable
+    And don't use "egrep -o", instead use "sed", because it isn't portable
     and not available on OpenBSD, for example.
 
 ngIRCd 20.1 (2013-01-02)
@@ -64,7 +219,7 @@ ngIRCd 20 (2012-12-17)
 
   - Allow user names ("INDENT") up to 20 characters when ngIRCd has not
     been configured for "strict RFC mode". This is useful if you are using
-    external (PAM) authenticaion mechanisms that require longer user names.
+    external (PAM) authentication mechanisms that require longer user names.
     Patch suggested by Brett Smith <brett@w3.org>, see
     <http://arthur.barton.de/pipermail/ngircd-ml/2012-October/000579.html>.
 

+ 12 - 1
INSTALL

@@ -12,11 +12,22 @@
 I. Upgrade Information
 ~~~~~~~~~~~~~~~~~~~~~~
 
+Differences to version 20.x
+
+- Starting with ngIRCd 21, the ciphers used by SSL are configurable and
+  default to HIGH:!aNULL:@STRENGTH (OpenSSL) or SECURE128 (GnuTLS).
+  Previous version were using the OpenSSL or GnuTLS defaults, DEFAULT
+  and NORMAL respectively.
+
+- When adding GLINE's or KLINE's to ngIRCd 21 (or newer), all clients matching
+  the new mask will be KILL'ed. This was not the case with earlier versions
+  that only added the mask but didn't kill already connected users.
+
 Differences to version 19.x
 
 - Starting with ngIRCd 20, users can "cloak" their hostname only when the
   configuration variable "CloakHostModeX" (introduced in 19.2) is set.
-  Otherwise, only IRC opertators, other servers, and services are allowed to
+  Otherwise, only IRC operators, other servers, and services are allowed to
   set mode +x. This prevents regular users from changing their hostmask to
   the name of the IRC server itself, which confused quite a few people ;-)
 

+ 11 - 7
Makefile.am

@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -15,9 +15,8 @@ SUBDIRS = doc src man contrib
 
 EXTRA_DIST = autogen.sh configure.ng .mailmap
 
-clean-local:
+clean-local: osxpkg-clean
 	rm -f build-stamp*
-	rm -rf ngircd.dest
 
 maintainer-clean-local:
 	rm -rf autom4te.cache
@@ -62,14 +61,18 @@ deb:
 	dpkg-buildpackage -rfakeroot -i
 
 osxpkg: have-packagemaker osxpkg-dest
-	cd contrib/MacOSX && packagemaker \
+	cd contrib/MacOSX && packagemaker --no-recommend \
 	 --doc ngIRCd.pmdoc \
 	 --out ../../$(distdir).mpkg
 	rm -f $(distdir).mpkg.zip
 	zip -ro9 $(distdir).mpkg.zip $(distdir).mpkg
+	make osxpkg-clean
+
+osxpkg-clean:
+	[ ! -e ngircd.dest ] || sudo -n rm -rf ngircd.dest
 	rm -rf ngircd.dest $(distdir).mpkg
 
-osxpkg-dest: have-xcodebuild clean
+osxpkg-dest: have-xcodebuild osxpkg-clean clean
 	./configure --prefix=/opt/ngircd
 	make xcode
 	make -C contrib/MacOSX de.barton.ngircd.plist
@@ -82,8 +85,9 @@ osxpkg-dest: have-xcodebuild clean
 	rm ngircd.dest/opt/ngircd/etc/ngircd.conf
 	echo "Have a nice day IRCing!" >ngircd.dest/opt/ngircd/etc/ngircd.motd
 	chmod -R a-s,og-w,a+rX ngircd.dest
+	sudo chown -R root:wheel ngircd.dest
 
-.PHONY: deb have-packagemaker have-xcodebuild lint osxpkg osxpkg-dest rpm \
-	srcdoc testsuite xcode xcode-clean
+.PHONY: deb have-packagemaker have-xcodebuild lint osxpkg osxpkg-clean \
+	osxpkg-dest rpm srcdoc testsuite xcode xcode-clean
 
 # -eof-

+ 11 - 7
Makefile.in

@@ -17,7 +17,7 @@
 
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -687,9 +687,8 @@ uninstall-am:
 	uninstall uninstall-am
 
 
-clean-local:
+clean-local: osxpkg-clean
 	rm -f build-stamp*
-	rm -rf ngircd.dest
 
 maintainer-clean-local:
 	rm -rf autom4te.cache
@@ -734,14 +733,18 @@ deb:
 	dpkg-buildpackage -rfakeroot -i
 
 osxpkg: have-packagemaker osxpkg-dest
-	cd contrib/MacOSX && packagemaker \
+	cd contrib/MacOSX && packagemaker --no-recommend \
 	 --doc ngIRCd.pmdoc \
 	 --out ../../$(distdir).mpkg
 	rm -f $(distdir).mpkg.zip
 	zip -ro9 $(distdir).mpkg.zip $(distdir).mpkg
+	make osxpkg-clean
+
+osxpkg-clean:
+	[ ! -e ngircd.dest ] || sudo -n rm -rf ngircd.dest
 	rm -rf ngircd.dest $(distdir).mpkg
 
-osxpkg-dest: have-xcodebuild clean
+osxpkg-dest: have-xcodebuild osxpkg-clean clean
 	./configure --prefix=/opt/ngircd
 	make xcode
 	make -C contrib/MacOSX de.barton.ngircd.plist
@@ -754,9 +757,10 @@ osxpkg-dest: have-xcodebuild clean
 	rm ngircd.dest/opt/ngircd/etc/ngircd.conf
 	echo "Have a nice day IRCing!" >ngircd.dest/opt/ngircd/etc/ngircd.motd
 	chmod -R a-s,og-w,a+rX ngircd.dest
+	sudo chown -R root:wheel ngircd.dest
 
-.PHONY: deb have-packagemaker have-xcodebuild lint osxpkg osxpkg-dest rpm \
-	srcdoc testsuite xcode xcode-clean
+.PHONY: deb have-packagemaker have-xcodebuild lint osxpkg osxpkg-clean \
+	osxpkg-dest rpm srcdoc testsuite xcode xcode-clean
 
 # -eof-
 

+ 85 - 1
NEWS

@@ -9,6 +9,90 @@
                                   -- NEWS --
 
 
+ngIRCd 21 (2013-10-30)
+
+  - Call arc4random_stir() in forked subprocesses, when available. This
+    is required by FreeBSD <10 and current NetBSD at least to correctly
+    initialize the "arc4" random number generator on these platforms.
+
+  ngIRCd 21~rc2 (2013-10-20)
+  - Report the correct configuration file name on configuration errors,
+    support longer configuration lines, and warn when lines are truncated.
+
+  ngIRCd 21~rc1 (2013-10-05)
+  - Actually KILL clients on GLINE/KLINE. (Closes bug #156)
+  - Add support to show all user links using the "STATS L" (uppercase)
+    command (restricted to IRC Operators).
+  - Implement configurable SSL cipher list selection for GnuTLS and OpenSSL
+    using the new configuration option "CipherList". In addition, this
+    changes the defaults to more secure values: "HIGH:!aNULL:@STRENGTH" for
+    OpenSSL, and "SECURE128" for GnuTLS.
+  - Show connection flag "s" (SSL) in RPL_TRACE{LINK|SERVER} messages: now
+    you can check if a server-to-server link is SSL-encrypted or not using
+    the IRC "TRACE" command.
+  - Implement the new configuration option "DefaultUserModes" which lists
+    user modes that become automatically set on new local clients right
+    after login. Please note that only modes can be set that the client
+    could set on itself, so you can't set "a" (away) or "o" (IRC Op),
+    for example! User modes "i" (invisible) or "x" (cloaked) etc. are
+    "interesting", though. (Closes bug #160)
+  - Add support for the new METADATA "account" property, which allows
+    services to automatically identify users after netsplits and across
+    service restarts.
+  - Implement a new configuration option "AllowedChannelTypes" that lists
+    all allowed channel types (channel prefixes) for newly created channels
+    on the local server. By default, all supported channel types are allowed.
+    If set to the empty string, local clients can't create new channels at
+    all, which equals the old "PredefChannelsOnly = yes" setting.
+    This change deprecates the "PredefChannelsOnly" variable, too, but it is
+    still supported and translated to the appropriate "AllowedChannelTypes"
+    setting. When the old "PredefChannelsOnly" variable is processed, a
+    warning message is logged. (Closes bug #152)
+  - Add support for "client certificate fingerprinting". When a client
+    passes an SSL certificate to the server, the "fingerprint" will be
+    forwarded in the network which enables IRC services to identify the
+    user using this certificate and not using passwords.
+  - Implement a new configuration option "IncludeDir" in the "[Options]"
+    section that can be used to specify a directory which can contain
+    further configuration files and configuration file snippets matching
+    the pattern "*.conf". These files are read in after the main server
+    configuration file ("ngircd.conf" by default) has been read in and
+    parsed.  The default is "$SYSCONFDIR/ngircd.conf.d", so that it is
+    possible to adjust the configuration only by placing additional files
+    into this directory. (Closes bug #157)
+  - Add Travis-CI configuration file (".travis.yml") to project.
+  - ngIRCd now accepts user names including "@" characters, saves the
+    unmodified name for authentication but stores only the part in front
+    of the "@" character as "IRC user name". And the latter is how
+    ircd2.11, Bahamut, and irc-seven behave as well. (Closes bug #155)
+  - Lots of IRC "information functions" like ADMIN, INFO, ... now accept
+    server masks and names of connected users (in addition to server names)
+    for specifying the target server of the command. (Closes bug #153)
+  - Implement a new configuration option "IdleTimeout" in the "[Limits]"
+    section of the configuration file which can be used to set a timeout
+    in seconds after which the whole daemon will shutdown when no more
+    connections are left active after handling at least one client.
+    The default is 0, "never".
+    This can be useful for testing or when ngIRCd is started using "socket
+    activation" with systemd(8), for example.
+  - Implement support for systemd(8) "socket activation".
+  - Enable WHOIS to display information about IRC Services using the new
+    numeric 310(RPL_WHOISSERVICE) This numeric is used for this purpose by
+    InspIRCd, for example -- but as usual, other numerics are in use, too,
+    like 613 in UltimateIRCd ...
+    Please note that neither the Operator (+o) not the "bot status" (+B)
+    of an IRC service is displayed in the output.
+  - Update systemd(8) example configuration files in ./contrib/ directory:
+    the "ngircd.service" file now uses the "forking" service type which
+    enhances the log messages shown by "systemctl status ngircd.service",
+    and the new "ngircd.socket" file configures a systemd socket that
+    configures a socket for ngIRCd and launches the daemon on demand.
+  - Enhance help system and the HELP command: now a "help text file" can be
+    set using the new configuration option "HelpFile" ("global" section),
+    which is read in and parsed on server startup and configuration reload,
+    and then is used to output individual help texts to specific topics.
+    Please see the file ./doc/Commands.txt for details.
+
 ngIRCd 20.3 (2013-08-23)
 
   - This release is a bugfix release only, without new features.
@@ -32,7 +116,7 @@ ngIRCd 20 (2012-12-17)
 
   - Allow user names ("INDENT") up to 20 characters when ngIRCd has not
     been configured for "strict RFC mode". This is useful if you are using
-    external (PAM) authenticaion mechanisms that require longer user names.
+    external (PAM) authentication mechanisms that require longer user names.
     Patch suggested by Brett Smith <brett@w3.org>, see
     <http://arthur.barton.de/pipermail/ngircd-ml/2012-October/000579.html>.
 

+ 22 - 7
autogen.sh

@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -73,8 +73,12 @@ Search()
 	for name in $searchlist; do
 		$EXIST "${name}" >/dev/null 2>&1
 		if [ $? -eq 0 ]; then
-			echo "${name}"
-			return 0
+			"${name}" --version 2>&1 \
+			 | grep -v "environment variable" >/dev/null 2>&1
+			if [ $? -eq 0 ]; then
+				echo "${name}"
+				return 0
+			fi
 		fi
 	done
 
@@ -144,6 +148,15 @@ echo "Searching for required tools ..."
 [ -z "$AUTOCONF" ] && AUTOCONF=`Search autoconf 2`
 [ "$VERBOSE" = "1" ] && echo " - AUTOCONF=$AUTOCONF"
 
+AUTOCONF_VERSION=`echo $AUTOCONF | cut -d'-' -f2-`
+[ -n "$AUTOCONF_VERSION" -a "$AUTOCONF_VERSION" != "autoconf" ] \
+	&& export AUTOCONF_VERSION || unset AUTOCONF_VERSION
+[ "$VERBOSE" = "1" ] && echo " - AUTOCONF_VERSION=$AUTOCONF_VERSION"
+AUTOMAKE_VERSION=`echo $AUTOMAKE | cut -d'-' -f2-`
+[ -n "$AUTOMAKE_VERSION" -a "$AUTOMAKE_VERSION" != "automake" ] \
+	&& export AUTOMAKE_VERSION || unset AUTOMAKE_VERSION
+[ "$VERBOSE" = "1" ] && echo " - AUTOMAKE_VERSION=$AUTOMAKE_VERSION"
+
 [ $# -gt 0 ] && CONFIGURE_ARGS=" $@" || CONFIGURE_ARGS=""
 [ -z "$GO" -a -n "$CONFIGURE_ARGS" ] && GO=1
 
@@ -156,21 +169,23 @@ echo "Searching for required tools ..."
 AM_VERSION=`$AUTOMAKE --version | head -n 1 | sed -e 's/.* //g'`
 ifs=$IFS; IFS="."; set $AM_VERSION; IFS=$ifs
 AM_MAJOR="$1"; AM_MINOR="$2"; AM_PATCHLEVEL="$3"
+echo "Detected automake $AM_VERSION ..."
 
 AM_MAKEFILES="src/ipaddr/Makefile.ng src/ngircd/Makefile.ng src/testsuite/Makefile.ng src/tool/Makefile.ng"
 
+# De-ANSI-fication?
 if [ "$AM_MAJOR" -eq "1" -a "$AM_MINOR" -lt "12" ]; then
 	# automake < 1.12 => automatic de-ANSI-fication support available
-	echo "Enabling de-ANSI-fication support (automake $AM_VERSION) ..."
+	echo " - Enabling de-ANSI-fication support."
 	sed -e "s|^__ng_PROTOTYPES__|AM_C_PROTOTYPES|g" configure.ng >configure.ac
 	DEANSI_START=""
 	DEANSI_END=""
 else
 	# automake >= 1.12 => no de-ANSI-fication support available
-	echo "Disabling de-ANSI-fication support (automake $AM_VERSION) ..."
+	echo " - Disabling de-ANSI-fication support."
 	sed -e "s|^__ng_PROTOTYPES__|AC_C_PROTOTYPES|g" configure.ng >configure.ac
 	DEANSI_START="#"
-	DEANSI_END="	# disabled by ./autogen.sh script"
+	DEANSI_END=" (disabled by ./autogen.sh script)"
 fi
 # Serial test harness?
 if [ "$AM_MAJOR" -eq "1" -a "$AM_MINOR" -ge "13" ]; then
@@ -193,7 +208,7 @@ done
 export ACLOCAL AUTOHEADER AUTOMAKE AUTOCONF
 
 # Generate files
-echo "Generating files using GNU $AUTOCONF and $AUTOMAKE ..."
+echo "Generating files using \"$AUTOCONF\" and \"$AUTOMAKE\" ..."
 Run $ACLOCAL && \
 	Run $AUTOCONF && \
 	Run $AUTOHEADER && \

+ 82 - 16
configure

@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.67 for ngIRCd 20.3.
+# Generated by GNU Autoconf 2.67 for ngIRCd 21.
 #
 # Report bugs to <ngircd-ml@ngircd.barton.de>.
 #
@@ -552,8 +552,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='ngIRCd'
 PACKAGE_TARNAME='ngircd'
-PACKAGE_VERSION='20.3'
-PACKAGE_STRING='ngIRCd 20.3'
+PACKAGE_VERSION='21'
+PACKAGE_STRING='ngIRCd 21'
 PACKAGE_BUGREPORT='ngircd-ml@ngircd.barton.de'
 PACKAGE_URL='http://ngircd.barton.de/'
 
@@ -1269,7 +1269,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures ngIRCd 20.3 to adapt to many kinds of systems.
+\`configure' configures ngIRCd 21 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1339,7 +1339,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ngIRCd 20.3:";;
+     short | recursive ) echo "Configuration of ngIRCd 21:";;
    esac
   cat <<\_ACEOF
 
@@ -1373,7 +1373,7 @@ Optional Packages:
   --with-tcp-wrappers     enable TCP wrappers support
   --with-ident            enable "IDENT" ("AUTH") protocol support
   --with-pam              enable user authentication using PAM
-  --with-iconv            enable character conversation using libiconv
+  --with-iconv            enable character conversion using libiconv
 
 Some influential environment variables:
   CC          C compiler command
@@ -1452,7 +1452,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ngIRCd configure 20.3
+ngIRCd configure 21
 generated by GNU Autoconf 2.67
 
 Copyright (C) 2010 Free Software Foundation, Inc.
@@ -1985,7 +1985,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by ngIRCd $as_me 20.3, which was
+It was created by ngIRCd $as_me 21, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
   $ $0 $@
@@ -2274,6 +2274,8 @@ as_fn_append ac_header_list " stdbool.h"
 as_fn_append ac_header_list " stddef.h"
 as_fn_append ac_header_list " stdint.h"
 as_fn_append ac_header_list " varargs.h"
+as_fn_append ac_func_list " arc4random"
+as_fn_append ac_func_list " arc4random_stir"
 as_fn_append ac_func_list " gai_strerror"
 as_fn_append ac_func_list " getaddrinfo"
 as_fn_append ac_func_list " getnameinfo"
@@ -2283,6 +2285,7 @@ as_fn_append ac_func_list " sigprocmask"
 as_fn_append ac_func_list " snprintf"
 as_fn_append ac_func_list " vsnprintf"
 as_fn_append ac_func_list " strdup"
+as_fn_append ac_func_list " strndup"
 as_fn_append ac_func_list " strlcpy"
 as_fn_append ac_func_list " strlcat"
 as_fn_append ac_func_list " strtok_r"
@@ -2898,7 +2901,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='ngircd'
- VERSION='20.3'
+ VERSION='21'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -5044,7 +5047,7 @@ esac
 # Add additional CFLAGS, eventually specified on the command line:
 test -n "$CFLAGS_ADD" && CFLAGS="$CFLAGS $CFLAGS_ADD"
 
-CFLAGS="$CFLAGS -DSYSCONFDIR='\"\$(sysconfdir)\"'"
+CFLAGS="$CFLAGS -DSYSCONFDIR='\"\$(sysconfdir)\"' -DDOCDIR='\"\$(docdir)\"'"
 
 # -- Headers --
 
@@ -5996,6 +5999,12 @@ done
 
 
 
+
+
+
+
+
+
 # -- Configuration options --
 
 # use syslog?
@@ -7120,14 +7129,71 @@ if test "x$ac_cv_func_iconv_open" = x""yes; then :
 #define HAVE_ICONV_OPEN 1
 _ACEOF
  x_iconv_on=yes
+fi
+done
+
+			if test "$x_iconv_on" != "yes"; then
+				{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libiconv_open in -liconv" >&5
+$as_echo_n "checking for libiconv_open in -liconv... " >&6; }
+if test "${ac_cv_lib_iconv_libiconv_open+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-liconv  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char libiconv_open ();
+int
+main ()
+{
+return libiconv_open ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_iconv_libiconv_open=yes
 else
-  as_fn_error $? "Can't enable libiconv support!" "$LINENO" 5
+  ac_cv_lib_iconv_libiconv_open=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iconv_libiconv_open" >&5
+$as_echo "$ac_cv_lib_iconv_libiconv_open" >&6; }
+if test "x$ac_cv_lib_iconv_libiconv_open" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBICONV 1
+_ACEOF
+
+  LIBS="-liconv $LIBS"
 
 fi
-done
 
-		  fi
+				for ac_func in libiconv_open
+do :
+  ac_fn_c_check_func "$LINENO" "libiconv_open" "ac_cv_func_libiconv_open"
+if test "x$ac_cv_func_libiconv_open" = x""yes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBICONV_OPEN 1
+_ACEOF
+ x_iconv_on=yes
+fi
+done
 
+			fi
+			if test "$x_iconv_on" != "yes"; then
+				as_fn_error $? "Can't enable libiconv support!" "$LINENO" 5
+			fi
+		fi
 
 fi
 
@@ -7766,7 +7832,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by ngIRCd $as_me 20.3, which was
+This file was extended by ngIRCd $as_me 21, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -7833,7 +7899,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-ngIRCd config.status 20.3
+ngIRCd config.status 21
 configured by $0, generated by GNU Autoconf 2.67,
   with options \\"\$ac_cs_config\\"
 
@@ -8780,7 +8846,7 @@ test "$x_ipv6_on" = "yes" \
 	&& echo $ECHO_N "yes   $ECHO_C" \
 	|| echo $ECHO_N "no    $ECHO_C"
 echo $ECHO_N "        I/O backend: $ECHO_C"
-	echo "\"$x_io_backend\""
+	echo "$x_io_backend"
 
 echo $ECHO_N "        PAM support: $ECHO_C"
 test "$x_pam_on" = "yes" \

+ 16 - 10
configure.ac

@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -110,7 +110,7 @@ esac
 # Add additional CFLAGS, eventually specified on the command line:
 test -n "$CFLAGS_ADD" && CFLAGS="$CFLAGS $CFLAGS_ADD"
 
-CFLAGS="$CFLAGS -DSYSCONFDIR='\"\$(sysconfdir)\"'"
+CFLAGS="$CFLAGS -DSYSCONFDIR='\"\$(sysconfdir)\"' -DDOCDIR='\"\$(docdir)\"'"
 
 # -- Headers --
 
@@ -187,8 +187,9 @@ AC_CHECK_FUNCS([ \
 
 # Optional functions
 AC_CHECK_FUNCS_ONCE([ \
-	gai_strerror getaddrinfo getnameinfo inet_aton sigaction sigprocmask \
-	snprintf vsnprintf strdup strlcpy strlcat strtok_r waitpid])
+	arc4random arc4random_stir gai_strerror getaddrinfo getnameinfo inet_aton
+	sigaction sigprocmask snprintf vsnprintf strdup strndup strlcpy strlcat \
+	strtok_r waitpid])
 
 # -- Configuration options --
 
@@ -518,7 +519,8 @@ if test "$x_ircplus_on" = "yes"; then
 	# CHARCONV is the only function depending on it.
 	x_iconv_on=no
 	AC_ARG_WITH(iconv,
-		[  --with-iconv            enable character conversation using libiconv],
+		AS_HELP_STRING([--with-iconv],
+			       [enable character conversion using libiconv]),
 		[ if test "$withval" != "no"; then
 			if test "$withval" != "yes"; then
 				CFLAGS="-I$withval/include $CFLAGS"
@@ -526,11 +528,15 @@ if test "$x_ircplus_on" = "yes"; then
 				LDFLAGS="-L$withval/lib $LDFLAGS"
 			fi
 			AC_CHECK_LIB(iconv, iconv_open)
-			AC_CHECK_FUNCS(iconv_open, x_iconv_on=yes,
+			AC_CHECK_FUNCS(iconv_open, x_iconv_on=yes)
+			if test "$x_iconv_on" != "yes"; then
+				AC_CHECK_LIB(iconv, libiconv_open)
+				AC_CHECK_FUNCS(libiconv_open, x_iconv_on=yes)
+			fi
+			if test "$x_iconv_on" != "yes"; then
 				AC_MSG_ERROR([Can't enable libiconv support!])
-			)
-		  fi
-		]
+			fi
+		fi ]
 	)
 	if test "$x_iconv_on" = "yes"; then
 		AC_DEFINE(ICONV, 1)
@@ -697,7 +703,7 @@ test "$x_ipv6_on" = "yes" \
 	&& echo $ECHO_N "yes   $ECHO_C" \
 	|| echo $ECHO_N "no    $ECHO_C"
 echo $ECHO_N "        I/O backend: $ECHO_C"
-	echo "\"$x_io_backend\""
+	echo "$x_io_backend"
 
 echo $ECHO_N "        PAM support: $ECHO_C"
 test "$x_pam_on" = "yes" \

+ 16 - 10
configure.ng

@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -110,7 +110,7 @@ esac
 # Add additional CFLAGS, eventually specified on the command line:
 test -n "$CFLAGS_ADD" && CFLAGS="$CFLAGS $CFLAGS_ADD"
 
-CFLAGS="$CFLAGS -DSYSCONFDIR='\"\$(sysconfdir)\"'"
+CFLAGS="$CFLAGS -DSYSCONFDIR='\"\$(sysconfdir)\"' -DDOCDIR='\"\$(docdir)\"'"
 
 # -- Headers --
 
@@ -187,8 +187,9 @@ AC_CHECK_FUNCS([ \
 
 # Optional functions
 AC_CHECK_FUNCS_ONCE([ \
-	gai_strerror getaddrinfo getnameinfo inet_aton sigaction sigprocmask \
-	snprintf vsnprintf strdup strlcpy strlcat strtok_r waitpid])
+	arc4random arc4random_stir gai_strerror getaddrinfo getnameinfo inet_aton
+	sigaction sigprocmask snprintf vsnprintf strdup strndup strlcpy strlcat \
+	strtok_r waitpid])
 
 # -- Configuration options --
 
@@ -518,7 +519,8 @@ if test "$x_ircplus_on" = "yes"; then
 	# CHARCONV is the only function depending on it.
 	x_iconv_on=no
 	AC_ARG_WITH(iconv,
-		[  --with-iconv            enable character conversation using libiconv],
+		AS_HELP_STRING([--with-iconv],
+			       [enable character conversion using libiconv]),
 		[ if test "$withval" != "no"; then
 			if test "$withval" != "yes"; then
 				CFLAGS="-I$withval/include $CFLAGS"
@@ -526,11 +528,15 @@ if test "$x_ircplus_on" = "yes"; then
 				LDFLAGS="-L$withval/lib $LDFLAGS"
 			fi
 			AC_CHECK_LIB(iconv, iconv_open)
-			AC_CHECK_FUNCS(iconv_open, x_iconv_on=yes,
+			AC_CHECK_FUNCS(iconv_open, x_iconv_on=yes)
+			if test "$x_iconv_on" != "yes"; then
+				AC_CHECK_LIB(iconv, libiconv_open)
+				AC_CHECK_FUNCS(libiconv_open, x_iconv_on=yes)
+			fi
+			if test "$x_iconv_on" != "yes"; then
 				AC_MSG_ERROR([Can't enable libiconv support!])
-			)
-		  fi
-		]
+			fi
+		fi ]
 	)
 	if test "$x_iconv_on" = "yes"; then
 		AC_DEFINE(ICONV, 1)
@@ -697,7 +703,7 @@ test "$x_ipv6_on" = "yes" \
 	&& echo $ECHO_N "yes   $ECHO_C" \
 	|| echo $ECHO_N "no    $ECHO_C"
 echo $ECHO_N "        I/O backend: $ECHO_C"
-	echo "\"$x_io_backend\""
+	echo "$x_io_backend"
 
 echo $ECHO_N "        PAM support: $ECHO_C"
 test "$x_pam_on" = "yes" \

+ 31 - 0
contrib/Debian/changelog

@@ -1,3 +1,34 @@
+ngircd (21-0ab1) unstable; urgency=low
+
+  * New "upstream" release: ngIRCd 21.
+
+ -- Alexander Barton <alex@barton.de>  Wed, 30 Oct 2013 22:13:55 +0100
+
+ngircd (21~rc2-0ab3) unstable; urgency=low
+
+  * Fix sed(1) rules adjusting "ngircd-full" package, error introduced
+    by last commit :-/
+
+ -- Alexander Barton <alex@barton.de>  Sun, 20 Oct 2013 18:31:16 +0200
+
+ngircd (21~rc2-0ab2) unstable; urgency=low
+
+  * Fix default "HelpFile" file name in ngircd.conf for "full" packages.
+
+ -- Alexander Barton <alex@barton.de>  Sun, 20 Oct 2013 17:18:28 +0200
+
+ngircd (21~rc2-0ab1) unstable; urgency=low
+
+  * New "upstream" release candidate 2 for ngIRCd Release 21.
+
+ -- Alexander Barton <alex@barton.de>  Sun, 20 Oct 2013 15:50:03 +0200
+
+ngircd (21~rc1-0ab1) unstable; urgency=low
+
+  * New "upstream" release candidate 1 for ngIRCd Release 21.
+
+ -- Alexander Barton <alex@barton.de>  Sat, 05 Oct 2013 23:24:09 +0200
+
 ngircd (20.3-0ab1) unstable; urgency=high
 
   * New "upstream" release, fixing a security related bug: ngIRCd 20.3.

+ 0 - 2
contrib/Debian/ngircd.default

@@ -1,8 +1,6 @@
 #
 # Defaults for ngIRCd start and stop script
 #
-# $Id: ngircd.default,v 1.1 2003/12/31 17:20:11 alex Exp $
-#
 
 # Parameters to pass to the ngircd daemon on startup, see ngircd(8) for
 # possible options (default: empty).

+ 4 - 3
contrib/Debian/ngircd.init

@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # ngIRCd start and stop script for Debian-based systems
-# Copyright 2008-2010 Alexander Barton <alex@barton.de>
+# Copyright 2008-2013 Alexander Barton <alex@barton.de>
 #
 
 ### BEGIN INIT INFO
@@ -24,13 +24,13 @@ PARAMS=""
 STARTTIME=1
 DIETIME=10
 
-test -x $DAEMON || exit 5
-
 test -h "$0" && me=`readlink $0` || me="$0"
 BASENAME=`basename $me`
 
 test -r /etc/default/$BASENAME && . /etc/default/$BASENAME
 
+test -x $DAEMON || exit 5
+
 # LSB compatibility functions that become used if there is no local
 # include file available.
 log_daemon_msg() {
@@ -84,6 +84,7 @@ Do_Start() {
 		log_warning_msg "$NAME seems to be already running, nothing to do."
 		exit 0
 	fi
+	rm -f "$PIDFILE"
 	start-stop-daemon --start \
 		--quiet --exec $DAEMON -- $PARAMS
 	sleep $STARTTIME

+ 15 - 13
contrib/Debian/rules

@@ -139,10 +139,10 @@ install-ngircd: build-ngircd
 	rm $(CURDIR)/debian/ngircd/usr/share/doc/ngircd/COPYING*
 	mkdir -p $(CURDIR)/debian/ngircd/var/run/ircd
 	cat $(CURDIR)/debian/ngircd/usr/share/doc/ngircd/sample-ngircd.conf | \
-	 sed -e "s/;ServerUID = 65534/ServerUID = irc/g" | \
-	 sed -e "s/;ServerGID = 65534/ServerGID = irc/g" | \
-	 sed -e "s/;MotdFile = \/usr\/local\/etc\/ngircd.motd/MotdFile = \/etc\/ngircd\/ngircd.motd/g" | \
-	 sed -e "s/;PidFile = \/var\/run\/ngircd\/ngircd.pid/PidFile = \/var\/run\/ircd\/ngircd.pid/g" \
+	 sed -e "s|;ServerUID = 65534|ServerUID = irc|g" | \
+	 sed -e "s|;ServerGID = 65534|ServerGID = irc|g" | \
+	 sed -e "s|;MotdFile = /usr/local/etc/ngircd.motd|MotdFile = |/etc/ngircd/ngircd.motd|g" | \
+	 sed -e "s|;PidFile = /var/run/ngircd/ngircd.pid|PidFile = /var/run/ircd/ngircd.pid|g" \
 	 >$(CURDIR)/debian/ngircd/etc/ngircd/ngircd.conf
 	touch $(CURDIR)/debian/ngircd/etc/ngircd/ngircd.motd
 
@@ -159,10 +159,11 @@ install-ngircd-full: build-ngircd-full
 	 $(CURDIR)/debian/ngircd-full/usr/share/doc/ngircd-full
 	mkdir -p $(CURDIR)/debian/ngircd-full/var/run/ircd
 	cat $(CURDIR)/debian/ngircd-full/usr/share/doc/ngircd-full/sample-ngircd.conf | \
-	 sed -e "s/;ServerUID = 65534/ServerUID = irc/g" | \
-	 sed -e "s/;ServerGID = 65534/ServerGID = irc/g" | \
-	 sed -e "s/;MotdFile = \/usr\/local\/etc\/ngircd.motd/MotdFile = \/etc\/ngircd\/ngircd.motd/g" | \
-	 sed -e "s/;PidFile = \/var\/run\/ngircd\/ngircd.pid/PidFile = \/var\/run\/ircd\/ngircd.pid/g" \
+	 sed -e "s|;ServerUID = 65534|ServerUID = irc|g" | \
+	 sed -e "s|;ServerGID = 65534|ServerGID = irc|g" | \
+	 sed -e "s|;MotdFile = /usr/local/etc/ngircd.motd|MotdFile = /etc/ngircd/ngircd.motd|g" | \
+	 sed -e "s|;HelpFile = /usr/share/doc/ngircd/Commands.txt|HelpFile = /usr/share/doc/ngircd-full/Commands.txt|g" | \
+	 sed -e "s|;PidFile = /var/run/ngircd/ngircd.pid|PidFile = /var/run/ircd/ngircd.pid|g" \
 	 >$(CURDIR)/debian/ngircd-full/etc/ngircd/ngircd.conf
 	touch $(CURDIR)/debian/ngircd-full/etc/ngircd/ngircd.motd
 	mkdir -p $(CURDIR)/debian/ngircd-full/etc/pam.d
@@ -181,10 +182,11 @@ install-ngircd-full-dbg: build-ngircd-full-dbg
 	 $(CURDIR)/debian/ngircd-full-dbg/usr/share/doc/ngircd-full-dbg
 	mkdir -p $(CURDIR)/debian/ngircd-full-dbg/var/run/ircd
 	cat $(CURDIR)/debian/ngircd-full-dbg/usr/share/doc/ngircd-full-dbg/sample-ngircd.conf | \
-	 sed -e "s/;ServerUID = 65534/ServerUID = irc/g" | \
-	 sed -e "s/;ServerGID = 65534/ServerGID = irc/g" | \
-	 sed -e "s/;MotdFile = \/usr\/local\/etc\/ngircd.motd/MotdFile = \/etc\/ngircd\/ngircd.motd/g" | \
-	 sed -e "s/;PidFile = \/var\/run\/ngircd\/ngircd.pid/PidFile = \/var\/run\/ircd\/ngircd.pid/g" \
+	 sed -e "s|;ServerUID = 65534|ServerUID = irc|g" | \
+	 sed -e "s|;ServerGID = 65534|ServerGID = irc|g" | \
+	 sed -e "s|;MotdFile = /usr/local/etc/ngircd.motd|MotdFile = /etc/ngircd/ngircd.motd|g" | \
+	 sed -e "s|;HelpFile = /usr/share/doc/ngircd/Commands.txt|HelpFile = /usr/share/doc/ngircd-full-dbg/Commands.txt|g" | \
+	 sed -e "s|;PidFile = /var/run/ngircd/ngircd.pid|PidFile = /var/run/ircd/ngircd.pid|g" \
 	 >$(CURDIR)/debian/ngircd-full-dbg/etc/ngircd/ngircd.conf
 	touch $(CURDIR)/debian/ngircd-full-dbg/etc/ngircd/ngircd.motd
 	mkdir -p $(CURDIR)/debian/ngircd-full-dbg/etc/pam.d
@@ -216,7 +218,7 @@ binary-arch: build install
 	dh_installdocs -a
 	dh_installinit -a
 	dh_strip -a --no-package=ngircd-full-dbg
-	dh_compress -a
+	dh_compress -a -XCommands.txt
 	dh_fixperms -a
 	dh_installdeb -a
 	dh_shlibdeps -a

+ 12 - 7
contrib/MacOSX/config.h

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -12,17 +12,22 @@
  */
 
 #define PACKAGE_NAME "ngIRCd"
-#define PACKAGE "ngircd"
+# define PACKAGE "ngircd"
 #ifndef VERSION
-#define VERSION "??("__DATE__")"
+# define VERSION "??("__DATE__")"
 #endif
-#define SYSCONFDIR "/etc/ngircd"
 
-#ifndef TARGET_VENDOR
-#define TARGET_VENDOR "apple"
-#define TARGET_OS "darwin"
+#ifndef HOST_VENDOR
+# define HOST_VENDOR "apple"
+# define HOST_OS "darwin"
+# ifdef __x86_64
+#  define HOST_CPU "x86_64"
+# endif
 #endif
 
+#define SYSCONFDIR "/etc/ngircd"
+#define DOCDIR "/usr/share/doc/ngircd"
+
 /* -- Build options -- */
 
 /* Define if debug-mode should be enabled */

File diff suppressed because it is too large
+ 1 - 1
contrib/MacOSX/ngIRCd.pmdoc/01ngircd-contents.xml


File diff suppressed because it is too large
+ 1 - 1
contrib/MacOSX/ngIRCd.pmdoc/01ngircd.xml


File diff suppressed because it is too large
+ 2 - 2
contrib/MacOSX/ngIRCd.pmdoc/index.xml


+ 67 - 129
contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj

@@ -64,12 +64,27 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
-		FA1A6BBC0D6857BB00AA8F71 /* misc-test.e */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = "misc-test.e"; sourceTree = "<group>"; };
+		FA18A63E16CEDDCE00132F66 /* configure.ng */ = {isa = PBXFileReference; lastKnownFileType = text; name = configure.ng; path = ../../configure.ng; sourceTree = "<group>"; };
+		FA18A63F16CEDE2300132F66 /* ngircd.service */ = {isa = PBXFileReference; lastKnownFileType = text; path = ngircd.service; sourceTree = "<group>"; };
+		FA18A64016CEDE2300132F66 /* ngircd.socket */ = {isa = PBXFileReference; lastKnownFileType = text; path = ngircd.socket; sourceTree = "<group>"; };
+		FA18A64116CEDE3500132F66 /* ngircd.pam */ = {isa = PBXFileReference; lastKnownFileType = text; path = ngircd.pam; sourceTree = "<group>"; };
+		FA18A64216CEDE5700132F66 /* de.barton.ngircd.plist.tmpl */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = de.barton.ngircd.plist.tmpl; sourceTree = "<group>"; };
+		FA18A64316CEDE8100132F66 /* Makefile.am */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
+		FA18A64416CEDFCE00132F66 /* Commands.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Commands.txt; sourceTree = "<group>"; };
+		FA18A64516CEE0C700132F66 /* Makefile.ng */ = {isa = PBXFileReference; lastKnownFileType = text; name = Makefile.ng; path = ipaddr/Makefile.ng; sourceTree = "<group>"; };
+		FA18A64616CEE0DD00132F66 /* Makefile.ng */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.ng; sourceTree = "<group>"; };
+		FA18A64716CEE14900132F66 /* Makefile.ng */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.ng; sourceTree = "<group>"; };
+		FA18A64A16CEE18100132F66 /* Makefile.ng */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.ng; sourceTree = "<group>"; };
+		FA18A64C16CEE1AC00132F66 /* mode-test.e */ = {isa = PBXFileReference; lastKnownFileType = text; path = "mode-test.e"; sourceTree = "<group>"; };
+		FA18A64D16CEE1D900132F66 /* whois-test.e */ = {isa = PBXFileReference; lastKnownFileType = text; path = "whois-test.e"; sourceTree = "<group>"; };
+		FA18A64E16CEE24B00132F66 /* misc-test.e */ = {isa = PBXFileReference; lastKnownFileType = text; path = "misc-test.e"; sourceTree = "<group>"; };
+		FA18A64F16CEE27700132F66 /* Makefile.ng */ = {isa = PBXFileReference; lastKnownFileType = text; path = Makefile.ng; sourceTree = "<group>"; };
 		FA1A6BBD0D6857D900AA8F71 /* who-test.e */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = "who-test.e"; sourceTree = "<group>"; };
+		FA1DBB6716C707D200D4F838 /* irc-macros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "irc-macros.h"; sourceTree = "<group>"; };
 		FA2D564811EA158B00D37A35 /* pam.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pam.h; sourceTree = "<group>"; };
 		FA2D564911EA158B00D37A35 /* pam.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pam.c; sourceTree = "<group>"; };
 		FA2D567A11EA1AB300D37A35 /* libpam.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpam.dylib; path = usr/lib/libpam.dylib; sourceTree = SDKROOT; };
-		FA322BBA0CEF72E4001761B3 /* ngIRCd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ngIRCd; sourceTree = BUILT_PRODUCTS_DIR; };
+		FA322BBA0CEF72E4001761B3 /* ngIRCd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; name = ngIRCd; path = ngircd; sourceTree = BUILT_PRODUCTS_DIR; };
 		FA322CD60CEF74B1001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		FA322CD90CEF74B1001761B3 /* array.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = array.c; sourceTree = "<group>"; };
 		FA322CDA0CEF74B1001761B3 /* array.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = array.h; sourceTree = "<group>"; };
@@ -112,7 +127,6 @@
 		FA322CFF0CEF74B1001761B3 /* lists.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = lists.h; sourceTree = "<group>"; };
 		FA322D000CEF74B1001761B3 /* log.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = log.c; sourceTree = "<group>"; };
 		FA322D010CEF74B1001761B3 /* log.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = log.h; sourceTree = "<group>"; };
-		FA322D020CEF74B1001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		FA322D030CEF74B1001761B3 /* match.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = match.c; sourceTree = "<group>"; };
 		FA322D040CEF74B1001761B3 /* match.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = match.h; sourceTree = "<group>"; };
 		FA322D050CEF74B1001761B3 /* messages.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = messages.h; sourceTree = "<group>"; };
@@ -126,7 +140,6 @@
 		FA322D110CEF74B1001761B3 /* ansi2knr.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = ansi2knr.c; sourceTree = "<group>"; };
 		FA322D120CEF74B1001761B3 /* exp.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = exp.h; sourceTree = "<group>"; };
 		FA322D130CEF74B1001761B3 /* imp.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = imp.h; sourceTree = "<group>"; };
-		FA322D140CEF74B1001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		FA322D150CEF74B1001761B3 /* portab.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = portab.h; sourceTree = "<group>"; };
 		FA322D160CEF74B1001761B3 /* portabtest.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = portabtest.c; sourceTree = "<group>"; };
 		FA322D170CEF74B1001761B3 /* splint.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = splint.h; sourceTree = "<group>"; };
@@ -138,8 +151,6 @@
 		FA322D1F0CEF74B1001761B3 /* connect-test.e */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = "connect-test.e"; sourceTree = "<group>"; };
 		FA322D200CEF74B1001761B3 /* functions.inc */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.pascal; path = functions.inc; sourceTree = "<group>"; };
 		FA322D210CEF74B1001761B3 /* getpid.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = getpid.sh; sourceTree = "<group>"; };
-		FA322D220CEF74B1001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
-		FA322D230CEF74B1001761B3 /* mode-test.e */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = "mode-test.e"; sourceTree = "<group>"; };
 		FA322D250CEF74B1001761B3 /* README */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = README; sourceTree = "<group>"; };
 		FA322D260CEF74B1001761B3 /* start-server.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = "start-server.sh"; sourceTree = "<group>"; };
 		FA322D270CEF74B1001761B3 /* stop-server.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = "stop-server.sh"; sourceTree = "<group>"; };
@@ -149,9 +160,6 @@
 		FA322D2B0CEF74B1001761B3 /* test-loop.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = "test-loop.sh"; sourceTree = "<group>"; };
 		FA322D2C0CEF74B1001761B3 /* tests.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = tests.sh; sourceTree = "<group>"; };
 		FA322D2D0CEF74B1001761B3 /* wait-tests.sh */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = "wait-tests.sh"; sourceTree = "<group>"; };
-		FA322D300CEF74B1001761B3 /* ansi2knr.1 */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.man; path = ansi2knr.1; sourceTree = "<group>"; };
-		FA322D310CEF74B1001761B3 /* ansi2knr.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = ansi2knr.c; sourceTree = "<group>"; };
-		FA322D320CEF74B1001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		FA322D330CEF74B1001761B3 /* tool.c */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.c; path = tool.c; sourceTree = "<group>"; };
 		FA322D340CEF74B1001761B3 /* tool.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = tool.h; sourceTree = "<group>"; };
 		FA322D5A0CEF750F001761B3 /* AUTHORS */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = AUTHORS; path = ../../AUTHORS; sourceTree = SOURCE_ROOT; };
@@ -159,7 +167,6 @@
 		FA322D5C0CEF750F001761B3 /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = ChangeLog; path = ../../ChangeLog; sourceTree = SOURCE_ROOT; };
 		FA322D5E0CEF750F001761B3 /* config.guess */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; name = config.guess; path = ../../config.guess; sourceTree = SOURCE_ROOT; };
 		FA322D5F0CEF750F001761B3 /* config.sub */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; name = config.sub; path = ../../config.sub; sourceTree = SOURCE_ROOT; };
-		FA322D600CEF750F001761B3 /* configure.in */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = configure.in; path = ../../configure.in; sourceTree = SOURCE_ROOT; };
 		FA322D610CEF750F001761B3 /* COPYING */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = COPYING; path = ../../COPYING; sourceTree = SOURCE_ROOT; };
 		FA322D620CEF750F001761B3 /* INSTALL */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = INSTALL; path = ../../INSTALL; sourceTree = SOURCE_ROOT; };
 		FA322D630CEF750F001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = Makefile.am; path = ../../Makefile.am; sourceTree = SOURCE_ROOT; };
@@ -174,9 +181,8 @@
 		FA322D700CEF7523001761B3 /* ngircd.init */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = ngircd.init; sourceTree = "<group>"; };
 		FA322D710CEF7523001761B3 /* ngircd.postinst */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.script.sh; path = ngircd.postinst; sourceTree = "<group>"; };
 		FA322D720CEF7523001761B3 /* rules */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = rules; sourceTree = "<group>"; };
-		FA322D8D0CEF7523001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
+		FA322D8D0CEF7523001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = Makefile.am; path = MacOSX/Makefile.am; 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>"; };
 		FA322D920CEF7523001761B3 /* ngindent */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngindent; 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>"; };
@@ -190,16 +196,13 @@
 		FA322DA00CEF752C001761B3 /* RFC.txt */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = RFC.txt; sourceTree = "<group>"; };
 		FA322DA40CEF752C001761B3 /* Doxyfile */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Doxyfile; sourceTree = "<group>"; };
 		FA322DA50CEF752C001761B3 /* footer.inc.html */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.html; path = footer.inc.html; sourceTree = "<group>"; };
-		FA322DA60CEF752C001761B3 /* header.inc.html */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.html; path = header.inc.html; sourceTree = "<group>"; };
 		FA322DA70CEF752C001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
-		FA322DA80CEF752C001761B3 /* ngircd-doc.css */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text.css; path = "ngircd-doc.css"; sourceTree = "<group>"; };
 		FA322DA90CEF752C001761B3 /* SSL.txt */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = SSL.txt; sourceTree = "<group>"; };
 		FA322DAD0CEF7538001761B3 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		FA322DAE0CEF7538001761B3 /* ngircd.8.tmpl */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngircd.8.tmpl; sourceTree = "<group>"; };
 		FA322DAF0CEF7538001761B3 /* ngircd.conf.5.tmpl */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = ngircd.conf.5.tmpl; sourceTree = "<group>"; };
 		FA322DB10CEF7565001761B3 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
 		FA322DC00CEF77CB001761B3 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
-		FA407F2B0DB159F400271AF1 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; name = Makefile.am; path = ipaddr/Makefile.am; 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>"; };
 		FA407F380DB15AC700271AF1 /* GIT.txt */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = text; path = GIT.txt; sourceTree = "<group>"; };
@@ -279,28 +282,32 @@
 		08FB7794FE84155DC02AAC07 /* ngIRCd */ = {
 			isa = PBXGroup;
 			children = (
-				FA322D970CEF752C001761B3 /* doc */,
+				FA322D630CEF750F001761B3 /* Makefile.am */,
 				FA322D660CEF7523001761B3 /* contrib */,
+				FA322D970CEF752C001761B3 /* doc */,
 				FA322DAB0CEF7538001761B3 /* man */,
 				FA322CD40CEF74B0001761B3 /* src */,
 				FA322D5A0CEF750F001761B3 /* AUTHORS */,
+				FA322D5C0CEF750F001761B3 /* ChangeLog */,
 				FA322D610CEF750F001761B3 /* COPYING */,
 				FA322D620CEF750F001761B3 /* INSTALL */,
 				FA322D640CEF750F001761B3 /* NEWS */,
 				FA322D650CEF750F001761B3 /* README */,
 				FA322D5B0CEF750F001761B3 /* autogen.sh */,
-				FA322D5C0CEF750F001761B3 /* ChangeLog */,
 				FA322D5E0CEF750F001761B3 /* config.guess */,
 				FA322D5F0CEF750F001761B3 /* config.sub */,
-				FA322D600CEF750F001761B3 /* configure.in */,
-				FA322D630CEF750F001761B3 /* Makefile.am */,
+				FA18A63E16CEDDCE00132F66 /* configure.ng */,
 				1AB674ADFE9D54B511CA2CBB /* Products */,
 				FA6BBC651605F6D60004247A /* libiconv.dylib */,
 				FA2D567A11EA1AB300D37A35 /* libpam.dylib */,
 				FA322DC00CEF77CB001761B3 /* libz.dylib */,
 			);
+			indentWidth = 8;
 			name = ngIRCd;
 			sourceTree = "<group>";
+			tabWidth = 8;
+			usesTabs = 1;
+			wrapsLines = 0;
 		};
 		1AB674ADFE9D54B511CA2CBB /* Products */ = {
 			isa = PBXGroup;
@@ -313,12 +320,12 @@
 		FA322CD40CEF74B0001761B3 /* src */ = {
 			isa = PBXGroup;
 			children = (
-				FA322CD70CEF74B1001761B3 /* ngircd */,
+				FA322CD60CEF74B1001761B3 /* Makefile.am */,
 				FA407F270DB1598D00271AF1 /* ipaddr */,
+				FA322CD70CEF74B1001761B3 /* ngircd */,
 				FA322D0E0CEF74B1001761B3 /* portab */,
 				FA322D1B0CEF74B1001761B3 /* testsuite */,
 				FA322D2E0CEF74B1001761B3 /* tool */,
-				FA322CD60CEF74B1001761B3 /* Makefile.am */,
 			);
 			name = src;
 			path = ../../src;
@@ -327,7 +334,7 @@
 		FA322CD70CEF74B1001761B3 /* ngircd */ = {
 			isa = PBXGroup;
 			children = (
-				FA322D020CEF74B1001761B3 /* Makefile.am */,
+				FA18A64616CEE0DD00132F66 /* Makefile.ng */,
 				FA322CD90CEF74B1001761B3 /* array.c */,
 				FA322CDA0CEF74B1001761B3 /* array.h */,
 				FA322CDB0CEF74B1001761B3 /* channel.c */,
@@ -347,10 +354,10 @@
 				FA6BBC601605F0AC0004247A /* conn-encoding.h */,
 				FA322CE10CEF74B1001761B3 /* conn-func.c */,
 				FA322CE20CEF74B1001761B3 /* conn-func.h */,
-				FA322CE30CEF74B1001761B3 /* conn-zip.c */,
-				FA322CE40CEF74B1001761B3 /* conn-zip.h */,
 				FAA3D2790F139CDC00B2447E /* conn-ssl.c */,
 				FAA3D27A0F139CDC00B2447E /* conn-ssl.h */,
+				FA322CE30CEF74B1001761B3 /* conn-zip.c */,
+				FA322CE40CEF74B1001761B3 /* conn-zip.h */,
 				FA322CE70CEF74B1001761B3 /* defines.h */,
 				FA322CE80CEF74B1001761B3 /* hash.c */,
 				FA322CE90CEF74B1001761B3 /* hash.h */,
@@ -368,6 +375,7 @@
 				FA322CEF0CEF74B1001761B3 /* irc-info.h */,
 				FA322CF00CEF74B1001761B3 /* irc-login.c */,
 				FA322CF10CEF74B1001761B3 /* irc-login.h */,
+				FA1DBB6716C707D200D4F838 /* irc-macros.h */,
 				FA4F1659164836B100DBD011 /* irc-metadata.c */,
 				FA4F165C164836BF00DBD011 /* irc-metadata.h */,
 				FA322CF20CEF74B1001761B3 /* irc-mode.c */,
@@ -412,19 +420,19 @@
 		FA322D0E0CEF74B1001761B3 /* portab */ = {
 			isa = PBXGroup;
 			children = (
-				FAA3D27C0F139CF800B2447E /* strtok_r.c */,
-				FAA3D27D0F139CF800B2447E /* waitpid.c */,
+				FA18A64716CEE14900132F66 /* Makefile.ng */,
 				FA322D100CEF74B1001761B3 /* ansi2knr.1 */,
 				FA322D110CEF74B1001761B3 /* ansi2knr.c */,
 				FA322D120CEF74B1001761B3 /* exp.h */,
 				FA322D130CEF74B1001761B3 /* imp.h */,
-				FA322D140CEF74B1001761B3 /* Makefile.am */,
 				FA322D150CEF74B1001761B3 /* portab.h */,
 				FA322D160CEF74B1001761B3 /* portabtest.c */,
 				FA322D170CEF74B1001761B3 /* splint.h */,
 				FA322D180CEF74B1001761B3 /* strdup.c */,
 				FA322D190CEF74B1001761B3 /* strlcpy.c */,
+				FAA3D27C0F139CF800B2447E /* strtok_r.c */,
 				FA322D1A0CEF74B1001761B3 /* vsnprintf.c */,
+				FAA3D27D0F139CF800B2447E /* waitpid.c */,
 			);
 			path = portab;
 			sourceTree = "<group>";
@@ -432,32 +440,33 @@
 		FA322D1B0CEF74B1001761B3 /* testsuite */ = {
 			isa = PBXGroup;
 			children = (
+				FA18A64A16CEE18100132F66 /* Makefile.ng */,
+				FA322D250CEF74B1001761B3 /* README */,
+				FA322D1D0CEF74B1001761B3 /* channel-test.e */,
+				FA322D1E0CEF74B1001761B3 /* check-idle.e */,
+				FA322D1F0CEF74B1001761B3 /* connect-test.e */,
 				FAA3D2700F139CB300B2447E /* invite-test.e */,
 				FAA3D2710F139CB300B2447E /* join-test.e */,
 				FAA3D2720F139CB300B2447E /* kick-test.e */,
 				FAA3D2730F139CB300B2447E /* message-test.e */,
-				FAA3D2740F139CB300B2447E /* ngircd-test1.conf */,
-				FAA3D2750F139CB300B2447E /* ngircd-test2.conf */,
+				FA18A64E16CEE24B00132F66 /* misc-test.e */,
+				FA18A64C16CEE1AC00132F66 /* mode-test.e */,
 				FAA3D2760F139CB300B2447E /* opless-channel-test.e */,
 				FAA3D2770F139CB300B2447E /* server-link-test.e */,
-				FA322D1D0CEF74B1001761B3 /* channel-test.e */,
-				FA322D1E0CEF74B1001761B3 /* check-idle.e */,
-				FA322D1F0CEF74B1001761B3 /* connect-test.e */,
+				FA322D280CEF74B1001761B3 /* stress-A.e */,
+				FA322D290CEF74B1001761B3 /* stress-B.e */,
+				FA1A6BBD0D6857D900AA8F71 /* who-test.e */,
+				FA18A64D16CEE1D900132F66 /* whois-test.e */,
 				FA322D200CEF74B1001761B3 /* functions.inc */,
+				FAA3D2740F139CB300B2447E /* ngircd-test1.conf */,
+				FAA3D2750F139CB300B2447E /* ngircd-test2.conf */,
 				FA322D210CEF74B1001761B3 /* getpid.sh */,
-				FA322D220CEF74B1001761B3 /* Makefile.am */,
-				FA1A6BBC0D6857BB00AA8F71 /* misc-test.e */,
-				FA322D230CEF74B1001761B3 /* mode-test.e */,
-				FA322D250CEF74B1001761B3 /* README */,
 				FA322D260CEF74B1001761B3 /* start-server.sh */,
 				FA322D270CEF74B1001761B3 /* stop-server.sh */,
-				FA322D280CEF74B1001761B3 /* stress-A.e */,
-				FA322D290CEF74B1001761B3 /* stress-B.e */,
 				FA322D2A0CEF74B1001761B3 /* stress-server.sh */,
 				FA322D2B0CEF74B1001761B3 /* test-loop.sh */,
 				FA322D2C0CEF74B1001761B3 /* tests.sh */,
 				FA322D2D0CEF74B1001761B3 /* wait-tests.sh */,
-				FA1A6BBD0D6857D900AA8F71 /* who-test.e */,
 			);
 			path = testsuite;
 			sourceTree = "<group>";
@@ -465,9 +474,7 @@
 		FA322D2E0CEF74B1001761B3 /* tool */ = {
 			isa = PBXGroup;
 			children = (
-				FA322D300CEF74B1001761B3 /* ansi2knr.1 */,
-				FA322D310CEF74B1001761B3 /* ansi2knr.c */,
-				FA322D320CEF74B1001761B3 /* Makefile.am */,
+				FA18A64F16CEE27700132F66 /* Makefile.ng */,
 				FA322D330CEF74B1001761B3 /* tool.c */,
 				FA322D340CEF74B1001761B3 /* tool.h */,
 			);
@@ -477,16 +484,18 @@
 		FA322D660CEF7523001761B3 /* contrib */ = {
 			isa = PBXGroup;
 			children = (
+				FA322D8D0CEF7523001761B3 /* Makefile.am */,
 				FA322D680CEF7523001761B3 /* Debian */,
 				FA322D730CEF7523001761B3 /* MacOSX */,
-				FA322D910CEF7523001761B3 /* Makefile.am */,
+				FA322D950CEF7523001761B3 /* README */,
 				FA322D920CEF7523001761B3 /* ngindent */,
 				FA4B08E513E7F8FB00765BA3 /* ngircd-bsd.sh */,
 				FA4B08E613E7F91700765BA3 /* ngIRCd-Logo.gif */,
 				FA4B08E713E7F91700765BA3 /* ngircd-redhat.init */,
+				FA18A63F16CEDE2300132F66 /* ngircd.service */,
+				FA18A64016CEDE2300132F66 /* ngircd.socket */,
 				FA322D940CEF7523001761B3 /* ngircd.spec */,
 				FA4B08E813E7F91C00765BA3 /* platformtest.sh */,
-				FA322D950CEF7523001761B3 /* README */,
 				FA322D960CEF7523001761B3 /* systrace.policy */,
 			);
 			name = contrib;
@@ -496,13 +505,14 @@
 		FA322D680CEF7523001761B3 /* Debian */ = {
 			isa = PBXGroup;
 			children = (
+				FA322D6E0CEF7523001761B3 /* Makefile.am */,
 				FA322D6A0CEF7523001761B3 /* changelog */,
 				FA322D6B0CEF7523001761B3 /* compat */,
 				FA322D6C0CEF7523001761B3 /* control */,
 				FA322D6D0CEF7523001761B3 /* copyright */,
-				FA322D6E0CEF7523001761B3 /* Makefile.am */,
 				FA322D6F0CEF7523001761B3 /* ngircd.default */,
 				FA322D700CEF7523001761B3 /* ngircd.init */,
+				FA18A64116CEDE3500132F66 /* ngircd.pam */,
 				FA322D710CEF7523001761B3 /* ngircd.postinst */,
 				FA322D720CEF7523001761B3 /* rules */,
 			);
@@ -512,88 +522,17 @@
 		FA322D730CEF7523001761B3 /* MacOSX */ = {
 			isa = PBXGroup;
 			children = (
+				FA18A64316CEDE8100132F66 /* Makefile.am */,
 				FAA3D2810F139D2E00B2447E /* ngIRCd.pmdoc */,
+				FA322DB10CEF7565001761B3 /* config.h */,
+				FA18A64216CEDE5700132F66 /* de.barton.ngircd.plist.tmpl */,
+				FA322D8E0CEF7523001761B3 /* ngIRCd.xcodeproj */,
 				FAA3D28A0F139D2E00B2447E /* postinstall.sh */,
 				FAA3D28B0F139D2E00B2447E /* preinstall.sh */,
-				FA322D750CEF7523001761B3 /* build */,
-				FA322D8D0CEF7523001761B3 /* Makefile.am */,
-				FA322D8E0CEF7523001761B3 /* ngIRCd.xcodeproj */,
-				FA322DB10CEF7565001761B3 /* config.h */,
 			);
 			path = MacOSX;
 			sourceTree = "<group>";
 		};
-		FA322D750CEF7523001761B3 /* build */ = {
-			isa = PBXGroup;
-			children = (
-				FA322D760CEF7523001761B3 /* ngIRCd.build */,
-			);
-			path = build;
-			sourceTree = "<group>";
-		};
-		FA322D760CEF7523001761B3 /* ngIRCd.build */ = {
-			isa = PBXGroup;
-			children = (
-				FA322D770CEF7523001761B3 /* Default */,
-				FA322D7F0CEF7523001761B3 /* ngIRCd.pbxindex */,
-			);
-			path = ngIRCd.build;
-			sourceTree = "<group>";
-		};
-		FA322D770CEF7523001761B3 /* Default */ = {
-			isa = PBXGroup;
-			children = (
-				FA322D780CEF7523001761B3 /* ngIRCd.build */,
-			);
-			path = Default;
-			sourceTree = "<group>";
-		};
-		FA322D780CEF7523001761B3 /* ngIRCd.build */ = {
-			isa = PBXGroup;
-			children = (
-				FA322D7A0CEF7523001761B3 /* Objects-normal */,
-			);
-			path = ngIRCd.build;
-			sourceTree = "<group>";
-		};
-		FA322D7A0CEF7523001761B3 /* Objects-normal */ = {
-			isa = PBXGroup;
-			children = (
-				FA322D7B0CEF7523001761B3 /* i386 */,
-				FA322D7D0CEF7523001761B3 /* ppc */,
-			);
-			path = "Objects-normal";
-			sourceTree = "<group>";
-		};
-		FA322D7B0CEF7523001761B3 /* i386 */ = {
-			isa = PBXGroup;
-			children = (
-			);
-			path = i386;
-			sourceTree = "<group>";
-		};
-		FA322D7D0CEF7523001761B3 /* ppc */ = {
-			isa = PBXGroup;
-			children = (
-			);
-			path = ppc;
-			sourceTree = "<group>";
-		};
-		FA322D7F0CEF7523001761B3 /* ngIRCd.pbxindex */ = {
-			isa = PBXGroup;
-			children = (
-				FA322D880CEF7523001761B3 /* strings.pbxstrings */,
-			);
-			path = ngIRCd.pbxindex;
-			sourceTree = "<group>";
-		};
-		FA322D880CEF7523001761B3 /* strings.pbxstrings */ = {
-			isa = PBXGroup;
-			children = (
-			);
-			path = strings.pbxstrings;
-			sourceTree = "<group>";
-		};
 		FA322D8F0CEF7523001761B3 /* Products */ = {
 			isa = PBXGroup;
 			children = (
@@ -605,8 +544,10 @@
 			isa = PBXGroup;
 			children = (
 				FA322D9B0CEF752C001761B3 /* Makefile.am */,
+				FA322DA20CEF752C001761B3 /* src */,
 				FAE22BD215270EA300F1A5AB /* Bopm.txt */,
 				FAD5852F15271A7800328741 /* Capabilities.txt */,
+				FA18A64416CEDFCE00132F66 /* Commands.txt */,
 				FAE22BD415270EA300F1A5AB /* Contributing.txt */,
 				FA322D9A0CEF752C001761B3 /* FAQ.txt */,
 				FA407F380DB15AC700271AF1 /* GIT.txt */,
@@ -622,7 +563,6 @@
 				FAA3D2800F139D1500B2447E /* Services.txt */,
 				FA322DA90CEF752C001761B3 /* SSL.txt */,
 				FA77849A133FB9FF00740057 /* sample-ngircd.conf.tmpl */,
-				FA322DA20CEF752C001761B3 /* src */,
 			);
 			name = doc;
 			path = ../../doc;
@@ -631,11 +571,9 @@
 		FA322DA20CEF752C001761B3 /* src */ = {
 			isa = PBXGroup;
 			children = (
+				FA322DA70CEF752C001761B3 /* Makefile.am */,
 				FA322DA40CEF752C001761B3 /* Doxyfile */,
 				FA322DA50CEF752C001761B3 /* footer.inc.html */,
-				FA322DA60CEF752C001761B3 /* header.inc.html */,
-				FA322DA70CEF752C001761B3 /* Makefile.am */,
-				FA322DA80CEF752C001761B3 /* ngircd-doc.css */,
 			);
 			path = src;
 			sourceTree = "<group>";
@@ -654,7 +592,7 @@
 		FA407F270DB1598D00271AF1 /* ipaddr */ = {
 			isa = PBXGroup;
 			children = (
-				FA407F2B0DB159F400271AF1 /* Makefile.am */,
+				FA18A64516CEE0C700132F66 /* Makefile.ng */,
 				FA407F2C0DB159F400271AF1 /* ng_ipaddr.c */,
 				FA407F2D0DB159F400271AF1 /* ng_ipaddr.h */,
 			);
@@ -664,12 +602,12 @@
 		FAA3D2810F139D2E00B2447E /* ngIRCd.pmdoc */ = {
 			isa = PBXGroup;
 			children = (
-				FAA3D2820F139D2E00B2447E /* 01ngircd-contents.xml */,
+				FAA3D2880F139D2E00B2447E /* Makefile.am */,
+				FAA3D2860F139D2E00B2447E /* index.xml */,
 				FAA3D2830F139D2E00B2447E /* 01ngircd.xml */,
-				FAA3D2840F139D2E00B2447E /* 02de-contents.xml */,
+				FAA3D2820F139D2E00B2447E /* 01ngircd-contents.xml */,
 				FAA3D2850F139D2E00B2447E /* 02de.xml */,
-				FAA3D2860F139D2E00B2447E /* index.xml */,
-				FAA3D2880F139D2E00B2447E /* Makefile.am */,
+				FAA3D2840F139D2E00B2447E /* 02de-contents.xml */,
 			);
 			path = ngIRCd.pmdoc;
 			sourceTree = "<group>";

+ 2 - 1
contrib/Makefile.am

@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -17,6 +17,7 @@ EXTRA_DIST = README \
 	ngIRCd-Logo.gif \
 	ngircd-redhat.init \
 	ngircd.service \
+	ngircd.socket \
 	ngircd.spec \
 	platformtest.sh \
 	systrace.policy

+ 2 - 1
contrib/Makefile.in

@@ -17,7 +17,7 @@
 
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -208,6 +208,7 @@ EXTRA_DIST = README \
 	ngIRCd-Logo.gif \
 	ngircd-redhat.init \
 	ngircd.service \
+	ngircd.socket \
 	ngircd.spec \
 	platformtest.sh \
 	systrace.policy

+ 11 - 2
contrib/README

@@ -2,18 +2,21 @@
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
 
-               (c)2001-2011 Alexander Barton and Contributors.
+               (c)2001-2013 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
 
-                            -- Contributions --
+                             -- Contributions --
 
 
 Debian/
  - Various files for building Debian GNU/Linux packages (".deb's").
+	- ngircd.init; ngircd.default: init script for Debian-based systems.
+	- ngircd.pam: example PAM configuraton.
 
 MacOSX/
  - Project files for XCode, the "project builder" of Apple Mac OS X.
+	- de.barton.ngircd.plist[.tmpl]: launchd(8) property list.
 
 ngindent
  - Script to indent the code of ngIRCd in the "standard way".
@@ -24,6 +27,12 @@ ngircd-bsd.sh
 ngircd-redhat.init
  - Start/stop script for RedHat-based distributions (like CentOS).
 
+ngircd.service
+ - systemd(8) service unit configuration file.
+
+ngircd.socket
+ - systemd(8) socket unit configuration file for "socket activation".
+
 ngircd.spec
  - RPM "spec" file.
 

+ 2 - 2
contrib/ngircd.service

@@ -3,8 +3,8 @@ Description=Next Generation IRC Daemon
 After=network.target
 
 [Service]
-# don't daemonize to simplify stuff
-ExecStart=/usr/sbin/ngircd -n
+Type=forking
+ExecStart=/usr/sbin/ngircd
 ExecReload=/bin/kill -HUP $MAINPID
 
 [Install]

+ 11 - 0
contrib/ngircd.socket

@@ -0,0 +1,11 @@
+[Unit]
+Description=Next Generation IRC Daemon (Socket)
+
+[Socket]
+BindIPv6Only=ipv6-only
+ListenStream=0.0.0.0:6667
+#ListenStream=[::]:6667
+IPTOS=low-delay
+
+[Install]
+WantedBy=sockets.target

+ 1 - 1
contrib/ngircd.spec

@@ -1,5 +1,5 @@
 %define name    ngircd
-%define version 20.3
+%define version 21
 %define release 1
 %define prefix  %{_prefix}
 

+ 41 - 18
contrib/platformtest.sh

@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -54,6 +54,16 @@ if [ $? -ne 0 ]; then
 	cd ..
 fi
 
+echo "$NAME: Checking for GIT tree ..."
+if [ -d .git ]; then
+	echo "$NAME: Checking for \"git\" command ..."
+	git version >/dev/null 2>&1
+	if [ $? -eq 0 ]; then
+		echo "$NAME: Running \"git clean\" ..."
+		[ -n "$VERBOSE" ] && git clean -dxf || git clean -dxf >/dev/null
+	fi
+fi
+
 echo "$NAME: Checking for \"./configure\" script ..."
 if [ ! -e ./configure ]; then
 	echo "$NAME: Running \"./autogen.sh\" ..."
@@ -99,6 +109,7 @@ if [ -r "Makefile" ]; then
 	CC=$(grep "^CC = " Makefile | cut -d' ' -f3)
 	$CC --version 2>&1 | grep -i "GCC" >/dev/null
 	if [ $? -eq 0 ]; then
+		# GCC, or compiler that mimics GCC
 		$CC --version 2>&1 | grep -i "Open64" >/dev/null
 		if [ $? -eq 0 ]; then
 			COMPILER="Open64"
@@ -108,12 +119,24 @@ if [ -r "Makefile" ]; then
 			COMPILER="gcc $COMPILER"
 		fi
 	else
-		case "$CC" in
-		  gcc*)
+		# Non-GCC compiler
+		$CC --version 2>&1 | grep -i "clang" >/dev/null
+		if [ $? -eq 0 ]; then
+			COMPILER=$($CC --version 2>/dev/null | head -1 \
+			  | cut -d'(' -f1 | cut -d'-' -f1 \
+			  | sed -e 's/version //g' | sed -e 's/Apple /A-/g' \
+			  | sed -e 's/Debian //g' | sed -e 's/LLVM /clang /g')
+		fi
+		$CC -version 2>&1 | grep -i "tcc" >/dev/null
+		if [ $? -eq 0 ]; then
+			COMPILER=$($CC -version 2>/dev/null | head -1 \
+			  | cut -d'(' -f1 | sed -e 's/version //g')
+		fi
+		if [ "$COMPILER" = "unknown" ]; then
 			v="`$CC --version 2>/dev/null | head -1`"
-			[ -n "$v" ] && COMPILER="gcc $v"
-			;;
-		esac
+			[ -z "$v" ] && v="`$CC -version 2>/dev/null | head -1`"
+			[ -n "$v" ] && COMPILER="$v"
+		fi
 	fi
 fi
 
@@ -128,14 +151,14 @@ esac
 
 # Get IO interface information
 if [ "$OS" = "linux-gnu" ]; then
-	COMMENT="(1)"
+	COMMENT="1"
 else
 	grep "^#define HAVE_SYS_DEVPOLL_H 1" src/config.h >/dev/null 2>&1
-	[ $? -eq 0 ] && COMMENT="(4)"
+	[ $? -eq 0 ] && COMMENT="4"
 	grep "^#define HAVE_EPOLL_CREATE 1" src/config.h >/dev/null 2>&1
-	[ $? -eq 0 ] && COMMENT="(5)"
+	[ $? -eq 0 ] && COMMENT="5"
 	grep "^#define HAVE_KQUEUE 1" src/config.h >/dev/null 2>&1
-	[ $? -eq 0 ] && COMMENT="(3)"
+	[ $? -eq 0 ] && COMMENT="3"
 fi
 
 [ -n "$R_CONFIGURE" ] && C="Y" || C="N"
@@ -145,16 +168,16 @@ fi
 [ -n "$COMMENT" ] && COMMENT=" $COMMENT"
 
 echo
-echo "                              the executable works (\"runs\") as expected --+"
-echo "                                tests run successfully (\"make check\") --+ |"
-echo "                                           ngIRCd compiles (\"make\") --+ | |"
-echo "                                                ./configure works --+ | | |"
-echo "                                                                    | | | |"
-echo "Platform                    Compiler     ngIRCd     Date     Tester C M T R See"
-echo "--------------------------- ------------ ---------- -------- ------ - - - - ---"
+echo "                                the executable works (\"runs\") as expected --+"
+echo "                                  tests run successfully (\"make check\") --+ |"
+echo "                                             ngIRCd compiles (\"make\") --+ | |"
+echo "                                                  ./configure works --+ | | |"
+echo "                                                                      | | | |"
+echo "Platform                    Compiler     ngIRCd     Date     Tester   C M T R *"
+echo "--------------------------- ------------ ---------- -------- -------- - - - - -"
 type printf >/dev/null 2>&1
 if [ $? -eq 0 ]; then
-	printf "%-27s %-12s %-10s %s %-6s %s %s %s %s%s" \
+	printf "%-27s %-12s %-10s %s %-8s %s %s %s %s%s" \
 	 "$PLATFORM" "$COMPILER" "$VERSION" "$DATE" "$USER" \
 	 "$C" "$M" "$T" "$R" "$COMMENT"
 else

+ 991 - 0
doc/Commands.txt

@@ -0,0 +1,991 @@
+
+                     ngIRCd - Next Generation IRC Server
+                           http://ngircd.barton.de/
+
+               (c)2001-2013 Alexander Barton and Contributors.
+               ngIRCd is free software and published under the
+                   terms of the GNU General Public License.
+
+                              -- Commands.txt --
+
+
+This file lists all commands available on ngIRCd. It is written in a format
+that is human readable as well as machine parseable and therefore can be used
+as "help text file" of the daemon.
+
+In short, the daemon reads this file on startup and parses it as following
+when an user issues a "HELP <cmd>" command:
+
+ 1. Search the file for a line "- <cmd>",
+ 2. Output all subsequent lines that start with a TAB (ASCII 9) character
+    to the client using NOTICE commands, treat lines containing a single "."
+    after the TAB as empty lines.
+ 3. Break at the first line not starting with a TAB character.
+
+This format allows to have information to each command stored in this file
+which will not be sent to an IRC user requesting help which enables us to
+have additional annotations stored here which further describe the origin,
+implementation details, or limits of the specific command which are not
+relevant to an end-user but administrators and developers.
+
+A special "Intro" block is returned to the user when the HELP command is
+used without a command name:
+
+
+- Intro
+	This is ngIRCd, a server software for Internet Relay Chat (IRC)
+	networks. You can find more information about ngIRCd on its homepage:
+		<http://ngircd.barton.de>
+	.
+	Use "HELP COMMANDS" to get a list of all available commands and
+	"HELP <command-name>" to get help for a specific IRC command, for
+	example "HELP quit" or "HELP privmsg".
+
+
+Connection Handling Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- CAP
+	CAP LS
+	CAP LIST
+	CAP REQ <capabilities>
+	CAP ACK <capabilities>
+	CAP NAK <capabilities>
+	CAP CLEAR
+	CAP END
+	.
+	List, request, and clear "IRC Capabilities".
+	.
+	Using this command, an IRC client can request additional "IRC
+	capabilities" during login or later on, which influences the
+	communication between server and client. Normally, these commands
+	aren't directly used by humans, but automatically by their client
+	software. And please note that issuing such commands manually can
+	irritate the client software used, because of the "non-standard"
+	behavior of the server!
+	.
+	- CAP LS: list all available capabilities.
+	- CAP LIST: list active capabilities of this connection.
+	- CAP REQ: Request particular capabilities.
+	- CAP ACK: Acknowledge a set of capabilities to be enabled/disabled.
+	- CAP NAK: Reject a set of capabilities.
+	- CAP CLEAR: Clear all set capabilities.
+	- CAP END: Indicate end of capability negotiation during login,
+	  ignored in an fully registered session.
+
+	Please note that the <capabilities> must be given in a single
+	parameter but whitespace separated, therefore a command could look
+	like this: "CAP REQ :capability1 capability2 capability3" for example.
+
+	References:
+	 - <http://ircv3.atheme.org/specification/capability-negotiation-3.1>
+	 - <http://ngircd.barton.de/doc/Capabilities.txt>
+	 - doc/Capabilities.txt
+
+- CHARCONV
+	CHARCONV <client-charset>
+	.
+	Set client character set encoding to <client-charset>.
+	.
+	After receiving such a command, the server translates all message
+	data received from the client using the set <client-charset> to the
+	server encoding (UTF-8), and all message data which is to be sent to
+	the client from the server encoding (UTF-8) to <client-charset>.
+	.
+	This enables older clients and clients using "strange" character sets
+	to transparently participate in channels and direct messages to
+	clients using UTF-8, which should be the default today.
+
+	References:
+	 - IRC+, <http://ngircd.barton.de/doc/Protocol.txt>
+	 - IRC+, doc/Protocol.txt
+
+- NICK
+	NICK <nickname>
+	NICK <nickname> [<hops>]
+	NICK <nickname> <hops> <username> <host> <servertoken> <usermodes> <realname>
+	.
+	Set or change the <nickname> of a client (first form) and register
+	remote clients (second and third form; servers only).
+
+	References:
+	 - RFC 1459, 4.1.2 "Nick message" (old client and server protocol)
+	 - RFC 2812, 3.1.2 "Nick message" (client protocol)
+	 - RFC 2813, 4.1.3 "Nick" (server protocol)
+
+- PASS
+	PASS <password>
+	PASS <password> <version> <flags> [<options>]
+	.
+	Set a connection <password>. This command must be the first command
+	sent to the server, even before the NICK/USER or SERVER commands.
+	.
+	The first form is used by user sessions or (old) RFC 1459 servers,
+	the second form is used by RFC 2812 or IRC+ compliant servers and
+	enables the server to indicate its version and supported protocol
+	features.
+
+	References:
+	 - RFC 1459, 4.1.1 "Password message" (old client and server protocol)
+	 - RFC 2812, 3.1.1 "Password message" (client protocol)
+	 - RFC 2813, 4.1.1 "Password message" (server protocol)
+	 - IRC+, <http://ngircd.barton.de/doc/Protocol.txt>
+	 - IRC+, doc/Protocol.txt
+
+- PING
+	PING <token> [<target>]
+	.
+	Tests the presence of a connection to a client or server.
+	.
+	If no <target> has been given, the local server is used. User clients
+	can only use other servers as <target>, no user clients.
+	.
+	A PING message results in a PONG reply containing the <token>, which
+	can be arbitrary text.
+
+	Please note:
+	The RFCs state that the <token> parameter is used to specify the
+	origin of the PING command when forwarded in the network, but this
+	is not the case: the sender is specified using the prefix as usual,
+	and the parameter is used to identify the PONG reply in practice.
+
+	References:
+	 - RFC 2812, 3.7.2 "Ping message"
+
+- PONG
+	PONG <target> [<token>]
+	.
+	Reply to a "PING" command, indicate that the connection is alive.
+	.
+	The <token> is the arbitrary text received in the "PING" command and
+	can be used to identify the correct PONG sent as answer.
+	.
+	When the "PONG" command is received from a user session, the <target>
+	parameter is ignored; otherwise the PONG is forwarded to this client.
+
+	References:
+	 - RFC 2812, 3.7.3 "Pong message"
+
+- QUIT
+	QUIT [<quit-message>]
+	.
+	Terminate a user session.
+	.
+	When received from a user, the server acknowledges this by sending
+	an "ERROR" message back to the client and terminates the connection.
+	.
+	When a <quit-message> has been given, it is sent to all the channels
+	that the client is a member of when leaving.
+
+	References:
+	 - RFC 2812, 3.1.7 "Quit"
+	 - RFC 2813, 4.1.5 "Quit"
+
+- USER
+	USER <username> <hostname> <unused> <realname>
+	.
+	Register (and authenticate) a new user session with a short <username>
+	and a human-readable <realname>.
+	.
+	The parameter <hostname> is only used when received by an other server
+	and ignored otherwise; and the parameter <unused> is always ignored.
+	But both parameters are required on each invocation by the protocol
+	and can be set to arbitrary characters/text when not used.
+	.
+	If <username> contains an "@" character, the full <username> is used
+	for authentication, but only the first part up to this character is
+	set as "user name" for this session.
+
+	References:
+	 - RFC 2812, 3.1.3 "User message"
+
+- WEBIRC
+	WEBIRC <password> <username> <hostname> <ip-address>
+	.
+	Allow Web-to-IRC gateway software (for example) to set the correct
+	user name and host name of users instead of their own.
+	.
+	It must be the very first command sent to the server, even before
+	USER and NICK commands!
+	.
+	The <password> must be set in the server configuration file to prevent
+	unauthorized clients to fake their identity; it is an arbitrary string.
+
+	References:
+	 - IRC+, <http://ngircd.barton.de/doc/Protocol.txt>
+	 - IRC+, doc/Protocol.txt
+
+
+General Commands
+~~~~~~~~~~~~~~~~
+
+- AWAY
+	AWAY [<message>]
+	.
+	Provides the server with a message to automatically send in reply to a
+	PRIVMSG directed at the user, but not to a channel they are on.
+	.
+	If <message> is omitted, the away status is removed.
+
+	References:
+	 - RFC 2812, 4.1 "Away"
+
+- HELP
+	HELP [<command>]
+	.
+	Show help information for a specific IRC <command>. The <command> name
+	is case-insensitive.
+	.
+	Use the command "HELP Commands" to get a list of all available commands.
+
+	The HELP command isn't specified by any RFC but implemented by most
+	daemons. If no help text could be read in, ngIRCd outputs a list of all
+	implemented commands when receiving a plain "HELP" command as well as
+	on "HELP Commands".
+
+	ngIRCd replies using "NOTICE" commands like ircd 2.10/2.11; other
+	implementations are using numerics 704, 705, and 706.
+
+- MODE
+	MODE <nickname> [{+|-}<mode>[<mode>] [{+|-}<mode>[<mode>] [...]]]
+	MODE <channel> [{+|-}<mode>[<mode>] [<arg> [<arg> [...]]] [{+|-}<mode>[<mode>] [<arg> [<arg> [...]]] [...]]]
+	.
+	Set and get user and channel modes.
+	.
+	When no mode parameters are given, the currently set user or channel
+	modes are returned. Otherwise the modes are adjusted accordingly
+	and the changes will be reported back to the client.
+	.
+	All user and channel "modes" are indicated by single case-sensitive
+	characters.
+	.
+	Please note that a user can only get and set his own modes, and not
+	all user "levels" are allowed to change all channel modes ...
+	.
+	The mode parameters can become quite complex, especially when dealing
+	with channel modes that require additional arguments:
+	.
+	  {+|-}<mode(s}>  -- set or unset one or more modes.
+	  +<mode(s)> -<mode(s)>  -- set some modes and unset others.
+	  +<modes> <arg1> <arg2>  -- set (at least) two modes with arguments.
+	.
+	Some examples:
+	.
+	  MODE nick +i  -- set user to "invisible".
+	  MODE #chan +tn  -- set "topic lock" and "no external messages".
+	  MODE #chan -t +l 50  -- remove "topic lock", set "user limit" to 50.
+	  MODE #chan +ov nick1 nick2  -- set "channel op" and "voice" mode
+	                                 to nick1 and nick2 in channel #chan.
+	.
+	A complete list of all modes supported by ngIRCd can be found online
+	here: <http://ngircd.barton.de/doc/Modes.txt>.
+
+	References:
+	 - RFC 2811, 4. "Channel Modes"
+	 - RFC 2812, 3.1.5 "User mode message"
+	 - RFC 2812, 3.2.3 "Channel mode message"
+	 - <http://ngircd.barton.de/doc/Modes.txt>
+	 - doc/Modes.txt
+
+- NOTICE
+	NOTICE <target>[,<target>[,...]] <message>
+	.
+	Send a <message> to a given <target>, which can be a user or a
+	channel, but DON'T report any error.
+	.
+	The "NOTICE" command exactly behaves like the "PRIVMSG" command, but
+	doesn't report any errors it encounters (like an unknown <target>).
+	Please see the help text of the "PRIVMSG" command for a detailed
+	description of the parameters!
+
+	References:
+	 - RFC 2812, 2.3.1 "Message format in Augmented BNF"
+	 - RFC 2812, 3.3 "Sending messages"
+	 - RFC 2812, 3.3.2 "Notice"
+
+- PRIVMSG
+	PRIVMSG <target>[,<target>[,...]] <message>
+	.
+	Send a <message> to a given <target>, which can be a user or a
+	channel, and report all errors.
+	.
+	The <target> must follow one of these syntax variants:
+	.
+	 - <nickname>
+	 - <channel>
+	 - <user>[%<host>]@<server>
+	 - <user>%<host>
+	 - <nickname>!<user>@<host>
+	.
+	If the <target> is a user, a private message is sent directly to this
+	user; if it resolves to a channel name, a public message is sent
+	to all the members of that channel.
+	.
+	In addition, IRC Ops can use these two forms to specify the <target>:
+	.
+	 - #<hostmask>
+	 - #<servermask>
+	.
+	The <mask> can contain the wildcard characters "*" and "?", but must
+	contain at least one dot (".") and no wildcard after the last one.
+	Then, the <message> is sent to all users matching this <mask>.
+	.
+	All warnings and errors are reported back to the initiator using
+	numeric status codes, which is the only difference to the "NOTICE"
+	command, which doesn't report back any errors or warnings at all.
+	.
+	Please note that clients often use "MSG" as an alias to PRIVMSG, and
+	a command "QUERY <nick> [<message>]" to initiate private chats. Both
+	are command extensions of the client and never sent to the server.
+
+	References:
+	 - RFC 2812, 2.3.1 "Message format in Augmented BNF"
+	 - RFC 2812, 3.3 "Sending messages"
+	 - RFC 2812, 3.3.1 "Private messages"
+
+Status and Informational Commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- ADMIN
+	ADMIN [<target>]
+	.
+	Show administrative information about an IRC server in the network.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+
+	References:
+	 - RFC 2812, 3.4.9 "Admin command"
+
+- INFO
+	INFO [<target>]
+	.
+	Show the version, birth & online time of an IRC server in the network.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+
+	References:
+	 - RFC 2812, 3.4.10 "Info command"
+
+- ISON
+	ISON <nickname> [<nickname> [...]]
+	.
+	Query online status of a list of nicknames. The server replies with
+	a list only containing nicknames actually connected to a server in
+	the network. If no nicknames of the given list are online, an empty
+	list is returned to the client requesting the information.
+
+	Please note that "all" IRC daemons even parse separate nicknames in
+	a single parameter (like ":nick1 nick2"), and therefore ngIRCd
+	implements this behaviour, too.
+
+	References:
+	 - RFC 2812, 4.9 "Ison message"
+
+- LINKS
+	LINKS [[<target>] [<mask>]
+	.
+	List all servers currently registered in the network matching <mask>,
+	or all servers if <mask> has been omitted, as seen by the server
+	specified by <target> or the local server when <target> is omitted.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+
+	References:
+	 - RFC 2812, 3.4.5 "Links message"
+
+- LUSERS
+	LUSERS [<mask> [<target>]]
+	.
+	Return statistics about the number of clients (users, servers,
+	services, ...) in the network as seen by the server <target>.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+
+	Please note that ngIRCd ignores the <mask> parameter entirely: it
+	is not possible to get information for a part of the network only.
+
+	References:
+	 - RFC 2812, 3.4.2 "Lusers message"
+
+- MOTD
+	MOTD [<target>]
+	.
+	Show the "Message of the Day" (MOTD) of an IRC server in the network.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+
+	References:
+	 - RFC 2812, 3.4.1 "Motd message"
+
+- NAMES
+	NAMES [<channel>[,<channel>[,...]] [<target>]]
+	.
+	Show the list of users that are members of a particular <channel>
+	(and that are visible for the client requesting this information) as
+	seen by the server <target>. More than one <channel> can be given
+	separated by "," (but not whitespaces!).
+	.
+	If <channel> has been omitted, all visible users are shown, grouped
+	by channel name, and all visible users not being members of at least
+	one channel are shown as members of the pseudo channel "*".
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+
+	References:
+	 - RFC 2812, 3.2.5 "Names message"
+
+- STATS
+	STATS [<query> [<target>]]
+	.
+	Show statistics and other information of type <query> of a particular
+	IRC server in the network.
+	.
+	The following <query> types are supported (case-insensitive where
+	applicable):
+	.
+	 - g  Network-wide bans ("G-Lines").
+	 - k  Server-local bans ("K-Lines").
+	 - L  Link status (servers and user links).
+	 - l  Link status (servers and own link).
+	 - m  Command usage count.
+	 - u  Server uptime.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+	.
+	To use "STATS L" the user must be an IRC Operator.
+
+	References:
+	 - RFC 2812, 3.4.4 "Stats message"
+
+- TIME
+	TIME [<target>]
+	.
+	Show the local time of an IRC server in the network.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+
+	References
+	 - RFC 2812, 3.4.6 "Time message"
+
+- TRACE
+	TRACE [<target>]
+	.
+	Find the route to a specific server and send information about its
+	peers. Each server that processes this command reports back to the
+	sender about it: the replies from pass-through servers form a chain
+	which shows the route to the destination.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+
+	References:
+	 - RFC 2812, 3.4.8 "Trace message"
+
+- USERHOST
+	USERHOST <nickname> [<nickname> [...]]
+	.
+	Show flags and the hostmasks (<user>@<host>) of the <nickname>s,
+	separated by spaces. The following flags are used:
+	.
+	 - "-"  The client is "away" (the mode "+a" is set on this client).
+	 - "+"  Client seems to be available, at least it isn't marked "away".
+	 - "*"  The client is an IRC operator (the mode "+o" is set).
+
+	References:
+	 - RFC 2812, 4.8 "Userhost message"
+
+- VERSION
+	VERSION [<target>]
+	.
+	Show version information about a particular IRC server in the network.
+	.
+	<target> can be a server name, the nickname of a client connected to
+	a specific server, or a mask matching a server name in the network.
+	The server of the current connection is used when <target> is omitted.
+	.
+	Please note: in normal operation, the version number ends in a dot
+	(".", for example "ngIRCd-20.1."). If it ends in ".1" (for example
+	"ngIRCd-20.1.1", same version than before!), the server is running in
+	debug-mode; and if it ends in ".2", the "network sniffer" is active!
+	Keep your privacy in mind ...
+
+	References:
+	 - RFC 2812, 3.4.3 "Version message"
+
+- WHO
+	WHO [<mask> ["o"]]
+	.
+	Show a list of users who match the <mask>, or all visible users when
+	the <mask> has been omitted. (Special case: the <mask> "0" is
+	equivalent to "*")
+	.
+	If the flag "o" is given, the server will only return information about
+	IRC Operators.
+
+	References:
+	 - RFC 2812, 3.6.1 "Who query"
+
+- WHOIS
+	WHOIS [<target>] <mask>[,<mask>[,...]]
+	.
+	Query information about users matching the <mask> parameter(s) as seen
+	by the server <target>; up to 3 <masks> are supported.
+	.
+	<target> can be a server name, the nickname of a client connected to a
+	specific server, or a mask matching a server name in the network. The
+	server of the current connection is used when <target> is omitted.
+
+	References:
+	 - RFC 2812, 3.6.2 "Whois query"
+
+- WHOWAS
+	WHOWAS <nickname>[,<nickname>[,...]] [<count> [<target>]]
+	.
+	Query information about nicknames no longer in use in the network,
+	either because of nickname changes or disconnects. The history is
+	searched backwards, returning the most recent entry first. If there
+	are multiple entries, up to <count> entries will be shown (or all of
+	them, if no <count> has been given).
+	.
+	<target> can be a server name, the nickname of a client connected to a
+	specific server, or a mask matching a server name in the network. The
+	server of the current connection is used when <target> is omitted.
+
+	References:
+	 - RFC 2812, 3.6.3 "Whowas"
+
+
+Channel Commands
+~~~~~~~~~~~~~~~~
+
+- INVITE
+	INVITE <nickname> <channel>
+	.
+	Invite <nickname> to join channel <channel>.
+	.
+	<channel> does not have to exist, but if it does, only members of the
+	channel are allowed to invite other users. If the channel mode "+i"
+	is set, only channel "half-ops" (and above) may invite other clients,
+	and if channel mode "+V" is set, nobody can invite other users.
+
+	References:
+	 - RFC 2812, 3.2.7 "Invite message"
+
+- JOIN
+	JOIN {<channel>[,<channel>[,...]] [<key>[,<key>[,...]]] | 0}
+	.
+	Makes the client join the <channel> (comma-separated list), specifying
+	the channel keys ("passwords"). A <channel-key> is only needed if the
+	<channel> has the mode "+k" set.
+	.
+	If the channel(s) do not exist, then they will be created.
+	.
+	Using "JOIN 0" parts all channels at once.
+
+	References:
+	 - RFC 2812, 3.2.1 "Join message" (client protocol)
+	 - RFC 2813, 4.2.1 "Join message" (server protocol)
+
+- KICK
+	KICK <channel>[,<channel>[,...]] <nickname>[,<nickname>[,...]] [<reason>]
+	.
+	Remove users(s) with <nickname>(s) from <channel>(s).
+	.
+	There must be either exactly one <channel> parameter and multiple
+	<nickname> parameters, or as many <channel> parameters as there are
+	<nickname> parameters. The <reason> is shown to the users being
+	kicked, and the nickname of the current user is used when <reason>
+	is omitted.
+
+	References:
+	 - RFC 2812, 3.2.8 "Kick command"
+
+- LIST
+	LIST [<channel>[,<channel>[,...]] [<server>]]
+	.
+	List all visible <channels> (comma-separated list).
+	.
+	If <server> is given, the command will be forwarded to <server> for
+	evaluation.
+
+	References:
+	 - RFC 2812, 3.2.6 "List message"
+
+- PART
+	PART <channel>[,<channel>[,...]] [<part-message>]
+	.
+	Leave <channel> (comma-separated list), optionally with sending a
+	<part-message> to all the other channel members.
+
+	References:
+	 - RFC 2812, 3.2.2 "Part message"
+
+- TOPIC
+	TOPIC <channel> [<topic>]
+	.
+	Change or view the topic of a channel.
+	.
+	The topic for channel <channel> is returned if there is no <topic>
+	given. If the <topic> parameter is present, the topic for that
+	channel will be changed, if this action is allowed for the user
+	requesting it. If the <topic> parameter is an empty string, the
+	topic for that channel will be removed.
+
+	References:
+	 - RFC 2812, 3.2.4 "Topic message"
+
+
+Administrative Commands
+~~~~~~~~~~~~~~~~~~~~~~~
+
+- CONNECT
+	CONNECT <server> [<port> [<remote-server> [<my-pwd> <peer-pwd>]]]
+	.
+	Instructs the current server, or <remote-server> if specified,
+	to connect to the server named <server>, which must be configured
+	in the server configuration file.
+	.
+	To use this command, the user must be an IRC Operator. To establish
+	a connection on a <remote-server>, you must have remote IRC operator
+	privileges.
+	.
+	If <port>, <my-pwd> and <peer-pwd> are given, these values override
+	the ones specified in the server configuration file.
+
+	References:
+	 - RFC 2812, 3.4.7 "Connect message"
+
+- DIE
+	DIE [<message>]
+	.
+	Instructs the server to shut down.
+	.
+	The optional (and non-standard) <message> text is sent to each client
+	connected to this server before all connections are closed.
+	.
+	To use this command, the user must be an IRC Operator.
+
+	References:
+	 - RFC 2812, 4.3 "Die message"
+
+- DISCONNECT
+	DISCONNECT <server>
+	.
+	Disconnect and disable a locally linked server.
+	.
+	To use this command, the user must be an IRC Operator.
+
+	References:
+	 - This command is not specified in the IRC RFCs, it is an extension
+	   of ngIRCd.
+
+- GLINE
+	GLINE <nick!user@hostmask> [<timeout> :<reason>]
+	.
+	This command provides timed G-Lines (network-wide bans).
+	.
+	If a client matches a G-Line, it cannot connect to any server on
+	the IRC network for <timeout> seconds. When <timeout> is 0, it make
+	the G-Line permanent.
+	.
+	If no <timeout> and no <reason> is given, the G-Line is removed.
+	.
+	To use this command, the user must be an IRC Operator.
+	.
+	"STATS g" can be used to list all currently active G-Lines.
+
+	References:
+	 - This command is not specified in the IRC RFCs, it is an extension
+	   of ngIRCd.
+
+- KILL
+	KILL <nickname> <reason>
+	.
+	Forcibly remove all users with a given <nickname> from the IRC
+	network and display the given <reason> to them.
+	.
+	This command is used internally between servers, too, for example
+	to disconnect duplicate <nickname>'s after a "net split".
+	.
+	To use this command, the user must be an IRC Operator.
+
+	References:
+	 - RFC 2812, 3.7.1 "Kill message"
+
+- KLINE
+	KLINE <nick!user@hostmask> [<timeout> :<reason>]
+	.
+	This command provides timed K-Lines (server-local bans).
+	.
+	If a client matches a K-Line, it cannot connect to this server for
+	<timeout> seconds. When <timeout> is 0, it makes the K-Line permanent.
+	.
+	If no <timeout> and no <reason> is given, the K-Line is removed.
+	.
+	To use this command, the user must be an IRC Operator.
+	.
+	"STATS k" can be used to list all currently active K-Lines.
+
+	References:
+	 - This command is not specified in the IRC RFCs, it is an extension
+	   of ngIRCd.
+
+- OPER
+	OPER <name> <password>
+	.
+	Authenticates a user named <name> as an IRC operator on the current
+	server/network.
+	.
+	This operator <name> must be configured in the server configuration.
+	.
+	Please note that <name> is NOT related to a nickname at all!
+
+	References:
+	 - RFC 2812, 3.1.4 "Oper message"
+
+- REHASH
+	REHASH
+	.
+	Causes the server to re-read and re-process its configuration file(s).
+	.
+	While rehashing, no new connections are accepted, but all already
+	established connections stay connected.
+	.
+	To use this command, the user must be an IRC Operator.
+
+	References:
+	 - RFC 2812, 4.2 "Rehash message"
+
+- RESTART
+	RESTART
+	.
+	Restart the server.
+	.
+	While restarting, all connections are reset and no new connections
+	are accepted.
+	.
+	To use this command, the user must be an IRC Operator.
+
+	References:
+	 - RFC 2812, 4.4 "Restart message"
+
+- WALLOPS
+	WALLOPS <message>
+	.
+	Sends <message> to all users with user mode "+w".
+	.
+	To use this command, the user must be an IRC Operator.
+
+	References:
+	 - RFC 2812, 4.7 "Operwall message"
+
+IRC Service Commands
+~~~~~~~~~~~~~~~~~~~~
+
+- SERVICE
+	SERVICE <name> <reserved1> <distribution> <type> <reserved2> <info>
+	SERVICE <name> <servertoken> <distribution> {<type>|+<modes>} <hops> <info>
+	.
+	Register a new service in the network.
+	.
+	The first form is used by directly linked services and isn't supported
+	by ngIRCd at the moment. The second form announces services connected
+	to remote "pseudo-servers" ("services hubs").
+	.
+	The <distribution> and <type> parameters are ignored by ngIRCd.
+
+	References:
+	 - RFC 2812, 3.1.6 "Service message"
+	 - RFC 2813, 4.1.4 "Service message"
+
+- SERVLIST
+	SERVLIST [<mask> [<type>]]
+	.
+	List all IRC services currently registered in the network.
+	.
+	The optional <mask> and <type> parameters can be used to limit the
+	listing to services matching the <mask> and that are of type <type>.
+	.
+	Please note that ngIRCd doesn't use any service types at the moment
+	and therefore all services are of type "0".
+
+	References:
+	 - RFC 2812, 3.5.1 "Servlist message"
+
+- SQUERY
+	SQUERY <target>[,<target>[,...]] <message>
+	.
+	Send a <message> to a given <target> IRC service, and report all
+	errors.
+	.
+	The "SQUERY" command exactly behaves like the "PRIVMSG" command, but
+	enforces that the <target> of the <message> is an IRC service.
+	Please see the help text of the "PRIVMSG" command for a detailed
+	description of the parameters!
+	.
+	If a user wants to interact with IRC services, he should use "SQUERY"
+	instead of "PRIVMSG" or "NOTICE": only "SQUERY makes sure that no
+	regular user, which uses the nickname of an IRC service, receives
+	the command in error, for example during a "net split"!
+
+	References:
+	 - RFC 2812, 2.3.1 "Message format in Augmented BNF"
+	 - RFC 2812, 3.3 "Sending messages"
+	 - RFC 2812, 3.3.2 "Notice"
+
+- SVSNICK
+	SVSNICK <oldnick> <newnick>
+	.
+	Forcefully change foreign user nicknames. This command is allowed
+	for servers only.
+	.
+	The "SVSNICK" command is forwarded to the server to which the user
+	with nickname <oldnick> is connected to, which in turn generates a
+	regular "NICK" command that then is sent to the client, so no special
+	support in the client software is required.
+
+	References:
+	 - ngIRCd GIT commit e3f300d3231f
+
+
+Server Protocol Commands
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+- CHANINFO
+	CHANINFO <channel> +<modes> [[<key> <limit>] <topic>]
+	.
+	CHANINFO is used by servers to inform each other about a channel:
+	its modes, channel key, user limits and its topic.
+	.
+	The CHANINFO command is allowed on server-links only.
+
+	References:
+	 - IRC+, <http://ngircd.barton.de/doc/Protocol.txt>
+	 - IRC+, doc/Protocol.txt
+
+- ERROR
+	ERROR [<message> [<> [...]]]
+	.
+	Inform a client or a server about an error condition. The first 
+	parameter, if given, is logged by the server receiving the message,
+	all other parameters are silently ignored.
+	.
+	This command is silently ignored on non-server and non-service links
+	and shouldn't be used by regular IRC clients.
+	.
+	The ERROR message is also sent before terminating a regular client
+	connection.
+
+	References:
+	 - RFC 2812, 3.7.4 "Error message"
+
+- METADATA
+	METADATA <target> <key> <value>
+	.
+	The METADATA command is used on server-links to update "metadata"
+	information of clients, like the hostname, the info text ("real name"),
+	or the user name.
+	.
+	The METADATA command is allowed on server-links only.
+
+	References:
+	 - IRC+, <http://ngircd.barton.de/doc/Protocol.txt>
+	 - IRC+, doc/Protocol.txt
+
+- NJOIN
+	NJOIN <channel> [<mode>]<nick>[,[<mode>]<nick>[,...]]
+	.
+	The NJOIN command is used on server-links to add users with <nick>
+	and <mode> to a <channel> while peering.
+	.
+	The NJOIN command is allowed on server-links only.
+
+	References:
+	 - RFC 2813, 4.2.2 "Njoin message"
+
+- SERVER
+	SERVER <servername> <info>
+	SERVER <servername> <hopcount> <info>
+	SERVER <servername> <hopcount> <token> <info>
+	.
+	The first form registers the local connection as a new server in the
+	network, the second (RFC 1459) and third (RFC 2812) form announce a
+	new remote server in the network.
+	.
+	The SERVER command is allowed on unregistered or server-links only.
+
+	References:
+	 - RFC 1459, 4.1.4 "Server message"
+	 - RFC 2813, 4.1.2 "Server message"
+
+- SQUIT
+	SQUIT <server> <comment>
+	.
+	Disconnects an IRC Server from the network.
+	.
+	This command is used on server-links, but can be used by IRC Operators
+	to forcefully disconnect servers from the network, too.
+
+	References:
+	 - RFC 2812, 3.1.8 "Squit"
+	 - RFC 2813, 4.1.6 "Server quit message"
+
+Dummy Commands
+~~~~~~~~~~~~~~
+
+- SUMMON
+	SUMMON <user> [<target> [<channel>]]
+	.
+	This command was intended to call people into IRC who are directly
+	connected to the terminal console of the IRC server -- but is
+	deprecated today. Therefore ngIRCd doesn't really implement this
+	command and always returns an error message, regardless of the
+	parameters given.
+
+	References:
+	 - RFC 2812, 4.5 "Summon message"
+
+- USERS
+	USERS [<target>]
+	.
+	This command was intended to list users directly logged in into the
+	console of the IRC server -- but is deprecated today. Therefore ngIRCd
+	doesn't really implement this command and always returns an error
+	message, regardless of the parameters given.
+
+	References:
+	 - RFC 2812, 4.6 "Users"
+
+- GET
+	GET [...]
+	.
+	Fake HTTP GET command. When received, the connection is shut down
+	immediately again to protect against crazy web browsers ...
+
+	References:
+	 - ngIRCd GIT commit 33e8c2480649
+
+- POST
+	POST [...]
+	.
+	Fake HTTP POST command. When received, the connection is shut down
+	immediately again to protect against crazy web browsers ...
+
+	References:
+	 - ngIRCd GIT commit 33e8c2480649

+ 3 - 1
doc/Makefile.am

@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -12,6 +12,7 @@
 .tmpl:
 	$(AM_V_GEN)sed \
 	    -e "s@:ETCDIR:@${sysconfdir}@" \
+	    -e "s@:DOCDIR:@${docdir}@" \
 	    <$< >$@
 
 SUFFIXES = .tmpl
@@ -19,6 +20,7 @@ SUFFIXES = .tmpl
 static_docs = \
 	Bopm.txt \
 	Capabilities.txt \
+	Commands.txt \
 	Contributing.txt \
 	FAQ.txt \
 	GIT.txt \

+ 3 - 1
doc/Makefile.in

@@ -17,7 +17,7 @@
 
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2013 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
@@ -205,6 +205,7 @@ SUFFIXES = .tmpl
 static_docs = \
 	Bopm.txt \
 	Capabilities.txt \
+	Commands.txt \
 	Contributing.txt \
 	FAQ.txt \
 	GIT.txt \
@@ -576,6 +577,7 @@ uninstall-am:
 .tmpl:
 	$(AM_V_GEN)sed \
 	    -e "s@:ETCDIR:@${sysconfdir}@" \
+	    -e "s@:DOCDIR:@${docdir}@" \
 	    <$< >$@
 
 maintainer-clean-local:

+ 12 - 3
doc/PAM.txt

@@ -1,9 +1,8 @@
 
                      ngIRCd - Next Generation IRC Server
+                           http://ngircd.barton.de/
 
-                        (c)2001-2010 Alexander Barton,
-                    alex@barton.de, http://www.barton.de/
-
+               (c)2001-2013 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
 
@@ -34,3 +33,13 @@ is running as. Therefore a lot of PAM modules aren't working as expected,
 because they need root privileges ("pam_unix", for example)!
 Only PAM modules not(!) requiring root privileges (such as "pam_pgsql",
 "pam_mysql", "pam_opendirectory" ...) can be used in conjunction with ngIRCd.
+
+More Examples:
+
+ * Use an own "password file" for ngIRCd:
+
+   Note: you can use the htpasswd(1) utility of Apache to manage password
+   files used by pam_pwdfile, see "man htpasswd"!
+
+	/etc/pam.d/ngircd:
+	  auth  required  pam_pwdfile.so pwdfile=/etc/ngircd/ngircd.passwd

+ 81 - 70
doc/Platforms.txt

@@ -2,87 +2,98 @@
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
 
-               (c)2001-2012 Alexander Barton and Contributors.
+               (c)2001-2013 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
 
                               -- Platforms.txt --
 
 
-This file lists the status of all platforms on which the ngIRCd has been
-tested. Included is the date and version of the last "official" test and
-the name of the tester/maintainer.
+This file lists the status of all platforms on which ngIRCd has been tested.
+Included is the date and version of the last test and the name of the tester
+or maintainer.
 
-If you successfully compiled and tested ngIRCd on a platform that isn't
-listed here, please contact Alexander Barton, <alex@barton.de>, so that this
-list can be updated. Thanks for your help!
+If you successfully compiled and tested ngIRCd on a platform that isn't listed
+here, please write to the mailing list so that this list can be updated.  The
+script "./contrib/platformtest.sh" should output a summary that is suitable
+for inclusion here. Thanks for your help!
 
 
-                              the executable works ("runs") as expected --+
-                                tests run successfully ("make check") --+ |
-                                           ngIRCd compiles ("make") --+ | |
-                                                ./configure works --+ | | |
-                                                                    | | | |
-Platform                    Compiler     ngIRCd     Date     Tester C M T R See
---------------------------- ------------ ---------- -------- ------ - - - - ---
-alpha/unknown/netbsd3.0     gcc 3.3.3    CVSHEAD    06-05-07 fw     Y Y Y Y (3)
-armv6l/unkn./linux-gnueabi  gcc 4.4.5    19.1       12-06-04 goetz  Y Y Y Y (5)
-armv7l/unkn./linux-gnueabi  gcc 4.4.3    19.1       12-04-29 goetz  Y Y Y Y (5)
-hppa/unknown/openbsd3.5     gcc 2.95.3   CVSHEAD    04-05-25 alex   Y Y Y Y
-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.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/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.11         gcc 3.4.3    19         12-02-26 alex   Y Y N Y (4)
-i386/pc/solaris2.11         gcc 4.2.3    19.1       12-05-29 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/freebsd6.2     gcc 3.4.6    20~rc1     12-11-13 alex   Y Y Y Y (3)
-i386/unknown/freebsd7.3     gcc 4.2.1    20~rc1     12-11-13 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)
-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/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/openbsd4.1     gcc 3.3.5    16         10-04-11 alex   Y Y Y Y (3)
-i586/pc/haiku               gcc 2.95.3   19.2~138   12-10-11 user   Y Y N N
-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/linux-gnu           gcc 2.7.2    19.1       12-05-30 goetz  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)
-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)
-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.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
-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.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/darwin7.9.0   gcc 3.3      19.1       12-05-22 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)
-powerpc/unknown/linux-gnu   gcc 3.3.3    0.8.0      04-05-30 alex   Y Y Y Y
-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.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
-x86_64/apple/darwin12.2.0   gcc 4.2.1    20~rc1     12-11-13 alex   Y Y Y Y (3)
-x86_64/unknown/freebsd8.1   gcc 4.2.1    20~rc1     12-11-13 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    20~rc1     12-02-26 alex   Y Y Y Y (1)
-x86_64/unknown/openbsd4.7   gcc 3.3.5    20~rc1     12-02-26 alex   Y Y Y Y (3)
-x86_64/unknown/openbsd4.8   gcc 4.2.1    20~rc1     12-11-13 alex   Y Y Y Y (3)
+                                the executable works ("runs") as expected --+
+                                  tests run successfully ("make check") --+ |
+                                             ngIRCd compiles ("make") --+ | |
+                                                  ./configure works --+ | | |
+                                                                      | | | |
+Platform                    Compiler     ngIRCd     Date     Tester   C M T R *
+--------------------------- ------------ ---------- -------- -------- - - - - -
+alpha/unknown/netbsd3.0     gcc 3.3.3    CVSHEAD    06-05-07 fw       Y Y Y Y 3
+armv6l/unk./linux-gnueabi   gcc 4.7.2    20.2       13-03-08 goetz    Y Y Y Y 5
+armv6l/unk./linux-gnueabihf gcc 4.6.3    21~rc2     13-10-26 pi       Y Y Y Y 5
+armv7l/unk./linux-gnueabi   gcc 4.4.3    19.1       12-04-29 goetz    Y Y Y Y 5
+hppa/unknown/openbsd3.5     gcc 2.95.3   CVSHEAD    04-05-25 alex     Y Y Y Y
+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.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/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.11         gcc 4.5.2    21~rc2     13-10-27 alex     Y Y N Y 4
+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    20~rc1     12-11-13 alex     Y Y Y Y 3
+i386/unknown/freebsd7.3     gcc 4.2.1    20~rc1     12-11-13 alex     Y Y Y Y 3
+i686/pc/minix               gcc 4.4.6    21~rc2     13-10-27 alex     Y Y N N
+i686/unknown/gnu0.3         gcc 4.4.5    19         12-02-29 alex     Y Y Y Y
+i686/unknown/gnu0.5         gcc 4.8.1    21~rc2     13-10-27 alex     Y Y Y Y
+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/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    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/openbsd4.1     gcc 3.3.5    16         10-04-11 alex     Y Y Y Y 3
+i386/unknown/openbsd5.3     gcc 4.2.1    21~rc2     13-10-21 dspruell Y Y Y Y 3
+i586/pc/haiku               gcc 2.95.3   19.2~138   12-10-11 user     Y Y N N
+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/linux-gnu           gcc 2.7.2    19.1       12-05-30 goetz    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
+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
+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.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
+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.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/darwin7.9.0   gcc 3.3      19.1       12-05-22 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
+powerpc/unknown/linux-gnu   gcc 3.3.3    0.8.0      04-05-30 alex     Y Y Y Y
+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.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
+x86_64/apple/darwin10.8.0   gcc 4.2.1    21~rc2     13-10-30 alex     Y Y Y Y 3
+x86_64/apple/darwin12.3.0   gcc 4.2.1    20.2       13-04-01 alex     Y Y Y Y 3
+x86_64/apple/darwin13.0.0   A-clang 5.0  21~rc2     13-10-20 alex     Y Y Y Y 3
+x86_64/unknown/freebsd8.4   gcc 4.2.1    21~rc2     13-10-27 alex     Y Y Y Y 3
+x86_64/unknown/freebsd9.1   gcc 4.2.1    21~rc2     13-10-27 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    clang 3.2    21~rc2     13-10-20 alex     Y Y Y Y 1
+x86_64/unknown/linux-gnu    gcc 4.8.1    21~rc2     13-10-20 alex     Y Y Y Y 1
+x86_64/unknown/linux-gnu    Open64       20.3       13-10-16 goetz    Y Y Y Y 1
+x86_64/unknown/linux-gnu    tcc 0.9.25   20.3       13-10-16 goetz    Y Y Y Y 1
+x86_64/unknown/openbsd4.7   gcc 3.3.5    20~rc1     12-02-26 alex     Y Y Y Y 3
+x86_64/unknown/openbsd4.8   gcc 4.2.1    21~rc2     13-10-27 alex     Y Y Y Y 3
+x86_64/unknown/openbsd5.1   gcc 4.2.1    21~rc2     13-10-27 alex     Y Y Y Y 3
 
 
-Notes
-~~~~~
+* Notes
+~~~~~~~
 
 (1) */*/linux-gnu (Linux platforms):
     ngIRCd has been tested with various Linux distributions, such as SuSE,

+ 3 - 1
doc/Protocol.txt

@@ -224,8 +224,10 @@ new server link", <serverflag> "M"), even if it doesn't support the given
 
 The following <key> names are defined:
 
- - "host": the hostname of a client (can't be empty)
+ - "accountname": the account name of a client (can't be empty)
+ - "certfp": the certificate fingerprint of a client (can't be empty)
  - "cloakhost": the cloaked hostname of a client
+ - "host": the hostname of a client (can't be empty)
  - "info": info text ("real name") of a client
  - "user": the user name of a client (can't be empty)
 

+ 3 - 5
doc/Services.txt

@@ -14,7 +14,7 @@ acting as a "regular servers" ("pseudo servers") are supported, either
 using the IRC protocol as defined in RFC 1459 or RFC 2812.
 
 Support for Services has been tested using
- - Anope 1.9.8 or later (<http://www.anope.org/>; unreleased!)
+ - Anope 1.9.8 or later (<http://www.anope.org/>)
  - Atheme 7.0.2 or later (<http://www.atheme.net>)
  - "IRC Services" 5.1.x by Andrew Church (<http://achurch.org/services/>)
 
@@ -44,10 +44,8 @@ Example:
 Setting up Anope 1.9.x
 ~~~~~~~~~~~~~~~~~~~~~~
 
-Anope 1.9.8 or later (<http://www.anope.org/>; unreleased as of 2012-11-10)
-may be used with ngIRCd using the "ngircd" protocol module.
-Until Anope 1.9.8 is released, you have to use the sources from the Anope
-development GIT tree, see <http://sourceforge.net/projects/anope/develop/>!
+Anope 1.9.8 or later (<http://www.anope.org/>) can be used with ngIRCd using
+the "ngircd" protocol module.
 
 At least the following settings have to be tweaked, in addition to all the
 settings marked as required by Anope:

+ 36 - 4
doc/sample-ngircd.conf.tmpl

@@ -33,6 +33,10 @@
 	;AdminInfo2 = Location
 	;AdminEMail = admin@irc.server
 
+	# Text file which contains the ngIRCd help text. This file is required
+	# to display help texts when using the "HELP <cmd>" command.
+	;HelpFile = :DOCDIR:/Commands.txt
+
 	# Info text of the server. This will be shown by WHOIS and
 	# LINKS requests for example.
 	Info = Server Info Text
@@ -84,6 +88,13 @@
 	# to not yet (or no longer) connected servers.
 	;ConnectRetry = 60
 
+	# Number of seconds after which the whole daemon should shutdown when
+	# no connections are left active after handling at least one client
+	# (0: never, which is the default).
+	# This can be useful for testing or when ngIRCd is started using
+	# "socket activation" with systemd(8), for example.
+	;IdleTimeout = 0
+
 	# Maximum number of simultaneous in- and outbound connections the
 	# server is allowed to accept (0: unlimited):
 	;MaxConnections = 0
@@ -117,6 +128,12 @@
 	# behavior of ngIRCd. If you want to get started quickly, you most
 	# probably don't have to make changes here -- they are all optional.
 
+	# List of allowed channel types (channel prefixes) for newly created
+	# channels on the local server. By default, all supported channel
+	# types are allowed. Set this variable to the empty string to disallow
+	# creation of new channels by local clients at all.
+	;AllowedChannelTypes = #&+
+
 	# Are remote IRC operators allowed to control this server, e.g.
 	# use commands like CONNECT, SQUIT, DIE, ...?
 	;AllowRemoteOper = no
@@ -148,7 +165,12 @@
 	;ConnectIPv6 = yes
 	;ConnectIPv4 = yes
 
-	# Do any DNS lookups when a client connects to the server.
+	# Default user mode(s) to set on new local clients. Please note that
+	# only modes can be set that the client could set on itself, you can't
+	# set "a" (away) or "o" (IRC Op), for example! Default: none.
+	;DefaultUserModes = i
+
+	# Do DNS lookups when a client connects to the server.
 	;DNS = yes
 
 	# Do IDENT lookups if ngIRCd has been compiled with support for it.
@@ -156,6 +178,10 @@
 	# prepended to their user name.
 	;Ident = yes
 
+	# Directory containing configuration snippets (*.conf), that should
+	# be read in after parsing this configuration file.
+	;IncludeDir = :ETCDIR:/conf.d
+
 	# Enhance user privacy slightly (useful for IRC server on TOR or I2P)
 	# by censoring some information like idle time, logon time, etc.
 	;MorePrivacy = no
@@ -194,9 +220,6 @@
 	# character prepended to their respective user names!
 	;PAMIsOptional = no
 
-	# Allow Pre-Defined Channels only (see Section [Channels])
-	;PredefChannelsOnly = no
-
 	# Let ngIRCd send an "authentication PING" when a new client connects,
 	# and register this client only after receiving the corresponding
 	# "PONG" reply.
@@ -225,6 +248,15 @@
 	# SSL Server Key Certificate
 	;CertFile = :ETCDIR:/ssl/server-cert.pem
 
+	# Select cipher suites allowed for SSL/TLS connections. This defaults
+	# to HIGH:!aNULL:@STRENGTH (OpenSSL) or SECURE128 (GnuTLS).
+	# See 'man 1ssl ciphers' (OpenSSL) or 'man 3 gnutls_priority_init'
+	# (GnuTLS) for details.
+	# For OpenSSL:
+	;CipherList = HIGH:!aNULL:@STRENGTH
+	# For GnuTLS:
+	;CipherList = SECURE128
+
 	# Diffie-Hellman parameters
 	;DHFile = :ETCDIR:/ssl/dhparams.pem
 

+ 17 - 88
doc/src/Doxyfile

@@ -16,106 +16,55 @@
 # Project related configuration options
 #---------------------------------------------------------------------------
 
-# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
-# by quotes) that should identify the project.
+DOXYFILE_ENCODING      = UTF-8
 
 PROJECT_NAME           = ngIRCd
-
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
-# base path where the generated documentation will be put. 
-# If a relative path is entered, it will be relative to the location 
-# where doxygen was started. If left blank the current directory will be used.
+PROJECT_BRIEF          = "Lightweight Internet Relay Chat server"
+PROJECT_LOGO           = "../../contrib/ngIRCd-Logo.gif"
 
 OUTPUT_DIRECTORY       = .
-
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
-# can be used to strip a user-defined part of the path. Stripping is 
-# only done if one of the specified strings matches the left-hand part of 
-# the path. The tag can be used to show relative paths in the file list. 
-# If left blank the directory from which doxygen is run is used as the 
-# path to strip.
-
 STRIP_FROM_PATH        = ../..
 
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
-# will interpret the first line (until the first dot) of a JavaDoc-style 
-# comment as the brief description. If set to NO, the JavaDoc 
-# comments will behave just like the Qt-style comments (thus requiring an 
-# explicit @brief command for a brief description.
-
 JAVADOC_AUTOBRIEF      = YES
-
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
-# sources only. Doxygen will then generate output that is more tailored for C. 
-# For instance, some of the names that are used will be different. The list 
-# of all members will be omitted, etc.
-
 OPTIMIZE_OUTPUT_FOR_C  = YES
+TYPEDEF_HIDES_STRUCT   = YES
+
+TAB_SIZE               = 8
 
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
 
-# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
-# documentation are documented, even if no documentation was available. 
-# Private class members and static file members will be hidden unless 
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
-
 EXTRACT_ALL            = YES
-
-# If the EXTRACT_STATIC tag is set to YES all static members of a file 
-# will be included in the documentation.
-
 EXTRACT_STATIC         = YES
 
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory
-# hierarchy in the documentation. The default is NO.
-
 SHOW_DIRECTORIES       = YES
 
 #---------------------------------------------------------------------------
-# configuration options related to the input files
+# configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
 
-# The INPUT tag can be used to specify the files and/or directories that
-# contain documented source files. You may enter file names like "myfile.cpp"
-# or directories like "/usr/src/myproject". Separate the files or directories 
-# with spaces.
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = YES
 
-INPUT                  = ../../src
-
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
-# should be searched for input files as well. Possible values are YES and NO. 
-# If left blank NO is used.
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
 
+INPUT                  = ../../src
+INPUT_ENCODING         = UTF-8
 RECURSIVE              = YES
 
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
 
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
-# be generated. Documented entities will be cross-referenced with these sources. 
-# Note: To get rid of all source code in the generated output, make sure also 
-# VERBATIM_HEADERS is set to NO.
-
 SOURCE_BROWSER         = YES
-
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C and C++ comments will always remain visible.
-
 STRIP_CODE_COMMENTS    = NO
-
-# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
-# function all documented functions referencing it will be listed.
-
 REFERENCED_BY_RELATION = YES
-
-# If the REFERENCES_RELATION tag is set to YES then for each documented
-# function all documented entities called/used by that function will be listed.
-
 REFERENCES_RELATION    = YES
 
 #---------------------------------------------------------------------------
@@ -123,19 +72,7 @@ REFERENCES_RELATION    = YES
 #---------------------------------------------------------------------------
 
 GENERATE_HTML          = YES
-
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for each
-# generated HTML page. If it is left blank doxygen will generate a standard
-# footer.
-
 HTML_FOOTER            = footer.inc.html
-
-# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
-# documentation will contain sections that can be hidden and shown after the
-# page has loaded. For this to work a browser that supports JavaScript and
-# DHTML is required (for instance Mozilla 1.0+, Firefox Netscape 6.0+,
-# Internet explorer 5.0+, Konqueror, or Safari).
-
 HTML_DYNAMIC_SECTIONS  = YES
 
 GENERATE_DOCSET        = NO
@@ -151,14 +88,6 @@ GENERATE_PERLMOD       = NO
 # Configuration options related to the preprocessor   
 #---------------------------------------------------------------------------
 
-# The PREDEFINED tag can be used to specify one or more macro names that 
-# are defined before the preprocessor is started (similar to the -D option of 
-# gcc). The argument of the tag is a list of macros of the form: name 
-# or name=definition (no spaces). If the definition and the = are 
-# omitted =1 is assumed. To prevent a macro definition from being 
-# undefined via #undef or recursively expanded use the := operator 
-# instead of the = operator.
-
 PREDEFINED             = DEBUG ZLIB PAM ZEROCONF CONN_MODULE __client_c__
 
 # -eof-

+ 1 - 1
doc/src/footer.inc.html

@@ -1,6 +1,6 @@
 
 <hr class="footer">
-<p>
+<p style="text-align: center">
   ngIRCd
   <a href="http://ngircd.barton.de/">Homepage</a>,
   <a href="http://ngircd.barton.de/cgi-bin/gitweb.cgi?p=ngircd.git">GIT-Repository</a>,

+ 8 - 6
man/ngircd.8.tmpl

@@ -1,7 +1,7 @@
 .\"
 .\" ngircd(8) manual page template
 .\"
-.TH ngircd 8 "Oct 2012" ngIRCd "ngIRCd Manual"
+.TH ngircd 8 "Oct 2013" ngIRCd "ngIRCd Manual"
 .SH NAME
 ngIRCd \- the "next generation" IRC daemon
 .SH SYNOPSIS
@@ -23,13 +23,15 @@ which is a little bit exaggerated:
 .IR "lightweight Internet Relay Chat server"
 most probably would have been a better name :-)
 .PP
-Currently supported platforms include AIX, A/UX, FreeBSD, HP-UX, IRIX,
-Linux, Mac OS X, NetBSD, OpenBSD, Solaris, and Windows with Cygwin.
-.PP
+Currently supported platforms include AIX, A/UX, FreeBSD, HP-UX, Hurd, IRIX,
+Linux, Mac OS X, Minix, NetBSD, OpenBSD, Solaris, and Windows with Cygwin.
 As ngIRCd relies on UNIX standards and uses GNU automake and GNU autoconf
 there are good chances that it also supports other UNIX-based operating
-systems as well. By default, ngIRCd writes diagnostic and informational
-messages using the syslog mechanism.
+systems as well.
+.PP
+By default ngIRCd logs diagnostic and informational messages using the syslog
+mechanism, or writes directly to the console when running in the foreground
+(see below).
 .SH OPTIONS
 The default behavior of
 .BR ngircd

+ 38 - 9
man/ngircd.conf.5.tmpl

@@ -1,7 +1,7 @@
 .\"
 .\" ngircd.conf(5) manual page template
 .\"
-.TH ngircd.conf 5 "Nov 2012" ngIRCd "ngIRCd Manual"
+.TH ngircd.conf 5 "Oct 2013" ngIRCd "ngIRCd Manual"
 .SH NAME
 ngircd.conf \- configuration file of ngIRCd
 .SH SYNOPSIS
@@ -101,6 +101,12 @@ IRC network and must contain at least one dot (".") character.
 Information about the server and the administrator, used by the ADMIN
 command. This information is not required by the server but by RFC!
 .TP
+\fBHelpFile\fR (string)
+Text file which contains the ngIRCd help text. This file is required
+to display help texts when using the "HELP <cmd>" command.
+Please note: Changes made to this file take effect when ngircd starts up
+or is instructed to re-read its configuration file.
+.TP
 \fBInfo\fR (string)
 Info text of the server. This will be shown by WHOIS and LINKS requests for
 example.
@@ -127,8 +133,8 @@ if ngIRCd is using PAM!
 .TP
 \fBPidFile\fR (string)
 This tells ngIRCd to write its current process ID to a file. Note that the
-pidfile is written AFTER chroot and switching the user ID, e.g. the directory
-the pidfile resides in must be writable by the ngIRCd user and exist in the
+"PID file" is written AFTER chroot and switching the user ID, e.g. the directory
+the file resides in must be writable by the ngIRCd user and exist in the
 chroot directory (if configured, see above).
 .TP
 \fBPorts\fR (list of numbers)
@@ -164,6 +170,12 @@ should be safe, but it is wise to double-check :-)
 The server tries every <ConnectRetry> seconds to establish a link to not yet
 (or no longer) connected servers. Default: 60.
 .TP
+\fBIdleTimeout\fR (number)
+Number of seconds after which the whole daemon should shutdown when no
+connections are left active after handling at least one client (0: never). This
+can be useful for testing or when ngIRCd is started using "socket activation"
+with systemd(8), for example. Default: 0.
+.TP
 \fBMaxConnections\fR (number)
 Maximum number of simultaneous in- and outbound connections the server is
 allowed to accept (0: unlimited). Default: 0.
@@ -197,6 +209,12 @@ Optional features and configuration options to further tweak the behavior of
 ngIRCd. If you want to get started quickly, you most probably don't have to
 make changes here -- they are all optional.
 .TP
+\fBAllowedChannelTypes\fR (string)
+List of allowed channel types (channel prefixes) for newly created channels
+on the local server. By default, all supported channel types are allowed.
+Set this variable to the empty string to disallow creation of new channels
+by local clients at all. Default: #&+
+.TP
 \fBAllowRemoteOper\fR (boolean)
 Are IRC operators connected to remote servers allowed to control this server,
 e.g. are they allowed to use administrative commands like CONNECT, DIE,
@@ -240,6 +258,12 @@ Set this to no if you do not want ngIRCd to connect to other IRC servers using
 the IPv6 protocol.
 Default: yes.
 .TP
+\fBDefaultUserModes\fR (string)
+Default user mode(s) to set on new local clients. Please note that only modes
+can be set that the client could set on itself, you can't set "a" (away) or
+"o" (IRC Op), for example!
+Default: none.
+.TP
 \fBDNS\fR (boolean)
 If set to false, ngIRCd will not make any DNS lookups when clients connect.
 If you configure the daemon to connect to other servers, ngIRCd may still
@@ -253,6 +277,11 @@ Users identified using IDENT are registered without the "~" character
 prepended to their user name.
 Default: yes.
 .TP
+.TP
+\fBIncludeDir\fR (string)
+Directory containing configuration snippets (*.conf), that should be read in
+after parsing the current configuration file.
+Default: none.
 \fBMorePrivacy\fR (boolean)
 This will cause ngIRCd to censor user idle time, logon time as well as the
 part/quit messages (that are sometimes used to inform everyone about which
@@ -302,12 +331,6 @@ 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)
-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
-file on this server.
-Default: no.
-.TP
 \fBRequireAuthPing\fR (boolean)
 Let ngIRCd send an "authentication PING" when a new client connects, and
 register this client only after receiving the corresponding "PONG" reply.
@@ -343,6 +366,12 @@ when it is compiled with support for SSL using OpenSSL or GnuTLS!
 \fBCertFile\fR (string)
 SSL Certificate file of the private server key.
 .TP
+\fBCipherList\fR (string)
+Select cipher suites allowed for SSL/TLS connections.  This defaults to
+"HIGH:!aNULL:@STRENGTH" (OpenSSL) or "SECURE128" (GnuTLS).
+Please see 'man 1ssl ciphers' (OpenSSL) and 'man 3 gnutls_priority_init'
+(GnuTLS) for details.
+.TP
 \fBDHFile\fR (string)
 Name of the Diffie-Hellman Parameter file. Can be created with GnuTLS
 "certtool \-\-generate-dh-params" or "openssl dhparam". If this file is not

+ 12 - 0
src/config.h.in

@@ -9,6 +9,12 @@
 /* Define to 1 if you have the `alarm' function. */
 #undef HAVE_ALARM
 
+/* Define to 1 if you have the `arc4random' function. */
+#undef HAVE_ARC4RANDOM
+
+/* Define to 1 if you have the `arc4random_stir' function. */
+#undef HAVE_ARC4RANDOM_STIR
+
 /* Define to 1 if you have the <arpa/inet.h> header file. */
 #undef HAVE_ARPA_INET_H
 
@@ -84,6 +90,9 @@
 /* Define to 1 if you have the `iconv' library (-liconv). */
 #undef HAVE_LIBICONV
 
+/* Define to 1 if you have the `libiconv_open' function. */
+#undef HAVE_LIBICONV_OPEN
+
 /* Define to 1 if you have the `ident' library (-lident). */
 #undef HAVE_LIBIDENT
 
@@ -204,6 +213,9 @@
 /* Define to 1 if you have the `strncasecmp' function. */
 #undef HAVE_STRNCASECMP
 
+/* Define to 1 if you have the `strndup' function. */
+#undef HAVE_STRNDUP
+
 /* Define to 1 if you have the `strrchr' function. */
 #undef HAVE_STRRCHR
 

+ 8 - 0
src/ipaddr/ng_ipaddr.h

@@ -47,6 +47,7 @@ typedef struct NG_IP_ADDR_DONTUSE ng_ipaddr_t;
 static inline int
 ng_ipaddr_af(const ng_ipaddr_t *a)
 {
+	assert(a != NULL);
 #ifdef WANT_IPV6
 	return a->sa.sa_family;
 #else
@@ -59,6 +60,7 @@ ng_ipaddr_af(const ng_ipaddr_t *a)
 static inline socklen_t
 ng_ipaddr_salen(const ng_ipaddr_t *a)
 {
+	assert(a != NULL);
 #ifdef WANT_IPV6
 	assert(a->sa.sa_family == AF_INET || a->sa.sa_family == AF_INET6);
 	if (a->sa.sa_family == AF_INET6)
@@ -75,11 +77,14 @@ ng_ipaddr_getport(const ng_ipaddr_t *a)
 #ifdef WANT_IPV6
 	int af = a->sa.sa_family;
 
+	assert(a != NULL);
 	assert(af == AF_INET || af == AF_INET6);
 
 	if (af == AF_INET6)
 		return ntohs(a->sin6.sin6_port);
 #endif /* WANT_IPV6 */
+
+	assert(a != NULL);
 	assert(a->sin4.sin_family == AF_INET);
 	return ntohs(a->sin4.sin_port);
 }
@@ -109,12 +114,15 @@ GLOBAL bool ng_ipaddr_tostr_r PARAMS((const ng_ipaddr_t *addr, char *dest));
 static inline const char*
 ng_ipaddr_tostr(const ng_ipaddr_t *addr)
 {
+	assert(addr != NULL);
 	return inet_ntoa(addr->sin4.sin_addr);
 }
 
 static inline bool
 ng_ipaddr_tostr_r(const ng_ipaddr_t *addr, char *d)
 {
+	assert(addr != NULL);
+	assert(d != NULL);
 	strlcpy(d, inet_ntoa(addr->sin4.sin_addr), NG_INET_ADDRSTRLEN);
 	return true;
 }

+ 1 - 0
src/ngircd/Makefile.am

@@ -86,6 +86,7 @@ noinst_HEADERS = \
 	irc-encoding.h \
 	irc-info.h \
 	irc-login.h \
+	irc-macros.h \
 	irc-metadata.h \
 	irc-mode.h \
 	irc-op.h \

+ 1 - 0
src/ngircd/Makefile.in

@@ -288,6 +288,7 @@ noinst_HEADERS = \
 	irc-encoding.h \
 	irc-info.h \
 	irc-login.h \
+	irc-macros.h \
 	irc-metadata.h \
 	irc-mode.h \
 	irc-op.h \

+ 1 - 0
src/ngircd/Makefile.ng

@@ -86,6 +86,7 @@ noinst_HEADERS = \
 	irc-encoding.h \
 	irc-info.h \
 	irc-login.h \
+	irc-macros.h \
 	irc-metadata.h \
 	irc-mode.h \
 	irc-op.h \

+ 71 - 68
src/ngircd/channel.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -131,11 +131,11 @@ Channel_InitPredefined( void )
 
 		new_chan = Channel_Create(conf_chan->name);
 		if (!new_chan) {
-			Log(LOG_ERR, "Can't create pre-defined channel \"%s\"",
+			Log(LOG_ERR, "Can't create pre-defined channel \"%s\"!",
 							conf_chan->name);
 			continue;
 		}
-		Log(LOG_INFO, "Created pre-defined channel \"%s\"",
+		Log(LOG_INFO, "Created pre-defined channel \"%s\".",
 						conf_chan->name);
 
 		Channel_ModeAdd(new_chan, 'P');
@@ -223,7 +223,7 @@ Channel_Join( CLIENT *Client, const char *Name )
 
 	/* Check that the channel name is valid */
 	if (! Channel_IsValidName(Name)) {
-		IRC_WriteStrClient(Client, ERR_NOSUCHCHANNEL_MSG,
+		IRC_WriteErrClient(Client, ERR_NOSUCHCHANNEL_MSG,
 				   Client_ID(Client), Name);
 		return false;
 	}
@@ -268,14 +268,14 @@ Channel_Part(CLIENT * Client, CLIENT * Origin, const char *Name, const char *Rea
 	/* Check that specified channel exists */
 	chan = Channel_Search(Name);
 	if (!chan) {
-		IRC_WriteStrClient(Client, ERR_NOSUCHCHANNEL_MSG,
+		IRC_WriteErrClient(Client, ERR_NOSUCHCHANNEL_MSG,
 				   Client_ID(Client), Name);
 		return false;
 	}
 
 	/* Check that the client is in the channel */
 	if (!Get_Cl2Chan(chan, Client)) {
-		IRC_WriteStrClient(Client, ERR_NOTONCHANNEL_MSG,
+		IRC_WriteErrClient(Client, ERR_NOTONCHANNEL_MSG,
 				   Client_ID(Client), Name);
 		return false;
 	}
@@ -299,7 +299,6 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name,
 	     const char *Reason )
 {
 	CHANNEL *chan;
-	char *ptr, *target_modes;
 	bool can_kick = false;
 
 	assert(Peer != NULL);
@@ -310,9 +309,9 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name,
 
 	/* Check that channel exists */
 	chan = Channel_Search( Name );
-	if( ! chan )
-	{
-		IRC_WriteStrClient( Origin, ERR_NOSUCHCHANNEL_MSG, Client_ID( Origin ), Name );
+	if (!chan) {
+		IRC_WriteErrClient(Origin, ERR_NOSUCHCHANNEL_MSG,
+				   Client_ID(Origin), Name);
 		return;
 	}
 
@@ -320,15 +319,15 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name,
 	    Client_Type(Origin) != CLIENT_SERVICE) {
 		/* Check that user is on the specified channel */
 		if (!Channel_IsMemberOf(chan, Origin)) {
-			IRC_WriteStrClient( Origin, ERR_NOTONCHANNEL_MSG,
-					    Client_ID(Origin), Name);
+			IRC_WriteErrClient(Origin, ERR_NOTONCHANNEL_MSG,
+					   Client_ID(Origin), Name);
 			return;
 		}
 	}
 
 	/* Check that the client to be kicked is on the specified channel */
 	if (!Channel_IsMemberOf(chan, Target)) {
-		IRC_WriteStrClient(Origin, ERR_USERNOTINCHANNEL_MSG,
+		IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG,
 				   Client_ID(Origin), Client_ID(Target), Name );
 		return;
 	}
@@ -336,55 +335,43 @@ Channel_Kick(CLIENT *Peer, CLIENT *Target, CLIENT *Origin, const char *Name,
 	if(Client_Type(Peer) == CLIENT_USER) {
 		/* Channel mode 'Q' and user mode 'q' on target: nobody but
 		 * IRC Operators and servers can kick the target user */
-		if ((strchr(Channel_Modes(chan), 'Q')
+		if ((Channel_HasMode(chan, 'Q')
 		     || Client_HasMode(Target, 'q')
 		     || Client_Type(Target) == CLIENT_SERVICE)
 		    && !Client_HasMode(Origin, 'o')) {
-			IRC_WriteStrClient(Origin, ERR_KICKDENY_MSG,
+			IRC_WriteErrClient(Origin, ERR_KICKDENY_MSG,
 					   Client_ID(Origin), Name,
 					   Client_ID(Target));
 			return;
 		}
 
 		/* Check if client has the rights to kick target */
-		ptr = Channel_UserModes(chan, Peer);
-		target_modes = Channel_UserModes(chan, Target);
-		while(*ptr) {
-			/* Owner can kick everyone */
-			if ( *ptr == 'q') {
-				can_kick = true;
-				break;
-			}
-			/* Admin can't kick owner */
-			if ( *ptr == 'a' ) {
-				if (!strchr(target_modes, 'q')) {
-					can_kick = true;
-					break;
-				}
-			}
-			/* Op can't kick owner | admin */
-			if ( *ptr == 'o' ) {
-				if (!strchr(target_modes, 'q') &&
-				    !strchr(target_modes, 'a')) {
-					can_kick = true;
-					break;
-				}
-			}
-			/* Half Op can't kick owner | admin | op */ 
-			if ( *ptr == 'h' ) {
-				if (!strchr(target_modes, 'q') &&
-				    !strchr(target_modes, 'a') &&
-				    !strchr(target_modes, 'o')) {
-					can_kick = true;
-					break;
-				}
-			}
-			ptr++;
-		}
+
+		/* Owner can kick everyone */
+		if (Channel_UserHasMode(chan, Peer, 'q'))
+			can_kick = true;
+
+		/* Admin can't kick owner */
+		else if (Channel_UserHasMode(chan, Peer, 'a') &&
+		    !Channel_UserHasMode(chan, Target, 'q'))
+			can_kick = true;
+
+		/* Op can't kick owner | admin */
+		else if (Channel_UserHasMode(chan, Peer, 'o') &&
+		    !Channel_UserHasMode(chan, Target, 'q') &&
+		    !Channel_UserHasMode(chan, Target, 'a'))
+			can_kick = true;
+			
+		/* Half Op can't kick owner | admin | op */ 
+		else if (Channel_UserHasMode(chan, Peer, 'h') &&
+		    !Channel_UserHasMode(chan, Target, 'q') &&
+		    !Channel_UserHasMode(chan, Target, 'a') &&
+		    !Channel_UserHasMode(chan, Target, 'o'))
+			can_kick = true;
 
 		if(!can_kick) {
-			IRC_WriteStrClient(Origin, ERR_CHANOPPRIVTOOLOW_MSG,
-				Client_ID(Origin), Name);
+			IRC_WriteErrClient(Origin, ERR_CHANOPPRIVTOOLOW_MSG,
+					   Client_ID(Origin), Name);
 			return;
 		}
 	}
@@ -433,7 +420,7 @@ Channel_CountVisible (CLIENT *Client)
 	c = My_Channels;
 	while(c) {
 		if (Client) {
-			if (!strchr(Channel_Modes(c), 's')
+			if (!Channel_HasMode(c, 's')
 			    || Channel_IsMemberOf(c, Client))
 				count++;
 		} else
@@ -499,6 +486,14 @@ Channel_Modes( CHANNEL *Chan )
 } /* Channel_Modes */
 
 
+GLOBAL bool
+Channel_HasMode( CHANNEL *Chan, char Mode )
+{
+	assert( Chan != NULL );
+	return strchr( Chan->modes, Mode ) != NULL;
+} /* Channel_HasMode */
+
+
 GLOBAL char *
 Channel_Key( CHANNEL *Chan )
 {
@@ -636,7 +631,7 @@ Channel_ModeAdd( CHANNEL *Chan, char Mode )
 	assert( Chan != NULL );
 
 	x[0] = Mode; x[1] = '\0';
-	if( ! strchr( Chan->modes, x[0] ))
+	if( ! Channel_HasMode( Chan, x[0] ))
 	{
 		/* Channel does not have this mode yet, set it */
 		strlcat( Chan->modes, x, sizeof( Chan->modes ));
@@ -746,6 +741,13 @@ Channel_UserModes( CHANNEL *Chan, CLIENT *Client )
 
 
 GLOBAL bool
+Channel_UserHasMode( CHANNEL *Chan, CLIENT *Client, char Mode )
+{
+	return strchr(Channel_UserModes(Chan, Client), Mode) != NULL;
+} /* Channel_UserHasMode */
+
+
+GLOBAL bool
 Channel_IsMemberOf( CHANNEL *Chan, CLIENT *Client )
 {
 	/* Test if Client is on Channel Chan */
@@ -873,15 +875,15 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 
 	if (Channel_IsMemberOf(Chan, From)) {
 		is_member = true;
-		if (strchr(Channel_UserModes(Chan, From), 'v'))
+		if (Channel_UserHasMode(Chan, From, 'v'))
 			has_voice = true;
-		if (strchr(Channel_UserModes(Chan, From), 'h'))
+		if (Channel_UserHasMode(Chan, From, 'h'))
 			is_halfop = true;
-		if (strchr(Channel_UserModes(Chan, From), 'o'))
+		if (Channel_UserHasMode(Chan, From, 'o'))
 			is_op = true;
-		if (strchr(Channel_UserModes(Chan, From), 'a'))
+		if (Channel_UserHasMode(Chan, From, 'a'))
 			is_chanadmin = true;
-		if (strchr(Channel_UserModes(Chan, From), 'q'))
+		if (Channel_UserHasMode(Chan, From, 'q'))
 			is_owner = true;
 	}
 
@@ -891,17 +893,17 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 	 * If channel mode n set: non-members cannot send to channel.
 	 * If channel mode m set: need voice.
 	 */
-	if (strchr(Channel_Modes(Chan), 'n') && !is_member)
+	if (Channel_HasMode(Chan, 'n') && !is_member)
 		return false;
 
-	if (strchr(Channel_Modes(Chan), 'M') && !Client_HasMode(From, 'R')
+	if (Channel_HasMode(Chan, 'M') && !Client_HasMode(From, 'R')
 	    && !Client_HasMode(From, 'o'))
 		return false;
 
 	if (has_voice || is_halfop || is_op || is_chanadmin || is_owner)
 		return true;
 
-	if (strchr(Channel_Modes(Chan), 'm'))
+	if (Channel_HasMode(Chan, 'm'))
 		return false;
 
 	if (Lists_Check(&Chan->list_excepts, From))
@@ -918,19 +920,20 @@ Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Command,
 	if (!Can_Send_To_Channel(Chan, From)) {
 		if (! SendErrors)
 			return CONNECTED;	/* no error, see RFC 2812 */
-		if (strchr(Channel_Modes(Chan), 'M'))
-			return IRC_WriteStrClient(From, ERR_NEEDREGGEDNICK_MSG,
+		if (Channel_HasMode(Chan, 'M'))
+			return IRC_WriteErrClient(From, ERR_NEEDREGGEDNICK_MSG,
 						  Client_ID(From), Channel_Name(Chan));
 		else
-			return IRC_WriteStrClient(From, ERR_CANNOTSENDTOCHAN_MSG,
+			return IRC_WriteErrClient(From, ERR_CANNOTSENDTOCHAN_MSG,
 					  Client_ID(From), Channel_Name(Chan));
 	}
 
 	if (Client_Conn(From) > NONE)
 		Conn_UpdateIdle(Client_Conn(From));
 
-	return IRC_WriteStrChannelPrefix(Client, Chan, From, true,
-			"%s %s :%s", Command, Channel_Name(Chan), Text);
+	IRC_WriteStrChannelPrefix(Client, Chan, From, true, "%s %s :%s",
+				  Command, Channel_Name(Chan), Text);
+	return CONNECTED;
 }
 
 
@@ -1089,7 +1092,7 @@ Remove_Client( int Type, CHANNEL *Chan, CLIENT *Client, CLIENT *Origin, const ch
 	}
 
 	/* When channel is empty and is not pre-defined, delete */
-	if( ! strchr( Channel_Modes( Chan ), 'P' ))
+	if( ! Channel_HasMode( Chan, 'P' ))
 	{
 		if( ! Get_First_Cl2Chan( NULL, Chan )) Delete_Channel( Chan );
 	}
@@ -1217,7 +1220,7 @@ Channel_CheckKey(CHANNEL *Chan, CLIENT *Client, const char *Key)
 	assert(Client != NULL);
 	assert(Key != NULL);
 
-	if (!strchr(Chan->modes, 'k'))
+	if (!Channel_HasMode(Chan, 'k'))
 		return true;
 	if (*Key == '\0')
 		return false;

+ 3 - 1
src/ngircd/channel.h

@@ -79,7 +79,6 @@ GLOBAL unsigned long Channel_MemberCount PARAMS(( CHANNEL *Chan ));
 GLOBAL int Channel_CountForUser PARAMS(( CLIENT *Client ));
 
 GLOBAL const char *Channel_Name PARAMS(( const CHANNEL *Chan ));
-GLOBAL char *Channel_Modes PARAMS(( CHANNEL *Chan ));
 GLOBAL char *Channel_Topic PARAMS(( CHANNEL *Chan ));
 GLOBAL char *Channel_Key PARAMS(( CHANNEL *Chan ));
 GLOBAL unsigned long Channel_MaxUsers PARAMS(( CHANNEL *Chan ));
@@ -106,9 +105,12 @@ GLOBAL bool Channel_IsValidName PARAMS(( const char *Name ));
 
 GLOBAL bool Channel_ModeAdd PARAMS(( CHANNEL *Chan, char Mode ));
 GLOBAL bool Channel_ModeDel PARAMS(( CHANNEL *Chan, char Mode ));
+GLOBAL bool Channel_HasMode PARAMS(( CHANNEL *Chan, char Mode ));
+GLOBAL char *Channel_Modes PARAMS(( CHANNEL *Chan ));
 
 GLOBAL bool Channel_UserModeAdd PARAMS(( CHANNEL *Chan, CLIENT *Client, char Mode ));
 GLOBAL bool Channel_UserModeDel PARAMS(( CHANNEL *Chan, CLIENT *Client, char Mode ));
+GLOBAL bool Channel_UserHasMode PARAMS(( CHANNEL *Chan, CLIENT *Client, char Mode ));
 GLOBAL char *Channel_UserModes PARAMS(( CHANNEL *Chan, CLIENT *Client ));
 
 GLOBAL bool Channel_IsMemberOf PARAMS(( CHANNEL *Chan, CLIENT *Client ));

+ 29 - 30
src/ngircd/class.c

@@ -33,8 +33,6 @@
 
 struct list_head My_Classes[CLASS_COUNT];
 
-char Reject_Reason[COMMAND_LEN];
-
 GLOBAL void
 Class_Init(void)
 {
@@ -49,32 +47,29 @@ Class_Exit(void)
 	for (i = 0; i < CLASS_COUNT; Lists_Free(&My_Classes[i++]));
 }
 
-GLOBAL char *
-Class_GetMemberReason(const int Class, CLIENT *Client)
+GLOBAL bool
+Class_GetMemberReason(const int Class, CLIENT *Client, char *reason, size_t len)
 {
-	char *reason;
+	char str[COMMAND_LEN] = "listed";
 
 	assert(Class < CLASS_COUNT);
 	assert(Client != NULL);
 
-	reason = Lists_CheckReason(&My_Classes[Class], Client);
-	if (!reason)
-		return NULL;
-
-	if (!*reason)
-		reason = "listed";
+	if (!Lists_CheckReason(&My_Classes[Class], Client, str, sizeof(str)))
+		return false;
 
 	switch(Class) {
 		case CLASS_GLINE:
-			snprintf(Reject_Reason, sizeof(Reject_Reason),
-				 "\"%s\" (G-Line)", reason);
-			return Reject_Reason;
+			snprintf(reason, len, "\"%s\" (G-Line)", str);
+			break;
 		case CLASS_KLINE:
-			snprintf(Reject_Reason, sizeof(Reject_Reason),
-				 "\"%s\" (K-Line)", reason);
-			return Reject_Reason;
+			snprintf(reason, len, "\"%s\" (K-Line)", str);
+			break;
+		default:
+			snprintf(reason, len, "%s", str);
+			break;
 	}
-	return reason;
+	return true;
 }
 
 /**
@@ -88,15 +83,13 @@ Class_GetMemberReason(const int Class, CLIENT *Client)
 GLOBAL bool
 Class_HandleServerBans(CLIENT *Client)
 {
-	char *rejectptr;
+	char reject[COMMAND_LEN];
 
 	assert(Client != NULL);
 
-	rejectptr = Class_GetMemberReason(CLASS_GLINE, Client);
-	if (!rejectptr)
-		rejectptr = Class_GetMemberReason(CLASS_KLINE, Client);
-	if (rejectptr) {
-		Client_Reject(Client, rejectptr, true);
+	if (Class_GetMemberReason(CLASS_GLINE, Client, reject, sizeof(reject)) ||
+	    Class_GetMemberReason(CLASS_KLINE, Client, reject, sizeof(reject))) {
+		Client_Reject(Client, reject, true);
 		return DISCONNECTED;
 	}
 
@@ -105,24 +98,30 @@ Class_HandleServerBans(CLIENT *Client)
 
 
 GLOBAL bool
-Class_AddMask(const int Class, const char *Mask, time_t ValidUntil,
+Class_AddMask(const int Class, const char *Pattern, time_t ValidUntil,
 	      const char *Reason)
 {
+	char mask[MASK_LEN];
+
 	assert(Class < CLASS_COUNT);
-	assert(Mask != NULL);
+	assert(Pattern != NULL);
 	assert(Reason != NULL);
 
-	return Lists_Add(&My_Classes[Class], Lists_MakeMask(Mask),
+	Lists_MakeMask(Pattern, mask, sizeof(mask));
+	return Lists_Add(&My_Classes[Class], mask,
 			 ValidUntil, Reason);
 }
 
 GLOBAL void
-Class_DeleteMask(const int Class, const char *Mask)
+Class_DeleteMask(const int Class, const char *Pattern)
 {
+	char mask[MASK_LEN];
+
 	assert(Class < CLASS_COUNT);
-	assert(Mask != NULL);
+	assert(Pattern != NULL);
 
-	Lists_Del(&My_Classes[Class], Lists_MakeMask(Mask));
+	Lists_MakeMask(Pattern, mask, sizeof(mask));
+	Lists_Del(&My_Classes[Class], mask);
 }
 
 GLOBAL struct list_head *

+ 4 - 3
src/ngircd/class.h

@@ -25,11 +25,12 @@
 GLOBAL void Class_Init PARAMS((void));
 GLOBAL void Class_Exit PARAMS((void));
 
-GLOBAL bool Class_AddMask PARAMS((const int Class, const char *Mask,
+GLOBAL bool Class_AddMask PARAMS((const int Class, const char *Pattern,
 				  const time_t ValidUntil, const char *Reason));
-GLOBAL void Class_DeleteMask PARAMS((const int Class, const char *Mask));
+GLOBAL void Class_DeleteMask PARAMS((const int Class, const char *Pattern));
 
-GLOBAL char *Class_GetMemberReason PARAMS((const int Class, CLIENT *Client));
+GLOBAL bool Class_GetMemberReason PARAMS((const int Class, CLIENT *Client,
+					  char *reason, size_t len));
 GLOBAL bool Class_HandleServerBans PARAMS((CLIENT *Client));
 
 GLOBAL struct list_head *Class_GetList PARAMS((const int Class));

+ 288 - 100
src/ngircd/client.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -41,6 +41,7 @@
 #include "hash.h"
 #include "irc-write.h"
 #include "log.h"
+#include "match.h"
 #include "messages.h"
 
 #include <exp.h>
@@ -61,6 +62,8 @@ static CLIENT *New_Client_Struct PARAMS(( void ));
 static void Generate_MyToken PARAMS(( CLIENT *Client ));
 static void Adjust_Counters PARAMS(( CLIENT *Client ));
 
+static void Free_Client PARAMS(( CLIENT **Client ));
+
 static CLIENT *Init_New_Client PARAMS((CONN_ID Idx, CLIENT *Introducer,
 				       CLIENT *TopServer, int Type, const char *ID,
 				       const char *User, const char *Hostname, const char *Info,
@@ -86,7 +89,7 @@ Client_Init( void )
 		exit( 1 );
 	}
 
-	/* Client-Struktur dieses Servers */
+	/* Client structure for this server */
 	This_Server->next = NULL;
 	This_Server->type = CLIENT_SERVER;
 	This_Server->conn_id = NONE;
@@ -119,14 +122,15 @@ Client_Exit( void )
 	
 	cnt = 0;
 	c = My_Clients;
-	while( c )
-	{
+	while(c) {
 		cnt++;
 		next = (CLIENT *)c->next;
-		free( c );
+		Free_Client(&c);
 		c = next;
 	}
-	if( cnt ) Log( LOG_INFO, "Freed %d client structure%s.", cnt, cnt == 1 ? "" : "s" );
+	if (cnt)
+		Log(LOG_INFO, "Freed %d client structure%s.",
+		    cnt, cnt == 1 ? "" : "s");
 } /* Client_Exit */
 
 
@@ -216,8 +220,8 @@ Init_New_Client(CONN_ID Idx, CLIENT *Introducer, CLIENT *TopServer,
 	if (Type == CLIENT_SERVER)
 		Generate_MyToken(client);
 
-	if (strchr(client->modes, 'a'))
-		strlcpy(client->away, DEFAULT_AWAY_MSG, sizeof(client->away));
+	if (Client_HasMode(client, 'a'))
+		client->away = strndup(DEFAULT_AWAY_MSG, CLIENT_AWAY_LEN - 1);
 
 	client->next = (POINTER *)My_Clients;
 	My_Clients = client;
@@ -234,14 +238,14 @@ Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool Sen
 	/* remove a client */
 	
 	CLIENT *last, *c;
-	char msg[LINE_LEN];
+	char msg[COMMAND_LEN];
 	const char *txt;
 
 	assert( Client != NULL );
 
-	if( LogMsg ) txt = LogMsg;
-	else txt = FwdMsg;
-	if( ! txt ) txt = "Reason unknown.";
+	txt = LogMsg ? LogMsg : FwdMsg;
+	if (!txt)
+		txt = "Reason unknown";
 
 	/* netsplit message */
 	if( Client->type == CLIENT_SERVER ) {
@@ -281,10 +285,15 @@ Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool Sen
 				Destroy_UserOrService(c, txt, FwdMsg, SendQuit);
 			else if( c->type == CLIENT_SERVER )
 			{
-				if( c != This_Server )
-				{
-					if( c->conn_id != NONE ) Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" unregistered (connection %d): %s", c->id, c->conn_id, txt );
-					else Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" unregistered: %s", c->id, txt );
+				if (c != This_Server) {
+					if (c->conn_id != NONE)
+						Log(LOG_NOTICE|LOG_snotice,
+						    "Server \"%s\" unregistered (connection %d): %s.",
+						c->id, c->conn_id, txt);
+					else
+						Log(LOG_NOTICE|LOG_snotice,
+						    "Server \"%s\" unregistered: %s.",
+						    c->id, txt);
 				}
 
 				/* inform other servers */
@@ -296,17 +305,23 @@ Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool Sen
 			}
 			else
 			{
-				if( c->conn_id != NONE )
-				{
-					if( c->id[0] ) Log( LOG_NOTICE, "Client \"%s\" unregistered (connection %d): %s", c->id, c->conn_id, txt );
-					else Log( LOG_NOTICE, "Client unregistered (connection %d): %s", c->conn_id, txt );
+				if (c->conn_id != NONE) {
+					if (c->id[0])
+						Log(LOG_NOTICE,
+						    "Client \"%s\" unregistered (connection %d): %s.",
+						    c->id, c->conn_id, txt);
+					else
+						Log(LOG_NOTICE,
+						    "Client unregistered (connection %d): %s.",
+						    c->conn_id, txt);
 				} else {
-					Log(LOG_WARNING, "Unregistered unknown client \"%s\": %s",
-								c->id[0] ? c->id : "(No Nick)", txt );
+					Log(LOG_WARNING,
+					    "Unregistered unknown client \"%s\": %s",
+					    c->id[0] ? c->id : "(No Nick)", txt);
 				}
 			}
 
-			free( c );
+			Free_Client(&c);
 			break;
 		}
 		last = c;
@@ -348,6 +363,27 @@ Client_SetHostname( CLIENT *Client, const char *Hostname )
 } /* Client_SetHostname */
 
 
+/**
+ * Set IP address to display for a client.
+ *
+ * @param Client The client.
+ * @param IPAText Textual representation of the IP address or NULL to unset.
+ */
+GLOBAL void
+Client_SetIPAText(CLIENT *Client, const char *IPAText)
+{
+	assert(Client != NULL);
+
+	if (Client->ipa_text)
+		free(Client->ipa_text);
+
+	if (*IPAText)
+		Client->ipa_text = strndup(IPAText, CLIENT_HOST_LEN - 1);
+	else
+		Client->ipa_text = NULL;
+}
+
+
 GLOBAL void
 Client_SetID( CLIENT *Client, const char *ID )
 {
@@ -441,6 +477,22 @@ Client_SetFlags( CLIENT *Client, const char *Flags )
 
 
 GLOBAL void
+Client_SetAccountName(CLIENT *Client, const char *AccountName)
+{
+	assert(Client != NULL);
+
+	if (Client->account_name)
+		free(Client->account_name);
+
+	if (*AccountName)
+		Client->account_name = strndup(AccountName,
+					       CLIENT_NICK_LEN - 1);
+	else
+		Client->account_name = NULL;
+}
+
+
+GLOBAL void
 Client_SetAway( CLIENT *Client, const char *Txt )
 {
 	/* Set AWAY reason of client */
@@ -448,7 +500,11 @@ Client_SetAway( CLIENT *Client, const char *Txt )
 	assert( Client != NULL );
 	assert( Txt != NULL );
 
-	strlcpy( Client->away, Txt, sizeof( Client->away ));
+	if (Client->away)
+		free(Client->away);
+
+	Client->away = strndup(Txt, CLIENT_AWAY_LEN - 1);
+
 	LogDebug("%s \"%s\" is away: %s", Client_TypeText(Client),
 		 Client_Mask(Client), Txt);
 } /* Client_SetAway */
@@ -489,19 +545,11 @@ Client_SetIntroducer( CLIENT *Client, CLIENT *Introducer )
 } /* Client_SetIntroducer */
 
 
-GLOBAL void
-Client_SetOperByMe( CLIENT *Client, bool OperByMe )
-{
-	assert( Client != NULL );
-	Client->oper_by_me = OperByMe;
-} /* Client_SetOperByMe */
-
-
 GLOBAL bool
 Client_ModeAdd( CLIENT *Client, char Mode )
 {
 	/* Set Mode.
-	 * If Client already alread had Mode, return false.
+	 * If Client already had Mode, return false.
 	 * If the Mode was newly set, return true.
 	 */
 
@@ -510,7 +558,7 @@ Client_ModeAdd( CLIENT *Client, char Mode )
 	assert( Client != NULL );
 
 	x[0] = Mode; x[1] = '\0';
-	if (!strchr( Client->modes, x[0])) {
+	if (!Client_HasMode(Client, x[0])) {
 		strlcat( Client->modes, x, sizeof( Client->modes ));
 		return true;
 	}
@@ -545,13 +593,14 @@ Client_ModeDel( CLIENT *Client, char Mode )
 } /* Client_ModeDel */
 
 
+/**
+ * Search CLIENT structure of a given nick name.
+ *
+ * @return Pointer to CLIENT structure or NULL if not found.
+ */
 GLOBAL CLIENT *
 Client_Search( const char *Nick )
 {
-	/* return Client-Structure that has the corresponding Nick.
-	 * If none is found, return NULL.
-	 */
-
 	char search_id[CLIENT_ID_LEN], *ptr;
 	CLIENT *c = NULL;
 	UINT32 search_hash;
@@ -572,7 +621,39 @@ Client_Search( const char *Nick )
 		c = (CLIENT *)c->next;
 	}
 	return NULL;
-} /* Client_Search */
+}
+
+
+/**
+ * Search first CLIENT structure matching a given mask of a server.
+ *
+ * The order of servers is arbitrary, but this function makes sure that the
+ * local server is always returned if the mask matches it.
+ *
+ * @return Pointer to CLIENT structure or NULL if no server could be found.
+ */
+GLOBAL CLIENT *
+Client_SearchServer(const char *Mask)
+{
+	CLIENT *c;
+
+	assert(Mask != NULL);
+
+	/* First check if mask matches the local server */
+	if (MatchCaseInsensitive(Mask, Client_ID(Client_ThisServer())))
+		return Client_ThisServer();
+
+	c = My_Clients;
+	while (c) {
+		if (Client_Type(c) == CLIENT_SERVER) {
+			/* This is a server: check if Mask matches */
+			if (MatchCaseInsensitive(Mask, c->id))
+				return c;
+		}
+		c = (CLIENT *)c->next;
+	}
+	return NULL;
+}
 
 
 /**
@@ -699,8 +780,6 @@ Client_HostnameCloaked(CLIENT *Client)
  * Get (potentially cloaked) hostname of a client to display it to other users.
  *
  * If the client has not enabled cloaking, the real hostname is used.
- * Please note that this function uses a global static buffer, so you can't
- * nest invocations without overwriting earlier results!
  *
  * @param Client Pointer to client structure
  * @return Pointer to client hostname
@@ -715,13 +794,28 @@ Client_HostnameDisplayed(CLIENT *Client)
 		return Client_Hostname(Client);
 
 	/* Use an already saved cloaked hostname, if there is one */
-	if (Client->cloaked[0])
+	if (Client->cloaked)
 		return Client->cloaked;
 
 	Client_UpdateCloakedHostname(Client, NULL, NULL);
 	return Client->cloaked;
 }
 
+GLOBAL const char *
+Client_IPAText(CLIENT *Client)
+{
+	assert(Client != NULL);
+
+	/* Not a local client? */
+	if (Client_Conn(Client) <= NONE)
+		return "0.0.0.0";
+
+	if (!Client->ipa_text)
+		return Conn_GetIPAInfo(Client_Conn(Client));
+	else
+		return Client->ipa_text;
+}
+
 /**
  * Update (and generate, if necessary) the cloaked hostname of a client.
  *
@@ -736,25 +830,32 @@ GLOBAL void
 Client_UpdateCloakedHostname(CLIENT *Client, CLIENT *Origin,
 			     const char *Hostname)
 {
-	static char Cloak_Buffer[CLIENT_HOST_LEN];
+	char Cloak_Buffer[CLIENT_HOST_LEN];
 
 	assert(Client != NULL);
 	if (!Origin)
 		Origin = Client_ThisServer();
 
+	if (!Client->cloaked) {
+		Client->cloaked = malloc(CLIENT_HOST_LEN);
+		if (!Client->cloaked)
+			return;
+	}
+
 	if (!Hostname) {
 		/* Generate new cloaked hostname */
 		if (*Conf_CloakHostModeX) {
-			strlcpy(Cloak_Buffer, Client->host, CLIENT_HOST_LEN);
+			strlcpy(Cloak_Buffer, Client->host,
+				sizeof(Cloak_Buffer));
 			strlcat(Cloak_Buffer, Conf_CloakHostSalt,
-				CLIENT_HOST_LEN);
-			snprintf(Client->cloaked, sizeof(Client->cloaked),
+				sizeof(Cloak_Buffer));
+			snprintf(Client->cloaked, CLIENT_HOST_LEN,
 				 Conf_CloakHostModeX, Hash(Cloak_Buffer));
 		} else
 			strlcpy(Client->cloaked, Client_ID(Client->introducer),
-				sizeof(Client->cloaked));
+				CLIENT_HOST_LEN);
 	} else
-		strlcpy(Client->cloaked, Hostname, sizeof(Client->cloaked));
+		strlcpy(Client->cloaked, Hostname, CLIENT_HOST_LEN);
 	LogDebug("Cloaked hostname of \"%s\" updated to \"%s\"",
 		 Client_ID(Client), Client->cloaked);
 
@@ -780,14 +881,6 @@ Client_Flags( CLIENT *Client )
 } /* Client_Flags */
 
 
-GLOBAL bool
-Client_OperByMe( CLIENT *Client )
-{
-	assert( Client != NULL );
-	return Client->oper_by_me;
-} /* Client_OperByMe */
-
-
 GLOBAL int
 Client_Hops( CLIENT *Client )
 {
@@ -905,6 +998,14 @@ Client_HasMode( CLIENT *Client, char Mode )
 } /* Client_HasMode */
 
 
+GLOBAL bool
+Client_HasFlag( CLIENT *Client, char Flag )
+{
+	assert( Client != NULL );
+	return strchr( Client->flags, Flag ) != NULL;
+} /* Client_HasFlag */
+
+
 GLOBAL char *
 Client_Away( CLIENT *Client )
 {
@@ -913,6 +1014,14 @@ Client_Away( CLIENT *Client )
 } /* Client_Away */
 
 
+GLOBAL char *
+Client_AccountName(CLIENT *Client)
+{
+	assert(Client != NULL);
+	return Client->account_name;
+}
+
+
 /**
  * Make sure that a given nickname is valid.
  *
@@ -931,11 +1040,11 @@ Client_CheckNick(CLIENT *Client, char *Nick)
 
 	if (!Client_IsValidNick(Nick)) {
 		if (strlen(Nick ) >= Conf_MaxNickLength)
-			IRC_WriteStrClient(Client, ERR_NICKNAMETOOLONG_MSG,
+			IRC_WriteErrClient(Client, ERR_NICKNAMETOOLONG_MSG,
 					   Client_ID(Client), Nick,
 					   Conf_MaxNickLength - 1);
 		else
-			IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
+			IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
 					   Client_ID(Client), Nick);
 		return false;
 	}
@@ -944,7 +1053,7 @@ Client_CheckNick(CLIENT *Client, char *Nick)
 	    && Client_Type(Client) != CLIENT_SERVICE) {
 		/* Make sure that this isn't a restricted/forbidden nickname */
 		if (Conf_NickIsBlocked(Nick)) {
-			IRC_WriteStrClient(Client, ERR_FORBIDDENNICKNAME_MSG,
+			IRC_WriteErrClient(Client, ERR_FORBIDDENNICKNAME_MSG,
 					   Client_ID(Client), Nick);
 			return false;
 		}
@@ -952,7 +1061,7 @@ Client_CheckNick(CLIENT *Client, char *Nick)
 
 	/* Nickname already registered? */
 	if (Client_Search(Nick)) {
-		IRC_WriteStrClient(Client, ERR_NICKNAMEINUSE_MSG,
+		IRC_WriteErrClient(Client, ERR_NICKNAMEINUSE_MSG,
 			Client_ID(Client), Nick);
 		return false;
 	}
@@ -973,7 +1082,8 @@ Client_CheckID( CLIENT *Client, char *ID )
 
 	/* ID too long? */
 	if (strlen(ID) > CLIENT_ID_LEN) {
-		IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG, Client_ID(Client), ID);
+		IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
+				   Client_ID(Client), ID);
 		return false;
 	}
 
@@ -1071,7 +1181,8 @@ Client_OperCount( void )
 	c = My_Clients;
 	while( c )
 	{
-		if( c && ( c->type == CLIENT_USER ) && ( strchr( c->modes, 'o' ))) cnt++;
+		if (c && c->type == CLIENT_USER && Client_HasMode(c, 'o' ))
+			cnt++;
 		c = (CLIENT *)c->next;
 	}
 	return cnt;
@@ -1278,11 +1389,14 @@ MyCount( CLIENT_TYPE Type )
 } /* MyCount */
 
 
+/**
+ * Allocate and initialize new CLIENT strcuture.
+ *
+ * @return Pointer to CLIENT structure or NULL on error.
+ */
 static CLIENT *
 New_Client_Struct( void )
 {
-	/* Neue CLIENT-Struktur pre-initialisieren */
-
 	CLIENT *c;
 
 	c = (CLIENT *)malloc( sizeof( CLIENT ));
@@ -1296,14 +1410,34 @@ New_Client_Struct( void )
 
 	c->type = CLIENT_UNKNOWN;
 	c->conn_id = NONE;
-	c->oper_by_me = false;
 	c->hops = -1;
 	c->token = -1;
 	c->mytoken = -1;
 
 	return c;
-} /* New_Client */
+}
 
+/**
+ * Free a CLIENT structure and its member variables.
+ */
+static void
+Free_Client(CLIENT **Client)
+{
+	assert(Client != NULL);
+	assert(*Client != NULL);
+
+	if ((*Client)->account_name)
+		free((*Client)->account_name);
+	if ((*Client)->away)
+		free((*Client)->away);
+	if ((*Client)->cloaked)
+		free((*Client)->cloaked);
+	if ((*Client)->ipa_text)
+		free((*Client)->ipa_text);
+
+	free(*Client);
+	*Client = NULL;
+}
 
 static void
 Generate_MyToken( CLIENT *Client )
@@ -1317,7 +1451,7 @@ Generate_MyToken( CLIENT *Client )
 	{
 		if( c->mytoken == token )
 		{
-			/* Das Token wurde bereits vergeben */
+			/* The token is already in use */
 			token++;
 			c = My_Clients;
 			continue;
@@ -1424,7 +1558,7 @@ Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool
 	if(Client->conn_id != NONE) {
 		/* Local (directly connected) client */
 		Log(LOG_NOTICE,
-		    "%s \"%s\" unregistered (connection %d): %s",
+		    "%s \"%s\" unregistered (connection %d): %s.",
 		    Client_TypeText(Client), Client_Mask(Client),
 		    Client->conn_id, Txt);
 		Log_ServerNotice('c', "Client exiting: %s (%s@%s) [%s]",
@@ -1442,7 +1576,7 @@ Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool
 		}
 	} else {
 		/* Remote client */
-		LogDebug("%s \"%s\" unregistered: %s",
+		LogDebug("%s \"%s\" unregistered: %s.",
 			 Client_TypeText(Client), Client_Mask(Client), Txt);
 
 		if(SendQuit) {
@@ -1468,9 +1602,6 @@ Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool
 /**
  * Introduce a new user or service client to a remote server.
  *
- * This function differentiates between RFC1459 and RFC2813 server links and
- * generates the appropriate commands to register the new user or service.
- *
  * @param To		The remote server to inform.
  * @param Prefix	Prefix for the generated commands.
  * @param data		CLIENT structure of the new client.
@@ -1479,43 +1610,100 @@ static void
 cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
 {
 	CLIENT *c = (CLIENT *)data;
+
+	(void)Client_Announce(To, Prefix, c);
+
+} /* cb_introduceClient */
+
+
+/**
+ * Announce an user or service to a server.
+ *
+ * This function differentiates between RFC1459 and RFC2813 server links and
+ * generates the appropriate commands to register the user or service.
+ *
+ * @param Client	Server
+ * @param Prefix	Prefix for the generated commands
+ * @param User		User to announce
+ */
+GLOBAL bool
+Client_Announce(CLIENT * Client, CLIENT * Prefix, CLIENT * User)
+{
 	CONN_ID conn;
 	char *modes, *user, *host;
 
-	modes = Client_Modes(c);
-	user = Client_User(c) ? Client_User(c) : "-";
-	host = Client_Hostname(c) ? Client_Hostname(c) : "-";
+	modes = Client_Modes(User);
+	user = Client_User(User) ? Client_User(User) : "-";
+	host = Client_Hostname(User) ? Client_Hostname(User) : "-";
 
-	conn = Client_Conn(To);
+	conn = Client_Conn(Client);
 	if (Conn_Options(conn) & CONN_RFC1459) {
 		/* RFC 1459 mode: separate NICK and USER commands */
-		Conn_WriteStr(conn, "NICK %s :%d", Client_ID(c),
-			      Client_Hops(c) + 1);
-		Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
-			      Client_ID(c), user, host,
-			      Client_ID(Client_Introducer(c)), Client_Info(c));
-		if (modes[0])
-			Conn_WriteStr(conn, ":%s MODE %s +%s",
-				      Client_ID(c), Client_ID(c), modes);
+		if (! Conn_WriteStr(conn, "NICK %s :%d",
+				    Client_ID(User), Client_Hops(User) + 1))
+			return DISCONNECTED;
+		if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
+				     Client_ID(User), user, host,
+				     Client_ID(Client_Introducer(User)),
+				     Client_Info(User)))
+			return DISCONNECTED;
+		if (modes[0]) {
+			if (! Conn_WriteStr(conn, ":%s MODE %s +%s",
+				     Client_ID(User), Client_ID(User),
+				     modes))
+				return DISCONNECTED;
+		}
 	} else {
 		/* RFC 2813 mode: one combined NICK or SERVICE command */
-		if (Client_Type(c) == CLIENT_SERVICE
-		    && strchr(Client_Flags(To), 'S'))
-			IRC_WriteStrClientPrefix(To, Prefix,
-						 "SERVICE %s %d * +%s %d :%s",
-						 Client_Mask(c),
-						 Client_MyToken(Client_Introducer(c)),
-						 Client_Modes(c), Client_Hops(c) + 1,
-						 Client_Info(c));
-		else
-			IRC_WriteStrClientPrefix(To, Prefix,
-						 "NICK %s %d %s %s %d +%s :%s",
-						 Client_ID(c), Client_Hops(c) + 1,
-						 user, host,
-						 Client_MyToken(Client_Introducer(c)),
-						 modes, Client_Info(c));
+		if (Client_Type(User) == CLIENT_SERVICE
+		    && Client_HasFlag(Client, 'S')) {
+			if (!IRC_WriteStrClientPrefix(Client, Prefix,
+					"SERVICE %s %d * +%s %d :%s",
+					Client_Mask(User),
+					Client_MyToken(Client_Introducer(User)),
+					modes, Client_Hops(User) + 1,
+					Client_Info(User)))
+				return DISCONNECTED;
+		} else {
+			if (!IRC_WriteStrClientPrefix(Client, Prefix,
+					"NICK %s %d %s %s %d +%s :%s",
+					Client_ID(User), Client_Hops(User) + 1,
+					user, host,
+					Client_MyToken(Client_Introducer(User)),
+					modes, Client_Info(User)))
+				return DISCONNECTED;
+		}
 	}
-} /* cb_introduceClient */
+
+	if (Client_HasFlag(Client, 'M')) {
+		/* Synchronize metadata */
+		if (Client_HostnameCloaked(User)) {
+			if (!IRC_WriteStrClientPrefix(Client, Prefix,
+					"METADATA %s cloakhost :%s",
+					Client_ID(User),
+					Client_HostnameCloaked(User)))
+				return DISCONNECTED;
+		}
+
+		if (Client_AccountName(User)) {
+			if (!IRC_WriteStrClientPrefix(Client, Prefix,
+					"METADATA %s accountname :%s",
+					Client_ID(User),
+					Client_AccountName(User)))
+				return DISCONNECTED;
+		}
+
+		if (Conn_GetCertFp(Client_Conn(User))) {
+			if (!IRC_WriteStrClientPrefix(Client, Prefix,
+					"METADATA %s certfp :%s",
+					Client_ID(User),
+					Conn_GetCertFp(Client_Conn(User))))
+				return DISCONNECTED;
+		}
+	}
+
+	return CONNECTED;
+} /* Client_Announce */
 
 
 #ifdef DEBUG

+ 25 - 17
src/ngircd/client.h

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -17,19 +17,20 @@
  * Client management (header)
  */
 
-#define CLIENT_UNKNOWN 1		/* connection of unknown type */
-#define CLIENT_GOTPASS 2		/* client did send PASS */
-#define CLIENT_GOTNICK 4		/* client did send NICK */
-#define CLIENT_GOTUSER 8		/* client did send USER */
-#define CLIENT_USER 16			/* client is an IRC user */
-#define CLIENT_SERVER 32		/* client is a server */
-#define CLIENT_SERVICE 64		/* client is a service */
-#define CLIENT_UNKNOWNSERVER 128	/* unregistered server connection */
-#define CLIENT_GOTPASS_2813 256		/* client did send PASS, RFC 2813 style */
+#define CLIENT_UNKNOWN		0x0001	/* connection of unknown type */
+#define CLIENT_GOTPASS		0x0002	/* client did send PASS */
+#define CLIENT_GOTNICK		0x0004	/* client did send NICK */
+#define CLIENT_GOTUSER		0x0008	/* client did send USER */
+#define CLIENT_USER		0x0010	/* client is an IRC user */
+#define CLIENT_SERVER		0x0020	/* client is a server */
+#define CLIENT_SERVICE		0x0040	/* client is a service */
+#define CLIENT_UNKNOWNSERVER	0x0080	/* unregistered server connection */
+#define CLIENT_GOTPASS_2813	0x0100	/* client did send PASS, RFC 2813 style */
 #ifndef STRICT_RFC
-# define CLIENT_WAITAUTHPING 512	/* waiting for AUTH PONG from client */
+# define CLIENT_WAITAUTHPING	0x0200	/* waiting for AUTH PONG from client */
 #endif
-#define CLIENT_WAITCAPEND 1024		/* waiting for "CAP END" command */
+#define CLIENT_WAITCAPEND	0x0400	/* waiting for "CAP END" command */
+#define CLIENT_ANY		0xFFFF
 
 #define CLIENT_TYPE int
 
@@ -48,7 +49,8 @@ typedef struct _CLIENT
 	struct _CLIENT *introducer;	/* ID of the servers which the client is connected to */
 	struct _CLIENT *topserver;	/* toplevel servers (only valid if client is a server) */
 	char host[CLIENT_HOST_LEN];	/* hostname of the client */
-	char cloaked[CLIENT_HOST_LEN];	/* cloaked hostname of the client */
+	char *cloaked;			/* cloaked hostname of the client */
+	char *ipa_text;			/* textual representaton of IP address */
 	char user[CLIENT_USER_LEN];	/* user name ("login") */
 #if defined(PAM) && defined(IDENTAUTH)
 	char orig_user[CLIENT_USER_LEN];/* user name supplied by USER command */
@@ -56,9 +58,9 @@ typedef struct _CLIENT
 	char info[CLIENT_INFO_LEN];	/* long user name (user) / info text (server) */
 	char modes[CLIENT_MODE_LEN];	/* client modes */
 	int hops, token, mytoken;	/* "hops" and "Token" (see SERVER command) */
-	bool oper_by_me;		/* client is local IRC operator on this server? */
-	char away[CLIENT_AWAY_LEN];	/* AWAY text (valid if mode 'a' is set) */
+	char *away;			/* AWAY text (valid if mode 'a' is set) */
 	char flags[CLIENT_FLAGS_LEN];	/* flags of the client */
+	char *account_name;		/* login account (for services) */
 	int capabilities;		/* enabled IRC capabilities */
 } CLIENT;
 
@@ -93,7 +95,10 @@ GLOBAL CLIENT *Client_ThisServer PARAMS(( void ));
 
 GLOBAL CLIENT *Client_GetFromToken PARAMS(( CLIENT *Client, int Token ));
 
+GLOBAL bool Client_Announce PARAMS(( CLIENT *Client, CLIENT *Prefix, CLIENT *User ));
+
 GLOBAL CLIENT *Client_Search PARAMS(( const char *ID ));
+GLOBAL CLIENT *Client_SearchServer PARAMS(( const char *ID ));
 GLOBAL CLIENT *Client_First PARAMS(( void ));
 GLOBAL CLIENT *Client_Next PARAMS(( CLIENT *c ));
 
@@ -110,21 +115,24 @@ GLOBAL char *Client_OrigUser PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Hostname PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_HostnameCloaked PARAMS((CLIENT *Client));
 GLOBAL char *Client_HostnameDisplayed PARAMS(( CLIENT *Client ));
+GLOBAL const char *Client_IPAText PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Modes PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Flags PARAMS(( CLIENT *Client ));
 GLOBAL CLIENT *Client_Introducer PARAMS(( CLIENT *Client ));
-GLOBAL bool Client_OperByMe PARAMS(( CLIENT *Client ));
 GLOBAL int Client_Hops PARAMS(( CLIENT *Client ));
 GLOBAL int Client_Token PARAMS(( CLIENT *Client ));
 GLOBAL int Client_MyToken PARAMS(( CLIENT *Client ));
 GLOBAL CLIENT *Client_TopServer PARAMS(( CLIENT *Client ));
 GLOBAL CLIENT *Client_NextHop PARAMS(( CLIENT *Client ));
 GLOBAL char *Client_Away PARAMS(( CLIENT *Client ));
+GLOBAL char *Client_AccountName PARAMS((CLIENT *Client));
 GLOBAL time_t Client_StartTime PARAMS(( CLIENT *Client ));
 
 GLOBAL bool Client_HasMode PARAMS(( CLIENT *Client, char Mode ));
+GLOBAL bool Client_HasFlag PARAMS(( CLIENT *Client, char Flag ));
 
 GLOBAL void Client_SetHostname PARAMS(( CLIENT *Client, const char *Hostname ));
+GLOBAL void Client_SetIPAText PARAMS(( CLIENT *Client, const char *IPAText ));
 GLOBAL void Client_SetID PARAMS(( CLIENT *Client, const char *Nick ));
 GLOBAL void Client_SetUser PARAMS(( CLIENT *Client, const char *User, bool Idented ));
 GLOBAL void Client_SetOrigUser PARAMS(( CLIENT *Client, const char *User ));
@@ -132,11 +140,11 @@ GLOBAL void Client_SetInfo PARAMS(( CLIENT *Client, const char *Info ));
 GLOBAL void Client_SetType PARAMS(( CLIENT *Client, int Type ));
 GLOBAL void Client_SetHops PARAMS(( CLIENT *Client, int Hops ));
 GLOBAL void Client_SetToken PARAMS(( CLIENT *Client, int Token ));
-GLOBAL void Client_SetOperByMe PARAMS(( CLIENT *Client, bool OperByMe ));
 GLOBAL void Client_SetModes PARAMS(( CLIENT *Client, const char *Modes ));
 GLOBAL void Client_SetFlags PARAMS(( CLIENT *Client, const char *Flags ));
 GLOBAL void Client_SetIntroducer PARAMS(( CLIENT *Client, CLIENT *Introducer ));
 GLOBAL void Client_SetAway PARAMS(( CLIENT *Client, const char *Txt ));
+GLOBAL void Client_SetAccountName PARAMS((CLIENT *Client, const char *AccountName));
 
 GLOBAL bool Client_ModeAdd PARAMS(( CLIENT *Client, char Mode ));
 GLOBAL bool Client_ModeDel PARAMS(( CLIENT *Client, char Mode ));

+ 1 - 0
src/ngircd/conf-ssl.h

@@ -37,6 +37,7 @@ struct ConnSSL_State {
 	void *cookie;		/* pointer to server configuration structure
 				   (for outgoing connections), or NULL. */
 #endif
+	char *fingerprint;
 };
 
 #endif

File diff suppressed because it is too large
+ 441 - 225
src/ngircd/conf.c


+ 13 - 3
src/ngircd/conf.h

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -75,6 +75,7 @@ struct SSLOptions {
 	char *DHFile;			/**< File containing DH parameters */
 	array ListenPorts;		/**< Array of listening SSL ports */
 	array KeyFilePassword;		/**< Key file password */
+	char *CipherList;		/**< Set SSL cipher list to use */
 };
 #endif
 
@@ -111,6 +112,9 @@ GLOBAL char Conf_ServerAdminMail[CLIENT_INFO_LEN];
 /** Message of the day (MOTD) of this server */
 GLOBAL array Conf_Motd;
 
+/** Help text of this server */
+GLOBAL array Conf_Helptext;
+
 /** Array of ports this server should listen on */
 GLOBAL array Conf_ListenPorts;
 
@@ -145,8 +149,8 @@ GLOBAL CONF_SERVER Conf_Server[MAX_SERVERS];
 /** Array of pre-defined channels */
 GLOBAL array Conf_Channels;
 
-/** Flag indicating if only pre-defined channels are allowed (true) or not */
-GLOBAL bool Conf_PredefChannelsOnly;
+/** String containing all locally allowed channel prefixes for new channels */
+GLOBAL char Conf_AllowedChannelTypes[8];
 
 /** Flag indicating if IRC operators are allowed to always use MODE (true) */
 GLOBAL bool Conf_OperCanMode;
@@ -199,6 +203,9 @@ GLOBAL bool Conf_PAMIsOptional;
 /** Disable all CTCP commands except for /me ? */
 GLOBAL bool Conf_ScrubCTCP;
 
+/** Default user modes for new local clients */
+GLOBAL char Conf_DefaultUserModes[CLIENT_MODE_LEN];
+
 /*
  * try to connect to remote systems using the ipv6 protocol,
  * if they have an ipv6 address? (default yes)
@@ -208,6 +215,9 @@ GLOBAL bool Conf_ConnectIPv6;
 /** Try to connect to remote systems using the IPv4 protocol (true) */
 GLOBAL bool Conf_ConnectIPv4;
 
+/** Idle timout (seconds), after which the daemon should exit */
+GLOBAL int Conf_IdleTimeout;
+
 /** Maximum number of simultaneous connections to this server */
 GLOBAL int Conf_MaxConnections;
 

+ 1 - 1
src/ngircd/conn-encoding.c

@@ -123,7 +123,7 @@ Convert_Message(iconv_t Handle, char *Message)
 	out_left = sizeof(Encoding_Buffer) - 1;
 
 	if (iconv(Handle, &Message, &in_left, &out, &out_left) == (size_t)(-1)) {
-		/* An error occured! */
+		/* An error occurred! */
 		LogDebug("Error converting message encoding!");
 		strlcpy(Encoding_Buffer, Message, sizeof(Encoding_Buffer));
 		iconv(Handle, NULL, NULL, NULL, NULL);

+ 0 - 8
src/ngircd/conn-func.c

@@ -137,8 +137,6 @@ Conn_Flag( CONN_ID Idx )
 GLOBAL void
 Conn_SetFlag( CONN_ID Idx, int Flag )
 {
-	/* Connection markieren */
-
 	assert( Idx > NONE );
 	My_Connections[Idx].flag = Flag;
 } /* Conn_SetFlag */
@@ -147,9 +145,6 @@ Conn_SetFlag( CONN_ID Idx, int Flag )
 GLOBAL CONN_ID
 Conn_First( void )
 {
-	/* Connection-Struktur der ersten Verbindung liefern;
-	 * Ist keine Verbindung vorhanden, wird NONE geliefert. */
-
 	CONN_ID i;
 	
 	for( i = 0; i < Pool_Size; i++ )
@@ -163,9 +158,6 @@ Conn_First( void )
 GLOBAL CONN_ID
 Conn_Next( CONN_ID Idx )
 {
-	/* Naechste Verbindungs-Struktur liefern; existiert keine
-	 * weitere, so wird NONE geliefert. */
-
 	CONN_ID i = NONE;
 
 	assert( Idx > NONE );

+ 235 - 64
src/ngircd/conn-ssl.c

@@ -54,11 +54,16 @@ static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c ));
 #define DH_BITS 2048
 #define DH_BITS_MIN 1024
 
+#define MAX_HASH_SIZE	64	/* from gnutls-int.h */
+
 static gnutls_certificate_credentials_t x509_cred;
 static gnutls_dh_params_t dh_params;
+static gnutls_priority_t priorities_cache;
 static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void ));
 #endif
 
+#define SHA256_STRING_LEN	(32 * 2 + 1)
+
 static bool ConnSSL_Init_SSL PARAMS(( CONNECTION *c ));
 static int ConnectAccept PARAMS(( CONNECTION *c, bool connect ));
 static int ConnSSL_HandleError PARAMS(( CONNECTION *c, const int code, const char *fname ));
@@ -109,17 +114,26 @@ out:
 
 
 #ifdef HAVE_LIBSSL
+/**
+ * Log OpenSSL error message.
+ *
+ * @param msg The error message.
+ * @param info Additional information text or NULL.
+ */
 static void
-LogOpenSSLError( const char *msg, const char *msg2 )
+LogOpenSSLError(const char *error, const char *info)
 {
 	unsigned long err = ERR_get_error();
-	char * errmsg = err ? ERR_error_string(err, NULL) : "Unable to determine error";
+	char * errmsg = err
+		? ERR_error_string(err, NULL)
+		: "Unable to determine error";
+
+	assert(error != NULL);
 
-	if (!msg) msg = "SSL Error";
-	if (msg2)
-		Log( LOG_ERR, "%s: %s: %s", msg, msg2, errmsg);
+	if (info)
+		Log(LOG_ERR, "%s: %s (%s)", error, info, errmsg);
 	else
-		Log( LOG_ERR, "%s: %s", msg, errmsg);
+		Log(LOG_ERR, "%s: %s", error, errmsg);
 }
 
 
@@ -138,13 +152,20 @@ pem_passwd_cb(char *buf, int size, int rwflag, void *password)
 	LogDebug("pem_passwd_cb buf size %d, array size %d", size, passlen);
 	assert(passlen >= 0);
 	if (passlen <= 0) {
-		Log(LOG_ERR, "pem_passwd_cb: password required, but not set");
+		Log(LOG_ERR, "PEM password required but not set [in pem_passwd_cb()]!");
 		return 0;
 	}
 	size = passlen > size ? size : passlen;
 	memcpy(buf, (char *)(array_start(pass)), size);
 	return size;
 }
+
+
+static int
+Verify_openssl(UNUSED int preverify_ok, UNUSED X509_STORE_CTX *x509_ctx)
+{
+	return 1;
+}
 #endif
 
 
@@ -166,7 +187,7 @@ Load_DH_params(void)
 	}
 	dh_params = PEM_read_DHparams(fp, NULL, NULL, NULL);
 	if (!dh_params) {
-		Log(LOG_ERR, "%s: PEM_read_DHparams failed!",
+		Log(LOG_ERR, "%s: Failed to read SSL DH parameters!",
 		    Conf_SSLOptions.DHFile);
 		ret = false;
 	}
@@ -180,7 +201,8 @@ Load_DH_params(void)
 
 	err = gnutls_dh_params_init(&tmp_dh_params);
 	if (err < 0) {
-		Log(LOG_ERR, "gnutls_dh_params_init: %s", gnutls_strerror(err));
+		Log(LOG_ERR, "Failed to initialize SSL DH parameters: %s",
+		    gnutls_strerror(err));
 		return false;
 	}
 	if (Conf_SSLOptions.DHFile) {
@@ -193,7 +215,9 @@ Load_DH_params(void)
 			if (err == 0)
 				need_dhgenerate = false;
 			else
-				Log(LOG_ERR, "gnutls_dh_params_init: %s", gnutls_strerror(err));
+				Log(LOG_ERR,
+				    "Failed to initialize SSL DH parameters: %s",
+				    gnutls_strerror(err));
 
 			memset(dhparms.data, 0, size);
 			free(dhparms.data);
@@ -205,7 +229,8 @@ Load_DH_params(void)
 		    DH_BITS);
 		err = gnutls_dh_params_generate2(tmp_dh_params, DH_BITS);
 		if (err < 0) {
-			Log(LOG_ERR, "gnutls_dh_params_generate2: %s", gnutls_strerror(err));
+			Log(LOG_ERR, "Failed to generate SSL DH parameters: %s",
+			    gnutls_strerror(err));
 			return false;
 		}
         }
@@ -223,6 +248,10 @@ void ConnSSL_Free(CONNECTION *c)
 		SSL_shutdown(ssl);
 		SSL_free(ssl);
 		c->ssl_state.ssl = NULL;
+		if (c->ssl_state.fingerprint) {
+			free(c->ssl_state.fingerprint);
+			c->ssl_state.fingerprint = NULL;
+		}
 	}
 #endif
 #ifdef HAVE_LIBGNUTLS
@@ -257,8 +286,10 @@ ConnSSL_InitLibrary( void )
 	if (!RAND_status()) {
 		Log(LOG_ERR, "OpenSSL PRNG not seeded: /dev/urandom missing?");
 		/*
-		 * it is probably best to fail and let the user install EGD or a similar program if no kernel random device is available.
-		 * According to OpenSSL RAND_egd(3): "The automatic query of /var/run/egd-pool et al was added in OpenSSL 0.9.7";
+		 * it is probably best to fail and let the user install EGD or
+		 * a similar program if no kernel random device is available.
+		 * According to OpenSSL RAND_egd(3): "The automatic query of
+		 * /var/run/egd-pool et al was added in OpenSSL 0.9.7";
 		 * so it makes little sense to deal with PRNGD seeding ourselves.
 		 */
 		array_free(&Conf_SSLOptions.ListenPorts);
@@ -267,7 +298,7 @@ ConnSSL_InitLibrary( void )
 
 	newctx = SSL_CTX_new(SSLv23_method());
 	if (!newctx) {
-		LogOpenSSLError("SSL_CTX_new()", NULL);
+		LogOpenSSLError("Failed to create SSL context", NULL);
 		array_free(&Conf_SSLOptions.ListenPorts);
 		return false;
 	}
@@ -275,8 +306,16 @@ ConnSSL_InitLibrary( void )
 	if (!ConnSSL_LoadServerKey_openssl(newctx))
 		goto out;
 
+	if (SSL_CTX_set_cipher_list(newctx, Conf_SSLOptions.CipherList) == 0) {
+		Log(LOG_ERR, "Failed to apply OpenSSL cipher list \"%s\"!",
+		    Conf_SSLOptions.CipherList);
+		goto out;
+	}
+
 	SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2);
 	SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+	SSL_CTX_set_verify(newctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
+			   Verify_openssl);
 	SSL_CTX_free(ssl_ctx);
 	ssl_ctx = newctx;
 	Log(LOG_INFO, "%s initialized.", SSLeay_version(SSLEAY_VERSION));
@@ -289,22 +328,37 @@ out:
 #ifdef HAVE_LIBGNUTLS
 	int err;
 	static bool initialized;
-	if (initialized) /* TODO: cannot reload gnutls keys: can't simply free x509 context -- it may still be in use */
+
+	if (initialized) {
+		/* TODO: cannot reload gnutls keys: can't simply free x509
+		 * context -- it may still be in use */
 		return false;
+	}
 
 	err = gnutls_global_init();
 	if (err) {
-		Log(LOG_ERR, "gnutls_global_init(): %s", gnutls_strerror(err));
-		array_free(&Conf_SSLOptions.ListenPorts);
-		return false;
+		Log(LOG_ERR, "Failed to initialize GnuTLS: %s",
+		    gnutls_strerror(err));
+		goto out;
 	}
-	if (!ConnSSL_LoadServerKey_gnutls()) {
-		array_free(&Conf_SSLOptions.ListenPorts);
-		return false;
+
+	if (!ConnSSL_LoadServerKey_gnutls())
+		goto out;
+
+	if (gnutls_priority_init(&priorities_cache, Conf_SSLOptions.CipherList,
+				 NULL) != GNUTLS_E_SUCCESS) {
+		Log(LOG_ERR,
+		    "Failed to apply GnuTLS cipher list \"%s\"!",
+		    Conf_SSLOptions.CipherList);
+		goto out;
 	}
-	Log(LOG_INFO, "gnutls %s initialized.", gnutls_check_version(NULL));
+
+	Log(LOG_INFO, "GnuTLS %s initialized.", gnutls_check_version(NULL));
 	initialized = true;
 	return true;
+out:
+	array_free(&Conf_SSLOptions.ListenPorts);
+	return false;
 #endif
 }
 
@@ -318,7 +372,8 @@ ConnSSL_LoadServerKey_gnutls(void)
 
 	err = gnutls_certificate_allocate_credentials(&x509_cred);
 	if (err < 0) {
-		Log(LOG_ERR, "gnutls_certificate_allocate_credentials: %s", gnutls_strerror(err));
+		Log(LOG_ERR, "Failed to allocate certificate credentials: %s",
+		    gnutls_strerror(err));
 		return false;
 	}
 
@@ -330,7 +385,7 @@ ConnSSL_LoadServerKey_gnutls(void)
 
 	if (array_bytes(&Conf_SSLOptions.KeyFilePassword))
 		Log(LOG_WARNING,
-		    "Ignoring KeyFilePassword: Not supported by GNUTLS.");
+		    "Ignoring SSL \"KeyFilePassword\": Not supported by GnuTLS.");
 
 	if (!Load_DH_params())
 		return false;
@@ -338,8 +393,11 @@ ConnSSL_LoadServerKey_gnutls(void)
 	gnutls_certificate_set_dh_params(x509_cred, dh_params);
 	err = gnutls_certificate_set_x509_key_file(x509_cred, cert_file, Conf_SSLOptions.KeyFile, GNUTLS_X509_FMT_PEM);
 	if (err < 0) {
-		Log(LOG_ERR, "gnutls_certificate_set_x509_key_file (cert %s, key %s): %s",
-				cert_file, Conf_SSLOptions.KeyFile ? Conf_SSLOptions.KeyFile : "(NULL)", gnutls_strerror(err));
+		Log(LOG_ERR,
+		    "Failed to set certificate key file (cert %s, key %s): %s",
+		    cert_file,
+		    Conf_SSLOptions.KeyFile ? Conf_SSLOptions.KeyFile : "(NULL)",
+		    gnutls_strerror(err));
 		return false;
 	}
 	return true;
@@ -364,26 +422,26 @@ ConnSSL_LoadServerKey_openssl(SSL_CTX *ctx)
 
 	if (SSL_CTX_use_PrivateKey_file(ctx, Conf_SSLOptions.KeyFile, SSL_FILETYPE_PEM) != 1) {
 		array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
-		LogOpenSSLError("SSL_CTX_use_PrivateKey_file",  Conf_SSLOptions.KeyFile);
+		LogOpenSSLError("Failed to add private key", Conf_SSLOptions.KeyFile);
 		return false;
 	}
 
 	cert_key = Conf_SSLOptions.CertFile ? Conf_SSLOptions.CertFile:Conf_SSLOptions.KeyFile;
 	if (SSL_CTX_use_certificate_chain_file(ctx, cert_key) != 1) {
 		array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
-		LogOpenSSLError("SSL_CTX_use_certificate_file", cert_key);
+		LogOpenSSLError("Failed to load certificate chain", cert_key);
 		return false;
 	}
 
 	array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
 
 	if (!SSL_CTX_check_private_key(ctx)) {
-		LogOpenSSLError("Server Private Key does not match certificate", NULL);
+		LogOpenSSLError("Server private key does not match certificate", NULL);
 		return false;
 	}
 	if (Load_DH_params()) {
 		if (SSL_CTX_set_tmp_dh(ctx, dh_params) != 1)
-			LogOpenSSLError("Error setting DH Parameters", Conf_SSLOptions.DHFile);
+			LogOpenSSLError("Error setting DH parameters", Conf_SSLOptions.DHFile);
 		/* don't return false here: the non-DH modes will still work */
 		DH_free(dh_params);
 		dh_params = NULL;
@@ -397,31 +455,39 @@ static bool
 ConnSSL_Init_SSL(CONNECTION *c)
 {
 	int ret;
+
+	LogDebug("Initializing SSL ...");
 	assert(c != NULL);
+
 #ifdef HAVE_LIBSSL
 	if (!ssl_ctx) {
-		Log(LOG_ERR, "Cannot init ssl_ctx: OpenSSL initialization failed at startup");
+		Log(LOG_ERR,
+		    "Can't initialize SSL context, OpenSSL initialization failed at startup!");
 		return false;
 	}
 	assert(c->ssl_state.ssl == NULL);
+	assert(c->ssl_state.fingerprint == NULL);
 
 	c->ssl_state.ssl = SSL_new(ssl_ctx);
 	if (!c->ssl_state.ssl) {
-		LogOpenSSLError("SSL_new()", NULL);
+		LogOpenSSLError("Failed to create SSL structure", NULL);
 		return false;
 	}
+	Conn_OPTION_ADD(c, CONN_SSL);
 
 	ret = SSL_set_fd(c->ssl_state.ssl, c->sock);
 	if (ret != 1) {
-		LogOpenSSLError("SSL_set_fd()", NULL);
+		LogOpenSSLError("Failed to set SSL file descriptor", NULL);
 		ConnSSL_Free(c);
 		return false;
 	}
 #endif
 #ifdef HAVE_LIBGNUTLS
-	ret = gnutls_set_default_priority(c->ssl_state.gnutls_session);
-	if (ret < 0) {
-		Log(LOG_ERR, "gnutls_set_default_priority: %s", gnutls_strerror(ret));
+	Conn_OPTION_ADD(c, CONN_SSL);
+	ret = gnutls_priority_set(c->ssl_state.gnutls_session, priorities_cache);
+	if (ret != GNUTLS_E_SUCCESS) {
+		Log(LOG_ERR, "Failed to set GnuTLS session priorities: %s",
+		    gnutls_strerror(ret));
 		ConnSSL_Free(c);
 		return false;
 	}
@@ -431,16 +497,20 @@ ConnSSL_Init_SSL(CONNECTION *c)
 	 * There doesn't seem to be an alternate GNUTLS API we could use instead, see e.g.
 	 * http://www.mail-archive.com/help-gnutls@gnu.org/msg00286.html
 	 */
-	gnutls_transport_set_ptr(c->ssl_state.gnutls_session, (gnutls_transport_ptr_t) (long) c->sock);
-	ret = gnutls_credentials_set(c->ssl_state.gnutls_session, GNUTLS_CRD_CERTIFICATE, x509_cred);
-	if (ret < 0) {
-		Log(LOG_ERR, "gnutls_credentials_set: %s", gnutls_strerror(ret));
+	gnutls_transport_set_ptr(c->ssl_state.gnutls_session,
+				 (gnutls_transport_ptr_t) (long) c->sock);
+	gnutls_certificate_server_set_request(c->ssl_state.gnutls_session,
+					      GNUTLS_CERT_REQUEST);
+	ret = gnutls_credentials_set(c->ssl_state.gnutls_session,
+				     GNUTLS_CRD_CERTIFICATE, x509_cred);
+	if (ret != 0) {
+		Log(LOG_ERR, "Failed to set SSL credentials: %s",
+		    gnutls_strerror(ret));
 		ConnSSL_Free(c);
 		return false;
 	}
 	gnutls_dh_set_prime_bits(c->ssl_state.gnutls_session, DH_BITS_MIN);
 #endif
-	Conn_OPTION_ADD(c, CONN_SSL);
 	return true;
 }
 
@@ -454,7 +524,8 @@ ConnSSL_PrepareConnect(CONNECTION *c, UNUSED CONF_SERVER *s)
 
 	err = gnutls_init(&c->ssl_state.gnutls_session, GNUTLS_CLIENT);
 	if (err) {
-		Log(LOG_ERR, "gnutls_init: %s", gnutls_strerror(err));
+		Log(LOG_ERR, "Failed to initialize new SSL session: %s",
+		    gnutls_strerror(err));
 		return false;
         }
 #endif
@@ -470,16 +541,23 @@ ConnSSL_PrepareConnect(CONNECTION *c, UNUSED CONF_SERVER *s)
 }
 
 
-/*
-  Check an Handle Error return code after failed calls to ssl/tls functions.
-  OpenSSL:
-  SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or SSL_write() on ssl.
-  GNUTLS:
-  gnutlsssl_read(), gnutls_write() or gnutls_handshake().
-  Return: -1 on fatal error, 0 if we can try again later.
+/**
+ * Check and handle error return codes after failed calls to SSL functions.
+ *
+ * OpenSSL:
+ * SSL_connect(), SSL_accept(), SSL_do_handshake(), SSL_read(), SSL_peek(), or
+ * SSL_write() on ssl.
+ *
+ * GnuTLS:
+ * gnutlsssl_read(), gnutls_write() or gnutls_handshake().
+ *
+ * @param c The connection handle.
+ * @prarm code The return code.
+ * @param fname The name of the function in which the error occurred.
+ * @return -1 on fatal errors, 0 if we can try again later.
  */
 static int
-ConnSSL_HandleError( CONNECTION *c, const int code, const char *fname )
+ConnSSL_HandleError(CONNECTION * c, const int code, const char *fname)
 {
 #ifdef HAVE_LIBSSL
 	int ret = SSL_ERROR_SYSCALL;
@@ -487,6 +565,7 @@ ConnSSL_HandleError( CONNECTION *c, const int code, const char *fname )
 	int real_errno = errno;
 
 	ret = SSL_get_error(c->ssl_state.ssl, code);
+
 	switch (ret) {
 	case SSL_ERROR_WANT_READ:
 		io_event_del(c->sock, IO_WANTWRITE);
@@ -498,31 +577,33 @@ ConnSSL_HandleError( CONNECTION *c, const int code, const char *fname )
 	case SSL_ERROR_NONE:
 		return 0;	/* try again later */
 	case SSL_ERROR_ZERO_RETURN:
-		LogDebug("TLS/SSL connection shut down normally");
+		LogDebug("SSL connection shut down normally.");
 		break;
-	/*
-	SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT, SSL_ERROR_WANT_X509_LOOKUP
-	*/
 	case SSL_ERROR_SYSCALL:
+		/* SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT,
+		 * and SSL_ERROR_WANT_X509_LOOKUP */
 		sslerr = ERR_get_error();
 		if (sslerr) {
-			Log( LOG_ERR, "%s: %s", fname, ERR_error_string(sslerr, NULL ));
+			Log(LOG_ERR, "SSL error: %s [in %s()]!",
+			    ERR_error_string(sslerr, NULL), fname);
 		} else {
-
 			switch (code) {	/* EOF that violated protocol */
 			case 0:
-				Log(LOG_ERR, "%s: Client Disconnected", fname );
+				Log(LOG_ERR,
+				    "SSL error, client disconnected [in %s()]!",
+				    fname);
 				break;
-			case -1: /* low level socket I/O error, check errno */
-				Log(LOG_ERR, "%s: %s", fname, strerror(real_errno));
+			case -1:	/* low level socket I/O error, check errno */
+				Log(LOG_ERR, "SSL error: %s [in %s()]!",
+				    strerror(real_errno), fname);
 			}
 		}
 		break;
 	case SSL_ERROR_SSL:
-		LogOpenSSLError("TLS/SSL Protocol Error", fname);
+		LogOpenSSLError("SSL protocol error", fname);
 		break;
 	default:
-		Log( LOG_ERR, "%s: Unknown error %d!", fname, ret);
+		Log(LOG_ERR, "Unknown SSL error %d [in %s()]!", ret, fname);
 	}
 	ConnSSL_Free(c);
 	return -1;
@@ -542,7 +623,8 @@ ConnSSL_HandleError( CONNECTION *c, const int code, const char *fname )
 	default:
 		assert(code < 0);
 		if (gnutls_error_is_fatal(code)) {
-			Log(LOG_ERR, "%s: %s", fname, gnutls_strerror(code));
+			Log(LOG_ERR, "SSL error: %s [%s].",
+			    gnutls_strerror(code), fname);
 			ConnSSL_Free(c);
 			return -1;
 		}
@@ -591,11 +673,11 @@ ConnSSL_Accept( CONNECTION *c )
 #ifdef HAVE_LIBGNUTLS
 		int err = gnutls_init(&c->ssl_state.gnutls_session, GNUTLS_SERVER);
 		if (err) {
-			Log(LOG_ERR, "gnutls_init: %s", gnutls_strerror(err));
+			Log(LOG_ERR, "Failed to initialize new SSL session: %s",
+			    gnutls_strerror(err));
 			return false;
 		}
 #endif
-		LogDebug("ConnSSL_Accept: Initializing SSL data");
 		if (!ConnSSL_Init_SSL(c))
 			return -1;
 	}
@@ -614,6 +696,80 @@ ConnSSL_Connect( CONNECTION *c )
 	return ConnectAccept(c, true);
 }
 
+static int
+ConnSSL_InitCertFp( CONNECTION *c )
+{
+	const char hex[] = "0123456789abcdef";
+	int i;
+
+#ifdef HAVE_LIBSSL
+	unsigned char digest[EVP_MAX_MD_SIZE];
+	unsigned int digest_size;
+	X509 *cert;
+
+	cert = SSL_get_peer_certificate(c->ssl_state.ssl);
+	if (!cert)
+		return 0;
+
+	if (!X509_digest(cert, EVP_sha256(), digest, &digest_size)) {
+		X509_free(cert);
+		return 0;
+	}
+
+	X509_free(cert);
+#endif /* HAVE_LIBSSL */
+#ifdef HAVE_LIBGNUTLS
+	gnutls_x509_crt_t cert;
+	unsigned int cert_list_size;
+	const gnutls_datum_t *cert_list;
+	unsigned char digest[MAX_HASH_SIZE];
+	size_t digest_size;
+
+	if (gnutls_certificate_type_get(c->ssl_state.gnutls_session) !=
+					GNUTLS_CRT_X509)
+		return 0;
+
+	if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS)
+		return 0;
+
+	cert_list_size = 0;
+	cert_list = gnutls_certificate_get_peers(c->ssl_state.gnutls_session,
+						 &cert_list_size);
+	if (!cert_list) {
+		gnutls_x509_crt_deinit(cert);
+		return 0;
+	}
+	
+	if (gnutls_x509_crt_import(cert, &cert_list[0],
+				   GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
+		gnutls_x509_crt_deinit(cert);
+		return 0;
+	}
+
+	digest_size = sizeof(digest);
+	if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, digest,
+					    &digest_size)) {
+		gnutls_x509_crt_deinit(cert);
+		return 0;
+	}
+
+	gnutls_x509_crt_deinit(cert);
+#endif /* HAVE_LIBGNUTLS */
+
+	assert(c->ssl_state.fingerprint == NULL);
+
+	c->ssl_state.fingerprint = malloc(SHA256_STRING_LEN);
+	if (!c->ssl_state.fingerprint)
+		return 0;
+
+	for (i = 0; i < (int)digest_size; i++) {
+		c->ssl_state.fingerprint[i * 2] = hex[digest[i] / 16];
+		c->ssl_state.fingerprint[i * 2 + 1] = hex[digest[i] % 16];
+	}
+	c->ssl_state.fingerprint[i * 2] = '\0';
+
+	return 1;
+}
 
 /* accept/connect wrapper. if connect is true, connect to peer, otherwise wait for incoming connection */
 static int
@@ -634,6 +790,8 @@ ConnectAccept( CONNECTION *c, bool connect)
 	if (ret)
 		return ConnSSL_HandleError(c, ret, "gnutls_handshake");
 #endif /* _GNUTLS */
+	(void)ConnSSL_InitCertFp(c);
+
 	Conn_OPTION_DEL(c, (CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_CONNECT));
 	ConnSSL_LogCertInfo(c);
 
@@ -725,6 +883,19 @@ ConnSSL_GetCipherInfo(CONNECTION *c, char *buf, size_t len)
 #endif
 }
 
+char *
+ConnSSL_GetCertFp(CONNECTION *c)
+{
+	return c->ssl_state.fingerprint;
+}
+
+bool
+ConnSSL_SetCertFp(CONNECTION *c, const char *fingerprint)
+{
+	assert (c != NULL);
+	c->ssl_state.fingerprint = strndup(fingerprint, SHA256_STRING_LEN - 1);
+	return c->ssl_state.fingerprint != NULL;
+}
 #else
 
 bool

+ 3 - 0
src/ngircd/conn-ssl.h

@@ -26,6 +26,9 @@ GLOBAL ssize_t ConnSSL_Write PARAMS(( CONNECTION *c, const void *buf, size_t cou
 GLOBAL ssize_t ConnSSL_Read PARAMS(( CONNECTION *c, void *buf, size_t count));
 
 GLOBAL bool ConnSSL_GetCipherInfo PARAMS(( CONNECTION *c, char *buf, size_t len ));
+GLOBAL char *ConnSSL_GetCertFp PARAMS(( CONNECTION *c ));
+GLOBAL bool ConnSSL_SetCertFp PARAMS(( CONNECTION *c, const char *fingerprint ));
+
 #endif /* SSL_SUPPORT */
 #endif /* conn_ssl_h */
 

+ 229 - 62
src/ngircd/conn.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -82,6 +82,8 @@
 #define MAX_COMMANDS_SERVER_MIN 10
 #define MAX_COMMANDS_SERVICE 10
 
+#define SD_LISTEN_FDS_START 3
+
 
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
 static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
@@ -119,6 +121,42 @@ static void cb_Read_Resolver_Result PARAMS((int sock, UNUSED short what));
 static void cb_Connect_to_Server PARAMS((int sock, UNUSED short what));
 static void cb_clientserver PARAMS((int sock, short what));
 
+time_t idle_t = 0;
+
+
+/**
+ * Get number of sockets available from systemd(8).
+ *
+ * ngIRCd needs to implement its own sd_listen_fds(3) function and can't
+ * use the one provided by systemd itself, because the sockets will be
+ * used in a forked child process with a new PID, and this would trigger
+ * an error in the standard implementation.
+ *
+ * @return Number of sockets available, -1 if sockets have already been
+ *         initialized, or 0 when no sockets have been passed.
+ */
+static int
+my_sd_listen_fds(void)
+{
+	const char *e;
+	int count;
+
+	/* Check if LISTEN_PID exists; but we ignore the result, because
+	 * normally ngircd forks a child before checking this, and therefore
+	 * the PID set in the environment is always wrong ... */
+	e = getenv("LISTEN_PID");
+	if (!e || !*e)
+		return 0;
+
+	e = getenv("LISTEN_FDS");
+	if (!e || !*e)
+		return -1;
+	count = atoi(e);
+	unsetenv("LISTEN_FDS");
+
+	return count;
+}
+
 
 /**
  * IO callback for listening sockets: handle new connections. This callback
@@ -184,7 +222,7 @@ cb_connserver(int sock, UNUSED short what)
 	if (server < 0) {
 		Log(LOG_ERR, "Connection on socket %d to \"%s\" aborted!",
 		    sock, My_Connections[idx].host);
-		Conn_Close(idx, "Connection aborted!", NULL, false);
+		Conn_Close(idx, "Connection aborted", NULL, false);
 		return;
 	}
 
@@ -205,7 +243,7 @@ cb_connserver(int sock, UNUSED short what)
 			    My_Connections[idx].host, Conf_Server[server].port,
  			    idx, strerror(err));
 
-		Conn_Close(idx, "Can't connect!", NULL, false);
+		Conn_Close(idx, "Can't connect", NULL, false);
 
 		if (ng_ipaddr_af(&Conf_Server[server].dst_addr[0])) {
 			/* more addresses to try... */
@@ -282,7 +320,7 @@ cb_connserver_login_ssl(int sock, short unused)
 		return;
 	case -1:
 		Log(LOG_ERR, "SSL connection on socket %d failed!", sock);
-		Conn_Close(idx, "Can't connect!", NULL, false);
+		Conn_Close(idx, "Can't connect", NULL, false);
 		return;
 	}
 
@@ -329,13 +367,13 @@ cb_clientserver(int sock, short what)
 
 #ifdef SSL_SUPPORT
 /**
- * IO callback for established SSL-enabled client and server connections.
+ * IO callback for new SSL-enabled client and server connections.
  *
  * @param sock	Socket descriptor.
  * @param what	IO specification (IO_WANTREAD/IO_WANTWRITE/...).
  */
 static void
-cb_clientserver_ssl(int sock, short what)
+cb_clientserver_ssl(int sock, UNUSED short what)
 {
 	CONN_ID idx = Socket2Index(sock);
 
@@ -352,14 +390,11 @@ cb_clientserver_ssl(int sock, short what)
 	case 0:
 		return;	/* EAGAIN: callback will be invoked again by IO layer */
 	default:
-		Conn_Close(idx, "SSL accept error, closing socket", "SSL accept error", false);
+		Conn_Close(idx,
+			   "SSL accept error, closing socket", "SSL accept error",
+			   false);
 		return;
 	}
-	if (what & IO_WANTREAD)
-		Read_Request(idx);
-
-	if (what & IO_WANTWRITE)
-		Handle_Write(idx);
 
 	io_event_setcb(sock, cb_clientserver);	/* SSL handshake completed */
 }
@@ -367,14 +402,13 @@ cb_clientserver_ssl(int sock, short what)
 
 
 /**
- * Initialize connecion module.
+ * Initialize connection module.
  */
 GLOBAL void
 Conn_Init( void )
 {
 	CONN_ID i;
 
-	/* Speicher fuer Verbindungs-Pool anfordern */
 	Pool_Size = CONNECTION_POOL;
 	if ((Conf_MaxConnections > 0) &&
 		(Pool_Size > Conf_MaxConnections))
@@ -454,7 +488,7 @@ Conn_CloseAllSockets(int ExceptOf)
  * @returns		Number of listening sockets created.
  */
 static unsigned int
-ports_initlisteners(array *a, const char *listen_addr, void (*func)(int,short))
+Init_Listeners(array *a, const char *listen_addr, void (*func)(int,short))
 {
 	unsigned int created = 0;
 	size_t len;
@@ -470,8 +504,9 @@ ports_initlisteners(array *a, const char *listen_addr, void (*func)(int,short))
 			continue;
 		}
 		if (!io_event_create( fd, IO_WANTREAD, func )) {
-			Log( LOG_ERR, "io_event_create(): Could not add listening fd %d (port %u): %s!",
-						fd, (unsigned int) *port, strerror(errno));
+			Log(LOG_ERR,
+			    "io_event_create(): Can't add fd %d (port %u): %s!",
+			    fd, (unsigned int) *port, strerror(errno));
 			close(fd);
 			port++;
 			continue;
@@ -493,14 +528,76 @@ Conn_InitListeners( void )
 {
 	/* Initialize ports on which the server should accept connections */
 	unsigned int created = 0;
-	char *copy, *listen_addr;
+	char *af_str, *copy, *listen_addr;
+	int count, fd, i, addr_len;
+	ng_ipaddr_t addr;
 
 	assert(Conf_ListenAddress);
 
+	count = my_sd_listen_fds();
+	if (count < 0) {
+		Log(LOG_INFO,
+		    "Not re-initializing listening sockets of systemd(8) ...");
+		return 0;
+	}
+	if (count > 0) {
+		/* systemd(8) passed sockets to us, so don't try to initialize
+		 * listening sockets on our own but use the passed ones */
+		LogDebug("Initializing %d systemd sockets ...", count);
+		for (i = 0; i < count; i++) {
+			fd = SD_LISTEN_FDS_START + i;
+			addr_len = (int)sizeof(addr);
+			getsockname(fd, (struct sockaddr *)&addr, (socklen_t*)&addr_len);
+#ifdef WANT_IPV6
+			if (addr.sin4.sin_family != AF_INET && addr.sin4.sin_family != AF_INET6)
+#else
+			if (addr.sin4.sin_family != AF_INET)
+#endif
+			{
+				/* Socket is of unsupported type! For example, systemd passed in
+				 * an IPv6 socket but ngIRCd isn't compiled with IPv6 support. */
+				switch (addr.sin4.sin_family)
+				{
+					case AF_UNSPEC: af_str = "AF_UNSPEC"; break;
+					case AF_UNIX: af_str = "AF_UNIX"; break;
+					case AF_INET: af_str = "AF_INET"; break;
+#ifdef AF_INET6
+					case AF_INET6: af_str = "AF_INET6"; break;
+#endif
+#ifdef AF_NETLINK
+					case AF_NETLINK: af_str = "AF_NETLINK"; break;
+#endif
+					default: af_str = "unknown"; break;
+				}
+				Log(LOG_CRIT,
+				    "Socket %d is of unsupported type \"%s\" (%d), have to ignore it!",
+				    fd, af_str, addr.sin4.sin_family);
+				close(fd);
+				continue;
+			}
+
+			Init_Socket(fd);
+			if (!io_event_create(fd, IO_WANTREAD, cb_listen)) {
+				Log(LOG_ERR,
+				    "io_event_create(): Can't add fd %d: %s!",
+				    fd, strerror(errno));
+				continue;
+			}
+			Log(LOG_INFO,
+			    "Initialized socket %d from systemd(8): %s:%d.", fd,
+			    ng_ipaddr_tostr(&addr), ng_ipaddr_getport(&addr));
+			created++;
+		}
+		return created;
+	}
+
+	/* not using systemd socket activation, initialize listening sockets: */
+
 	/* can't use Conf_ListenAddress directly, see below */
 	copy = strdup(Conf_ListenAddress);
 	if (!copy) {
-		Log(LOG_CRIT, "Cannot copy %s: %s", Conf_ListenAddress, strerror(errno));
+		Log(LOG_CRIT, "Cannot copy %s: %s", Conf_ListenAddress,
+		    strerror(errno));
 		return 0;
 	}
 	listen_addr = strtok(copy, ",");
@@ -508,9 +605,11 @@ Conn_InitListeners( void )
 	while (listen_addr) {
 		ngt_TrimStr(listen_addr);
 		if (*listen_addr) {
-			created += ports_initlisteners(&Conf_ListenPorts, listen_addr, cb_listen);
+			created += Init_Listeners(&Conf_ListenPorts,
+						  listen_addr, cb_listen);
 #ifdef SSL_SUPPORT
-			created += ports_initlisteners(&Conf_SSLOptions.ListenPorts, listen_addr, cb_listen_ssl);
+			created += Init_Listeners(&Conf_SSLOptions.ListenPorts,
+						  listen_addr, cb_listen_ssl);
 #endif
 		}
 
@@ -537,7 +636,12 @@ Conn_ExitListeners( void )
 	int *fd;
 	size_t arraylen;
 
+	/* Get number of listening sockets to shut down. There can be none
+	 * if ngIRCd has been "socket activated" by systemd. */
 	arraylen = array_length(&My_Listeners, sizeof (int));
+	if (arraylen < 1)
+		return;
+
 	Log(LOG_INFO,
 	    "Shutting down all listening sockets (%d total) ...", arraylen);
 	fd = array_start(&My_Listeners);
@@ -568,7 +672,7 @@ InitSinaddrListenAddr(ng_ipaddr_t *addr, const char *listen_addrstr, UINT16 Port
 	ret = ng_ipaddr_init(addr, listen_addrstr, Port);
 	if (!ret) {
 		assert(listen_addrstr);
-		Log(LOG_CRIT, "Can't bind to [%s]:%u: can't convert ip address \"%s\"",
+		Log(LOG_CRIT, "Can't bind to [%s]:%u: can't convert ip address \"%s\"!",
 						listen_addrstr, Port, listen_addrstr);
 	}
 	return ret;
@@ -620,8 +724,9 @@ NewListener(const char *listen_addr, UINT16 Port)
 
 	af = ng_ipaddr_af(&addr);
 	sock = socket(af, SOCK_STREAM, 0);
-	if( sock < 0 ) {
-		Log(LOG_CRIT, "Can't create socket (af %d) : %s!", af, strerror(errno));
+	if (sock < 0) {
+		Log(LOG_CRIT, "Can't create socket (af %d) : %s!", af,
+		    strerror(errno));
 		return -1;
 	}
 
@@ -631,22 +736,23 @@ NewListener(const char *listen_addr, UINT16 Port)
 		return -1;
 
 	if (bind(sock, (struct sockaddr *)&addr, ng_ipaddr_salen(&addr)) != 0) {
-		Log(LOG_CRIT, "Can't bind socket to address %s:%d - %s",
-			ng_ipaddr_tostr(&addr), Port, strerror(errno));
+		Log(LOG_CRIT, "Can't bind socket to address %s:%d - %s!",
+		    ng_ipaddr_tostr(&addr), Port, strerror(errno));
 		close(sock);
 		return -1;
 	}
 
-	if( listen( sock, 10 ) != 0 ) {
-		Log( LOG_CRIT, "Can't listen on socket: %s!", strerror( errno ));
-		close( sock );
+	if (listen(sock, 10) != 0) {
+		Log(LOG_CRIT, "Can't listen on socket: %s!", strerror(errno));
+		close(sock);
 		return -1;
 	}
 
 	/* keep fd in list so we can close it when ngircd restarts/shuts down */
-	if (!array_catb( &My_Listeners,(char*) &sock, sizeof(int) )) {
-		Log( LOG_CRIT, "Can't add socket to My_Listeners array: %s!", strerror( errno ));
-		close( sock );
+	if (!array_catb(&My_Listeners, (char *)&sock, sizeof(int))) {
+		Log(LOG_CRIT, "Can't add socket to My_Listeners array: %s!",
+		    strerror(errno));
+		close(sock);
 		return -1;
 	}
 
@@ -746,8 +852,7 @@ Conn_Handler(void)
 		/* Look for non-empty read buffers ... */
 		for (i = 0; i < Pool_Size; i++) {
 			if ((My_Connections[i].sock > NONE)
-			    && (array_bytes(&My_Connections[i].rbuf) > 0)
-			    && (My_Connections[i].delaytime <= t)) {
+			    && (array_bytes(&My_Connections[i].rbuf) > 0)) {
 				/* ... and try to handle the received data */
 				bytes_processed = Handle_Buffer(i);
 				/* if we processed data, and there might be
@@ -817,7 +922,7 @@ Conn_Handler(void)
 		 * which is the granularity with witch we handle "penalty
 		 * times" for example.
 		 * Note: tv_sec/usec are undefined(!) after io_dispatch()
-		 * returns, so we have to set it beforce each call to it! */
+		 * returns, so we have to set it before each call to it! */
 		tv.tv_usec = 0;
 		tv.tv_sec = 1;
 
@@ -830,6 +935,15 @@ Conn_Handler(void)
 			    PACKAGE_NAME);
 			exit(1);
 		}
+
+		/* Should ngIRCd timeout when idle? */
+		if (Conf_IdleTimeout > 0 && NumConnectionsAccepted > 0
+		    && idle_t > 0 && time(NULL) - idle_t >= Conf_IdleTimeout) {
+			LogDebug("Server idle timeout reached: %d second%s. Initiating shutdown ...",
+				 Conf_IdleTimeout,
+				 Conf_IdleTimeout == 1 ? "" : "s");
+			NGIRCd_SignalQuit = true;
+		}
 	}
 
 	if (NGIRCd_SignalQuit)
@@ -893,7 +1007,7 @@ va_dcl
 		 *
 		 * So we have a big problem here: we should send more bytes
 		 * to the network than we are allowed to and we don't know
-		 * the originator (any more). The "old" behaviour of blaming
+		 * the originator (any more). The "old" behavior of blaming
 		 * the receiver ("next hop") is a bad idea (it could be just
 		 * an other server only routing the message!), so the only
 		 * option left is to shorten the string and to hope that the
@@ -1067,8 +1181,8 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
 	/* Is this link already shutting down? */
 	if( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_ISCLOSING )) {
 		/* Conn_Close() has been called recursively for this link;
-		 * probabe reason: Handle_Write() failed  -- see below. */
-		LogDebug("Recursive request to close connection: %d", Idx );
+		 * probable reason: Handle_Write() failed -- see below. */
+		LogDebug("Recursive request to close connection %d!", Idx );
 		return;
 	}
 
@@ -1078,7 +1192,7 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
 	Conn_OPTION_ADD( &My_Connections[Idx], CONN_ISCLOSING );
 
 	port = ng_ipaddr_getport(&My_Connections[Idx].addr);
-	Log(LOG_INFO, "Shutting down connection %d (%s) with %s:%d ...", Idx,
+	Log(LOG_INFO, "Shutting down connection %d (%s) with \"%s:%d\" ...", Idx,
 	    LogMsg ? LogMsg : FwdMsg, My_Connections[Idx].host, port);
 
 	/* Search client, if any */
@@ -1114,7 +1228,7 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
 	c = Conn_GetClient( Idx );
 #ifdef SSL_SUPPORT
 	if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_SSL )) {
-		Log(LOG_INFO, "SSL connection %d shutting down ...", Idx);
+		LogDebug("SSL connection %d shutting down ...", Idx);
 		ConnSSL_Free(&My_Connections[Idx]);
 	}
 #endif
@@ -1152,7 +1266,7 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
 		in_p = (int)(( in_k * 100 ) / in_z_k );
 		out_p = (int)(( out_k * 100 ) / out_z_k );
 		Log(LOG_INFO,
-		    "Connection %d with %s:%d closed (in: %.1fk/%.1fk/%d%%, out: %.1fk/%.1fk/%d%%).",
+		    "Connection %d with \"%s:%d\" closed (in: %.1fk/%.1fk/%d%%, out: %.1fk/%.1fk/%d%%).",
 		    Idx, My_Connections[Idx].host, port,
 		    in_k, in_z_k, in_p, out_k, out_z_k, out_p);
 	}
@@ -1160,7 +1274,7 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
 #endif
 	{
 		Log(LOG_INFO,
-		    "Connection %d with %s:%d closed (in: %.1fk, out: %.1fk).",
+		    "Connection %d with \"%s:%d\" closed (in: %.1fk, out: %.1fk).",
 		    Idx, My_Connections[Idx].host, port,
 		    in_k, out_k);
 	}
@@ -1191,6 +1305,8 @@ Conn_Close( CONN_ID Idx, const char *LogMsg, const char *FwdMsg, bool InformClie
 		NumConnections--;
 	LogDebug("Shutdown of connection %d completed, %ld connection%s left.",
 		 Idx, NumConnections, NumConnections != 1 ? "s" : "");
+
+	idle_t = NumConnections > 0 ? 0 : time(NULL);
 } /* Conn_Close */
 
 
@@ -1334,9 +1450,14 @@ Handle_Write( CONN_ID Idx )
 		if (errno == EAGAIN || errno == EINTR)
 			return true;
 
-		Log(LOG_ERR, "Write error on connection %d (socket %d): %s!",
-		    Idx, My_Connections[Idx].sock, strerror(errno));
-		Conn_Close(Idx, "Write error!", NULL, false);
+		if (!Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ISCLOSING))
+			Log(LOG_ERR,
+			    "Write error on connection %d (socket %d): %s!",
+			    Idx, My_Connections[Idx].sock, strerror(errno));
+		else
+			LogDebug("Recursive write error on connection %d (socket %d): %s!",
+				 Idx, My_Connections[Idx].sock, strerror(errno));
+		Conn_Close(Idx, "Write error", NULL, false);
 		return false;
 	}
 
@@ -1500,7 +1621,7 @@ New_Connection(int Sock, UNUSED bool IsSSL)
 
 	Client_SetHostname(c, My_Connections[new_sock].host);
 
-	Log(LOG_INFO, "Accepted connection %d from %s:%d on socket %d.",
+	Log(LOG_INFO, "Accepted connection %d from \"%s:%d\" on socket %d.",
 	    new_sock, My_Connections[new_sock].host,
 	    ng_ipaddr_getport(&new_addr), Sock);
 	Account_Connection();
@@ -1566,6 +1687,7 @@ static void
 Account_Connection(void)
 {
 	NumConnections++;
+	idle_t = 0;
 	if (NumConnections > NumConnectionsMax)
 		NumConnectionsMax = NumConnections;
 	LogDebug("Total number of connections now %lu (max %lu).",
@@ -1595,7 +1717,7 @@ Socket2Index( int Sock )
 
 
 /**
- * Read data from the network to the read buffer. If an error occures,
+ * Read data from the network to the read buffer. If an error occurs,
  * the socket of this connection will be shut down.
  *
  * @param Idx	Connection index.
@@ -1633,13 +1755,10 @@ Read_Request( CONN_ID Idx )
 #endif
 	len = read(My_Connections[Idx].sock, readbuf, sizeof(readbuf));
 	if (len == 0) {
-		Log(LOG_INFO, "%s:%u (%s) is closing the connection ...",
-				My_Connections[Idx].host,
-				(unsigned int) ng_ipaddr_getport(&My_Connections[Idx].addr),
-				ng_ipaddr_tostr(&My_Connections[Idx].addr));
-		Conn_Close(Idx,
-			   "Socket closed!", "Client closed connection",
-			   false);
+		LogDebug("Client \"%s:%u\" is closing connection %d ...",
+			 My_Connections[Idx].host,
+			 ng_ipaddr_tostr(&My_Connections[Idx].addr), Idx);
+		Conn_Close(Idx, NULL, "Client closed connection", false);
 		return;
 	}
 
@@ -1647,7 +1766,7 @@ Read_Request( CONN_ID Idx )
 		if( errno == EAGAIN ) return;
 		Log(LOG_ERR, "Read error on connection %d (socket %d): %s!",
 		    Idx, My_Connections[Idx].sock, strerror(errno));
-		Conn_Close(Idx, "Read error!", "Client closed connection",
+		Conn_Close(Idx, "Read error", "Client closed connection",
 			   false);
 		return;
 	}
@@ -1747,7 +1866,7 @@ Handle_Buffer(CONN_ID Idx)
 		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 synchronization. */
 		if (Conn_LastPing(Idx) == 0)
 			maxcmd *= 5;
 		break;
@@ -2012,6 +2131,7 @@ New_Server( int Server , ng_ipaddr_t *dest)
 
 	if (!ng_ipaddr_tostr_r(dest, ip_str)) {
 		Log(LOG_WARNING, "New_Server: Could not convert IP to string");
+		Conf_Server[Server].conn_id = NONE;
 		return;
 	}
 
@@ -2026,11 +2146,14 @@ New_Server( int Server , ng_ipaddr_t *dest)
 	if (new_sock < 0) {
 		Log(LOG_CRIT, "Can't create socket (af %d): %s!",
 		    af_dest, strerror(errno));
+		Conf_Server[Server].conn_id = NONE;
 		return;
 	}
 
-	if (!Init_Socket(new_sock))
+	if (!Init_Socket(new_sock)) {
+		Conf_Server[Server].conn_id = NONE;
 		return;
+	}
 
 	/* is a bind address configured? */
 	res = ng_ipaddr_af(&Conf_Server[Server].bind_addr);
@@ -2046,6 +2169,7 @@ New_Server( int Server , ng_ipaddr_t *dest)
 	if(( res != 0 ) && ( errno != EINPROGRESS )) {
 		Log( LOG_CRIT, "Can't connect socket: %s!", strerror( errno ));
 		close( new_sock );
+		Conf_Server[Server].conn_id = NONE;
 		return;
 	}
 
@@ -2054,12 +2178,14 @@ New_Server( int Server , ng_ipaddr_t *dest)
 		    "Cannot allocate memory for server connection (socket %d)",
 		    new_sock);
 		close( new_sock );
+		Conf_Server[Server].conn_id = NONE;
 		return;
 	}
 
 	if (!io_event_create( new_sock, IO_WANTWRITE, cb_connserver)) {
 		Log(LOG_ALERT, "io_event_create(): could not add fd %d", strerror(errno));
 		close(new_sock);
+		Conf_Server[Server].conn_id = NONE;
 		return;
 	}
 
@@ -2074,6 +2200,7 @@ New_Server( int Server , ng_ipaddr_t *dest)
 	if (!c) {
 		Log( LOG_ALERT, "Can't establish connection: can't create client structure!" );
 		io_close(new_sock);
+		Conf_Server[Server].conn_id = NONE;
 		return;
 	}
 
@@ -2241,7 +2368,7 @@ cb_Connect_to_Server(int fd, UNUSED short events)
 
 /**
  * Read results of a resolver sub-process from the pipe and update the
- * apropriate connection/client structure(s): hostname and/or IDENT user name.
+ * appropriate connection/client structure(s): hostname and/or IDENT user name.
  *
  * @param r_fd		File descriptor of the pipe to the sub-process.
  * @param events	(ignored IO specification)
@@ -2335,9 +2462,9 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 					*ptr ? "" : ": ",
 					*ptr ? "" : identptr);
 			}
-		} else {
+		} else if(Conf_Ident) {
 			Log(LOG_INFO, "IDENT lookup for connection %d: no result.", i);
-			if (Conf_NoticeAuth && Conf_Ident)
+			if (Conf_NoticeAuth)
 				(void)Conn_WriteStr(i,
 					"NOTICE AUTH :*** No ident response");
 		}
@@ -2354,7 +2481,8 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 		Class_HandleServerBans(c);
 	}
 #ifdef DEBUG
-		else Log( LOG_DEBUG, "Resolver: discarding result for already registered connection %d.", i );
+	else
+		LogDebug("Resolver: discarding result for already registered connection %d.", i);
 #endif
 } /* cb_Read_Resolver_Result */
 
@@ -2473,7 +2601,7 @@ Conn_SetAuthPing(CONN_ID Idx, long ID)
 #ifdef SSL_SUPPORT
 
 /**
- * Get information about used SSL chiper.
+ * Get information about used SSL cipher.
  *
  * @param Idx	Connection index number.
  * @param buf	Buffer for returned information text.
@@ -2505,6 +2633,45 @@ Conn_UsesSSL(CONN_ID Idx)
 	return Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL);
 }
 
+
+GLOBAL char *
+Conn_GetCertFp(CONN_ID Idx)
+{
+	if (Idx < 0)
+		return NULL;
+	assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION)));
+	return ConnSSL_GetCertFp(&My_Connections[Idx]);
+}
+
+
+GLOBAL bool
+Conn_SetCertFp(CONN_ID Idx, const char *fingerprint)
+{
+	if (Idx < 0)
+		return false;
+	assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION)));
+	return ConnSSL_SetCertFp(&My_Connections[Idx], fingerprint);
+}
+#else
+GLOBAL bool
+Conn_UsesSSL(UNUSED CONN_ID Idx)
+{
+	return false;
+}
+
+
+GLOBAL char *
+Conn_GetCertFp(UNUSED CONN_ID Idx)
+{
+	return NULL;
+}
+
+
+GLOBAL bool
+Conn_SetCertFp(UNUSED CONN_ID Idx, UNUSED const char *fingerprint)
+{
+	return true;
+}
 #endif
 
 

+ 4 - 5
src/ngircd/conn.h

@@ -139,13 +139,12 @@ GLOBAL CONN_ID Conn_GetFromProc PARAMS((int fd));
 GLOBAL CLIENT* Conn_GetClient PARAMS((CONN_ID i));
 GLOBAL PROC_STAT* Conn_GetProcStat PARAMS((CONN_ID i));
 
+GLOBAL char *Conn_GetCertFp PARAMS((CONN_ID Idx));
+GLOBAL bool Conn_SetCertFp PARAMS((CONN_ID Idx, const char *fingerprint));
+GLOBAL bool Conn_UsesSSL PARAMS((CONN_ID Idx));
+
 #ifdef SSL_SUPPORT
 GLOBAL bool Conn_GetCipherInfo PARAMS((CONN_ID Idx, char *buf, size_t len));
-GLOBAL bool Conn_UsesSSL PARAMS((CONN_ID Idx));
-#else
-static inline bool
-Conn_UsesSSL(UNUSED CONN_ID Idx)
-{ return false; }
 #endif
 
 GLOBAL const char *Conn_GetIPAInfo PARAMS((CONN_ID Idx));

+ 15 - 4
src/ngircd/defines.h

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -36,7 +36,7 @@
 /* Generic buffer sizes */
 
 /** Max. length of a line in the configuration file. */
-#define LINE_LEN 256
+#define LINE_LEN 1024
 
 /** Max. length of a log message. */
 #define MAX_LOG_MSG_LEN 256
@@ -50,7 +50,6 @@
 /** Max. length of random salt */
 #define RANDOM_SALT_LEN 32
 
-
 /* Size of structures */
 
 /** Max. count of configurable servers. */
@@ -74,9 +73,15 @@
 /** Configuration file name. */
 #define CONFIG_FILE "/ngircd.conf"
 
+/** Directory containing optional configuration snippets. */
+#define CONFIG_DIR "/ngircd.conf.d"
+
 /** Name of the MOTD file. */
 #define MOTD_FILE "/ngircd.motd"
 
+/** Name of the help file. */
+#define HELP_FILE "/Commands.txt"
+
 /** Default chroot() directory. */
 #define CHROOT_DIR ""
 
@@ -96,7 +101,7 @@
 #define CLIENT_NICK_LEN 32
 
 /** Max. password length (including NULL). */
-#define CLIENT_PASS_LEN 21
+#define CLIENT_PASS_LEN 65
 
 /** Max. length of user name ("login"; incl. NULL), RFC 2812, section 1.2.1. */
 #ifndef STRICT_RFC
@@ -111,6 +116,9 @@
 /** Max. host name length (including NULL). */
 #define CLIENT_HOST_LEN 64
 
+/** Max. mask lenght (including NULL). */
+#define MASK_LEN (2 * CLIENT_HOST_LEN)
+
 /** Max. length of all client modes (including NULL). */
 #define CLIENT_MODE_LEN 21
 
@@ -170,6 +178,9 @@
 /** Supported channel modes. */
 #define CHANMODES "abehiIklmMnoOPqQrRstvVz"
 
+/** Supported channel types. */
+#define CHANTYPES "#&+"
+
 /** Away message for users connected to linked servers. */
 #define DEFAULT_AWAY_MSG "Away"
 

+ 1 - 1
src/ngircd/hash.c

@@ -37,7 +37,7 @@ static UINT32 jenkins_hash PARAMS((UINT8 *k, UINT32 length, UINT32 initval));
 GLOBAL UINT32
 Hash( const char *String )
 {
-	char buffer[LINE_LEN];
+	char buffer[COMMAND_LEN];
 
 	strlcpy(buffer, String, sizeof(buffer));
 	return jenkins_hash((UINT8 *)ngt_LowerStr(buffer),

+ 1 - 1
src/ngircd/io.c

@@ -631,7 +631,7 @@ io_library_init_kqueue(unsigned int eventsize)
 	io_masterfd = kqueue();
 
 	Log(LOG_INFO,
-	    "IO subsystem: kqueue (initial maxfd %u, masterfd %d)",
+	    "IO subsystem: kqueue (initial maxfd %u, masterfd %d).",
 	    eventsize, io_masterfd);
 	if (io_masterfd >= 0)
 		library_initialized = true;

+ 113 - 125
src/ngircd/irc-cap.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -24,6 +24,7 @@
 #include "conn.h"
 #include "channel.h"
 #include "client-cap.h"
+#include "irc-macros.h"
 #include "irc-write.h"
 #include "log.h"
 #include "login.h"
@@ -33,70 +34,91 @@
 #include "exp.h"
 #include "irc-cap.h"
 
-bool Handle_CAP_LS PARAMS((CLIENT *Client, char *Arg));
-bool Handle_CAP_LIST PARAMS((CLIENT *Client, char *Arg));
-bool Handle_CAP_REQ PARAMS((CLIENT *Client, char *Arg));
-bool Handle_CAP_ACK PARAMS((CLIENT *Client, char *Arg));
-bool Handle_CAP_CLEAR PARAMS((CLIENT *Client));
-bool Handle_CAP_END PARAMS((CLIENT *Client));
+/* Local functions */
 
-void Set_CAP_Negotiation PARAMS((CLIENT *Client));
+/**
+ * Set CAP negotiation status and mark client as "supports capabilities".
+ *
+ * @param Client The client to handle.
+ */
+static void
+Set_CAP_Negotiation(CLIENT *Client)
+{
+	assert(Client != NULL);
 
-int Parse_CAP PARAMS((int Capabilities, char *Args));
-char *Get_CAP_String PARAMS((int Capabilities));
+	if (Client_Type(Client) != CLIENT_USER)
+		Client_CapAdd(Client, CLIENT_CAP_PENDING);
+	Client_CapAdd(Client, CLIENT_CAP_SUPPORTED);
+}
 
 /**
- * Handler for the IRCv3 "CAP" command.
+ * Parse capability string and return numeric flag value.
  *
- * @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 Args The string containing space-separated capability names.
+ * @return Changed capability flags or 0 on error.
  */
-GLOBAL bool
-IRC_CAP(CLIENT *Client, REQUEST *Req)
+static int
+Parse_CAP(int Capabilities, char *Args)
 {
-	assert(Client != NULL);
-	assert(Req != NULL);
+	static char tmp[COMMAND_LEN];
+	char *ptr;
 
-	/* Bad number of prameters? */
-	if (Req->argc < 1 || Req->argc > 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
+	assert(Args != NULL);
 
-	LogDebug("Got \"%s %s\" command from \"%s\" ...",
-		 Req->command, Req->argv[0], Client_ID(Client));
+	strlcpy(tmp, Args, sizeof(tmp));
 
-	if (Req->argc == 1) {
-		if (strcasecmp(Req->argv[0], "CLEAR") == 0)
-			return Handle_CAP_CLEAR(Client);
-		if (strcasecmp(Req->argv[0], "END") == 0)
-			return Handle_CAP_END(Client);
-	}
-	if (Req->argc >= 1 && Req->argc <= 2) {
-		if (strcasecmp(Req->argv[0], "LS") == 0)
-			return Handle_CAP_LS(Client, Req->argv[1]);
-		if (strcasecmp(Req->argv[0], "LIST") == 0)
-			return Handle_CAP_LIST(Client, Req->argv[1]);
-	}
-	if (Req->argc == 2) {
-		if (strcasecmp(Req->argv[0], "REQ") == 0)
-			return Handle_CAP_REQ(Client, Req->argv[1]);
-		if (strcasecmp(Req->argv[0], "ACK") == 0)
-			return Handle_CAP_ACK(Client, Req->argv[1]);
+	ptr = strtok(tmp, " ");
+	while (ptr) {
+		if (*ptr == '-') {
+			/* drop capabilities */
+			ptr++;
+			if (strcmp(ptr, "multi-prefix") == 0)
+				Capabilities &= ~CLIENT_CAP_MULTI_PREFIX;
+			else
+				return -1;
+		} else {
+			/* request capabilities */
+			if (strcmp(ptr, "multi-prefix") == 0)
+				Capabilities |= CLIENT_CAP_MULTI_PREFIX;
+			else
+				return -1;
+		}
+		ptr = strtok(NULL, " ");
 	}
 
-	return IRC_WriteStrClient(Client, ERR_INVALIDCAP_MSG,
-				  Client_ID(Client), Req->argv[0]);
+	return Capabilities;
 }
 
 /**
- * Handler for the "CAP LS" command.
+ * Return textual representation of capability flags.
+ *
+ * Please note: this function returns a pointer to a global buffer and
+ * therefore isn't thread safe!
+ *
+ * @param Capabilities Capability flags (bitmask).
+ * @return Pointer to textual representation.
+ */
+static char *
+Get_CAP_String(int Capabilities)
+{
+	static char txt[COMMAND_LEN];
+
+	txt[0] = '\0';
+
+	if (Capabilities & CLIENT_CAP_MULTI_PREFIX)
+		strlcat(txt, "multi-prefix ", sizeof(txt));
+
+	return txt;
+}
+
+/**
+ * Handler for the IRCv3 sub-command "CAP LS".
  *
  * @param Client The client from which this command has been received.
  * @param Arg Command argument or NULL.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_LS(CLIENT *Client, UNUSED char *Arg)
 {
 	assert(Client != NULL);
@@ -109,13 +131,13 @@ Handle_CAP_LS(CLIENT *Client, UNUSED char *Arg)
 }
 
 /**
- * Handler for the "CAP LIST" command.
+ * Handler for the IRCv3 sub-command "CAP LIST".
  *
  * @param Client The client from which this command has been received.
  * @param Arg Command argument or NULL.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_LIST(CLIENT *Client, UNUSED char *Arg)
 {
 	assert(Client != NULL);
@@ -125,13 +147,13 @@ Handle_CAP_LIST(CLIENT *Client, UNUSED char *Arg)
 }
 
 /**
- * Handler for the "CAP REQ" command.
+ * Handler for the IRCv3 sub-command "CAP REQ".
  *
  * @param Client The client from which this command has been received.
  * @param Arg Command argument.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_REQ(CLIENT *Client, char *Arg)
 {
 	int new_cap;
@@ -153,13 +175,13 @@ Handle_CAP_REQ(CLIENT *Client, char *Arg)
 }
 
 /**
- * Handler for the "CAP ACK" command.
+ * Handler for the IRCv3 sub-command "CAP ACK".
  *
  * @param Client The client from which this command has been received.
  * @param Arg Command argument.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_ACK(UNUSED CLIENT *Client, UNUSED char *Arg)
 {
 	assert(Client != NULL);
@@ -169,12 +191,12 @@ Handle_CAP_ACK(UNUSED CLIENT *Client, UNUSED char *Arg)
 }
 
 /**
- * Handler for the "CAP CLEAR" command.
+ * Handler for the IRCv3 sub-command "CAP CLEAR".
  *
  * @param Client The client from which this command has been received.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_CLEAR(CLIENT *Client)
 {
 	int cap_old;
@@ -190,12 +212,12 @@ Handle_CAP_CLEAR(CLIENT *Client)
 }
 
 /**
- * Handler for the "CAP END" command.
+ * Handler for the IRCv3 sub-command "CAP END".
  *
  * @param Client The client from which this command has been received.
- * @returns CONNECTED or DISCONNECTED.
+ * @return CONNECTED or DISCONNECTED.
  */
-bool
+static bool
 Handle_CAP_END(CLIENT *Client)
 {
 	assert(Client != NULL);
@@ -213,79 +235,45 @@ Handle_CAP_END(CLIENT *Client)
 	return CONNECTED;
 }
 
-/**
- * Set CAP negotiation status and mark client as "supports capabilities".
- *
- * @param Client The client to handle.
- */
-void
-Set_CAP_Negotiation(CLIENT *Client)
-{
-	assert(Client != NULL);
-
-	if (Client_Type(Client) != CLIENT_USER)
-		Client_CapAdd(Client, CLIENT_CAP_PENDING);
-	Client_CapAdd(Client, CLIENT_CAP_SUPPORTED);
-}
+/* Global functions */
 
 /**
- * Parse capability string and return numeric flag value.
+ * Handler for the IRCv3 command "CAP".
  *
- * @param Args The string containing space-separated capability names.
- * @return Changed capability flags or 0 on error.
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
  */
-int
-Parse_CAP(int Capabilities, char *Args)
+GLOBAL bool
+IRC_CAP(CLIENT *Client, REQUEST *Req)
 {
-	static char tmp[COMMAND_LEN];
-	char *ptr;
-
-	assert(Args != NULL);
+	assert(Client != NULL);
+	assert(Req != NULL);
 
-	strlcpy(tmp, Args, sizeof(tmp));
+	LogDebug("Got \"%s %s\" command from \"%s\" ...",
+		 Req->command, Req->argv[0], Client_ID(Client));
 
-	ptr = strtok(tmp, " ");
-	while (ptr) {
-		if (*ptr == '-') {
-			/* drop capabilities */
-			ptr++;
-			if (strcmp(ptr, "multi-prefix") == 0)
-				Capabilities &= ~CLIENT_CAP_MULTI_PREFIX;
-			else
-				return -1;
-		} else {
-			/* request capabilities */
-			if (strcmp(ptr, "multi-prefix") == 0)
-				Capabilities |= CLIENT_CAP_MULTI_PREFIX;
-			else
-				return -1;
-		}
-		ptr = strtok(NULL, " ");
+	if (Req->argc == 1) {
+		if (strcasecmp(Req->argv[0], "CLEAR") == 0)
+			return Handle_CAP_CLEAR(Client);
+		if (strcasecmp(Req->argv[0], "END") == 0)
+			return Handle_CAP_END(Client);
+	}
+	if (Req->argc >= 1 && Req->argc <= 2) {
+		if (strcasecmp(Req->argv[0], "LS") == 0)
+			return Handle_CAP_LS(Client, Req->argv[1]);
+		if (strcasecmp(Req->argv[0], "LIST") == 0)
+			return Handle_CAP_LIST(Client, Req->argv[1]);
+	}
+	if (Req->argc == 2) {
+		if (strcasecmp(Req->argv[0], "REQ") == 0)
+			return Handle_CAP_REQ(Client, Req->argv[1]);
+		if (strcasecmp(Req->argv[0], "ACK") == 0)
+			return Handle_CAP_ACK(Client, Req->argv[1]);
 	}
 
-	return Capabilities;
-}
-
-/**
- * Return textual representation of capability flags.
- *
- * Please note: this function returns a pointer to a global buffer and
- * therefore isn't thread safe!
- *
- * @param Capabilities Capability flags (bitmask).
- * @return Pointer to textual representation.
- */
-char *
-Get_CAP_String(int Capabilities)
-{
-	static char txt[COMMAND_LEN];
-
-	txt[0] = '\0';
-
-	if (Capabilities & CLIENT_CAP_MULTI_PREFIX)
-		strlcat(txt, "multi-prefix ", sizeof(txt));
-
-	return txt;
+	return IRC_WriteErrClient(Client, ERR_INVALIDCAP_MSG,
+				  Client_ID(Client), Req->argv[0]);
 }
 
 /* -eof- */

+ 136 - 189
src/ngircd/irc-channel.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -33,13 +33,13 @@
 #include "parse.h"
 #include "irc.h"
 #include "irc-info.h"
+#include "irc-macros.h"
 #include "irc-write.h"
 #include "conf.h"
 
 #include "exp.h"
 #include "irc-channel.h"
 
-
 /**
  * Part from all channels.
  *
@@ -67,7 +67,6 @@ part_from_all_channels(CLIENT* client, CLIENT *target)
 	return CONNECTED;
 } /* part_from_all_channels */
 
-
 /**
  * Check weather a local client is allowed to join an already existing
  * channel or not.
@@ -83,10 +82,9 @@ join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
 	     const char *key)
 {
 	bool is_invited, is_banned, is_exception;
-	const char *channel_modes;
 
 	/* Allow IRC operators to overwrite channel limits */
-	if (strchr(Client_Modes(Client), 'o'))
+	if (Client_HasMode(Client, 'o'))
 		return true;
 
 	is_banned = Lists_Check(Channel_GetListBans(chan), Client);
@@ -95,15 +93,14 @@ join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
 
 	if (is_banned && !is_invited && !is_exception) {
 		/* Client is banned from channel (and not on invite list) */
-		IRC_WriteStrClient(Client, ERR_BANNEDFROMCHAN_MSG,
+		IRC_WriteErrClient(Client, ERR_BANNEDFROMCHAN_MSG,
 				   Client_ID(Client), channame);
 		return false;
 	}
 
-	channel_modes = Channel_Modes(chan);
-	if (strchr(channel_modes, 'i') && !is_invited) {
+	if (Channel_HasMode(chan, 'i') && !is_invited) {
 		/* Channel is "invite-only" and client is not on invite list */
-		IRC_WriteStrClient(Client, ERR_INVITEONLYCHAN_MSG,
+		IRC_WriteErrClient(Client, ERR_INVITEONLYCHAN_MSG,
 				   Client_ID(Client), channame);
 		return false;
 	}
@@ -111,37 +108,37 @@ join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
 	if (!Channel_CheckKey(chan, Client, key ? key : "")) {
 		/* Channel is protected by a channel key and the client
 		 * didn't specify the correct one */
-		IRC_WriteStrClient(Client, ERR_BADCHANNELKEY_MSG,
+		IRC_WriteErrClient(Client, ERR_BADCHANNELKEY_MSG,
 				   Client_ID(Client), channame);
 		return false;
 	}
 
-	if (strchr(channel_modes, 'l') &&
+	if (Channel_HasMode(chan, 'l') &&
 	    (Channel_MaxUsers(chan) <= Channel_MemberCount(chan))) {
 		/* There are more clints joined to this channel than allowed */
-		IRC_WriteStrClient(Client, ERR_CHANNELISFULL_MSG,
+		IRC_WriteErrClient(Client, ERR_CHANNELISFULL_MSG,
 				   Client_ID(Client), channame);
 		return false;
 	}
 
-	if (strchr(channel_modes, 'z') && !Conn_UsesSSL(Client_Conn(Client))) {
+	if (Channel_HasMode(chan, 'z') && !Conn_UsesSSL(Client_Conn(Client))) {
 		/* Only "secure" clients are allowed, but clients doesn't
 		 * use SSL encryption */
-		IRC_WriteStrClient(Client, ERR_SECURECHANNEL_MSG,
+		IRC_WriteErrClient(Client, ERR_SECURECHANNEL_MSG,
 				   Client_ID(Client), channame);
 		return false;
 	}
 
-	if (strchr(channel_modes, 'O') && !Client_OperByMe(Client)) {
+	if (Channel_HasMode(chan, 'O') && !Client_HasMode(Client, 'o')) {
 		/* Only IRC operators are allowed! */
-		IRC_WriteStrClient(Client, ERR_OPONLYCHANNEL_MSG,
+		IRC_WriteErrClient(Client, ERR_OPONLYCHANNEL_MSG,
 				   Client_ID(Client), channame);
 		return false;
 	}
 
-	if (strchr(channel_modes, 'R') && !strchr(Client_Modes(Client), 'R')) {
+	if (Channel_HasMode(chan, 'R') && !Client_HasMode(Client, 'R')) {
 		/* Only registered users are allowed! */
-		IRC_WriteStrClient(Client, ERR_REGONLYCHANNEL_MSG,
+		IRC_WriteErrClient(Client, ERR_REGONLYCHANNEL_MSG,
 				   Client_ID(Client), channame);
 		return false;
 	}
@@ -149,7 +146,6 @@ join_allowed(CLIENT *Client, CHANNEL *chan, const char *channame,
 	return true;
 } /* join_allowed */
 
-
 /**
  * Set user channel modes.
  *
@@ -169,16 +165,15 @@ join_set_channelmodes(CHANNEL *chan, CLIENT *target, const char *flags)
 
 	/* If the channel is persistent (+P) and client is an IRC op:
 	 * make client chanop, if not disabled in configuration. */
-	if (strchr(Channel_Modes(chan), 'P') && Conf_OperChanPAutoOp
-	    && strchr(Client_Modes(target), 'o'))
+	if (Channel_HasMode(chan, 'P') && Conf_OperChanPAutoOp
+	    && Client_HasMode(target, 'o'))
 		Channel_UserModeAdd(chan, target, 'o');
 } /* join_set_channelmodes */
 
-
 /**
  * Forward JOIN command to a specific server
  *
- * This function diffentiates between servers using RFC 2813 mode that
+ * This function differentiates between servers using RFC 2813 mode that
  * support the JOIN command with appended ASCII 7 character and channel
  * modes, and servers using RFC 1459 protocol which require separate JOIN
  * and MODE commands.
@@ -211,7 +206,6 @@ cb_join_forward(CLIENT *To, CLIENT *Prefix, void *Data)
 					 Client_ID(Prefix));
 } /* cb_join_forward */
 
-
 /**
  * Forward JOIN command to all servers
  *
@@ -256,9 +250,8 @@ join_forward(CLIENT *Client, CLIENT *target, CHANNEL *chan,
 	}
 } /* join_forward */
 
-
 /**
- * Aknowledge user JOIN request and send "channel info" numerics.
+ * Acknowledge user JOIN request and send "channel info" numerics.
  *
  * @param Client	Client used to prefix the genrated commands
  * @param target	Forward commands/numerics to this user
@@ -299,15 +292,12 @@ join_send_topic(CLIENT *Client, CLIENT *target, CHANNEL *chan,
 				  Channel_Name(chan));
 } /* join_send_topic */
 
-
 /**
  * Handler for the IRC "JOIN" command.
  *
- * 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.
+ * @return CONNECTED or DISCONNECTED.
  */
 GLOBAL bool
 IRC_JOIN( CLIENT *Client, REQUEST *Req )
@@ -319,20 +309,7 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 	assert (Client != NULL);
 	assert (Req != NULL);
 
-	/* Bad number of arguments? */
-	if (Req->argc < 1 || Req->argc > 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
-	/* Who is the sender? */
-	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);
+	_IRC_GET_SENDER_OR_RETURN_(target, Req, Client)
 
 	/* Is argument "0"? */
 	if (Req->argc == 1 && !strncmp("0", Req->argv[0], 2))
@@ -346,9 +323,11 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 	channame = strtok_r(channame, ",", &lastchan);
 
 	/* Make sure that "channame" is not the empty string ("JOIN :") */
-	if (! channame)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+	if (!channame) {
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);
+	}
 
 	while (channame) {
 		flags = NULL;
@@ -363,12 +342,6 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 		}
 
 		chan = Channel_Search(channame);
-		if (!chan && Conf_PredefChannelsOnly) {
-			 /* channel must be created, but forbidden by config */
-			IRC_WriteStrClient(Client, ERR_NOSUCHCHANNEL_MSG,
-					   Client_ID(Client), channame);
-			goto join_next;
-		}
 
 		/* Local client? */
 		if (Client_Type(Client) == CLIENT_USER) {
@@ -376,12 +349,21 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 				/* Already existing channel: already member? */
 				if (Channel_IsMemberOf(chan, Client))
 				    goto join_next;
+			} else {
+				/* Channel must be created */
+				if (!strchr(Conf_AllowedChannelTypes, channame[0])) {
+					/* ... but channel type is not allowed! */
+					IRC_WriteErrClient(Client,
+						ERR_NOSUCHCHANNEL_MSG,
+						Client_ID(Client), channame);
+					goto join_next;
+				}
 			}
 
 			/* Test if the user has reached the channel limit */
 			if ((Conf_MaxJoins > 0) &&
 			    (Channel_CountForUser(Client) >= Conf_MaxJoins)) {
-				if (!IRC_WriteStrClient(Client,
+				if (!IRC_WriteErrClient(Client,
 						ERR_TOOMANYCHANNELS_MSG,
 						Client_ID(Client), channame))
 					return DISCONNECTED;
@@ -443,15 +425,12 @@ IRC_JOIN( CLIENT *Client, REQUEST *Req )
 	return CONNECTED;
 } /* IRC_JOIN */
 
-
 /**
  * Handler for the IRC "PART" command.
  *
- * See RFC 2812, 3.2.2: "Part 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.
+ * @return CONNECTED or DISCONNECTED.
  */
 GLOBAL bool
 IRC_PART(CLIENT * Client, REQUEST * Req)
@@ -462,26 +441,17 @@ IRC_PART(CLIENT * Client, REQUEST * Req)
 	assert(Client != NULL);
 	assert(Req != NULL);
 
-	if (Req->argc < 1 || Req->argc > 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
-	/* Get the sender */
-	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);
+	_IRC_GET_SENDER_OR_RETURN_(target, Req, Client)
 
 	/* Loop over all the given channel names */
 	chan = strtok(Req->argv[0], ",");
 
 	/* Make sure that "chan" is not the empty string ("PART :") */
-	if (! chan)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+	if (!chan) {
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);
+	}
 
 	while (chan) {
 		Channel_Part(target, Client, chan,
@@ -496,15 +466,12 @@ IRC_PART(CLIENT * Client, REQUEST * Req)
 	return CONNECTED;
 } /* IRC_PART */
 
-
 /**
  * Handler for the IRC "TOPIC" command.
  *
- * See RFC 2812, 3.2.4 "Topic 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.
+ * @return CONNECTED or DISCONNECTED.
  */
 GLOBAL bool
 IRC_TOPIC( CLIENT *Client, REQUEST *Req )
@@ -517,32 +484,23 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
 	assert( Client != NULL );
 	assert( Req != NULL );
 
-	if (Req->argc < 1 || Req->argc > 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
+	IRC_SetPenalty(Client, 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);
+	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
 
 	chan = Channel_Search(Req->argv[0]);
 	if (!chan)
-		return IRC_WriteStrClient(from, ERR_NOSUCHCHANNEL_MSG,
+		return IRC_WriteErrClient(from, ERR_NOSUCHCHANNEL_MSG,
 					  Client_ID(from), Req->argv[0]);
 
 	/* Only remote servers and channel members are allowed to change the
-	 * channel topic, and IRC opreators when the Conf_OperCanMode option
+	 * channel topic, and IRC operators when the Conf_OperCanMode option
 	 * is set in the server configuration. */
 	if (Client_Type(Client) != CLIENT_SERVER) {
 		topic_power = Client_HasMode(from, 'o');
 		if (!Channel_IsMemberOf(chan, from)
 		    && !(Conf_OperCanMode && topic_power))
-			return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG,
+			return IRC_WriteErrClient(from, ERR_NOTONCHANNEL_MSG,
 						  Client_ID(from), Req->argv[0]);
 	} else
 		topic_power = true;
@@ -571,14 +529,14 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
 						  Channel_Name(chan));
 	}
 
-	if (strchr(Channel_Modes(chan), 't')) {
+	if (Channel_HasMode(chan, 't')) {
 		/* Topic Lock. Is the user a channel op or IRC operator? */
 		if(!topic_power &&
-		   !strchr(Channel_UserModes(chan, from), 'h') &&
-		   !strchr(Channel_UserModes(chan, from), 'o') &&
-		   !strchr(Channel_UserModes(chan, from), 'a') &&
-		   !strchr(Channel_UserModes(chan, from), 'q'))
-			return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
+		   !Channel_UserHasMode(chan, from, 'h') &&
+		   !Channel_UserHasMode(chan, from, 'o') &&
+		   !Channel_UserHasMode(chan, from, 'a') &&
+		   !Channel_UserHasMode(chan, from, 'q'))
+			return IRC_WriteErrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
 						  Client_ID(from),
 						  Channel_Name(chan));
 	}
@@ -606,15 +564,9 @@ IRC_TOPIC( CLIENT *Client, REQUEST *Req )
 		return CONNECTED;
 } /* IRC_TOPIC */
 
-
 /**
  * Handler for the IRC "LIST" command.
  *
- * See RFC 2812, 3.2.6 "List message".
- *
- * This implementation handles the local case as well as the forwarding of the
- * 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.
  * @return CONNECTED or DISCONNECTED.
@@ -630,31 +582,20 @@ IRC_LIST( CLIENT *Client, REQUEST *Req )
 	assert(Client != NULL);
 	assert(Req != NULL);
 
-	/* Bad number of prameters? */
-	if (Req->argc > 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
+	IRC_SetPenalty(Client, 2);
+
+	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
 
 	if (Req->argc > 0)
 		pattern = strtok(Req->argv[0], ",");
 	else
 		pattern = "*";
 
-	/* Get sender from prefix, if any */
-	if (Client_Type(Client) == CLIENT_SERVER)
-		from = Client_Search(Req->prefix);
-	else
-		from = Client;
-
-	if (!from)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
-					  Client_ID(Client), Req->prefix);
-
 	if (Req->argc == 2) {
 		/* Forward to other server? */
 		target = Client_Search(Req->argv[1]);
 		if (! target || Client_Type(target) != CLIENT_SERVER)
-			return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
+			return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
 						  Client_ID(Client),
 						  Req->argv[1]);
 
@@ -676,9 +617,12 @@ IRC_LIST( CLIENT *Client, REQUEST *Req )
 			/* Check search pattern */
 			if (MatchCaseInsensitive(pattern, Channel_Name(chan))) {
 				/* Gotcha! */
-				if (!strchr(Channel_Modes(chan), 's')
+				if (!Channel_HasMode(chan, 's')
 				    || Channel_IsMemberOf(chan, from)
-				    || (!Conf_MorePrivacy && Client_OperByMe(Client))) {
+				    || (!Conf_MorePrivacy
+					&& Client_HasMode(Client, 'o')
+					&& Client_Conn(Client) > NONE))
+				{
 					if ((Conf_MaxListSize > 0)
 					    && IRC_CheckListTooBig(from, count,
 								   Conf_MaxListSize,
@@ -703,25 +647,20 @@ IRC_LIST( CLIENT *Client, REQUEST *Req )
 			pattern = NULL;
 	}
 
-	IRC_SetPenalty(from, 2);
 	return IRC_WriteStrClient(from, RPL_LISTEND_MSG, Client_ID(from));
 } /* IRC_LIST */
 
-
 /**
- * Handler for the IRC+ command "CHANINFO".
+ * Handler for the IRC+ "CHANINFO" command.
  *
- * See doc/Protocol.txt, section II.3:
- * "Exchange channel-modes, topics, and persistent channels".
- *
- * @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
 IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
 {
-	char modes_add[COMMAND_LEN], l[16], *ptr;
+	char modes_add[COMMAND_LEN], l[16];
 	CLIENT *from;
 	CHANNEL *chan;
 	int arg_topic;
@@ -730,73 +669,70 @@ IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
 	assert( Req != NULL );
 
 	/* Bad number of parameters? */
-	if (Req->argc < 2 || Req->argc == 4 || Req->argc > 5)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+	if (Req->argc < 2 || Req->argc == 4 || Req->argc > 5) {
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);
+	}
 
 	/* Compatibility kludge */
-	if( Req->argc == 5 ) arg_topic = 4;
-	else if( Req->argc == 3 ) arg_topic = 2;
-	else arg_topic = -1;
+	if (Req->argc == 5)
+		arg_topic = 4;
+	else if(Req->argc == 3)
+		arg_topic = 2;
+	else
+		arg_topic = -1;
 
-	/* Search origin */
-	from = Client_Search( Req->prefix );
-	if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
+	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
 
 	/* Search or create channel */
 	chan = Channel_Search( Req->argv[0] );
-	if( ! chan ) chan = Channel_Create( Req->argv[0] );
-	if( ! chan ) return CONNECTED;
-
-	if( Req->argv[1][0] == '+' )
-	{
-		ptr = Channel_Modes( chan );
-		if( ! *ptr )
-		{
-			/* OK, this channel doesn't have modes jet, set the received ones: */
-			Channel_SetModes( chan, &Req->argv[1][1] );
-
-			if( Req->argc == 5 )
-			{
-				if( strchr( Channel_Modes( chan ), 'k' )) Channel_SetKey( chan, Req->argv[2] );
-				if( strchr( Channel_Modes( chan ), 'l' )) Channel_SetMaxUsers( chan, atol( Req->argv[3] ));
-			}
-			else
-			{
+	if (!chan)
+		chan = Channel_Create( Req->argv[0] );
+	if (!chan)
+		return CONNECTED;
+
+	if (Req->argv[1][0] == '+') {
+		if (!*Channel_Modes(chan)) {
+			/* OK, this channel doesn't have modes yet,
+			 * set the received ones: */
+			Channel_SetModes(chan, &Req->argv[1][1]);
+
+			if(Req->argc == 5) {
+				if(Channel_HasMode(chan, 'k'))
+					Channel_SetKey(chan, Req->argv[2]);
+				if(Channel_HasMode(chan, 'l'))
+					Channel_SetMaxUsers(chan, atol(Req->argv[3]));
+			} else {
 				/* Delete modes which we never want to inherit */
-				Channel_ModeDel( chan, 'l' );
-				Channel_ModeDel( chan, 'k' );
+				Channel_ModeDel(chan, 'l');
+				Channel_ModeDel(chan, 'k');
 			}
 
-			strcpy( modes_add, "" );
-			ptr = Channel_Modes( chan );
-			while( *ptr )
-			{
-				if( *ptr == 'l' )
-				{
-					snprintf( l, sizeof( l ), " %lu", Channel_MaxUsers( chan ));
-					strlcat( modes_add, l, sizeof( modes_add ));
-				}
-				if( *ptr == 'k' )
-				{
-					strlcat( modes_add, " ", sizeof( modes_add ));
-					strlcat( modes_add, Channel_Key( chan ), sizeof( modes_add ));
-				}
-	     			ptr++;
+			strcpy(modes_add, "");
+			if (Channel_HasMode(chan, 'l'))  {
+				snprintf(l, sizeof(l), " %lu",
+					 Channel_MaxUsers(chan));
+				strlcat(modes_add, l, sizeof(modes_add));
+			}
+			if (Channel_HasMode(chan, 'k'))  {
+				strlcat(modes_add, " ", sizeof(modes_add));
+				strlcat(modes_add, Channel_Key(chan),
+					sizeof(modes_add));
 			}
 
 			/* Inform members of this channel */
-			IRC_WriteStrChannelPrefix( Client, chan, from, false, "MODE %s +%s%s", Req->argv[0], Channel_Modes( chan ), modes_add );
+			IRC_WriteStrChannelPrefix(Client, chan, from, false,
+						  "MODE %s +%s%s", Req->argv[0],
+						  Channel_Modes(chan), modes_add);
 		}
 	}
-	else Log( LOG_WARNING, "CHANINFO: invalid MODE format ignored!" );
+	else
+		Log(LOG_WARNING, "CHANINFO: invalid MODE format ignored!");
 
-	if( arg_topic > 0 )
-	{
+	if (arg_topic > 0) {
 		/* We got a topic */
-		ptr = Channel_Topic( chan );
-		if(( ! *ptr ) && ( Req->argv[arg_topic][0] ))
-		{
+		if (!*Channel_Topic(chan) && Req->argv[arg_topic][0]) {
 			/* OK, there is no topic jet */
 			Channel_SetTopic(chan, Client, Req->argv[arg_topic]);
 			IRC_WriteStrChannelPrefix(Client, chan, from, false,
@@ -804,13 +740,24 @@ IRC_CHANINFO( CLIENT *Client, REQUEST *Req )
 		}
 	}
 
-	/* Forward CHANINFO to other serevrs */
-	if( Req->argc == 5 ) IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2], Req->argv[3], Req->argv[4] );
-	else if( Req->argc == 3 ) IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s :%s", Req->argv[0], Req->argv[1], Req->argv[2] );
-	else IRC_WriteStrServersPrefixFlag( Client, from, 'C', "CHANINFO %s %s", Req->argv[0], Req->argv[1] );
+	/* Forward CHANINFO to other servers */
+	if (Req->argc == 5)
+		IRC_WriteStrServersPrefixFlag(Client, from, 'C',
+					      "CHANINFO %s %s %s %s :%s",
+					      Req->argv[0], Req->argv[1],
+					      Req->argv[2], Req->argv[3],
+					      Req->argv[4]);
+	else if (Req->argc == 3)
+		IRC_WriteStrServersPrefixFlag(Client, from, 'C',
+					      "CHANINFO %s %s :%s",
+					      Req->argv[0], Req->argv[1],
+					      Req->argv[2]);
+	else
+		IRC_WriteStrServersPrefixFlag(Client, from, 'C',
+					      "CHANINFO %s %s",
+					      Req->argv[0], Req->argv[1]);
 
 	return CONNECTED;
 } /* IRC_CHANINFO */
 
-
 /* -eof- */

+ 2 - 6
src/ngircd/irc-encoding.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -48,15 +48,11 @@ IRC_CHARCONV(CLIENT *Client, REQUEST *Req)
 	assert (Client != NULL);
 	assert (Req != NULL);
 
-	if (Req->argc != 1)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	strlcpy(encoding, Req->argv[0], sizeof(encoding));
 	ngt_UpperStr(encoding);
 
 	if (!Conn_SetEncoding(Client_Conn(Client), encoding))
-		return IRC_WriteStrClient(Client, ERR_IP_CHARCONV_MSG,
+		return IRC_WriteErrClient(Client, ERR_IP_CHARCONV_MSG,
 					  Client_ID(Client), encoding);
 
 	return IRC_WriteStrClient(Client, RPL_IP_CHARCONV_MSG,

File diff suppressed because it is too large
+ 730 - 823
src/ngircd/irc-info.c


+ 126 - 210
src/ngircd/irc-login.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -33,24 +33,21 @@
 #include "parse.h"
 #include "irc.h"
 #include "irc-info.h"
+#include "irc-macros.h"
 #include "irc-write.h"
 
 #include "exp.h"
 #include "irc-login.h"
 
-static void Kill_Nick PARAMS((char *Nick, char *Reason));
 static void Change_Nick PARAMS((CLIENT * Origin, CLIENT * Target, char *NewNick,
 				bool InformClient));
 
-
 /**
  * Handler for the IRC "PASS" command.
  *
- * See RFC 2813 section 4.1.1, and RFC 2812 section 3.1.1.
- *
- * @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
 IRC_PASS( CLIENT *Client, REQUEST *Req )
@@ -63,7 +60,7 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
 
 	/* Return an error if this is not a local client */
 	if (Client_Conn(Client) <= NONE)
-		return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
+		return IRC_WriteErrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
 					  Client_ID(Client), Req->command);
 
 	if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) {
@@ -83,11 +80,12 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
 	} else if (Client_Type(Client) == CLIENT_UNKNOWN ||
 		   Client_Type(Client) == CLIENT_UNKNOWNSERVER) {
 		/* Unregistered connection, but wrong number of arguments: */
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);
 	} else {
 		/* Registered connection, PASS command is not allowed! */
-		return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
+		return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
 					  Client_ID(Client));
 	}
 
@@ -146,7 +144,7 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
 			} else
 				flags = "";
 			Log(LOG_INFO,
-			    "Peer on conenction %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
+			    "Peer on connection %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
 			    Client_Conn(Client), impl, serverver,
 			    protohigh, protolow, flags);
 		} else {
@@ -167,19 +165,12 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
 	return CONNECTED;
 } /* IRC_PASS */
 
-
 /**
  * Handler for the IRC "NICK" command.
  *
- * See RFC 2812, 3.1.2 "Nick message", and RFC 2813, 4.1.3 "Nick".
- *
- * This function implements the IRC command "NICK" which is used to register
- * with the server, to change already registered nicknames and to introduce
- * new users which are connected to other servers.
- *
- * @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
 IRC_NICK( CLIENT *Client, REQUEST *Req )
@@ -204,31 +195,22 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 	    || (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1))
 	{
 		/* User registration or change of nickname */
-
-		/* Wrong number of arguments? */
-		if( Req->argc != 1 )
-			return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
-						   Client_ID( Client ),
-						   Req->command );
+		_IRC_ARGC_EQ_OR_RETURN_(Client, Req, 1)
 
 		/* Search "target" client */
-		if( Client_Type( Client ) == CLIENT_SERVER )
-		{
-			target = Client_Search( Req->prefix );
-			if( ! target )
-				return IRC_WriteStrClient( Client,
-							   ERR_NOSUCHNICK_MSG,
-							   Client_ID( Client ),
-							   Req->argv[0] );
-		}
-		else
-		{
+		if (Client_Type(Client) == CLIENT_SERVER) {
+			target = Client_Search(Req->prefix);
+			if (!target)
+				return IRC_WriteErrClient(Client,
+							  ERR_NOSUCHNICK_MSG,
+							  Client_ID(Client),
+							  Req->argv[0]);
+		} else {
 			/* Is this a restricted client? */
-			if( Client_HasMode( Client, 'r' ))
-				return IRC_WriteStrClient( Client,
-							   ERR_RESTRICTED_MSG,
-							   Client_ID( Client ));
-
+			if (Client_HasMode(Client, 'r'))
+				return IRC_WriteErrClient(Client,
+							  ERR_RESTRICTED_MSG,
+							  Client_ID(Client));
 			target = Client;
 		}
 
@@ -237,15 +219,14 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 		 * do anything. This is how the original ircd behaves and some
 		 * clients (for example Snak) expect it to be like this.
 		 * But I doubt that this is "really the right thing" ... */
-		if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 )
+		if (strcmp(Client_ID(target), Req->argv[0]) == 0)
 			return CONNECTED;
 #endif
 
 		/* Check that the new nickname is available. Special case:
 		 * the client only changes from/to upper to lower case. */
-		if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
-		{
-			if( ! Client_CheckNick( target, Req->argv[0] ))
+		if (strcasecmp(Client_ID(target), Req->argv[0]) != 0) {
+			if (!Client_CheckNick(target, Req->argv[0]))
 				return CONNECTED;
 		}
 
@@ -261,7 +242,11 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 
 #ifndef STRICT_RFC
 			if (Conf_AuthPing) {
+#ifdef HAVE_ARC4RANDOM
+				Conn_SetAuthPing(Client_Conn(Client), arc4random());
+#else
 				Conn_SetAuthPing(Client_Conn(Client), rand());
+#endif
 				IRC_WriteStrClient(Client, "PING :%ld",
 					Conn_GetAuthPing(Client_Conn(Client)));
 				LogDebug("Connection %d: sent AUTH PING %ld ...",
@@ -289,9 +274,11 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 		/* Server or service introduces new client */
 
 		/* Bad number of parameters? */
-		if (Req->argc != 2 && Req->argc != 7)
-			return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+		if (Req->argc != 2 && Req->argc != 7) {
+			IRC_SetPenalty(Client, 2);
+			return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 						  Client_ID(Client), Req->command);
+		}
 
 		if (Req->argc >= 7) {
 			/* RFC 2813 compatible syntax */
@@ -319,28 +306,33 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 			 * the new nick is already present on this server:
 			 * the new and the old one have to be disconnected now.
 			 */
-			Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
-			Kill_Nick( Req->argv[0], "Nick collision" );
-			return CONNECTED;
+			Log(LOG_ERR,
+			    "Server %s introduces already registered nick \"%s\"!",
+			    Client_ID(Client), Req->argv[0]);
+			return IRC_KillClient(Client, NULL, Req->argv[0],
+					      "Nick collision");
 		}
 
 		/* Find the Server this client is connected to */
 		intr_c = Client_GetFromToken(Client, token);
-		if( ! intr_c )
-		{
-			Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
-			Kill_Nick( Req->argv[0], "Unknown server" );
-			return CONNECTED;
+		if (!intr_c) {
+			Log(LOG_ERR,
+			    "Server %s introduces nick \"%s\" on unknown server!?",
+			    Client_ID(Client), Req->argv[0]);
+			return IRC_KillClient(Client, NULL, Req->argv[0],
+					      "Unknown server");
 		}
 
 		c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
 					 token, modes, info, true);
-		if( ! c )
-		{
-			/* out of memory, need to disconnect client to keep network state consistent */
-			Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
-			Kill_Nick( Req->argv[0], "Server error" );
-			return CONNECTED;
+		if (!c) {
+			/* Out of memory, we need to disconnect client to keep
+			 * network state consistent! */
+			Log(LOG_ALERT,
+			    "Can't create client structure! (on connection %d)",
+			    Client_Conn(Client));
+			return IRC_KillClient(Client, NULL, Req->argv[0],
+					      "Server error");
 		}
 
 		/* RFC 2813: client is now fully registered, inform all the
@@ -356,10 +348,11 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 
 		return CONNECTED;
 	}
-	else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
+	else
+		return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
+					  Client_ID(Client));
 } /* IRC_NICK */
 
-
 /**
  * Handler for the IRC "SVSNICK" command.
  *
@@ -375,10 +368,6 @@ IRC_SVSNICK(CLIENT *Client, REQUEST *Req)
 	assert(Client != NULL);
 	assert(Req != NULL);
 
-	if (Req->argc != 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	/* Search the originator */
 	from = Client_Search(Req->prefix);
 	if (!from)
@@ -387,7 +376,7 @@ IRC_SVSNICK(CLIENT *Client, REQUEST *Req)
 	/* Search the target */
 	target = Client_Search(Req->argv[0]);
 	if (!target || Client_Type(target) != CLIENT_USER) {
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
 					  Client_ID(Client), Req->argv[0]);
 	}
 
@@ -411,11 +400,9 @@ IRC_SVSNICK(CLIENT *Client, REQUEST *Req)
 /**
  * Handler for the IRC "USER" command.
  *
- * See RFC 2812, 3.1.3 "User 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.
+ * @return CONNECTED or DISCONNECTED.
  */
 GLOBAL bool
 IRC_USER(CLIENT * Client, REQUEST * Req)
@@ -433,18 +420,14 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 	    Client_Type(Client) == CLIENT_GOTPASS)
 	{
 		/* New connection */
-		if (Req->argc != 4)
-			return IRC_WriteStrClient(Client,
-						  ERR_NEEDMOREPARAMS_MSG,
-						  Client_ID(Client),
-						  Req->command);
+		_IRC_ARGC_EQ_OR_RETURN_(Client, Req, 4)
 
 		/* User name: only alphanumeric characters and limited
 		   punctuation is allowed.*/
 		ptr = Req->argv[0];
 		while (*ptr) {
 			if (!isalnum((int)*ptr) &&
-			    *ptr != '+' && *ptr != '-' &&
+			    *ptr != '+' && *ptr != '-' && *ptr != '@' &&
 			    *ptr != '.' && *ptr != '_') {
 				Conn_Close(Client_Conn(Client), NULL,
 					   "Invalid user name", true);
@@ -453,6 +436,13 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 			ptr++;
 		}
 
+		/* Save the received username for authentication, and use
+		 * it up to the first '@' as default user name (like ircd2.11,
+		 * bahamut, ircd-seven, ...), prefixed with '~', if needed: */
+		Client_SetOrigUser(Client, Req->argv[0]);
+		ptr = strchr(Req->argv[0], '@');
+		if (ptr)
+			*ptr = '\0';
 #ifdef IDENTAUTH
 		ptr = Client_User(Client);
 		if (!ptr || !*ptr || *ptr == '~')
@@ -460,7 +450,6 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 #else
 		Client_SetUser(Client, Req->argv[0], false);
 #endif
-		Client_SetOrigUser(Client, Req->argv[0]);
 
 		/* "Real name" or user info text: Don't set it to the empty
 		 * string, the original ircd can't deal with such "real names"
@@ -481,14 +470,11 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 	} else if (Client_Type(Client) == CLIENT_SERVER ||
 		   Client_Type(Client) == CLIENT_SERVICE) {
 		/* Server/service updating an user */
-		if (Req->argc != 4)
-			return IRC_WriteStrClient(Client,
-						  ERR_NEEDMOREPARAMS_MSG,
-						  Client_ID(Client),
-						  Req->command);
+		_IRC_ARGC_EQ_OR_RETURN_(Client, Req, 4)
+
 		c = Client_Search(Req->prefix);
 		if (!c)
-			return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+			return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
 						  Client_ID(Client),
 						  Req->prefix);
 
@@ -508,29 +494,25 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 		return CONNECTED;
 	} else if (Client_Type(Client) == CLIENT_USER) {
 		/* Already registered connection */
-		return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
+		return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
 					  Client_ID(Client));
 	} else {
 		/* Unexpected/invalid connection state? */
-		return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOTREGISTERED_MSG,
 					  Client_ID(Client));
 	}
 } /* IRC_USER */
 
-
 /**
  * Handler for the IRC "SERVICE" command.
  *
- * This function implements IRC Services registration using the SERVICE command
- * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
- *
  * At the moment ngIRCd doesn't support directly linked services, so this
  * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
  * received from a peer server.
  *
- * @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
 IRC_SERVICE(CLIENT *Client, REQUEST *Req)
@@ -544,22 +526,13 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req)
 
 	if (Client_Type(Client) != CLIENT_GOTPASS &&
 	    Client_Type(Client) != CLIENT_SERVER)
-		return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
+		return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
 					  Client_ID(Client));
 
-	if (Req->argc != 6)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	if (Client_Type(Client) != CLIENT_SERVER)
-		return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
+		return IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
 				  Client_ID(Client), Req->argv[0]);
 
-	/* Bad number of parameters? */
-	if (Req->argc != 6)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	nick = Req->argv[0];
 	user = NULL; host = NULL;
 	token = atoi(Req->argv[1]);
@@ -569,20 +542,20 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req)
 	/* Validate service name ("nickname") */
 	c = Client_Search(nick);
 	if(c) {
-		/* Nickname collission: disconnect (KILL) both clients! */
-		Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
+		/* Nickname collision: disconnect (KILL) both clients! */
+		Log(LOG_ERR,
+		    "Server %s introduces already registered service \"%s\"!",
 		    Client_ID(Client), nick);
-		Kill_Nick(nick, "Nick collision");
-		return CONNECTED;
+		return IRC_KillClient(Client, NULL, nick, "Nick collision");
 	}
 
 	/* Get the server to which the service is connected */
 	intr_c = Client_GetFromToken(Client, token);
 	if (! intr_c) {
-		Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
+		Log(LOG_ERR,
+		    "Server %s introduces service \"%s\" on unknown server!?",
 		    Client_ID(Client), nick);
-		Kill_Nick(nick, "Unknown server");
-		return CONNECTED;
+		return IRC_KillClient(Client, NULL, nick, "Unknown server");
 	}
 
 	/* Get user and host name */
@@ -611,37 +584,28 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req)
 	if (! c) {
 		/* Couldn't create client structure, so KILL the service to
 		 * keep network status consistent ... */
-		Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
+		Log(LOG_ALERT,
+		    "Can't create client structure! (on connection %d)",
 		    Client_Conn(Client));
-		Kill_Nick(nick, "Server error");
-		return CONNECTED;
+		return IRC_KillClient(Client, NULL, nick, "Server error");
 	}
 
 	Client_Introduce(Client, c, CLIENT_SERVICE);
 	return CONNECTED;
 } /* IRC_SERVICE */
 
-
 /**
  * Handler for the IRC "WEBIRC" command.
  *
- * See doc/Protocol.txt, section II.4:
- * "Update webchat/proxy client information".
- *
- * @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
 IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
 {
-	/* Exactly 4 parameters are requited */
-	if (Req->argc != 4)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
-		return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
+		return IRC_WriteErrClient(Client, ERR_PASSWDMISMATCH_MSG,
 					  Client_ID(Client));
 
 	LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
@@ -650,33 +614,27 @@ IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
 	Client_SetUser(Client, Req->argv[1], true);
 	Client_SetOrigUser(Client, Req->argv[1]);
 	Client_SetHostname(Client, Req->argv[2]);
+	Client_SetIPAText(Client, Req->argv[3]);
+
 	return CONNECTED;
 } /* IRC_WEBIRC */
 
-
 /**
  * Handler for the IRC "QUIT" command.
  *
- * See RFC 2812, 3.1.7 "Quit", and RFC 2813, 4.1.5 "Quit".
- *
- * @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
 IRC_QUIT( CLIENT *Client, REQUEST *Req )
 {
 	CLIENT *target;
-	char quitmsg[LINE_LEN];
+	char quitmsg[COMMAND_LEN];
 
 	assert(Client != NULL);
 	assert(Req != NULL);
 
-	/* Wrong number of arguments? */
-	if (Req->argc > 1)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	if (Req->argc == 1)
 		strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
 
@@ -691,11 +649,11 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
 		}
 
 		if (target != Client) {
-			Client_Destroy(target, "Got QUIT command.",
+			Client_Destroy(target, "Got QUIT command",
 				       Req->argc == 1 ? quitmsg : NULL, true);
 			return CONNECTED;
 		} else {
-			Conn_Close(Client_Conn(Client), "Got QUIT command.",
+			Conn_Close(Client_Conn(Client), "Got QUIT command",
 				   Req->argc == 1 ? quitmsg : NULL, true);
 			return DISCONNECTED;
 		}
@@ -708,14 +666,13 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
 		}
 
 		/* User, Service, or not yet registered */
-		Conn_Close(Client_Conn(Client), "Got QUIT command.",
+		Conn_Close(Client_Conn(Client), "Got QUIT command",
 			   Req->argc == 1 ? quitmsg : NULL, true);
 
 		return DISCONNECTED;
 	}
 } /* IRC_QUIT */
 
-
 #ifndef STRICT_RFC
 
 /**
@@ -724,9 +681,9 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
  * We handle these commands here to avoid the quite long timeout when
  * some user tries to access this IRC daemon using an web browser ...
  *
- * @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
 IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
@@ -738,15 +695,12 @@ IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
 
 #endif
 
-
 /**
  * Handler for the IRC "PING" command.
  *
- * See RFC 2812, 3.7.2 "Ping 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.
+ * @return CONNECTED or DISCONNECTED.
  */
 GLOBAL bool
 IRC_PING(CLIENT *Client, REQUEST *Req)
@@ -757,13 +711,11 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
 	assert(Req != NULL);
 
 	if (Req->argc < 1)
-		return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOORIGIN_MSG,
 					  Client_ID(Client));
 #ifdef STRICT_RFC
 	/* Don't ignore additional arguments when in "strict" mode */
-	if (Req->argc > 2)
-		 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					   Client_ID(Client), Req->command);
+	_IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
 #endif
 
 	if (Req->argc > 1) {
@@ -771,7 +723,7 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
 		target = Client_Search(Req->argv[1]);
 
 		if (!target || Client_Type(target) != CLIENT_SERVER)
-			return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
+			return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
 					Client_ID(Client), Req->argv[1]);
 
 		if (target != Client_ThisServer()) {
@@ -781,7 +733,7 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
 			else
 				from = Client;
 			if (!from)
-				return IRC_WriteStrClient(Client,
+				return IRC_WriteErrClient(Client,
 						ERR_NOSUCHSERVER_MSG,
 						Client_ID(Client), Req->prefix);
 
@@ -799,7 +751,7 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
 	} else
 		from = Client_ThisServer();
 	if (!from)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
 					Client_ID(Client), Req->prefix);
 
 	Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
@@ -816,15 +768,12 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
 #endif
 } /* IRC_PING */
 
-
 /**
  * Handler for the IRC "PONG" command.
  *
- * See RFC 2812, 3.7.3 "Pong 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.
+ * @return CONNECTED or DISCONNECTED.
  */
 GLOBAL bool
 IRC_PONG(CLIENT *Client, REQUEST *Req)
@@ -842,26 +791,20 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 	/* Wrong number of arguments? */
 	if (Req->argc < 1) {
 		if (Client_Type(Client) == CLIENT_USER)
-			return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
+			return IRC_WriteErrClient(Client, ERR_NOORIGIN_MSG,
 						  Client_ID(Client));
 		else
 			return CONNECTED;
 	}
-	if (Req->argc > 2) {
-		if (Client_Type(Client) == CLIENT_USER)
-			return IRC_WriteStrClient(Client,
-						  ERR_NEEDMOREPARAMS_MSG,
-						  Client_ID(Client),
-						  Req->command);
-		else
-			return CONNECTED;
+	if (Client_Type(Client) == CLIENT_USER) {
+		_IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
 	}
 
 	/* Forward? */
 	if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
 		target = Client_Search(Req->argv[0]);
 		if (!target)
-			return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
+			return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
 					Client_ID(Client), Req->argv[0]);
 
 		from = Client_Search(Req->prefix);
@@ -869,7 +812,7 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 		if (target != Client_ThisServer() && target != from) {
 			/* Ok, we have to forward the message. */
 			if (!from)
-				return IRC_WriteStrClient(Client,
+				return IRC_WriteErrClient(Client,
 						ERR_NOSUCHSERVER_MSG,
 						Client_ID(Client), Req->prefix);
 
@@ -907,8 +850,9 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 
 	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]",
+		    "Synchronization with \"%s\" done (connection %d): %ld second%s [%ld users, %ld channels].",
 		    Client_ID(Client), conn, time(NULL) - Conn_GetSignon(conn),
+		    time(NULL) - Conn_GetSignon(conn) == 1 ? "" : "s",
 		    Client_UserCount(), Channel_CountVisible(NULL));
 		Conn_UpdatePing(conn);
 	} else
@@ -918,33 +862,6 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 	return CONNECTED;
 } /* IRC_PONG */
 
-
-/**
- * Kill all users with a specific nickname in the network.
- *
- * @param Nick		Nickname.
- * @param Reason	Reason for the KILL.
- */
-static void
-Kill_Nick(char *Nick, char *Reason)
-{
-	REQUEST r;
-
-	assert (Nick != NULL);
-	assert (Reason != NULL);
-
-	r.prefix = NULL;
-	r.argv[0] = Nick;
-	r.argv[1] = Reason;
-	r.argc = 2;
-
-	Log(LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s",
-	    Nick, Reason);
-
-	IRC_KILL(Client_ThisServer(), &r);
-} /* Kill_Nick */
-
-
 /**
  * Change the nickname of a client.
  *
@@ -984,5 +901,4 @@ Change_Nick(CLIENT *Origin, CLIENT *Target, char *NewNick, bool InformClient)
 	Client_SetID(Target, NewNick);
 }
 
-
 /* -eof- */

+ 113 - 0
src/ngircd/irc-macros.h

@@ -0,0 +1,113 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2013 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 __irc_macros_h__
+#define __irc_macros_h__
+
+/**
+ * @file
+ * Macros for functions that handle IRC commands.
+ */
+
+/**
+ * Make sure that number of passed parameters is equal to Count.
+ *
+ * If there are not exactly Count parameters, send an error to the client and
+ * return from the function.
+ */
+#define _IRC_ARGC_EQ_OR_RETURN_(Client, Req, Count) \
+if (Req->argc != Count) { \
+	IRC_SetPenalty(Client, 2); \
+	return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, \
+				  Client_ID(Client), Req->command); \
+}
+
+/**
+ * Make sure that number of passed parameters is less or equal than Max.
+ *
+ * If there are more than Max parameters, send an error to the client and
+ * return from the function.
+ */
+#define _IRC_ARGC_LE_OR_RETURN_(Client, Req, Max) \
+if (Req->argc > Max) { \
+	IRC_SetPenalty(Client, 2); \
+	return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, \
+				  Client_ID(Client), Req->command); \
+}
+
+/**
+ * Make sure that number of passed parameters is greater or equal than Min.
+ *
+ * If there aren't at least Min parameters, send an error to the client and
+ * return from the function.
+ */
+#define _IRC_ARGC_GE_OR_RETURN_(Client, Req, Min) \
+if (Req->argc < Min) { \
+	IRC_SetPenalty(Client, 2); \
+	return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, \
+				  Client_ID(Client), Req->command); \
+}
+
+/**
+ * Make sure that number of passed parameters is in between Min and Max.
+ *
+ * If there aren't at least Min parameters or if there are more than Max
+ * parameters, send an error to the client and return from the function.
+ */
+#define _IRC_ARGC_BETWEEN_OR_RETURN_(Client, Req, Min, Max) \
+if (Req->argc < Min || Req->argc > Max) { \
+	IRC_SetPenalty(Client, 2); \
+	return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, \
+				  Client_ID(Client), Req->command); \
+}
+
+/**
+ * Get sender of an IRC command.
+ *
+ * The sender is either stored in the prefix if the command has been
+ * received from a server or set to the client. If the sender is invalid,
+ * send an error to the client and return from the function.
+ */
+#define _IRC_GET_SENDER_OR_RETURN_(Sender, Req, Client) \
+	if (Client_Type(Client) == CLIENT_SERVER) \
+		Sender = Client_Search(Req->prefix); \
+	else \
+		Sender = Client; \
+	if (!Sender) \
+		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, \
+					  Client_ID(Client), Req->prefix);
+
+/**
+ * Get target of an IRC command and make sure that it is a server.
+ *
+ * Set the target to the local server if no target parameter is given in the
+ * received command, and send an error to the client and return from the
+ * function if the given target isn't resolvable to a server: the target
+ * parameter can be a server name, a nick name (then the target is set to
+ * the server to which this nick is connected), or a mask matching at least
+ * one server name in the network.
+ */
+#define _IRC_GET_TARGET_SERVER_OR_RETURN_(Target, Req, Argc, From) \
+	if (Req->argc > Argc) { \
+		Target = Client_Search(Req->argv[Argc]); \
+		if (!Target) \
+			Target = Client_SearchServer(Req->argv[Argc]); \
+		if (!Target) \
+			return IRC_WriteStrClient(From, ERR_NOSUCHSERVER_MSG, \
+					  Client_ID(From), Req->argv[Argc]); \
+		if (Client_Type(Target) != CLIENT_SERVER) \
+			Target = Client_Introducer(Target); \
+	} else \
+		Target = Client_ThisServer();
+
+#endif	/* __irc_macros_h__ */
+
+/* -eof- */

+ 9 - 9
src/ngircd/irc-metadata.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -51,26 +51,22 @@ IRC_METADATA(CLIENT *Client, REQUEST *Req)
 	assert(Client != NULL);
 	assert(Req != NULL);
 
-	if (Req->argc != 3)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	prefix = Client_Search(Req->prefix);
 	if (!prefix)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
 					  Client_ID(Client), Req->prefix);
 
 	target = Client_Search(Req->argv[0]);
 	if (!target)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
 					  Client_ID(Client), Req->argv[0]);
 
 	LogDebug("Got \"METADATA\" command from \"%s\" for client \"%s\": \"%s=%s\".",
 		 Client_ID(prefix), Client_ID(target),
 		 Req->argv[1], Req->argv[2]);
 
-	/* Mark client: it has receiveda a METADATA command */
-	if (!strchr(Client_Flags(target), 'M')) {
+	/* Mark client: it has received a METADATA command */
+	if (!Client_HasFlag(target, 'M')) {
 		snprintf(new_flags, sizeof new_flags, "%sM",
 			 Client_Flags(target));
 		Client_SetFlags(target, new_flags);
@@ -96,6 +92,10 @@ IRC_METADATA(CLIENT *Client, REQUEST *Req)
 		Client_SetInfo(target, Req->argv[2]);
 	else if (*Req->argv[2] && strcasecmp(Req->argv[1], "user") == 0)
 		Client_SetUser(target, Req->argv[2], true);
+	else if (strcasecmp(Req->argv[1], "accountname") == 0)
+		Client_SetAccountName(target, Req->argv[2]);
+	else if (*Req->argv[2] && strcasecmp(Req->argv[1], "certfp") == 0)
+		Conn_SetCertFp(Client_Conn(target), Req->argv[2]);
 	else
 		Log(LOG_WARNING,
 		    "Ignored metadata update from \"%s\" for client \"%s\": \"%s=%s\" - unknown key!",

+ 113 - 128
src/ngircd/irc-mode.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -25,6 +25,7 @@
 #include "defines.h"
 #include "conn.h"
 #include "channel.h"
+#include "irc-macros.h"
 #include "irc-write.h"
 #include "lists.h"
 #include "log.h"
@@ -35,7 +36,6 @@
 #include "exp.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,
@@ -50,16 +50,15 @@ 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").
+ * This function detects whether user or channel modes should be modified
+ * and calls the appropriate sub-functions.
  *
- * @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
 IRC_MODE( CLIENT *Client, REQUEST *Req )
@@ -70,20 +69,7 @@ IRC_MODE( CLIENT *Client, REQUEST *Req )
 	assert(Client != NULL);
 	assert(Req != NULL);
 
-	/* No parameters? */
-	if (Req->argc < 1)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
-	/* 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;
+	_IRC_GET_SENDER_OR_RETURN_(origin, Req, Client)
 
 	/* Channel or user mode? */
 	cl = NULL; chan = NULL;
@@ -98,11 +84,10 @@ IRC_MODE( CLIENT *Client, REQUEST *Req )
 		return Channel_Mode(Client, Req, origin, chan);
 
 	/* No target found! */
-	return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+	return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
 			Client_ID(Client), Req->argv[0]);
 } /* IRC_MODE */
 
-
 /**
  * Check if the "mode limit" for a client has been reached.
  *
@@ -123,15 +108,14 @@ Mode_Limit_Reached(CLIENT *Client, int Count)
 	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.
+ * @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.
+ * @return CONNECTED or DISCONNECTED.
  */
 static bool
 Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
@@ -146,7 +130,7 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 	if (Client_Type(Client) == CLIENT_USER) {
 		/* Users are only allowed to manipulate their own modes! */
 		if (Target != Client)
-			return IRC_WriteStrClient(Client,
+			return IRC_WriteErrClient(Client,
 						  ERR_USERSDONTMATCH_MSG,
 						  Client_ID(Client));
 	}
@@ -168,7 +152,7 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 		set = false;
 		strcpy(the_modes, "-");
 	} else
-		return IRC_WriteStrClient(Origin, ERR_UMODEUNKNOWNFLAG_MSG,
+		return IRC_WriteErrClient(Origin, ERR_UMODEUNKNOWNFLAG_MSG,
 					  Client_ID(Origin));
 
 	x[1] = '\0';
@@ -227,43 +211,34 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 				x[0] = 'a';
 				Client_SetAway(Origin, DEFAULT_AWAY_MSG);
 			} else
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(Origin,
 							ERR_NOPRIVILEGES_MSG,
 							Client_ID(Origin));
 			break;
 		case 'B': /* Bot */
 			if (Client_HasMode(Client, 'r'))
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(Origin,
 							ERR_RESTRICTED_MSG,
 							Client_ID(Origin));
 			else
 				x[0] = 'B';
 			break;
-		case 'c': /* Receive connect notices
-			   * (only settable by IRC operators!) */
+		case 'c': /* Receive connect notices */
+		case 'q': /* KICK-protected user */
+			  /* (only settable by IRC operators!) */
 			if (!set || Client_Type(Client) == CLIENT_SERVER
-			    || Client_OperByMe(Origin))
-				x[0] = 'c';
+			    || Client_HasMode(Origin, 'o'))
+				x[0] = *mode_ptr;
 			else
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(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 'q': /* KICK-protected user */
-			if (!set || Client_Type(Client) == CLIENT_SERVER
-			    || Client_OperByMe(Origin))
-				x[0] = 'q';
-			else
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(Origin,
 							ERR_NOPRIVILEGES_MSG,
 							Client_ID(Origin));
 			break;
@@ -271,7 +246,7 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 			if (set || Client_Type(Client) == CLIENT_SERVER)
 				x[0] = 'r';
 			else
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(Origin,
 							ERR_RESTRICTED_MSG,
 							Client_ID(Origin));
 			break;
@@ -279,22 +254,22 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 			if (Client_Type(Client) == CLIENT_SERVER)
 				x[0] = 'R';
 			else
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(Origin,
 							ERR_NICKREGISTER_MSG,
 							Client_ID(Origin));
 			break;
 		case 'x': /* Cloak hostname */
 			if (Client_HasMode(Client, 'r'))
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(Origin,
 							ERR_RESTRICTED_MSG,
 							Client_ID(Origin));
 			else if (!set || Conf_CloakHostModeX[0]
 				 || Client_Type(Client) == CLIENT_SERVER
-				 || Client_OperByMe(Client)) {
+				 || Client_HasMode(Origin, 'o')) {
 				x[0] = 'x';
 				send_RPL_HOSTHIDDEN_MSG = true;
 			} else
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(Origin,
 							ERR_NOPRIVILEGES_MSG,
 							Client_ID(Origin));
 			break;
@@ -304,7 +279,7 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 				    "Unknown mode \"%c%c\" from \"%s\"!?",
 				    set ? '+' : '-', *mode_ptr,
 				    Client_ID(Origin));
-				ok = IRC_WriteStrClient(Origin,
+				ok = IRC_WriteErrClient(Origin,
 							ERR_UMODEUNKNOWNFLAG2_MSG,
 							Client_ID(Origin),
 							set ? '+' : '-',
@@ -369,7 +344,7 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 		}
 
 		if (send_RPL_HOSTHIDDEN_MSG && Client_Conn(Target) > NONE) {
-			/* A new (cloaked) hostname must be annoucned */
+			/* A new (cloaked) hostname must be announced */
 			IRC_WriteStrClientPrefix(Target, Origin,
 						 RPL_HOSTHIDDEN_MSG,
 						 Client_ID(Target),
@@ -386,7 +361,13 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 	return ok;
 } /* Client_Mode */
 
-
+/*
+ * Reply to a channel mode request.
+ *
+ * @param Origin The originator of the MODE command (prefix).
+ * @param Channel The channel of which the modes should be sent.
+ * @return CONNECTED or DISCONNECTED.
+ */
 static bool
 Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
 {
@@ -432,15 +413,20 @@ Channel_Mode_Answer_Request(CLIENT *Origin, CHANNEL *Channel)
 	return CONNECTED;
 }
 
-
 /**
  * Handle channel mode and channel-user mode changes
+ *
+ * @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 Channel The target channel of this MODE command.
+ * @return CONNECTED or DISCONNECTED.
  */
 static bool
 Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 {
 	char the_modes[COMMAND_LEN], the_args[COMMAND_LEN], x[2],
-	    argadd[CLIENT_PASS_LEN], *mode_ptr, *o_mode_ptr;
+	    argadd[CLIENT_PASS_LEN], *mode_ptr;
 	bool connected, set, skiponce, retval, use_servermode,
 	     is_halfop, is_op, is_admin, is_owner, is_machine, is_oper;
 	int mode_arg, arg_arg, mode_arg_count = 0;
@@ -451,7 +437,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 	is_halfop = is_op = is_admin = is_owner = is_machine = is_oper = false;
 
 	if (Channel_IsModeless(Channel))
-		return IRC_WriteStrClient(Client, ERR_NOCHANMODES_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOCHANMODES_MSG,
 				Client_ID(Client), Channel_Name(Channel));
 
 	/* Mode request: let's answer it :-) */
@@ -460,7 +446,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 
 	/* Check if origin is oper and opers can use mode */
 	use_servermode = Conf_OperServerMode;
-	if(Client_OperByMe(Client) && Conf_OperCanMode) {
+	if(Client_HasMode(Client, 'o') && Conf_OperCanMode) {
 		is_oper = true;
 	}
 
@@ -472,7 +458,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 
 	/* Check if client is member of channel or an oper or an server/service */
 	if(!Channel_IsMemberOf(Channel, Client) && !is_oper && !is_machine)
-		return IRC_WriteStrClient(Origin, ERR_NOTONCHANNEL_MSG,
+		return IRC_WriteErrClient(Origin, ERR_NOTONCHANNEL_MSG,
 					  Client_ID(Origin),
 					  Channel_Name(Channel));
 
@@ -551,18 +537,14 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 			arg_arg = -1;
 
 		if(!is_machine && !is_oper) {
-			o_mode_ptr = Channel_UserModes(Channel, Client);
-			while( *o_mode_ptr ) {
-				if ( *o_mode_ptr == 'q')
-					is_owner = true;
-				if ( *o_mode_ptr == 'a')
-					is_admin = true;
-				if ( *o_mode_ptr == 'o')
-					is_op = true;
-				if ( *o_mode_ptr == 'h')
-					is_halfop = true;
-				o_mode_ptr++;
-			}
+			if (Channel_UserHasMode(Channel, Client, 'q'))
+				is_owner = true;
+			if (Channel_UserHasMode(Channel, Client, 'a'))
+				is_admin = true;
+			if (Channel_UserHasMode(Channel, Client, 'o'))
+				is_op = true;
+			if (Channel_UserHasMode(Channel, Client, 'h'))
+				is_halfop = true;
 		}
 
 		/* Validate modes */
@@ -576,7 +558,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 		case 'z': /* Secure connections only */
 			if(!is_oper && !is_machine && !is_owner &&
 			   !is_admin && !is_op) {
-				connected = IRC_WriteStrClient(Origin,
+				connected = IRC_WriteErrClient(Origin,
 					ERR_CHANOPRIVSNEEDED_MSG,
 					Client_ID(Origin), Channel_Name(Channel));
 				goto chan_exit;
@@ -592,7 +574,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 			   is_admin || is_op || is_halfop)
 				x[0] = *mode_ptr;
 			else
-				connected = IRC_WriteStrClient(Origin,
+				connected = IRC_WriteErrClient(Origin,
 					ERR_CHANOPRIVSNEEDED_MSG,
 					Client_ID(Origin), Channel_Name(Channel));
 			break;
@@ -604,7 +586,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				    is_admin || is_op || is_halfop)
 					x[0] = *mode_ptr;
 				else
-					connected = IRC_WriteStrClient(Origin,
+					connected = IRC_WriteErrClient(Origin,
 						ERR_CHANOPRIVSNEEDED_MSG,
 						Client_ID(Origin),
 						Channel_Name(Channel));
@@ -620,7 +602,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 						sizeof(argadd));
 					x[0] = *mode_ptr;
 				} else {
-					connected = IRC_WriteStrClient(Origin,
+					connected = IRC_WriteErrClient(Origin,
 						ERR_CHANOPRIVSNEEDED_MSG,
 						Client_ID(Origin),
 						Channel_Name(Channel));
@@ -631,7 +613,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 #ifdef STRICT_RFC
 				/* Only send error message in "strict" mode,
 				 * this is how ircd2.11 and others behave ... */
-				connected = IRC_WriteStrClient(Origin,
+				IRC_SetPenalty(Origin, 2);
+				connected = IRC_WriteErrClient(Origin,
 					ERR_NEEDMOREPARAMS_MSG,
 					Client_ID(Origin), Req->command);
 #endif
@@ -646,7 +629,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				    is_admin || is_op || is_halfop)
 					x[0] = *mode_ptr;
 				else
-					connected = IRC_WriteStrClient(Origin,
+					connected = IRC_WriteErrClient(Origin,
 						ERR_CHANOPRIVSNEEDED_MSG,
 						Client_ID(Origin),
 						Channel_Name(Channel));
@@ -664,7 +647,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 						x[0] = *mode_ptr;
 					}
 				} else {
-					connected = IRC_WriteStrClient(Origin,
+					connected = IRC_WriteErrClient(Origin,
 						ERR_CHANOPRIVSNEEDED_MSG,
 						Client_ID(Origin),
 						Channel_Name(Channel));
@@ -675,7 +658,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 #ifdef STRICT_RFC
 				/* Only send error message in "strict" mode,
 				 * this is how ircd2.11 and others behave ... */
-				connected = IRC_WriteStrClient(Origin,
+				IRC_SetPenalty(Origin, 2);
+				connected = IRC_WriteErrClient(Origin,
 					ERR_NEEDMOREPARAMS_MSG,
 					Client_ID(Origin), Req->command);
 #endif
@@ -689,14 +673,14 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				if(is_oper || is_machine)
 					x[0] = 'O';
 				else
-					connected = IRC_WriteStrClient(Origin,
+					connected = IRC_WriteErrClient(Origin,
 						ERR_NOPRIVILEGES_MSG,
 						Client_ID(Origin));
 			} else if(is_oper || is_machine || is_owner ||
 				  is_admin || is_op)
 				x[0] = 'O';
 			else
-				connected = IRC_WriteStrClient(Origin,
+				connected = IRC_WriteErrClient(Origin,
 					ERR_CHANOPRIVSNEEDED_MSG,
 					Client_ID(Origin),
 					Channel_Name(Channel));
@@ -708,14 +692,14 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				if(is_oper || is_machine)
 					x[0] = 'P';
 				else
-					connected = IRC_WriteStrClient(Origin,
+					connected = IRC_WriteErrClient(Origin,
 						ERR_NOPRIVILEGES_MSG,
 						Client_ID(Origin));
 			} else if(is_oper || is_machine || is_owner ||
 				  is_admin || is_op)
 				x[0] = 'P';
 			else
-				connected = IRC_WriteStrClient(Origin,
+				connected = IRC_WriteErrClient(Origin,
 					ERR_CHANOPRIVSNEEDED_MSG,
 					Client_ID(Origin),
 					Channel_Name(Channel));
@@ -724,7 +708,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 		case 'q': /* Owner */
 		case 'a': /* Channel admin */
 			if(!is_oper && !is_machine && !is_owner && !is_admin) {
-				connected = IRC_WriteStrClient(Origin,
+				connected = IRC_WriteErrClient(Origin,
 					ERR_CHANOPPRIVTOOLOW_MSG,
 					Client_ID(Origin),
 					Channel_Name(Channel));
@@ -733,7 +717,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 		case 'o': /* Channel operator */
 			if(!is_oper && !is_machine && !is_owner &&
 			   !is_admin && !is_op) {
-				connected = IRC_WriteStrClient(Origin,
+				connected = IRC_WriteErrClient(Origin,
 					ERR_CHANOPRIVSNEEDED_MSG,
 					Client_ID(Origin),
 					Channel_Name(Channel));
@@ -742,7 +726,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 		case 'h': /* Half Op */
 			if(!is_oper && !is_machine && !is_owner &&
 			   !is_admin && !is_op) {
-				connected = IRC_WriteStrClient(Origin,
+				connected = IRC_WriteErrClient(Origin,
 					ERR_CHANOPRIVSNEEDED_MSG,
 					Client_ID(Origin),
 					Channel_Name(Channel));
@@ -756,12 +740,12 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 					if (client)
 						x[0] = *mode_ptr;
 					else
-						connected = IRC_WriteStrClient(Origin,
+						connected = IRC_WriteErrClient(Origin,
 							ERR_NOSUCHNICK_MSG,
 							Client_ID(Origin),
 							Req->argv[arg_arg]);
 				} else {
-					connected = IRC_WriteStrClient(Origin,
+					connected = IRC_WriteErrClient(Origin,
 						ERR_CHANOPRIVSNEEDED_MSG,
 						Client_ID(Origin),
 						Channel_Name(Channel));
@@ -776,7 +760,8 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				 * mode, because most other servers don't do
 				 * it as well and some clients send "wired"
 				 * MODE commands like "MODE #chan -ooo nick". */
-				connected = IRC_WriteStrClient(Origin,
+				IRC_SetPenalty(Origin, 2);
+				connected = IRC_WriteErrClient(Origin,
 					ERR_NEEDMOREPARAMS_MSG,
 					Client_ID(Origin), Req->command);
 #endif
@@ -801,7 +786,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 						Client, Channel,
 						Req->argv[arg_arg]);
 				} else {
-					connected = IRC_WriteStrClient(Origin,
+					connected = IRC_WriteErrClient(Origin,
 						ERR_CHANOPRIVSNEEDED_MSG,
 						Client_ID(Origin),
 						Channel_Name(Channel));
@@ -828,7 +813,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 				    "Unknown mode \"%c%c\" from \"%s\" on %s!?",
 				    set ? '+' : '-', *mode_ptr,
 				    Client_ID(Origin), Channel_Name(Channel));
-				connected = IRC_WriteStrClient(Origin,
+				connected = IRC_WriteErrClient(Origin,
 					ERR_UNKNOWNMODE_MSG,
 					Client_ID(Origin), *mode_ptr,
 					Channel_Name(Channel));
@@ -851,12 +836,11 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 
 		/* Validate target client */
 		if (client && (!Channel_IsMemberOf(Channel, client))) {
-			if (!IRC_WriteStrClient
-			    (Origin, ERR_USERNOTINCHANNEL_MSG,
-			     Client_ID(Origin), Client_ID(client),
-			     Channel_Name(Channel)))
+			if (!IRC_WriteErrClient(Origin, ERR_USERNOTINCHANNEL_MSG,
+						Client_ID(Origin),
+						Client_ID(client),
+						Channel_Name(Channel)))
 				break;
-
 			continue;
 		}
 
@@ -941,31 +925,35 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 	return connected;
 } /* Channel_Mode */
 
-
+/**
+ * Handler for the IRC "AWAY" command.
+ *
+ * @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_AWAY( CLIENT *Client, REQUEST *Req )
 {
-	assert( Client != NULL );
-	assert( Req != NULL );
-
-	if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
-
-	if(( Req->argc == 1 ) && (Req->argv[0][0] ))
-	{
-		Client_SetAway( Client, Req->argv[0] );
-		Client_ModeAdd( Client, 'a' );
-		IRC_WriteStrServersPrefix( Client, Client, "MODE %s :+a", Client_ID( Client ));
-		return IRC_WriteStrClient( Client, RPL_NOWAWAY_MSG, Client_ID( Client ));
-	}
-	else
-	{
-		Client_ModeDel( Client, 'a' );
-		IRC_WriteStrServersPrefix( Client, Client, "MODE %s :-a", Client_ID( Client ));
-		return IRC_WriteStrClient( Client, RPL_UNAWAY_MSG, Client_ID( Client ));
+	assert (Client != NULL);
+	assert (Req != NULL);
+
+	if (Req->argc == 1 && Req->argv[0][0]) {
+		Client_SetAway(Client, Req->argv[0]);
+		Client_ModeAdd(Client, 'a');
+		IRC_WriteStrServersPrefix(Client, Client, "MODE %s :+a",
+					  Client_ID( Client));
+		return IRC_WriteStrClient(Client, RPL_NOWAWAY_MSG,
+					  Client_ID( Client));
+	} else {
+		Client_ModeDel(Client, 'a');
+		IRC_WriteStrServersPrefix(Client, Client, "MODE %s :-a",
+					  Client_ID( Client));
+		return IRC_WriteStrClient(Client, RPL_UNAWAY_MSG,
+					  Client_ID( Client));
 	}
 } /* IRC_AWAY */
 
-
 /**
  * Add entries to channel invite, ban and exception lists.
  *
@@ -980,7 +968,7 @@ static bool
 Add_To_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
 	    const char *Pattern)
 {
-	const char *mask;
+	char mask[MASK_LEN];
 	struct list_head *list = NULL;
 	long int current_count;
 
@@ -989,7 +977,7 @@ Add_To_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
 	assert(Pattern != NULL);
 	assert(what == 'I' || what == 'b' || what == 'e');
 
-	mask = Lists_MakeMask(Pattern);
+	Lists_MakeMask(Pattern, mask, sizeof(mask));
 	current_count = Lists_Count(Channel_GetListInvites(Channel))
 			+ Lists_Count(Channel_GetListExcepts(Channel))
 			+ Lists_Count(Channel_GetListBans(Channel));
@@ -1010,7 +998,7 @@ Add_To_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
 		return CONNECTED;
 	if (Client_Type(Client) == CLIENT_USER &&
 	    current_count >= MAX_HNDL_CHANNEL_LISTS)
-		return IRC_WriteStrClient(Client, ERR_LISTFULL_MSG,
+		return IRC_WriteErrClient(Client, ERR_LISTFULL_MSG,
 					  Client_ID(Client),
 					  Channel_Name(Channel), mask,
 					  MAX_HNDL_CHANNEL_LISTS);
@@ -1032,9 +1020,8 @@ Add_To_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
 	return Send_ListChange(true, what, Prefix, Client, Channel, mask);
 }
 
-
 /**
- * Delete entries from channel invite, ban and exeption lists.
+ * Delete entries from 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.
@@ -1047,7 +1034,7 @@ static bool
 Del_From_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
 	      const char *Pattern)
 {
-	const char *mask;
+	char mask[MASK_LEN];
 	struct list_head *list = NULL;
 
 	assert(Client != NULL);
@@ -1055,7 +1042,7 @@ Del_From_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
 	assert(Pattern != NULL);
 	assert(what == 'I' || what == 'b' || what == 'e');
 
-	mask = Lists_MakeMask(Pattern);
+	Lists_MakeMask(Pattern, mask, sizeof(mask));
 
 	switch (what) {
 		case 'I':
@@ -1076,7 +1063,6 @@ Del_From_List(char what, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel,
 	return Send_ListChange(false, what, Prefix, Client, Channel, mask);
 }
 
-
 /**
  * Send information about changed channel invite/ban/exception lists to clients.
  *
@@ -1114,5 +1100,4 @@ Send_ListChange(const bool IsAdd, const char ModeChar, CLIENT *Prefix,
 	return ok;
 } /* Send_ListChange */
 
-
 /* -eof- */

+ 48 - 46
src/ngircd/irc-op.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2005 by Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -24,6 +24,7 @@
 #include "defines.h"
 #include "conn.h"
 #include "channel.h"
+#include "irc-macros.h"
 #include "irc-write.h"
 #include "lists.h"
 #include "log.h"
@@ -33,6 +34,7 @@
 #include "exp.h"
 #include "irc-op.h"
 
+/* Local functions */
 
 static bool
 try_kick(CLIENT *peer, CLIENT* from, const char *nick, const char *channel,
@@ -41,13 +43,22 @@ try_kick(CLIENT *peer, CLIENT* from, const char *nick, const char *channel,
 	CLIENT *target = Client_Search(nick);
 
 	if (!target)
-		return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG, Client_ID(from), nick);
+		return IRC_WriteErrClient(from, ERR_NOSUCHNICK_MSG,
+					  Client_ID(from), nick);
 
 	Channel_Kick(peer, target, from, channel, reason);
 	return true;
 }
 
+/* Global functions */
 
+/**
+ * Handler for the IRC command "KICK".
+ *
+ * @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_KICK(CLIENT *Client, REQUEST *Req)
 {
@@ -60,9 +71,7 @@ IRC_KICK(CLIENT *Client, REQUEST *Req)
 	assert( Client != NULL );
 	assert( Req != NULL );
 
-	if ((Req->argc < 2) || (Req->argc > 3))
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					Client_ID(Client), Req->command);
+	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
 
 	while (*itemList) {
 		if (*itemList == ',') {
@@ -81,15 +90,6 @@ IRC_KICK(CLIENT *Client, REQUEST *Req)
 		itemList++;
 	}
 
-	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);
-
 	reason = Req->argc == 3 ? Req->argv[2] : Client_ID(from);
 	currentNick = Req->argv[1];
 	currentChannel = Req->argv[0];
@@ -122,13 +122,20 @@ IRC_KICK(CLIENT *Client, REQUEST *Req)
 			nickCount--;
 		}
 	} else {
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					Client_ID(Client), Req->command);
 	}
 	return true;
 } /* IRC_KICK */
 
-
+/**
+ * Handler for the IRC command "INVITE".
+ *
+ * @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_INVITE(CLIENT *Client, REQUEST *Req)
 {
@@ -140,51 +147,47 @@ IRC_INVITE(CLIENT *Client, REQUEST *Req)
 	assert( Client != NULL );
 	assert( Req != NULL );
 
-	if (Req->argc != 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					Client_ID(Client), Req->command);
-
-	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);
+	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
 
 	/* Search user */
 	target = Client_Search(Req->argv[0]);
 	if (!target || (Client_Type(target) != CLIENT_USER))
-		return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
-				Client_ID(Client), Req->argv[0]);
+		return IRC_WriteErrClient(from, ERR_NOSUCHNICK_MSG,
+					  Client_ID(Client), Req->argv[0]);
 
 	chan = Channel_Search(Req->argv[1]);
 	if (chan) {
 		/* Channel exists. Is the user a valid member of the channel? */
 		if (!Channel_IsMemberOf(chan, from))
-			return IRC_WriteStrClient(from, ERR_NOTONCHANNEL_MSG, Client_ID(Client), Req->argv[1]);
+			return IRC_WriteErrClient(from, ERR_NOTONCHANNEL_MSG,
+						  Client_ID(Client),
+						  Req->argv[1]);
 
 		/* Is the channel "invite-disallow"? */
-		if (strchr(Channel_Modes(chan), 'V'))
-			return IRC_WriteStrClient(from, ERR_NOINVITE_MSG,
-				Client_ID(from), Channel_Name(chan));
+		if (Channel_HasMode(chan, 'V'))
+			return IRC_WriteErrClient(from, ERR_NOINVITE_MSG,
+						  Client_ID(from),
+						  Channel_Name(chan));
 
 		/* Is the channel "invite-only"? */
-		if (strchr(Channel_Modes(chan), 'i')) {
+		if (Channel_HasMode(chan, 'i')) {
 			/* Yes. The user must be channel owner/admin/operator/halfop! */
-			if (!strchr(Channel_UserModes(chan, from), 'q') &&
-			    !strchr(Channel_UserModes(chan, from), 'a') &&
-			    !strchr(Channel_UserModes(chan, from), 'o') &&
-			    !strchr(Channel_UserModes(chan, from), 'h'))
-				return IRC_WriteStrClient(from, ERR_CHANOPRIVSNEEDED_MSG,
-						Client_ID(from), Channel_Name(chan));
+			if (!Channel_UserHasMode(chan, from, 'q') &&
+			    !Channel_UserHasMode(chan, from, 'a') &&
+			    !Channel_UserHasMode(chan, from, 'o') &&
+			    !Channel_UserHasMode(chan, from, 'h'))
+				return IRC_WriteErrClient(from,
+							  ERR_CHANOPRIVSNEEDED_MSG,
+							  Client_ID(from),
+							  Channel_Name(chan));
 			remember = true;
 		}
 
 		/* Is the target user already member of the channel? */
 		if (Channel_IsMemberOf(chan, target))
-			return IRC_WriteStrClient(from, ERR_USERONCHANNEL_MSG,
-					Client_ID(from), Req->argv[0], Req->argv[1]);
+			return IRC_WriteErrClient(from, ERR_USERONCHANNEL_MSG,
+						  Client_ID(from),
+						  Req->argv[0], Req->argv[1]);
 
 		/* If the target user is banned on that channel: remember invite */
 		if (Lists_Check(Channel_GetListBans(chan), target))
@@ -197,8 +200,8 @@ IRC_INVITE(CLIENT *Client, REQUEST *Req)
 		}
 	}
 
-	LogDebug("User \"%s\" invites \"%s\" to \"%s\" ...", Client_Mask(from), Req->argv[0], Req->argv[1]);
-
+	LogDebug("User \"%s\" invites \"%s\" to \"%s\" ...", Client_Mask(from),
+		 Req->argv[0], Req->argv[1]);
 
 	/*
 	 * RFC 2812 says:
@@ -218,7 +221,7 @@ IRC_INVITE(CLIENT *Client, REQUEST *Req)
 			Client_ID(from), Req->argv[0], colon_if_necessary, Req->argv[1]))
 			return DISCONNECTED;
 
-		if (strchr(Client_Modes(target), 'a') &&
+		if (Client_HasMode(target, 'a') &&
 			!IRC_WriteStrClient(from, RPL_AWAY_MSG, Client_ID(from),
 					Client_ID(target), Client_Away(target)))
 				return DISCONNECTED;
@@ -226,5 +229,4 @@ IRC_INVITE(CLIENT *Client, REQUEST *Req)
 	return CONNECTED;
 } /* IRC_INVITE */
 
-
 /* -eof- */

+ 56 - 80
src/ngircd/irc-oper.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -28,11 +28,14 @@
 #include "conf.h"
 #include "channel.h"
 #include "class.h"
+#include "parse.h"
+#include "irc.h"
+#include "irc-macros.h"
 #include "irc-write.h"
+#include "lists.h"
 #include "log.h"
 #include "match.h"
 #include "messages.h"
-#include "parse.h"
 #include "op.h"
 
 #include <exp.h>
@@ -47,16 +50,13 @@ Bad_OperPass(CLIENT *Client, char *errtoken, char *errmsg)
 {
 	Log(LOG_WARNING, "Got invalid OPER from \"%s\": \"%s\" -- %s",
 	    Client_Mask(Client), errtoken, errmsg);
-	IRC_SetPenalty(Client, 3);
-	return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
+	return IRC_WriteErrClient(Client, ERR_PASSWDMISMATCH_MSG,
 				  Client_ID(Client));
 } /* 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.
@@ -70,10 +70,6 @@ IRC_OPER( CLIENT *Client, REQUEST *Req )
 	assert( Client != NULL );
 	assert( Req != NULL );
 
-	if (Req->argc != 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	len = array_length(&Conf_Opers, sizeof(*op));
 	op = array_start(&Conf_Opers);
 	for (i = 0; i < len && strcmp(op[i].name, Req->argv[0]); i++)
@@ -96,20 +92,16 @@ IRC_OPER( CLIENT *Client, REQUEST *Req )
 					  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));
+	Log(LOG_NOTICE|LOG_snotice,
+	    "Got valid OPER for \"%s\" from \"%s\", user is an IRC operator now.",
+	    Req->argv[0], Client_Mask(Client));
 
-	Client_SetOperByMe(Client, true);
 	return IRC_WriteStrClient(Client, RPL_YOUREOPER_MSG, Client_ID(Client));
 } /* 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.
@@ -128,15 +120,6 @@ IRC_DIE(CLIENT * Client, REQUEST * Req)
 	if (!Op_Check(Client, Req))
 		return Op_NoPrivileges(Client, Req);
 
-	/* Bad number of parameters? */
-#ifdef STRICT_RFC
-	if (Req->argc != 0)
-#else
-	if (Req->argc > 1)
-#endif
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	/* Is a message given? */
 	if (Req->argc > 0) {
 		c = Conn_First();
@@ -159,8 +142,6 @@ IRC_DIE(CLIENT * Client, REQUEST * Req)
 /**
  * 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.
@@ -176,11 +157,6 @@ IRC_REHASH( CLIENT *Client, REQUEST *Req )
 	if (!Op_Check(Client, Req))
 		return Op_NoPrivileges(Client, Req);
 
-	/* Bad number of parameters? */
-	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));
 	IRC_WriteStrClient(Client, RPL_REHASHING_MSG, Client_ID(Client));
@@ -193,8 +169,6 @@ IRC_REHASH( CLIENT *Client, REQUEST *Req )
 /**
  * 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.
@@ -210,11 +184,6 @@ IRC_RESTART( CLIENT *Client, REQUEST *Req )
 	if (!Op_Check(Client, Req))
 		return Op_NoPrivileges(Client, Req);
 
-	/* Bad number of parameters? */
-	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));
 	NGIRCd_SignalRestart = true;
@@ -225,8 +194,6 @@ IRC_RESTART( CLIENT *Client, REQUEST *Req )
 /**
  * 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.
@@ -239,20 +206,24 @@ IRC_CONNECT(CLIENT * Client, REQUEST * Req)
 	assert(Client != NULL);
 	assert(Req != NULL);
 
-	if (Client_Type(Client) != CLIENT_SERVER
-	    && !Client_HasMode(Client, 'o'))
-		return Op_NoPrivileges(Client, Req);
-
 	/* Bad number of parameters? */
 	if (Req->argc != 1 && Req->argc != 2 && Req->argc != 3 &&
-	    Req->argc != 5 && Req->argc != 6)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+	    Req->argc != 5 && Req->argc != 6) {
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);
+	}
 
 	/* Invalid port number? */
-	if ((Req->argc > 1) && atoi(Req->argv[1]) < 1)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+	if ((Req->argc > 1) && atoi(Req->argv[1]) < 1) {
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);
+	}
+
+	if (Client_Type(Client) != CLIENT_SERVER
+	    && !Client_HasMode(Client, 'o'))
+		return Op_NoPrivileges(Client, Req);
 
 	from = Client;
 	target = Client_ThisServer();
@@ -262,14 +233,14 @@ IRC_CONNECT(CLIENT * Client, REQUEST * Req)
 		if (Client_Type(Client) == CLIENT_SERVER && Req->prefix)
 			from = Client_Search(Req->prefix);
 		if (! from)
-			return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
-					Client_ID(Client), Req->prefix);
+			return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
+						  Client_ID(Client), Req->prefix);
 
 		target = (Req->argc == 3) ? Client_Search(Req->argv[2])
 					  : Client_Search(Req->argv[5]);
 		if (! target || Client_Type(target) != CLIENT_SERVER)
-			return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
-					Client_ID(from), Req->argv[0]);
+			return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
+						  Client_ID(from), Req->argv[0]);
 	}
 
 	if (target != Client_ThisServer()) {
@@ -292,7 +263,7 @@ IRC_CONNECT(CLIENT * Client, REQUEST * Req)
 	switch (Req->argc) {
 	case 1:
 		if (!Conf_EnablePassiveServer(Req->argv[0]))
-			return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
+			return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
 						  Client_ID(from),
 						  Req->argv[0]);
 		break;
@@ -301,7 +272,7 @@ IRC_CONNECT(CLIENT * Client, REQUEST * Req)
 		/* Connect configured server */
 		if (!Conf_EnableServer
 		    (Req->argv[0], (UINT16) atoi(Req->argv[1])))
-			return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
+			return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
 						  Client_ID(from),
 						  Req->argv[0]);
 		break;
@@ -310,7 +281,7 @@ IRC_CONNECT(CLIENT * Client, REQUEST * Req)
 		if (!Conf_AddServer
 		    (Req->argv[0], (UINT16) atoi(Req->argv[1]), Req->argv[2],
 		     Req->argv[3], Req->argv[4]))
-			return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
+			return IRC_WriteErrClient(from, ERR_NOSUCHSERVER_MSG,
 						  Client_ID(from),
 						  Req->argv[0]);
 	}
@@ -346,11 +317,6 @@ IRC_DISCONNECT(CLIENT * Client, REQUEST * Req)
 	if (!Op_Check(Client, Req))
 		return Op_NoPrivileges(Client, Req);
 
-	/* Bad number of parameters? */
-	if (Req->argc != 1)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	IRC_SendWallops(Client_ThisServer(), Client_ThisServer(),
 			"Received DISCONNECT %s from %s",
 			Req->argv[0], Client_ID(Client));
@@ -364,7 +330,7 @@ IRC_DISCONNECT(CLIENT * Client, REQUEST * Req)
 
 	/* Disconnect configured server */
 	if (!Conf_DisableServer(Req->argv[0]))
-		return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
 					  Client_ID(Client), Req->argv[0]);
 
 	/* Are we still connected or were we killed, too? */
@@ -377,8 +343,6 @@ IRC_DISCONNECT(CLIENT * Client, REQUEST * Req)
 /**
  * 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.
@@ -391,15 +355,10 @@ IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
 	assert( Client != NULL );
 	assert( Req != NULL );
 
-	if (Req->argc != 1)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	switch (Client_Type(Client)) {
 	case CLIENT_USER:
-		if (!Client_OperByMe(Client))
-			return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG,
-						  Client_ID(Client));
+		if (!Op_Check(Client, Req))
+			return Op_NoPrivileges(Client, Req);
 		from = Client;
 		break;
 	case CLIENT_SERVER:
@@ -410,7 +369,7 @@ IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
 	}
 
 	if (!from)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
 					  Client_ID(Client), Req->prefix);
 
 	IRC_SendWallops(Client, from, "%s", Req->argv[0]);
@@ -427,22 +386,25 @@ IRC_WALLOPS( CLIENT *Client, REQUEST *Req )
 GLOBAL bool
 IRC_xLINE(CLIENT *Client, REQUEST *Req)
 {
-	CLIENT *from;
+	CLIENT *from, *c, *c_next;
+	char reason[COMMAND_LEN], class_c;
+	struct list_head *list;
 	int class;
-	char class_c;
 
 	assert(Client != NULL);
 	assert(Req != NULL);
 
+	/* Bad number of parameters? */
+	if (Req->argc != 1 && Req->argc != 3) {
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
+	}
+
 	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':
@@ -487,6 +449,20 @@ IRC_xLINE(CLIENT *Client, REQUEST *Req)
 						Req->argv[0], Req->argv[1],
 						Req->argv[2]);
 			}
+
+			/* Check currently connected clients */
+			snprintf(reason, sizeof(reason), "%c-Line by \"%s\": \"%s\"",
+				 class_c, Client_ID(from), Req->argv[2]);
+			list = Class_GetList(class);
+			c = Client_First();
+			while (c) {
+				c_next = Client_Next(c);
+				if ((class == CLASS_GLINE || Client_Conn(c) > NONE)
+				    && Lists_Check(list, c))
+					IRC_KillClient(Client, NULL,
+						       Client_ID(c), reason);
+				c = c_next;
+			}
 		}
 	}
 

+ 74 - 43
src/ngircd/irc-server.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2007 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -29,7 +29,6 @@
 #include "conn-zip.h"
 #include "conf.h"
 #include "channel.h"
-#include "irc-write.h"
 #include "lists.h"
 #include "log.h"
 #include "messages.h"
@@ -37,20 +36,24 @@
 #include "numeric.h"
 #include "ngircd.h"
 #include "irc-info.h"
+#include "irc-macros.h"
+#include "irc-write.h"
 #include "op.h"
 
 #include "exp.h"
 #include "irc-server.h"
 
-
 /**
- * Handler for the IRC command "SERVER".
- * See RFC 2813 section 4.1.2.
+ * Handler for the IRC "SERVER" command.
+ *
+ * @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_SERVER( CLIENT *Client, REQUEST *Req )
 {
-	char str[LINE_LEN];
+	char str[100];
 	CLIENT *from, *c;
 	int i;
 
@@ -59,7 +62,7 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
 
 	/* Return an error if this is not a local client */
 	if (Client_Conn(Client) <= NONE)
-		return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
+		return IRC_WriteErrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
 					  Client_ID(Client), Req->command);
 
 	if (Client_Type(Client) == CLIENT_GOTPASS ||
@@ -69,17 +72,19 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
 		LogDebug("Connection %d: got SERVER command (new server link) ...",
 			Client_Conn(Client));
 
-		if (Req->argc != 2 && Req->argc != 3)
-			return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+		if (Req->argc != 2 && Req->argc != 3) {
+			IRC_SetPenalty(Client, 2);
+			return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 						  Client_ID(Client),
 						  Req->command);
+		}
 
 		/* Get configuration index of new remote server ... */
 		for (i = 0; i < MAX_SERVERS; i++)
 			if (strcasecmp(Req->argv[0], Conf_Server[i].name) == 0)
 				break;
 
-		/* Makre sure the remote server is configured here */
+		/* Make sure the remote server is configured here */
 		if (i >= MAX_SERVERS) {
 			Log(LOG_ERR,
 			    "Connection %d: Server \"%s\" not configured here!",
@@ -149,17 +154,17 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
 		Client_SetType(Client, CLIENT_UNKNOWNSERVER);
 
 #ifdef ZLIB
-		if (strchr(Client_Flags(Client), 'Z')
+		if (Client_HasFlag(Client, 'Z')
 		    && !Zip_InitConn(Client_Conn(Client))) {
 			Conn_Close(Client_Conn(Client),
-				   "Can't inizialize compression (zlib)!",
+				   "Can't initialize compression (zlib)!",
 				   NULL, false );
 			return DISCONNECTED;
 		}
 #endif
 
 #ifdef IRCPLUS
-		if (strchr(Client_Flags(Client), 'H')) {
+		if (Client_HasFlag(Client, 'H')) {
 			LogDebug("Peer supports IRC+ extended server handshake ...");
 			if (!IRC_Send_ISUPPORT(Client))
 				return DISCONNECTED;
@@ -180,41 +185,70 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req )
 	{
 		/* New server is being introduced to the network */
 
-		if( Req->argc != 4 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+		if (Req->argc != 4) {
+			IRC_SetPenalty(Client, 2);
+			return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+						  Client_ID(Client), Req->command);
+		}
 
 		/* check for existing server with same ID */
-		if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED;
+		if (!Client_CheckID(Client, Req->argv[0]))
+			return DISCONNECTED;
 
 		from = Client_Search( Req->prefix );
-		if( ! from )
-		{
+		if (! from) {
 			/* Uh, Server, that introduced the new server is unknown?! */
-			Log( LOG_ALERT, "Unknown ID in prefix of SERVER: \"%s\"! (on connection %d)", Req->prefix, Client_Conn( Client ));
-			Conn_Close( Client_Conn( Client ), NULL, "Unknown ID in prefix of SERVER", true);
+			Log(LOG_ALERT,
+			    "Unknown ID in prefix of SERVER: \"%s\"! (on connection %d)",
+			    Req->prefix, Client_Conn(Client));
+			Conn_Close(Client_Conn(Client), NULL,
+				   "Unknown ID in prefix of SERVER", true);
 			return DISCONNECTED;
 		}
 
-		c = Client_NewRemoteServer(Client, Req->argv[0], from, atoi(Req->argv[1]), atoi(Req->argv[2]), Req->argv[3], true);
+		c = Client_NewRemoteServer(Client, Req->argv[0], from,
+					   atoi(Req->argv[1]), atoi(Req->argv[2]),
+					   Req->argv[3], true);
 		if (!c) {
-			Log( LOG_ALERT, "Can't create client structure for server! (on connection %d)", Client_Conn( Client ));
-			Conn_Close( Client_Conn( Client ), NULL, "Can't allocate client structure for remote server", true);
+			Log(LOG_ALERT,
+			    "Can't create client structure for server! (on connection %d)",
+			    Client_Conn(Client));
+			Conn_Close(Client_Conn(Client), NULL,
+				   "Can't allocate client structure for remote server",
+				   true);
 			return DISCONNECTED;
 		}
 
-		if(( Client_Hops( c ) > 1 ) && ( Req->prefix[0] )) snprintf( str, sizeof( str ), "connected to %s, ", Client_ID( from ));
-		else strcpy( str, "" );
-		Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" registered (via %s, %s%d hop%s).", Client_ID( c ), Client_ID( Client ), str, Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" );
+		if (Client_Hops(c) > 1 && Req->prefix[0])
+			snprintf(str, sizeof(str), "connected to %s, ",
+				 Client_ID(from));
+		else
+			strcpy(str, "");
+		Log(LOG_NOTICE|LOG_snotice,
+		    "Server \"%s\" registered (via %s, %s%d hop%s).",
+		    Client_ID(c), Client_ID(Client), str, Client_Hops(c),
+		    Client_Hops(c) > 1 ? "s": "" );
 
 		/* notify other servers */
-		IRC_WriteStrServersPrefix( Client, from, "SERVER %s %d %d :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_MyToken( c ), Client_Info( c ));
+		IRC_WriteStrServersPrefix(Client, from, "SERVER %s %d %d :%s",
+					  Client_ID(c), Client_Hops(c) + 1,
+					  Client_MyToken(c), Client_Info(c));
 
 		return CONNECTED;
-	} else
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+	} else {
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);
+	}
 } /* IRC_SERVER */
 
-
+/*
+ * Handler for the IRC "NJOIN" command.
+ *
+ * @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_NJOIN( CLIENT *Client, REQUEST *Req )
 {
@@ -226,8 +260,6 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
 	assert( Client != NULL );
 	assert( Req != NULL );
 
-	if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
-
 	strlcpy( nick_in, Req->argv[1], sizeof( nick_in ));
 	strcpy( nick_out, "" );
 
@@ -288,15 +320,19 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
 	}
 
 	/* forward to other servers */
-	if( nick_out[0] != '\0' ) IRC_WriteStrServersPrefix( Client, Client_ThisServer( ), "NJOIN %s :%s", Req->argv[0], nick_out );
+	if (nick_out[0] != '\0')
+		IRC_WriteStrServersPrefix(Client, Client_ThisServer(),
+					  "NJOIN %s :%s", Req->argv[0], nick_out);
 
 	return CONNECTED;
 } /* IRC_NJOIN */
 
-
 /**
- * Handler for the IRC command "SQUIT".
- * See RFC 2813 section 4.1.2 and RFC 2812 section 3.1.8.
+ * Handler for the IRC "SQUIT" command.
+ *
+ * @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_SQUIT(CLIENT * Client, REQUEST * Req)
@@ -313,11 +349,6 @@ IRC_SQUIT(CLIENT * Client, REQUEST * Req)
 	    && !Client_HasMode(Client, 'o'))
 		return Op_NoPrivileges(Client, Req);
 
-	/* Bad number of arguments? */
-	if (Req->argc != 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
-
 	if (Client_Type(Client) == CLIENT_SERVER && Req->prefix) {
 		from = Client_Search(Req->prefix);
 		if (Client_Type(from) != CLIENT_SERVER
@@ -326,7 +357,7 @@ IRC_SQUIT(CLIENT * Client, REQUEST * Req)
 	} else
 		from = Client;
 	if (!from)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
 					  Client_ID(Client), Req->prefix);
 
 	if (Client_Type(Client) == CLIENT_USER)
@@ -352,7 +383,7 @@ IRC_SQUIT(CLIENT * Client, REQUEST * Req)
 
 	if (Req->argv[1][0])
 		if (Client_NextHop(from) != Client || con > NONE)
-			snprintf(msg, sizeof(msg), "%s (SQUIT from %s)",
+			snprintf(msg, sizeof(msg), "\"%s\" (SQUIT from %s)",
 				 Req->argv[1], Client_ID(from));
 		else
 			strlcpy(msg, Req->argv[1], sizeof(msg));
@@ -385,7 +416,7 @@ IRC_SQUIT(CLIENT * Client, REQUEST * Req)
 			logmsg[0] = '\0';
 			if (!strchr(msg, '('))
 				snprintf(logmsg, sizeof(logmsg),
-					 "%s (SQUIT from %s)", Req->argv[1],
+					 "\"%s\" (SQUIT from %s)", Req->argv[1],
 					 Client_ID(from));
 			Client_Destroy(target, logmsg[0] ? logmsg : msg,
 				       msg, false);

+ 193 - 84
src/ngircd/irc-write.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -33,17 +33,58 @@
 #include "exp.h"
 #include "irc-write.h"
 
-
 #define SEND_TO_USER 1
 #define SEND_TO_SERVER 2
 
-
 static const char *Get_Prefix PARAMS((CLIENT *Target, CLIENT *Client));
 static void cb_writeStrServersPrefixFlag PARAMS((CLIENT *Client,
 					 CLIENT *Prefix, void *Buffer));
-static bool Send_Marked_Connections PARAMS((CLIENT *Prefix, const char *Buffer));
+static void Send_Marked_Connections PARAMS((CLIENT *Prefix, const char *Buffer));
 
+/**
+ * Send an error message to a client and enforce a penalty time.
+ *
+ * @param Client The target client.
+ * @param Format Format string.
+ * @return CONNECTED or DISCONNECTED.
+ */
+#ifdef PROTOTYPES
+GLOBAL bool
+IRC_WriteErrClient( CLIENT *Client, const char *Format, ... )
+#else
+GLOBAL bool
+IRC_WriteErrClient( Client, Format, va_alist )
+CLIENT *Client;
+const char *Format;
+va_dcl
+#endif
+{
+	char buffer[1000];
+	va_list ap;
+
+	assert(Client != NULL);
+	assert(Format != NULL);
+
+#ifdef PROTOTYPES
+	va_start(ap, Format);
+#else
+	va_start(ap);
+#endif
+	vsnprintf(buffer, 1000, Format, ap);
+	va_end(ap);
+
+	IRC_SetPenalty(Client, 2);
+	return IRC_WriteStrClientPrefix(Client, Client_ThisServer(),
+					"%s", buffer);
+}
 
+/**
+ * Send a message to a client.
+ *
+ * @param Client The target client.
+ * @param Format Format string.
+ * @return CONNECTED or DISCONNECTED.
+ */
 #ifdef PROTOTYPES
 GLOBAL bool
 IRC_WriteStrClient( CLIENT *Client, const char *Format, ... )
@@ -56,27 +97,31 @@ va_dcl
 #endif
 {
 	char buffer[1000];
-	bool ok = CONNECTED;
 	va_list ap;
 
-	assert( Client != NULL );
-	assert( Format != NULL );
+	assert(Client != NULL);
+	assert(Format != NULL);
 
 #ifdef PROTOTYPES
-	va_start( ap, Format );
+	va_start(ap, Format);
 #else
-	va_start( ap );
+	va_start(ap);
 #endif
-	vsnprintf( buffer, 1000, Format, ap );
-	va_end( ap );
-
-	/* to the client itself */
-	ok = IRC_WriteStrClientPrefix( Client, Client_ThisServer( ), "%s", buffer );
-
-	return ok;
-} /* IRC_WriteStrClient */
+	vsnprintf(buffer, 1000, Format, ap);
+	va_end(ap);
 
+	return IRC_WriteStrClientPrefix(Client, Client_ThisServer(),
+					"%s", buffer);
+}
 
+/**
+ * Send a message to a client using a specific prefix.
+ *
+ * @param Client The target client.
+ * @param Prefix The prefix to use.
+ * @param Format Format string.
+ * @return CONNECTED or DISCONNECTED.
+ */
 #ifdef PROTOTYPES
 GLOBAL bool
 IRC_WriteStrClientPrefix(CLIENT *Client, CLIENT *Prefix, const char *Format, ...)
@@ -108,15 +153,24 @@ va_dcl
 
 	return Conn_WriteStr(Client_Conn(Client_NextHop(Client)), ":%s %s",
 			Get_Prefix(Client_NextHop(Client), Prefix), buffer);
-} /* IRC_WriteStrClientPrefix */
-
+}
 
+/**
+ * Send a message to all client in a channel.
+ *
+ * The message is only sent once per remote server.
+ *
+ * @param Client The sending client, excluded while forwarding the message.
+ * @param Channel The target channel.
+ * @param Remote If not set, the message is sent to local clients only.
+ * @param Format Format string.
+ */
 #ifdef PROTOTYPES
-GLOBAL bool
+GLOBAL void
 IRC_WriteStrChannel(CLIENT *Client, CHANNEL *Chan, bool Remote,
 		    const char *Format, ...)
 #else
-GLOBAL bool
+GLOBAL void
 IRC_WriteStrChannel(Client, Chan, Remote, Format, va_alist)
 CLIENT *Client;
 CHANNEL *Chan;
@@ -139,21 +193,27 @@ va_dcl
 	vsnprintf( buffer, 1000, Format, ap );
 	va_end( ap );
 
-	return IRC_WriteStrChannelPrefix( Client, Chan, Client_ThisServer( ), Remote, "%s", buffer );
-} /* IRC_WriteStrChannel */
-
-
+	IRC_WriteStrChannelPrefix(Client, Chan, Client_ThisServer(),
+				  Remote, "%s", buffer);
+}
 
 /**
- * send message to all clients in the same channel, but only send message
- * once per remote server.
+ * Send a message to all client in a channel using a specific prefix.
+ *
+ * The message is only sent once per remote server.
+ *
+ * @param Client The sending client, excluded while forwarding the message.
+ * @param Channel The target channel.
+ * @param Prefix The prefix to use.
+ * @param Remote If not set, the message is sent to local clients only.
+ * @param Format Format string.
  */
 #ifdef PROTOTYPES
-GLOBAL bool
+GLOBAL void
 IRC_WriteStrChannelPrefix(CLIENT *Client, CHANNEL *Chan, CLIENT *Prefix,
 			  bool Remote, const char *Format, ...)
 #else
-GLOBAL bool
+GLOBAL void
 IRC_WriteStrChannelPrefix(Client, Chan, Prefix, Remote, Format, va_alist)
 CLIENT *Client;
 CHANNEL *Chan;
@@ -185,29 +245,36 @@ va_dcl
 	Conn_ClearFlags( );
 
 	cl2chan = Channel_FirstMember( Chan );
-	while( cl2chan )
-	{
+	while(cl2chan) {
 		c = Channel_GetClient( cl2chan );
-		if( ! Remote )
-		{
-			if( Client_Conn( c ) <= NONE ) c = NULL;
-			else if( Client_Type( c ) == CLIENT_SERVER ) c = NULL;
+		if (!Remote) {
+			if (Client_Conn(c) <= NONE)
+				c = NULL;
+			else if(Client_Type(c) == CLIENT_SERVER)
+				c = NULL;
 		}
-		if( c ) c = Client_NextHop( c );
+		if(c)
+			c = Client_NextHop(c);
 
-		if( c && ( c != Client ))
-		{
+		if(c && c != Client) {
 			/* Ok, another Client */
-			conn = Client_Conn( c );
-			if( Client_Type( c ) == CLIENT_SERVER )	Conn_SetFlag( conn, SEND_TO_SERVER );
-			else Conn_SetFlag( conn, SEND_TO_USER );
+			conn = Client_Conn(c);
+			if (Client_Type(c) == CLIENT_SERVER)
+				Conn_SetFlag(conn, SEND_TO_SERVER);
+			else
+				Conn_SetFlag(conn, SEND_TO_USER);
 		}
-		cl2chan = Channel_NextMember( Chan, cl2chan );
+		cl2chan = Channel_NextMember(Chan, cl2chan);
 	}
-	return Send_Marked_Connections(Prefix, buffer);
-} /* IRC_WriteStrChannelPrefix */
-
+	Send_Marked_Connections(Prefix, buffer);
+}
 
+/**
+ * Send a message to all the servers in the network.
+ *
+ * @param Client The sending client, excluded while forwarding the message.
+ * @param Format Format string.
+ */
 #ifdef PROTOTYPES
 GLOBAL void
 IRC_WriteStrServers(CLIENT *ExceptOf, const char *Format, ...)
@@ -232,10 +299,16 @@ va_dcl
 	vsnprintf( buffer, 1000, Format, ap );
 	va_end( ap );
 
-	IRC_WriteStrServersPrefix( ExceptOf, Client_ThisServer( ), "%s", buffer );
-} /* IRC_WriteStrServers */
-
+	IRC_WriteStrServersPrefix(ExceptOf, Client_ThisServer(), "%s", buffer);
+}
 
+/**
+ * Send a message to all the servers in the network using a specific prefix.
+ *
+ * @param Client The sending client, excluded while forwarding the message.
+ * @param Prefix The prefix to use.
+ * @param Format Format string.
+ */
 #ifdef PROTOTYPES
 GLOBAL void
 IRC_WriteStrServersPrefix(CLIENT *ExceptOf, CLIENT *Prefix,
@@ -264,9 +337,17 @@ va_dcl
 	va_end( ap );
 
 	IRC_WriteStrServersPrefixFlag( ExceptOf, Prefix, '\0', "%s", buffer );
-} /* IRC_WriteStrServersPrefix */
-
+}
 
+/**
+ * Send a message to all the servers in the network using a specific prefix
+ * and matching a "client flag".
+ *
+ * @param Client The sending client, excluded while forwarding the message.
+ * @param Prefix The prefix to use.
+ * @param Flag Client flag that must be set on the target.
+ * @param Format Format string.
+ */
 #ifdef PROTOTYPES
 GLOBAL void
 IRC_WriteStrServersPrefixFlag(CLIENT *ExceptOf, CLIENT *Prefix, char Flag,
@@ -297,9 +378,18 @@ va_dcl
 
 	IRC_WriteStrServersPrefixFlag_CB(ExceptOf, Prefix, Flag,
 					 cb_writeStrServersPrefixFlag, buffer);
-} /* IRC_WriteStrServersPrefixFlag */
-
+}
 
+/**
+ * Send a message to all the servers in the network using a specific prefix
+ * and matching a "client flag" using a callback function.
+ *
+ * @param Client The sending client, excluded while forwarding the message.
+ * @param Prefix The prefix to use.
+ * @param Flag Client flag that must be set on the target.
+ * @param callback Callback function.
+ * @param Format Format string.
+ */
 GLOBAL void
 IRC_WriteStrServersPrefixFlag_CB(CLIENT *ExceptOf, CLIENT *Prefix, char Flag,
 		void (*callback)(CLIENT *, CLIENT *, void *), void *cb_data)
@@ -311,24 +401,32 @@ IRC_WriteStrServersPrefixFlag_CB(CLIENT *ExceptOf, CLIENT *Prefix, char Flag,
 		if (Client_Type(c) == CLIENT_SERVER && Client_Conn(c) > NONE &&
 		    c != Client_ThisServer() && c != ExceptOf) {
 			/* Found a target server, do the flags match? */
-			if (Flag == '\0' || strchr(Client_Flags(c), Flag))
+			if (Flag == '\0' || Client_HasFlag(c, Flag))
 				callback(c, Prefix, cb_data);
 		}
 		c = Client_Next(c);
 	}
-} /* IRC_WriteStrServersPrefixFlag */
-
+}
 
 /**
- * send message to all clients that are in the same channels as the client sending this message.
- * only send message once per reote server.
+ * Send a message to all "related" clients.
+ *
+ * Related clients are the one that share one ore more channels with the client
+ * sending this message.
+ *
+ * The message is only sent once per remote server.
+ *
+ * @param Client The sending client, excluded while forwarding the message.
+ * @param Prefix The prefix to use.
+ * @param Remote If not set, the message is sent to local clients only.
+ * @param Format Format string.
  */
 #ifdef PROTOTYPES
-GLOBAL bool
+GLOBAL void
 IRC_WriteStrRelatedPrefix(CLIENT *Client, CLIENT *Prefix, bool Remote,
 			  const char *Format, ...)
 #else
-GLOBAL bool
+GLOBAL void
 IRC_WriteStrRelatedPrefix(Client, Prefix, Remote, Format, va_alist)
 CLIENT *Client;
 CLIENT *Prefix;
@@ -384,13 +482,16 @@ va_dcl
 
 		chan_cl2chan = Channel_NextChannelOf( Client, chan_cl2chan );
 	}
-	return Send_Marked_Connections(Prefix, buffer);
+	Send_Marked_Connections(Prefix, buffer);
 } /* IRC_WriteStrRelatedPrefix */
 
-
 /**
  * Send WALLOPS message.
- */
+ *
+ * @param Client The sending client, excluded while forwarding the message.
+ * @param From The (remote) sender of the message.
+ * @param Format Format string.
+*/
 #ifdef PROTOTYPES
 GLOBAL void
 IRC_SendWallops(CLIENT *Client, CLIENT *From, const char *Format, ...)
@@ -434,22 +535,31 @@ va_dcl
 	}
 } /* IRC_SendWallops */
 
-
+/**
+ * Set a "penalty time" for an IRC client.
+ *
+ * Note: penalty times are never set for server links or remote clients!
+ *
+ * @param Client The client.
+ * @param Seconds The additional "penalty time" to enforce.
+ */
 GLOBAL void
-IRC_SetPenalty( CLIENT *Client, time_t Seconds )
+IRC_SetPenalty(CLIENT *Client, time_t Seconds)
 {
 	CONN_ID c;
 
-	assert( Client != NULL );
-	assert( Seconds > 0 );
+	assert(Client != NULL);
+	assert(Seconds > 0);
 
-	if( Client_Type( Client ) == CLIENT_SERVER ) return;
+	if (Client_Type(Client) == CLIENT_SERVER)
+		return;
 
-	c = Client_Conn( Client );
-	if (c > NONE)
-		Conn_SetPenalty(c, Seconds);
-} /* IRC_SetPenalty */
+	c = Client_Conn(Client);
+	if (c <= NONE)
+		return;
 
+	Conn_SetPenalty(c, Seconds);
+} /* IRC_SetPenalty */
 
 static const char *
 Get_Prefix(CLIENT *Target, CLIENT *Client)
@@ -463,19 +573,22 @@ Get_Prefix(CLIENT *Target, CLIENT *Client)
 		return Client_MaskCloaked(Client);
 } /* Get_Prefix */
 
-
 static void
 cb_writeStrServersPrefixFlag(CLIENT *Client, CLIENT *Prefix, void *Buffer)
 {
 	IRC_WriteStrClientPrefix(Client, Prefix, "%s", Buffer);
 } /* cb_writeStrServersPrefixFlag */
 
-
-static bool
+/**
+ * Send a message to all marked connections using a specific prefix.
+ *
+ * @param Prefix The prefix to use.
+ * @param Buffer The message to send.
+ */
+static void
 Send_Marked_Connections(CLIENT *Prefix, const char *Buffer)
 {
 	CONN_ID conn;
-	bool ok = CONNECTED;
 
 	assert(Prefix != NULL);
 	assert(Buffer != NULL);
@@ -483,17 +596,13 @@ Send_Marked_Connections(CLIENT *Prefix, const char *Buffer)
 	conn = Conn_First();
 	while (conn != NONE) {
 		if (Conn_Flag(conn) == SEND_TO_SERVER)
-			ok = Conn_WriteStr(conn, ":%s %s",
-					   Client_ID(Prefix), Buffer);
+			Conn_WriteStr(conn, ":%s %s",
+				      Client_ID(Prefix), Buffer);
 		else if (Conn_Flag(conn) == SEND_TO_USER)
-			ok = Conn_WriteStr(conn, ":%s %s",
-					   Client_MaskCloaked(Prefix), Buffer);
-		if (!ok)
-			break;
-		conn = Conn_Next( conn );
+			Conn_WriteStr(conn, ":%s %s",
+				      Client_MaskCloaked(Prefix), Buffer);
+		conn = Conn_Next(conn);
 	}
-	return ok;
 }
 
-
 /* -eof- */

+ 6 - 4
src/ngircd/irc-write.h

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -17,13 +17,15 @@
  * Sending IRC commands over the network (header)
  */
 
+GLOBAL bool IRC_WriteErrClient PARAMS((CLIENT *Client, const char *Format, ...));
+
 GLOBAL bool IRC_WriteStrClient PARAMS((CLIENT *Client, const char *Format, ...));
 GLOBAL bool IRC_WriteStrClientPrefix PARAMS((CLIENT *Client, CLIENT *Prefix,
 		const char *Format, ...));
 
-GLOBAL bool IRC_WriteStrChannel PARAMS((CLIENT *Client, CHANNEL *Chan,
+GLOBAL void IRC_WriteStrChannel PARAMS((CLIENT *Client, CHANNEL *Chan,
 		bool Remote, const char *Format, ...));
-GLOBAL bool IRC_WriteStrChannelPrefix PARAMS((CLIENT *Client, CHANNEL *Chan,
+GLOBAL void IRC_WriteStrChannelPrefix PARAMS((CLIENT *Client, CHANNEL *Chan,
 		CLIENT *Prefix, bool Remote, const char *Format, ...));
 
 GLOBAL void IRC_WriteStrServers PARAMS((CLIENT *ExceptOf,
@@ -36,7 +38,7 @@ GLOBAL void IRC_WriteStrServersPrefixFlag_CB PARAMS((CLIENT *ExceptOf,
 		CLIENT *Prefix, char Flag,
 		void (*callback)(CLIENT *, CLIENT *, void *), void *cb_data));
 
-GLOBAL bool IRC_WriteStrRelatedPrefix PARAMS((CLIENT *Client, CLIENT *Prefix,
+GLOBAL void IRC_WriteStrRelatedPrefix PARAMS((CLIENT *Client, CLIENT *Prefix,
 		bool Remote, const char *Format, ...));
 
 GLOBAL void IRC_SendWallops PARAMS((CLIENT *Client, CLIENT *From,

+ 290 - 154
src/ngircd/irc.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -27,24 +27,25 @@
 #include "channel.h"
 #include "conn-encoding.h"
 #include "defines.h"
+#include "irc-macros.h"
 #include "irc-write.h"
 #include "log.h"
 #include "match.h"
 #include "messages.h"
 #include "parse.h"
+#include "op.h"
 #include "tool.h"
 
 #include "exp.h"
 #include "irc.h"
 
-
 static char *Option_String PARAMS((CONN_ID Idx));
 static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType,
 				 bool SendErrors));
 static bool Send_Message_Mask PARAMS((CLIENT *from, char *command,
 				      char *targetMask, char *message,
 				      bool SendErrors));
-
+static bool Help PARAMS((CLIENT *Client, const char *Topic));
 
 /**
  * Check if a list limit is reached and inform client accordingly.
@@ -74,9 +75,15 @@ IRC_CheckListTooBig(CLIENT *From, const int Count, const int Limit,
 	return true;
 }
 
-
+/**
+ * Handler for the IRC "ERROR" command.
+ *
+ * @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_ERROR( CLIENT *Client, REQUEST *Req )
+IRC_ERROR(CLIENT *Client, REQUEST *Req)
 {
 	assert( Client != NULL );
 	assert( Req != NULL );
@@ -102,11 +109,10 @@ IRC_ERROR( CLIENT *Client, REQUEST *Req )
 	return CONNECTED;
 } /* IRC_ERROR */
 
-
 /**
  * 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" which is used to selectively
  * disconnect clients. It can be used by IRC operators and servers, for example
  * to "solve" nick collisions after netsplits. See RFC 2812 section 3.7.1.
  *
@@ -114,28 +120,22 @@ IRC_ERROR( CLIENT *Client, REQUEST *Req )
  * KILL command being received over the network! Client is Client_ThisServer()
  * 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.
+ * @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.
+ * @return CONNECTED or DISCONNECTED.
  */
 GLOBAL bool
-IRC_KILL( CLIENT *Client, REQUEST *Req )
+IRC_KILL(CLIENT *Client, REQUEST *Req)
 {
-	CLIENT *prefix, *c;
-	char reason[COMMAND_LEN], *msg;
-	CONN_ID my_conn, conn;
+	CLIENT *prefix;
+	char reason[COMMAND_LEN];
 
 	assert (Client != NULL);
 	assert (Req != NULL);
 
-	if (Client_Type(Client) != CLIENT_SERVER && !Client_OperByMe(Client))
-		return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG,
-					  Client_ID(Client));
-
-	if (Req->argc != 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
+	if (Client_Type(Client) != CLIENT_SERVER && !Op_Check(Client, Req))
+		return Op_NoPrivileges(Client, Req);
 
 	/* Get prefix (origin); use the client if no prefix is given. */
 	if (Req->prefix)
@@ -144,7 +144,8 @@ IRC_KILL( CLIENT *Client, REQUEST *Req )
 		prefix = Client;
 
 	/* Log a warning message and use this server as origin when the
-	 * prefix (origin) is invalid. */
+	 * prefix (origin) is invalid. And this is the reason why we don't
+	 * use the _IRC_GET_SENDER_OR_RETURN_ macro above! */
 	if (!prefix) {
 		Log(LOG_WARNING, "Got KILL with invalid prefix: \"%s\"!",
 		    Req->prefix );
@@ -153,7 +154,7 @@ IRC_KILL( CLIENT *Client, REQUEST *Req )
 
 	if (Client != Client_ThisServer())
 		Log(LOG_NOTICE|LOG_snotice,
-		    "Got KILL command from \"%s\" for \"%s\": %s",
+		    "Got KILL command from \"%s\" for \"%s\": \"%s\".",
 		    Client_Mask(prefix), Req->argv[0], Req->argv[1]);
 
 	/* Build reason string: Prefix the "reason" if the originator is a
@@ -164,74 +165,28 @@ IRC_KILL( CLIENT *Client, REQUEST *Req )
 	else
 		strlcpy(reason, Req->argv[1], sizeof(reason));
 
-	/* Inform other servers */
-	IRC_WriteStrServersPrefix(Client, prefix, "KILL %s :%s",
-				  Req->argv[0], reason);
-
-	/* Save ID of this connection */
-	my_conn = Client_Conn( Client );
-
-	/* Do we host such a client? */
-	c = Client_Search( Req->argv[0] );
-	if( c )
-	{
-		if(( Client_Type( c ) != CLIENT_USER ) &&
-		   ( Client_Type( c ) != CLIENT_GOTNICK ))
-		{
-			/* Target of this KILL is not a regular user, this is
-			 * invalid! So we ignore this case if we received a
-			 * regular KILL from the network and try to kill the
-			 * client/connection anyway (but log an error!) if the
-			 * origin is the local server. */
-
-			if( Client != Client_ThisServer( ))
-			{
-				/* Invalid KILL received from remote */
-				if( Client_Type( c ) == CLIENT_SERVER )
-					msg = ERR_CANTKILLSERVER_MSG;
-				else
-					msg = ERR_NOPRIVILEGES_MSG;
-				return IRC_WriteStrClient( Client, msg,
-					Client_ID( Client ));
-			}
-
-			Log( LOG_ERR, "Got KILL for invalid client type: %d, \"%s\"!",
-			     Client_Type( c ), Req->argv[0] );
-		}
-
-		/* Kill the client NOW:
-		 *  - Close the local connection (if there is one),
-		 *  - Destroy the CLIENT structure for remote clients.
-		 * Note: Conn_Close() removes the CLIENT structure as well. */
-		conn = Client_Conn( c );
-		if(conn > NONE)
-			Conn_Close(conn, NULL, reason, true);
-		else
-			Client_Destroy(c, NULL, reason, false);
-	}
-	else
-		Log( LOG_NOTICE, "Client with nick \"%s\" is unknown here.", Req->argv[0] );
-
-	/* Are we still connected or were we killed, too? */
-	if(( my_conn > NONE ) && ( Conn_GetClient( my_conn )))
-		return CONNECTED;
-	else
-		return DISCONNECTED;
-} /* IRC_KILL */
-
+	return IRC_KillClient(Client, prefix, Req->argv[0], reason);
+}
 
 /**
- * Handler for the IRC command NOTICE.
- */
+ * Handler for the IRC "NOTICE" command.
+ *
+ * @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_NOTICE(CLIENT *Client, REQUEST *Req)
 {
 	return Send_Message(Client, Req, CLIENT_USER, false);
 } /* IRC_NOTICE */
 
-
 /**
- * Handler for the IRC command PRIVMSG.
+ * Handler for the IRC "PRIVMSG" command.
+ *
+ * @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_PRIVMSG(CLIENT *Client, REQUEST *Req)
@@ -239,9 +194,12 @@ IRC_PRIVMSG(CLIENT *Client, REQUEST *Req)
 	return Send_Message(Client, Req, CLIENT_USER, true);
 } /* IRC_PRIVMSG */
 
-
 /**
- * Handler for the IRC command SQUERY.
+ * Handler for the IRC "SQUERY" command.
+ *
+ * @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_SQUERY(CLIENT *Client, REQUEST *Req)
@@ -249,95 +207,269 @@ IRC_SQUERY(CLIENT *Client, REQUEST *Req)
 	return Send_Message(Client, Req, CLIENT_SERVICE, true);
 } /* IRC_SQUERY */
 
-
-GLOBAL bool
-IRC_TRACE( CLIENT *Client, REQUEST *Req )
+/*
+ * Handler for the IRC "TRACE" command.
+ *
+ * @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_TRACE(CLIENT *Client, REQUEST *Req)
 {
 	CLIENT *from, *target, *c;
 	CONN_ID idx, idx2;
 	char user[CLIENT_USER_LEN];
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert(Client != NULL);
+	assert(Req != NULL);
 
-	/* Bad number of arguments? */
-	if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NORECIPIENT_MSG, Client_ID( Client ), Req->command );
+	IRC_SetPenalty(Client, 3);
 
-	/* Search sender */
-	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 );
+	_IRC_GET_SENDER_OR_RETURN_(from, Req, Client)
+	_IRC_GET_TARGET_SERVER_OR_RETURN_(target, Req, 0, from)
 
-	/* Search target */
-	if( Req->argc == 1 ) target = Client_Search( Req->argv[0] );
-	else target = Client_ThisServer( );
-	
 	/* Forward command to other server? */
-	if( target != Client_ThisServer( ))
-	{
-		if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[0] );
-
+	if (target != Client_ThisServer()) {
 		/* Send RPL_TRACELINK back to initiator */
-		idx = Client_Conn( Client ); assert( idx > NONE );
-		idx2 = Client_Conn( Client_NextHop( target )); assert( idx2 > NONE );
-		if( ! IRC_WriteStrClient( from, RPL_TRACELINK_MSG, Client_ID( from ), PACKAGE_NAME, PACKAGE_VERSION, Client_ID( target ), Client_ID( Client_NextHop( target )), Option_String( idx2 ), time( NULL ) - Conn_StartTime( idx2 ), Conn_SendQ( idx ), Conn_SendQ( idx2 ))) return DISCONNECTED;
+		idx = Client_Conn(Client);
+		assert(idx > NONE);
+		idx2 = Client_Conn(Client_NextHop(target));
+		assert(idx2 > NONE);
+
+		if (!IRC_WriteStrClient(from, RPL_TRACELINK_MSG,
+					Client_ID(from), PACKAGE_NAME,
+					PACKAGE_VERSION, Client_ID(target),
+					Client_ID(Client_NextHop(target)),
+					Option_String(idx2),
+					time(NULL) - Conn_StartTime(idx2),
+					Conn_SendQ(idx), Conn_SendQ(idx2)))
+			return DISCONNECTED;
 
 		/* Forward command */
-		IRC_WriteStrClientPrefix( target, from, "TRACE %s", Req->argv[0] );
+		IRC_WriteStrClientPrefix(target, from, "TRACE %s", Req->argv[0]);
 		return CONNECTED;
 	}
 
 	/* Infos about all connected servers */
-	c = Client_First( );
-	while( c )
-	{
-		if( Client_Conn( c ) > NONE )
-		{
+	c = Client_First();
+	while (c) {
+		if (Client_Conn(c) > NONE) {
 			/* Local client */
-			if( Client_Type( c ) == CLIENT_SERVER )
-			{
+			if (Client_Type(c) == CLIENT_SERVER) {
 				/* Server link */
-				strlcpy( user, Client_User( c ), sizeof( user ));
-				if( user[0] == '~' ) strlcpy( user, "unknown", sizeof( user ));
-				if( ! IRC_WriteStrClient( from, RPL_TRACESERVER_MSG, Client_ID( from ), Client_ID( c ), user, Client_Hostname( c ), Client_Mask( Client_ThisServer( )), Option_String( Client_Conn( c )))) return DISCONNECTED;
+				strlcpy(user, Client_User(c), sizeof(user));
+				if (user[0] == '~')
+					strlcpy(user, "unknown", sizeof(user));
+				if (!IRC_WriteStrClient(from,
+						RPL_TRACESERVER_MSG,
+						Client_ID(from), Client_ID(c),
+						user, Client_Hostname(c),
+						Client_Mask(Client_ThisServer()),
+						Option_String(Client_Conn(c))))
+					return DISCONNECTED;
 			}
-			if(( Client_Type( c ) == CLIENT_USER ) && ( strchr( Client_Modes( c ), 'o' )))
-			{
+			if (Client_Type(c) == CLIENT_USER
+			    && Client_HasMode(c, 'o')) {
 				/* IRC Operator */
-				if( ! IRC_WriteStrClient( from, RPL_TRACEOPERATOR_MSG, Client_ID( from ), Client_ID( c ))) return DISCONNECTED;
+				if (!IRC_WriteStrClient(from,
+						RPL_TRACEOPERATOR_MSG,
+						Client_ID(from), Client_ID(c)))
+					return DISCONNECTED;
 			}
 		}
 		c = Client_Next( c );
 	}
 
-	IRC_SetPenalty( Client, 3 );
-	return IRC_WriteStrClient( from, RPL_TRACEEND_MSG, Client_ID( from ), Conf_ServerName, PACKAGE_NAME, PACKAGE_VERSION, NGIRCd_DebugLevel );
+	return IRC_WriteStrClient(from, RPL_TRACEEND_MSG, Client_ID(from),
+				  Conf_ServerName, PACKAGE_NAME,
+				  PACKAGE_VERSION, NGIRCd_DebugLevel);
 } /* IRC_TRACE */
 
-
+/**
+ * Handler for the IRC "HELP" command.
+ *
+ * @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_HELP( CLIENT *Client, REQUEST *Req )
+IRC_HELP(CLIENT *Client, REQUEST *Req)
 {
 	COMMAND *cmd;
 
-	assert( Client != NULL );
-	assert( Req != NULL );
+	assert(Client != NULL);
+	assert(Req != NULL);
 
-	/* Bad number of arguments? */
-	if( Req->argc > 0 ) return IRC_WriteStrClient( Client, ERR_NORECIPIENT_MSG, Client_ID( Client ), Req->command );
+	IRC_SetPenalty(Client, 2);
 
-	cmd = Parse_GetCommandStruct( );
-	while( cmd->name )
-	{
-		if( ! IRC_WriteStrClient( Client, "NOTICE %s :%s", Client_ID( Client ), cmd->name )) return DISCONNECTED;
+	if ((Req->argc == 0 && array_bytes(&Conf_Helptext) > 0)
+	    || (Req->argc >= 1 && strcasecmp(Req->argv[0], "Commands") != 0)) {
+		/* Help text available and requested */
+		if (Req->argc >= 1)
+			return Help(Client, Req->argv[0]);
+
+		if (!Help(Client, "Intro"))
+			return DISCONNECTED;
+		return CONNECTED;
+	}
+
+	cmd = Parse_GetCommandStruct();
+	while(cmd->name) {
+		if (!IRC_WriteStrClient(Client, "NOTICE %s :%s",
+					Client_ID(Client), cmd->name))
+			return DISCONNECTED;
 		cmd++;
 	}
-	
-	IRC_SetPenalty( Client, 2 );
 	return CONNECTED;
 } /* IRC_HELP */
 
+/**
+ * Kill an client identified by its nick name.
+ *
+ * Please note that after killig a client, its CLIENT cond CONNECTION
+ * structures are invalid. So the caller must make sure on its own not to
+ * access data of probably killed clients after calling this function!
+ *
+ * @param Client The client from which the command leading to the KILL has
+ *		been received, or NULL. The KILL will no be forwarded in this
+ *		direction. Only relevant when From is set, too.
+ * @param From The client from which the command originated, or NULL for
+		the local server.
+ * @param Nick The nick name to kill.
+ * @param Reason Text to send as reason to the client and other servers.
+ */
+GLOBAL bool
+IRC_KillClient(CLIENT *Client, CLIENT *From, const char *Nick, const char *Reason)
+{
+	const char *msg;
+	CONN_ID my_conn, conn;
+	CLIENT *c;
+
+	/* Do we know such a client in the network? */
+	c = Client_Search(Nick);
+	if (!c) {
+		LogDebug("Client with nick \"%s\" is unknown, not forwaring.", Nick);
+		return CONNECTED;
+	}
+
+	/* Inform other servers */
+	IRC_WriteStrServersPrefix(From ? Client : NULL,
+				  From ? From : Client_ThisServer(),
+				  "KILL %s :%s", Nick, Reason);
+
+	if (Client_Type(c) != CLIENT_USER && Client_Type(c) != CLIENT_GOTNICK) {
+		/* Target of this KILL is not a regular user, this is
+		 * invalid! So we ignore this case if we received a
+		 * regular KILL from the network and try to kill the
+		 * client/connection anyway (but log an error!) if the
+		 * origin is the local server. */
+
+		if (Client != Client_ThisServer()) {
+			/* Invalid KILL received from remote */
+			if (Client_Type(c) == CLIENT_SERVER)
+				msg = ERR_CANTKILLSERVER_MSG;
+			else
+				msg = ERR_NOPRIVILEGES_MSG;
+			return IRC_WriteErrClient(Client, msg, Client_ID(Client));
+		}
 
+		Log(LOG_ERR,
+		    "Got KILL for invalid client type: %d, \"%s\"!",
+		    Client_Type(c), Nick);
+	}
+
+	/* Save ID of this connection */
+	my_conn = Client_Conn(Client);
+
+	/* Kill the client NOW:
+	 *  - Close the local connection (if there is one),
+	 *  - Destroy the CLIENT structure for remote clients.
+	 * Note: Conn_Close() removes the CLIENT structure as well. */
+	conn = Client_Conn(c);
+	if(conn > NONE)
+		Conn_Close(conn, NULL, Reason, true);
+	else
+		Client_Destroy(c, NULL, Reason, false);
+
+	/* Are we still connected or were we killed, too? */
+	if (my_conn > NONE && Conn_GetClient(my_conn))
+		return CONNECTED;
+	else
+		return DISCONNECTED;
+}
+
+/**
+ * Send help for a given topic to the client.
+ *
+ * @param Client The client requesting help.
+ * @param Topoc The help topic requested.
+ * @return CONNECTED or DISCONNECTED.
+ */
+static bool
+Help(CLIENT *Client, const char *Topic)
+{
+	char *line;
+	size_t helptext_len, len_str, idx_start, lines = 0;
+	bool in_article = false;
+
+	assert(Client != NULL);
+	assert(Topic != NULL);
+
+	helptext_len = array_bytes(&Conf_Helptext);
+	line = array_start(&Conf_Helptext);
+	while (helptext_len > 0) {
+		len_str = strlen(line) + 1;
+		assert(helptext_len >= len_str);
+		helptext_len -= len_str;
+
+		if (in_article) {
+			/* The first character in each article text line must
+			 * be a TAB (ASCII 9) character which will be stripped
+			 * in the output. If it is not a TAB, the end of the
+			 * article has been reached. */
+			if (line[0] != '\t') {
+				if (lines > 0)
+					return CONNECTED;
+				else
+					break;
+			}
+
+			/* A single '.' character indicates an empty line */
+			if (line[1] == '.' && line[2] == '\0')
+				idx_start = 2;
+			else
+				idx_start = 1;
+
+			if (!IRC_WriteStrClient(Client, "NOTICE %s :%s",
+						Client_ID(Client),
+						&line[idx_start]))
+				return DISCONNECTED;
+			lines++;
+
+		} else {
+			if (line[0] == '-' && line[1] == ' '
+			    && strcasecmp(&line[2], Topic) == 0)
+				in_article = true;
+		}
+
+		line += len_str;
+	}
+
+	/* Help topic not found (or empty)! */
+	if (!IRC_WriteStrClient(Client, "NOTICE %s :No help for \"%s\" found!",
+				Client_ID(Client), Topic))
+		return DISCONNECTED;
+
+	return CONNECTED;
+}
+
+/**
+ * Get pointer to a static string representing the connection "options".
+ *
+ * @param Idx Connection index.
+ * @return Pointer to static (global) string buffer.
+ */
 static char *
 #ifdef ZLIB
 Option_String(CONN_ID Idx)
@@ -348,21 +480,26 @@ Option_String(UNUSED CONN_ID Idx)
 	static char option_txt[8];
 #ifdef ZLIB
 	UINT16 options;
-
-	options = Conn_Options(Idx);
 #endif
 
+	assert(Idx != NONE);
+
+	options = Conn_Options(Idx);
 	strcpy(option_txt, "F");	/* No idea what this means, but the
 					 * original ircd sends it ... */
+#ifdef SSL_SUPPORT
+	if(options & CONN_SSL)		/* SSL encrypted link */
+		strlcat(option_txt, "s", sizeof(option_txt));
+#endif
 #ifdef ZLIB
-	if(options & CONN_ZIP)		/* zlib compression supported. */
-		strcat(option_txt, "z");
+	if(options & CONN_ZIP)		/* zlib compression enabled */
+		strlcat(option_txt, "z", sizeof(option_txt));
 #endif
+	LogDebug(" *** %d: %d = %s", Idx, options, option_txt);
 
 	return option_txt;
 } /* Option_String */
 
-
 static bool
 Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 {
@@ -379,19 +516,20 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 	if (Req->argc == 0) {
 		if (!SendErrors)
 			return CONNECTED;
-		return IRC_WriteStrClient(Client, ERR_NORECIPIENT_MSG,
+		return IRC_WriteErrClient(Client, ERR_NORECIPIENT_MSG,
 					  Client_ID(Client), Req->command);
 	}
 	if (Req->argc == 1) {
 		if (!SendErrors)
 			return CONNECTED;
-		return IRC_WriteStrClient(Client, ERR_NOTEXTTOSEND_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOTEXTTOSEND_MSG,
 					  Client_ID(Client));
 	}
 	if (Req->argc > 2) {
 		if (!SendErrors)
 			return CONNECTED;
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+		IRC_SetPenalty(Client, 2);
+		return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);
 	}
 
@@ -400,7 +538,7 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 	else
 		from = Client;
 	if (!from)
-		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
 					  Client_ID(Client), Req->prefix);
 
 #ifdef ICONV
@@ -495,7 +633,7 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 #else
 			if (Client_Type(cl) != ForceType) {
 #endif
-				if (SendErrors && !IRC_WriteStrClient(
+				if (SendErrors && !IRC_WriteErrClient(
 				    from, ERR_NOSUCHNICK_MSG,Client_ID(from),
 				    currentTarget))
 					return DISCONNECTED;
@@ -516,7 +654,7 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 			    !Client_HasMode(from, 'o') &&
 			    !(Client_Type(from) == CLIENT_SERVER) &&
 			    !(Client_Type(from) == CLIENT_SERVICE)) {
-				if (SendErrors && !IRC_WriteStrClient(from,
+				if (SendErrors && !IRC_WriteErrClient(from,
 						ERR_NONONREG_MSG,
 						Client_ID(from), Client_ID(cl)))
 					return DISCONNECTED;
@@ -532,7 +670,7 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 					cl2chan = Channel_NextChannelOf(cl, cl2chan);
 				}
 				if (!cl2chan) {
-					if (SendErrors && !IRC_WriteStrClient(
+					if (SendErrors && !IRC_WriteErrClient(
 					    from, ERR_NOTONSAMECHANNEL_MSG,
 					    Client_ID(from), Client_ID(cl)))
 						return DISCONNECTED;
@@ -541,7 +679,7 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 			}
 
 			if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
-			    && strchr(Client_Modes(cl), 'a')) {
+			    && Client_HasMode(cl, 'a')) {
 				/* Target is away */
 				if (!IRC_WriteStrClient(from, RPL_AWAY_MSG,
 							Client_ID(from),
@@ -572,7 +710,7 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 		} else {
 			if (!SendErrors)
 				return CONNECTED;
-			if (!IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
+			if (!IRC_WriteErrClient(from, ERR_NOSUCHNICK_MSG,
 						Client_ID(from), currentTarget))
 				return DISCONNECTED;
 		}
@@ -586,7 +724,6 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 	return CONNECTED;
 } /* Send_Message */
 
-
 static bool
 Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
 		  char * message, bool SendErrors)
@@ -598,10 +735,10 @@ Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
 
 	cl = NULL;
 
-	if (strchr(Client_Modes(from), 'o') == NULL) {
+	if (!Client_HasMode(from, 'o')) {
 		if (!SendErrors)
 			return true;
-		return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
+		return IRC_WriteErrClient(from, ERR_NOPRIVILEGES_MSG,
 					  Client_ID(from));
 	}
 
@@ -616,7 +753,7 @@ Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
 	{
 		if (!SendErrors)
 			return true;
-		return IRC_WriteStrClient(from, ERR_WILDTOPLEVEL, targetMask);
+		return IRC_WriteErrClient(from, ERR_WILDTOPLEVEL, targetMask);
 	}
 
 	/* #: hostmask, see RFC 2812, sec. 3.3.1 */
@@ -646,5 +783,4 @@ Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
 	return CONNECTED;
 } /* Send_Message_Mask */
 
-
 /* -eof- */

+ 4 - 1
src/ngircd/irc.h

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -28,6 +28,9 @@ GLOBAL bool IRC_SQUERY PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_TRACE PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_HELP PARAMS((CLIENT *Client, REQUEST *Req));
 
+GLOBAL bool IRC_KillClient PARAMS((CLIENT *Client, CLIENT *From,
+				   const char *Nick, const char *Reason));
+
 #endif
 
 /* -eof- */

+ 30 - 39
src/ngircd/lists.c

@@ -34,8 +34,6 @@
 #include "exp.h"
 #include "lists.h"
 
-#define MASK_LEN	(2*CLIENT_HOST_LEN)
-
 struct list_elem {
 	struct list_elem *next;	/** pointer to next list element */
 	char mask[MASK_LEN];	/** IRC mask */
@@ -130,7 +128,8 @@ Lists_Add(struct list_head *h, const char *Mask, time_t ValidUntil,
 	if (e) {
 		e->valid_until = ValidUntil;
 		if (Reason) {
-			free(e->reason);
+			if (e->reason)
+				free(e->reason);
 			e->reason = strdup(Reason);
 		}
 		return true;
@@ -261,17 +260,13 @@ Lists_CheckDupeMask(const struct list_head *h, const char *Mask )
 /**
  * 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.
+ * @param mask    Buffer to store the mask.
+ * @param len     Size of the buffer.
  */
-GLOBAL const char *
-Lists_MakeMask(const char *Pattern)
+GLOBAL void
+Lists_MakeMask(const char *Pattern, char *mask, size_t len)
 {
-	static char TheMask[MASK_LEN];
 	char *excl, *at;
 
 	assert(Pattern != NULL);
@@ -284,30 +279,22 @@ Lists_MakeMask(const char *Pattern)
 
 	if (!at && !excl) {
 		/* Neither "!" nor "@" found: use string as nickname */
-		strlcpy(TheMask, Pattern, sizeof(TheMask) - 5);
-		strlcat(TheMask, "!*@*", sizeof(TheMask));
-		return TheMask;
-	}
-
-	if (!at && excl) {
+		strlcpy(mask, Pattern, len - 5);
+		strlcat(mask, "!*@*", len);
+	} else if (!at && excl) {
 		/* Domain part is missing */
-		strlcpy(TheMask, Pattern, sizeof(TheMask) - 3);
-		strlcat(TheMask, "@*", sizeof(TheMask));
-		return TheMask;
-	}
-
-	if (at && !excl) {
+		strlcpy(mask, Pattern, len - 3);
+		strlcat(mask, "@*", len);
+	} else if (at && !excl) {
 		/* User name is missing */
 		*at = '\0'; at++;
-		strlcpy(TheMask, Pattern, sizeof(TheMask) - 5);
-		strlcat(TheMask, "!*@", sizeof(TheMask));
-		strlcat(TheMask, at, sizeof(TheMask));
-		return TheMask;
+		strlcpy(mask, Pattern, len - 5);
+		strlcat(mask, "!*@", len);
+		strlcat(mask, at, len);
+	} else {
+		/* All parts (nick, user and domain name) are given */
+		strlcpy(mask, Pattern, len);
 	}
-
-	/* All parts (nick, user and domain name) are given */
-	strlcpy(TheMask, Pattern, sizeof(TheMask));
-	return TheMask;
 } /* Lists_MakeMask */
 
 /**
@@ -320,18 +307,20 @@ Lists_MakeMask(const char *Pattern)
 bool
 Lists_Check(struct list_head *h, CLIENT *Client)
 {
-	return Lists_CheckReason(h, Client) != NULL;
+	return Lists_CheckReason(h, Client, NULL, 0);
 }
 
 /**
- * Check if a client is listed in a list and return the "reason".
+ * Check if a client is listed in a list and store the reason.
  *
- * @param h List head.
+ * @param h      List head.
  * @param Client Client to check.
+ * @param reason Buffer to store the reason.
+ * @param len    Size of the buffer if reason should be saved.
  * @return true if client is listed, false if not.
  */
-char *
-Lists_CheckReason(struct list_head *h, CLIENT *Client)
+bool
+Lists_CheckReason(struct list_head *h, CLIENT *Client, char *reason, size_t len)
 {
 	struct list_elem *e, *last, *next;
 
@@ -342,20 +331,22 @@ Lists_CheckReason(struct list_head *h, CLIENT *Client)
 
 	while (e) {
 		next = e->next;
-		if (Match(e->mask, Client_Mask(Client))) {
+		if (Match(e->mask, Client_MaskCloaked(Client))) {
+			if (len && e->reason)
+				strlcpy(reason, e->reason, len);
 			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 e->reason ? e->reason : "";
+			return true;
 		}
 		last = e;
 		e = next;
 	}
 
-	return NULL;
+	return false;
 }
 
 /**

+ 3 - 2
src/ngircd/lists.h

@@ -30,7 +30,8 @@ GLOBAL struct list_elem *Lists_GetFirst PARAMS((const struct list_head *));
 GLOBAL struct list_elem *Lists_GetNext PARAMS((const struct list_elem *));
 
 GLOBAL bool Lists_Check PARAMS((struct list_head *head, CLIENT *client));
-GLOBAL char *Lists_CheckReason PARAMS((struct list_head *head, CLIENT *client));
+GLOBAL bool Lists_CheckReason PARAMS((struct list_head *head, CLIENT *client,
+				      char *reason, size_t len));
 GLOBAL struct list_elem *Lists_CheckDupeMask PARAMS((const struct list_head *head,
 					const char *mask));
 
@@ -41,7 +42,7 @@ GLOBAL unsigned long Lists_Count PARAMS((struct list_head *h));
 
 GLOBAL void Lists_Free PARAMS((struct list_head *head));
 
-GLOBAL const char *Lists_MakeMask PARAMS((const char *Pattern));
+GLOBAL void Lists_MakeMask PARAMS((const char *Pattern, char *mask, size_t len));
 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));

+ 5 - 4
src/ngircd/log.c

@@ -53,7 +53,7 @@ Log_Message(int Level, const char *msg)
 	if (!Is_Daemon) {
 		/* log to console */
 		fprintf(stdout, "[%ld:%d %4ld] %s\n", (long)getpid(), Level,
-				(long)time(NULL) - NGIRCd_Start, msg);
+				(long)(time(NULL) - NGIRCd_Start), msg);
 		fflush(stdout);
 	}
 #ifdef SYSLOG
@@ -109,8 +109,9 @@ Log_ReInit(void)
 GLOBAL void
 Log_Exit( void )
 {
-	Log(LOG_NOTICE, "%s done%s, served %lu connections.", PACKAGE_NAME,
-	    NGIRCd_SignalRestart ? " (restarting)" : "", Conn_CountAccepted());
+	Log(LOG_INFO, "%s done%s, served %lu connection%s.", PACKAGE_NAME,
+	    NGIRCd_SignalRestart ? " (restarting)" : "", Conn_CountAccepted(),
+	    Conn_CountAccepted() == 1 ? "" : "s");
 #ifdef SYSLOG
 	closelog();
 #endif
@@ -159,7 +160,7 @@ va_dcl
  * suitable for the mode ngIRCd is running in (daemon vs. non-daemon).
  * If LOG_snotice is set, the log messages goes to all user with the mode +s
  * set and the local &SERVER channel, too.
- * Please note: you sould use LogDebug(...) for debug messages!
+ * Please note: you should use LogDebug(...) for debug messages!
  * @param Level syslog level (LOG_xxx)
  * @param Format Format string like printf().
  * @param ... Further arguments.

+ 27 - 5
src/ngircd/login.c

@@ -19,6 +19,7 @@
 #include "imp.h"
 #include <assert.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <strings.h>
 #include <unistd.h>
@@ -37,6 +38,7 @@
 #include "ngircd.h"
 #include "pam.h"
 #include "irc-info.h"
+#include "irc-mode.h"
 #include "irc-write.h"
 
 #include "exp.h"
@@ -90,7 +92,7 @@ Login_User(CLIENT * Client)
 #ifdef PAM
 	if (!Conf_PAM) {
 		/* Don't do any PAM authentication at all, instead emulate
-		 * the beahiour of the daemon compiled without PAM support:
+		 * the behavior of the daemon compiled without PAM support:
 		 * because there can't be any "server password", all
 		 * passwords supplied are classified as "wrong". */
 		if(Conn_Password(conn)[0] == '\0')
@@ -151,6 +153,9 @@ Login_User(CLIENT * Client)
 GLOBAL bool
 Login_User_PostAuth(CLIENT *Client)
 {
+	REQUEST Req;
+	char modes[CLIENT_MODE_LEN + 1];
+
 	assert(Client != NULL);
 
 	if (Class_HandleServerBans(Client) != CONNECTED)
@@ -185,8 +190,17 @@ Login_User_PostAuth(CLIENT *Client)
 	if (!IRC_Show_MOTD(Client))
 		return DISCONNECTED;
 
-	/* Suspend the client for a second ... */
-	IRC_SetPenalty(Client, 1);
+	/* Set default user modes */
+	if (Conf_DefaultUserModes[0]) {
+		snprintf(modes, sizeof(modes), "+%s", Conf_DefaultUserModes);
+		Req.prefix = Client_ThisServer();
+		Req.command = "MODE";
+		Req.argc = 2;
+		Req.argv[0] = Client_ID(Client);
+		Req.argv[1] = modes;
+		IRC_MODE(Client, &Req);
+	} else
+		IRC_SetPenalty(Client, 1);
 
 	return CONNECTED;
 }
@@ -194,7 +208,7 @@ Login_User_PostAuth(CLIENT *Client)
 #ifdef PAM
 
 /**
- * Read result of the authenticatior sub-process from pipe
+ * Read result of the authenticator sub-process from pipe
  *
  * @param r_fd		File descriptor of the pipe.
  * @param events	(ignored IO specification)
@@ -202,6 +216,7 @@ Login_User_PostAuth(CLIENT *Client)
 static void
 cb_Read_Auth_Result(int r_fd, UNUSED short events)
 {
+	char user[CLIENT_USER_LEN], *ptr;
 	CONN_ID conn;
 	CLIENT *client;
 	int result;
@@ -233,7 +248,14 @@ cb_Read_Auth_Result(int r_fd, UNUSED short events)
 	}
 
 	if (result == true) {
-		Client_SetUser(client, Client_OrigUser(client), true);
+		/* Authentication succeeded, now set the correct user name
+		 * supplied by the client (without prepended '~' for exmaple),
+		 * but cut it at the first '@' character: */
+		strlcpy(user, Client_OrigUser(client), sizeof(user));
+		ptr = strchr(user, '@');
+		if (ptr)
+			*ptr = '\0';
+		Client_SetUser(client, user, true);
 		(void)Login_User_PostAuth(client);
 	} else
 		Client_Reject(client, "Bad password", false);

+ 0 - 1
src/ngircd/match.c

@@ -55,7 +55,6 @@ static int Matche_After_Star PARAMS(( const char *p, const char *t ));
 GLOBAL bool
 Match( const char *Pattern, const char *String )
 {
-	/* Pattern mit String vergleichen */
 	if( Matche( Pattern, String ) == MATCH_VALID ) return true;
 	else return false;
 } /* Match */

+ 4 - 2
src/ngircd/messages.h

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2013 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
@@ -21,7 +21,7 @@
 #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_MYINFO_MSG			"004 %s %s ngircd-%s %s %s"
-#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CHARSET=UTF-8 CASEMAPPING=ascii PREFIX=(qaohv)~&@%%+ CHANTYPES=#&+ CHANMODES=beI,k,l,imMnOPQRstVz CHANLIMIT=#&+:%d :are supported on this server"
+#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CHARSET=UTF-8 CASEMAPPING=ascii PREFIX=(qaohv)~&@%%+ CHANTYPES=%s CHANMODES=beI,k,l,imMnOPQRstVz CHANLIMIT=%s:%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"
@@ -49,6 +49,7 @@
 #define RPL_NETUSERS_MSG		"266 %s %lu %lu :Current global users: %lu, Max: %lu"
 #define RPL_STATSCONN_MSG		"250 %s :Highest connection count: %lu (%lu connections received)"
 #define RPL_WHOISSSL_MSG		"275 %s %s :is connected via SSL (secure link)"
+#define RPL_WHOISCERTFP_MSG		"276 %s %s :has client certificate fingerprint %s"
 
 #define RPL_AWAY_MSG			"301 %s %s :%s"
 #define RPL_USERHOST_MSG		"302 %s :"
@@ -56,6 +57,7 @@
 #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_WHOISREGNICK_MSG		"307 %s %s :is a registered nick"
+#define RPL_WHOISSERVICE_MSG		"310 %s %s :is an IRC service"
 #define RPL_WHOISUSER_MSG		"311 %s %s %s %s * :%s"
 #define RPL_WHOISSERVER_MSG		"312 %s %s %s :%s"
 #define RPL_WHOISOPERATOR_MSG		"313 %s %s :is an IRC operator"

+ 38 - 20
src/ngircd/ngircd.c

@@ -264,7 +264,7 @@ main(int argc, const char *argv[])
 		/* Initialize the "main program": chroot environment, user and
 		 * group ID, ... */
 		if (!NGIRCd_Init(NGIRCd_NoDaemon)) {
-			Log(LOG_ALERT, "Fatal: Initialization failed");
+			Log(LOG_ALERT, "Fatal: Initialization failed, exiting!");
 			exit(1);
 		}
 
@@ -297,10 +297,10 @@ main(int argc, const char *argv[])
 			 PROTOVER, PROTOIRCPLUS, PACKAGE_NAME, PACKAGE_VERSION,
 			 IRCPLUSFLAGS);
 #ifdef ZLIB
-		strcat(NGIRCd_ProtoID, "Z");
+		strlcat(NGIRCd_ProtoID, "Z", sizeof NGIRCd_ProtoID);
 #endif
 		if (Conf_OperCanMode)
-			strcat(NGIRCd_ProtoID, "o");
+			strlcat(NGIRCd_ProtoID, "o", sizeof NGIRCd_ProtoID);
 #else /* IRCPLUS */
 		snprintf(NGIRCd_ProtoID, sizeof NGIRCd_ProtoID, "%s%s %s|%s",
 			 PROTOVER, PROTOIRC, PACKAGE_NAME, PACKAGE_VERSION);
@@ -535,21 +535,23 @@ Pidfile_Create(pid_t pid)
 
 	len = snprintf(pidbuf, sizeof pidbuf, "%ld\n", (long)pid);
 	if (len < 0 || len >= (int)sizeof pidbuf) {
-		Log(LOG_ERR, "Error converting pid");
+		Log(LOG_ERR, "Error converting process ID!");
 		close(pidfd);
 		return;
 	}
 	
 	if (write(pidfd, pidbuf, (size_t)len) != (ssize_t)len)
-		Log( LOG_ERR, "Can't write PID file (%s): %s", Conf_PidFile, strerror( errno ));
+		Log(LOG_ERR, "Can't write PID file (%s): %s!", Conf_PidFile,
+		    strerror(errno));
 
-	if( close(pidfd) != 0 )
-		Log( LOG_ERR, "Error closing PID file (%s): %s", Conf_PidFile, strerror( errno ));
+	if (close(pidfd) != 0)
+		Log(LOG_ERR, "Error closing PID file (%s): %s!", Conf_PidFile,
+		    strerror(errno));
 } /* Pidfile_Create */
 
 
 /**
- * Redirect stdin, stdout and stderr to apropriate file handles.
+ * Redirect stdin, stdout and stderr to appropriate file handles.
  *
  * @param fd	The file handle stdin, stdout and stderr should be redirected to.
  */
@@ -611,6 +613,13 @@ NGIRCd_getNobodyID(uid_t *uid, gid_t *gid )
 #endif
 
 
+#ifdef HAVE_ARC4RANDOM
+static void
+Random_Init(void)
+{
+
+}
+#else
 static bool
 Random_Init_Kern(const char *file)
 {
@@ -640,6 +649,7 @@ Random_Init(void)
 		return;
 	srand(rand() ^ (unsigned)getpid() ^ (unsigned)time(NULL));
 }
+#endif
 
 
 /**
@@ -671,21 +681,22 @@ NGIRCd_Init(bool NGIRCd_NoDaemon)
 	}
 
 	/* SSL initialization */
-	if (!ConnSSL_InitLibrary())
-		Log(LOG_WARNING,
-		    "Error during SSL initialization, continuing without SSL ...");
+	if (!ConnSSL_InitLibrary()) {
+		Log(LOG_ERR, "Error during SSL initialization!");
+		goto out;
+	}
 
 	/* Change root */
 	if (Conf_Chroot[0]) {
 		if (chdir(Conf_Chroot) != 0) {
-			Log(LOG_ERR, "Can't chdir() in ChrootDir (%s): %s",
+			Log(LOG_ERR, "Can't chdir() in ChrootDir (%s): %s!",
 			    Conf_Chroot, strerror(errno));
 			goto out;
 		}
 
 		if (chroot(Conf_Chroot) != 0) {
 			Log(LOG_ERR,
-			    "Can't change root directory to \"%s\": %s",
+			    "Can't change root directory to \"%s\": %s!",
 			    Conf_Chroot, strerror(errno));
 			goto out;
 		} else {
@@ -716,12 +727,19 @@ NGIRCd_Init(bool NGIRCd_NoDaemon)
 		if (setgid(Conf_GID) != 0) {
 			real_errno = errno;
 			grp = getgrgid(Conf_GID);
-			Log(LOG_ERR, "Can't change group ID to %s(%u): %s",
+			Log(LOG_ERR, "Can't change group ID to %s(%u): %s!",
 			    grp ? grp->gr_name : "?", Conf_GID,
-			    strerror(errno));
+			    strerror(real_errno));
 			if (real_errno != EPERM) 
 				goto out;
 		}
+		if (setgroups(0, NULL) != 0) {
+			real_errno = errno;
+			Log(LOG_ERR, "Can't drop supplementary group IDs: %s!",
+					strerror(errno));
+			if (real_errno != EPERM)
+				goto out;
+		}
 	}
 #endif
 
@@ -730,9 +748,9 @@ NGIRCd_Init(bool NGIRCd_NoDaemon)
 		if (setuid(Conf_UID) != 0) {
 			real_errno = errno;
 			pwd = getpwuid(Conf_UID);
-			Log(LOG_ERR, "Can't change user ID to %s(%u): %s",
+			Log(LOG_ERR, "Can't change user ID to %s(%u): %s!",
 			    pwd ? pwd->pw_name : "?", Conf_UID,
-			    strerror(errno));
+			    strerror(real_errno));
 			if (real_errno != EPERM)
 				goto out;
 		}
@@ -764,7 +782,7 @@ NGIRCd_Init(bool NGIRCd_NoDaemon)
 		setpgrp(0, getpid());
 #endif
 		if (chdir("/") != 0)
-			Log(LOG_ERR, "Can't change directory to '/': %s",
+			Log(LOG_ERR, "Can't change directory to '/': %s!",
 				     strerror(errno));
 
 		/* Detach stdin, stdout and stderr */
@@ -807,8 +825,8 @@ NGIRCd_Init(bool NGIRCd_NoDaemon)
 			    "Changed working directory to \"%s\" ...",
 			    pwd->pw_dir);
 		else
-			Log(LOG_INFO,
-			    "Notice: Can't change working directory to \"%s\": %s",
+			Log(LOG_ERR,
+			    "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);

+ 13 - 79
src/ngircd/numeric.c

@@ -47,12 +47,12 @@ Announce_Channel(CLIENT *Client, CHANNEL *Chan)
 {
 	CL2CHAN *cl2chan;
 	CLIENT *cl;
-	char str[LINE_LEN], *ptr;
+	char str[COMMAND_LEN], *ptr;
 	bool njoin, xop;
 
 	/* Check features of remote server */
 	njoin = Conn_Options(Client_Conn(Client)) & CONN_RFC1459 ? false : true;
-	xop = strchr(Client_Flags(Client), 'X') ? true : false;
+	xop = Client_HasFlag(Client, 'X') ? true : false;
 
 	/* Get all the members of this channel */
 	cl2chan = Channel_FirstMember(Chan);
@@ -68,21 +68,21 @@ Announce_Channel(CLIENT *Client, CHANNEL *Chan)
 				strlcat(str, ",", sizeof(str));
 
 			/* Prepare user prefix (ChanOp, voiced, ...) */
-			if (xop && strchr(Channel_UserModes(Chan, cl), 'q'))
+			if (xop && Channel_UserHasMode(Chan, cl, 'q'))
 				strlcat(str, "~", sizeof(str));
-			if (xop && strchr(Channel_UserModes(Chan, cl), 'a'))
+			if (xop && Channel_UserHasMode(Chan, cl, 'a'))
 				strlcat(str, "&", sizeof(str));
-			if (strchr(Channel_UserModes(Chan, cl), 'o'))
+			if (Channel_UserHasMode(Chan, cl, 'o'))
 				strlcat(str, "@", sizeof(str));
-			if (xop && strchr(Channel_UserModes(Chan, cl), 'h'))
+			if (xop && Channel_UserHasMode(Chan, cl, 'h'))
 				strlcat(str, "%", sizeof(str));
-			if (strchr(Channel_UserModes(Chan, cl), 'v'))
+			if (Channel_UserHasMode(Chan, cl, 'v'))
 				strlcat(str, "+", sizeof(str));
 
 			strlcat(str, Client_ID(cl), sizeof(str));
 
 			/* Send the data if the buffer is "full" */
-			if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 8)) {
+			if (strlen(str) > (sizeof(str) - CLIENT_NICK_LEN - 8)) {
 				if (!IRC_WriteStrClient(Client, "%s", str))
 					return DISCONNECTED;
 				snprintf(str, sizeof(str), "NJOIN %s :",
@@ -150,72 +150,6 @@ Announce_Server(CLIENT * Client, CLIENT * Server)
 } /* Announce_Server */
 
 
-/**
- * Announce existing user to a new server
- * @param Client New server
- * @param User Existing user in the network
- */
-static bool
-Announce_User(CLIENT * Client, CLIENT * User)
-{
-	CONN_ID conn;
-	char *modes;
-
-	conn = Client_Conn(Client);
-	if (Conn_Options(conn) & CONN_RFC1459) {
-		/* RFC 1459 mode: separate NICK and USER commands */
-		if (! Conn_WriteStr(conn, "NICK %s :%d",
-				    Client_ID(User), Client_Hops(User) + 1))
-			return DISCONNECTED;
-		if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
-				     Client_ID(User), Client_User(User),
-				     Client_Hostname(User),
-				     Client_ID(Client_Introducer(User)),
-				     Client_Info(User)))
-			return DISCONNECTED;
-		modes = Client_Modes(User);
-		if (modes[0]) {
-			return Conn_WriteStr(conn, ":%s MODE %s +%s",
-				     Client_ID(User), Client_ID(User),
-				     modes);
-		}
-	} else {
-		/* RFC 2813 mode: one combined NICK or SERVICE command */
-		if (Client_Type(User) == CLIENT_SERVICE
-		    && strchr(Client_Flags(Client), 'S')) {
-			if (!IRC_WriteStrClient(Client,
-					"SERVICE %s %d * +%s %d :%s",
-					Client_Mask(User),
-					Client_MyToken(Client_Introducer(User)),
-					Client_Modes(User), Client_Hops(User) + 1,
-					Client_Info(User)))
-				return DISCONNECTED;
-		} else {
-			if (!IRC_WriteStrClient(Client,
-					"NICK %s %d %s %s %d +%s :%s",
-					Client_ID(User), Client_Hops(User) + 1,
-					Client_User(User), Client_Hostname(User),
-					Client_MyToken(Client_Introducer(User)),
-					Client_Modes(User), Client_Info(User)))
-				return DISCONNECTED;
-		}
-	}
-
-	if (strchr(Client_Flags(Client), 'M')) {
-		/* Synchronize metadata */
-		if (Client_HostnameCloaked(User)) {
-			if (!IRC_WriteStrClient(Client,
-						"METADATA %s cloakhost :%s",
-						Client_ID(User),
-						Client_HostnameCloaked(User)))
-				return DISCONNECTED;
-		}
-	}
-
-	return CONNECTED;
-} /* Announce_User */
-
-
 #ifdef IRCPLUS
 
 /**
@@ -298,8 +232,8 @@ Send_CHANINFO(CLIENT * Client, CHANNEL * Chan)
 	if (!*modes && !*topic)
 		return CONNECTED;
 
-	has_k = strchr(modes, 'k') != NULL;
-	has_l = strchr(modes, 'l') != NULL;
+	has_k = Channel_HasMode(Chan, 'k');
+	has_l = Channel_HasMode(Chan, 'l');
 
 	/* send CHANINFO */
 	if (!has_k && !has_l) {
@@ -372,7 +306,7 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 	while (c) {
 		if (Client_Type(c) == CLIENT_USER ||
 		    Client_Type(c) == CLIENT_SERVICE) {
-			if (!Announce_User(Client, c))
+			if (!Client_Announce(Client, Client_ThisServer(), c))
 				return DISCONNECTED;
 		}
 		c = Client_Next(c);
@@ -387,7 +321,7 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 		}
 #ifdef IRCPLUS
 		/* Send CHANINFO if the peer supports it */
-		if (strchr(Client_Flags(Client), 'C')) {
+		if (Client_HasFlag(Client, 'C')) {
 			if (!Send_CHANINFO(Client, chan))
 				return DISCONNECTED;
 		}
@@ -401,7 +335,7 @@ IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
 	}
 
 #ifdef IRCPLUS
-	if (strchr(Client_Flags(Client), 'L')) {
+	if (Client_HasFlag(Client, 'L')) {
 		LogDebug("Synchronizing INVITE- and BAN-lists ...");
 		if (!Synchronize_Lists(Client))
 			return DISCONNECTED;

+ 4 - 4
src/ngircd/op.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -46,12 +46,12 @@ Op_NoPrivileges(CLIENT * Client, REQUEST * Req)
 	if (from) {
 		Log(LOG_NOTICE, "No privileges: client \"%s\" (%s), command \"%s\"",
 		    Req->prefix, Client_Mask(Client), Req->command);
-		return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
+		return IRC_WriteErrClient(from, ERR_NOPRIVILEGES_MSG,
 					  Client_ID(from));
 	} else {
 		Log(LOG_NOTICE, "No privileges: client \"%s\", command \"%s\"",
 		    Client_Mask(Client), Req->command);
-		return IRC_WriteStrClient(Client, ERR_NOPRIVILEGES_MSG,
+		return IRC_WriteErrClient(Client, ERR_NOPRIVILEGES_MSG,
 					  Client_ID(Client));
 	}
 } /* Op_NoPrivileges */
@@ -86,7 +86,7 @@ Op_Check(CLIENT * Client, REQUEST * Req)
 		return c;
 	if (!Client_HasMode(c, 'o'))
 		return NULL;
-	if (!Client_OperByMe(c) && !Conf_AllowRemoteOper)
+	if (Client_Conn(c) <= NONE && !Conf_AllowRemoteOper)
 		return NULL;
 
 	/* The client is an local IRC operator, or this server is configured

+ 1 - 1
src/ngircd/pam.c

@@ -15,7 +15,7 @@
 
 /**
  * @file
- * PAM User Authentification
+ * PAM User Authentication
  */
 
 #include "imp.h"

+ 1 - 1
src/ngircd/pam.h

@@ -16,7 +16,7 @@
 
 /**
  * @file
- * PAM User Authentification (header)
+ * PAM User Authentication (header)
  */
 
 GLOBAL bool PAM_Authenticate PARAMS((CLIENT *Client));

+ 99 - 89
src/ngircd/parse.c

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 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
@@ -60,74 +60,78 @@ struct _NUMERIC {
 
 static COMMAND My_Commands[] =
 {
-	{ "ADMIN", IRC_ADMIN, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "AWAY", IRC_AWAY, CLIENT_USER, 0, 0, 0 },
-	{ "CAP", IRC_CAP, 0xFFFF, 0, 0, 0 },
-	{ "CONNECT", IRC_CONNECT, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "DIE", IRC_DIE, CLIENT_USER, 0, 0, 0 },
-	{ "DISCONNECT", IRC_DISCONNECT, CLIENT_USER, 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 },
-	{ "INFO", IRC_INFO, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "INVITE", IRC_INVITE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "ISON", IRC_ISON, CLIENT_USER, 0, 0, 0 },
-	{ "JOIN", IRC_JOIN, 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 },
-	{ "KLINE", IRC_xLINE, 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 },
-	{ "LUSERS", IRC_LUSERS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "METADATA", IRC_METADATA, CLIENT_SERVER, 0, 0, 0 },
-	{ "MODE", IRC_MODE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "MOTD", IRC_MOTD, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "NAMES", IRC_NAMES, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "NICK", IRC_NICK, 0xFFFF, 0, 0, 0 },
-	{ "NJOIN", IRC_NJOIN, CLIENT_SERVER, 0, 0, 0 },
-	{ "NOTICE", IRC_NOTICE, 0xFFFF, 0, 0, 0 },
-	{ "OPER", IRC_OPER, CLIENT_USER, 0, 0, 0 },
-	{ "PART", IRC_PART, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "PASS", IRC_PASS, 0xFFFF, 0, 0, 0 },
-	{ "PING", IRC_PING, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "PONG", IRC_PONG, 0xFFFF, 0, 0, 0 },
-	{ "PRIVMSG", IRC_PRIVMSG, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "QUIT", IRC_QUIT, 0xFFFF, 0, 0, 0 },
-	{ "REHASH", IRC_REHASH, CLIENT_USER, 0, 0, 0 },
-	{ "RESTART", IRC_RESTART, CLIENT_USER, 0, 0, 0 },
-	{ "SERVER", IRC_SERVER, 0xFFFF, 0, 0, 0 },
-	{ "SERVICE", IRC_SERVICE, 0xFFFF, 0, 0, 0 },
-	{ "SERVLIST", IRC_SERVLIST, CLIENT_USER, 0, 0, 0 },
-	{ "SQUERY", IRC_SQUERY, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "SQUIT", IRC_SQUIT, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "SVSNICK", IRC_SVSNICK, CLIENT_SERVER, 0, 0, 0 },
-	{ "SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "TIME", IRC_TIME, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "TOPIC", IRC_TOPIC, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "TRACE", IRC_TRACE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "USER", IRC_USER, 0xFFFF, 0, 0, 0 },
-	{ "USERHOST", IRC_USERHOST, CLIENT_USER, 0, 0, 0 },
-	{ "USERS", IRC_USERS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "VERSION", IRC_VERSION, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "WALLOPS", IRC_WALLOPS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "WEBIRC", IRC_WEBIRC, CLIENT_UNKNOWN, 0, 0, 0 },
-	{ "WHO", IRC_WHO, CLIENT_USER, 0, 0, 0 },
-	{ "WHOIS", IRC_WHOIS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "WHOWAS", IRC_WHOWAS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
+	{ "ADMIN", IRC_ADMIN, CLIENT_USER|CLIENT_SERVER, 0, 1, 0, 0, 0 },
+	{ "AWAY", IRC_AWAY, CLIENT_USER, 0, 1, 0, 0, 0 },
+	{ "CAP", IRC_CAP, CLIENT_ANY, 1, 2, 0, 0, 0 },
+	{ "CONNECT", IRC_CONNECT, CLIENT_USER|CLIENT_SERVER, 0, -1, 0, 0, 0 },
+#ifdef STRICT_RFC
+	{ "DIE", IRC_DIE, CLIENT_USER, 0, 0, 0, 0, 0 },
+#else
+	{ "DIE", IRC_DIE, CLIENT_USER, 0, 1, 0, 0, 0 },
+#endif
+	{ "DISCONNECT", IRC_DISCONNECT, CLIENT_USER, 1, 1, 0, 0, 0 },
+	{ "ERROR", IRC_ERROR, CLIENT_ANY, 0, -1, 0, 0, 0 },
+	{ "GLINE", IRC_xLINE, CLIENT_USER|CLIENT_SERVER, 0, -1, 0, 0, 0 },
+	{ "HELP", IRC_HELP, CLIENT_USER, 0, 1, 0, 0, 0 },
+	{ "INFO", IRC_INFO, CLIENT_USER|CLIENT_SERVER, 0, 1, 0, 0, 0 },
+	{ "INVITE", IRC_INVITE, CLIENT_USER|CLIENT_SERVER, 2, 2, 0, 0, 0 },
+	{ "ISON", IRC_ISON, CLIENT_USER, 1, -1, 0, 0, 0 },
+	{ "JOIN", IRC_JOIN, CLIENT_USER|CLIENT_SERVER, 1, 2, 0, 0, 0 },
+	{ "KICK", IRC_KICK, CLIENT_USER|CLIENT_SERVER, 2, 3, 0, 0, 0 },
+	{ "KILL", IRC_KILL, CLIENT_USER|CLIENT_SERVER, 2, 2, 0, 0, 0 },
+	{ "KLINE", IRC_xLINE, CLIENT_USER|CLIENT_SERVER, 0, -1, 0, 0, 0 },
+	{ "LINKS", IRC_LINKS, CLIENT_USER|CLIENT_SERVER, 0, 2, 0, 0, 0 },
+	{ "LIST", IRC_LIST, CLIENT_USER|CLIENT_SERVER, 0, 2, 0, 0, 0 },
+	{ "LUSERS", IRC_LUSERS, CLIENT_USER|CLIENT_SERVER, 0, 2, 0, 0, 0 },
+	{ "METADATA", IRC_METADATA, CLIENT_SERVER, 3, 3, 0, 0, 0 },
+	{ "MODE", IRC_MODE, CLIENT_USER|CLIENT_SERVER, 1, -1, 0, 0, 0 },
+	{ "MOTD", IRC_MOTD, CLIENT_USER|CLIENT_SERVER, 0, 1, 0, 0, 0 },
+	{ "NAMES", IRC_NAMES, CLIENT_USER|CLIENT_SERVER, 0, 2, 0, 0, 0 },
+	{ "NICK", IRC_NICK, CLIENT_ANY, 0, -1, 0, 0, 0 },
+	{ "NJOIN", IRC_NJOIN, CLIENT_SERVER, 2, 2, 0, 0, 0 },
+	{ "NOTICE", IRC_NOTICE, CLIENT_ANY, 0, -1, 0, 0, 0 },
+	{ "OPER", IRC_OPER, CLIENT_USER, 2, 2, 0, 0, 0 },
+	{ "PART", IRC_PART, CLIENT_USER|CLIENT_SERVER, 1, 2, 0, 0, 0 },
+	{ "PASS", IRC_PASS, CLIENT_ANY, 0, -1, 0, 0, 0 },
+	{ "PING", IRC_PING, CLIENT_USER|CLIENT_SERVER, 0, -1, 0, 0, 0 },
+	{ "PONG", IRC_PONG, CLIENT_ANY, 0, -1, 0, 0, 0 },
+	{ "PRIVMSG", IRC_PRIVMSG, CLIENT_USER|CLIENT_SERVER, 0, 2, 0, 0, 0 },
+	{ "QUIT", IRC_QUIT, CLIENT_ANY, 0, 1, 0, 0, 0 },
+	{ "REHASH", IRC_REHASH, CLIENT_USER, 0, 0, 0, 0, 0 },
+	{ "RESTART", IRC_RESTART, CLIENT_USER, 0, 0, 0, 0, 0 },
+	{ "SERVER", IRC_SERVER, CLIENT_ANY, 0, -1, 0, 0, 0 },
+	{ "SERVICE", IRC_SERVICE, CLIENT_ANY, 6, 6, 0, 0, 0 },
+	{ "SERVLIST", IRC_SERVLIST, CLIENT_USER, 0, 2, 0, 0, 0 },
+	{ "SQUERY", IRC_SQUERY, CLIENT_USER|CLIENT_SERVER, 0, 2, 0, 0, 0 },
+	{ "SQUIT", IRC_SQUIT, CLIENT_USER|CLIENT_SERVER, 2, 2, 0, 0, 0 },
+	{ "STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 2, 0, 0, 0 },
+	{ "SVSNICK", IRC_SVSNICK, CLIENT_SERVER, 2, 2, 0, 0, 0 },
+	{ "SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, -1, 0, 0, 0 },
+	{ "TIME", IRC_TIME, CLIENT_USER|CLIENT_SERVER, 0, 1, 0, 0, 0 },
+	{ "TOPIC", IRC_TOPIC, CLIENT_USER|CLIENT_SERVER, 1, 2, 0, 0, 0 },
+	{ "TRACE", IRC_TRACE, CLIENT_USER|CLIENT_SERVER, 0, 1, 0, 0, 0 },
+	{ "USER", IRC_USER, CLIENT_ANY, 0, -1, 0, 0, 0 },
+	{ "USERHOST", IRC_USERHOST, CLIENT_USER, 1, -1, 0, 0, 0 },
+	{ "USERS", IRC_USERS, CLIENT_USER|CLIENT_SERVER, 0, -1, 0, 0, 0 },
+	{ "VERSION", IRC_VERSION, CLIENT_USER|CLIENT_SERVER, 0, 1, 0, 0, 0 },
+	{ "WALLOPS", IRC_WALLOPS, CLIENT_USER|CLIENT_SERVER, 1, 1, 0, 0, 0 },
+	{ "WEBIRC", IRC_WEBIRC, CLIENT_UNKNOWN, 4, 4, 0, 0, 0 },
+	{ "WHO", IRC_WHO, CLIENT_USER, 0, 2, 0, 0, 0 },
+	{ "WHOIS", IRC_WHOIS, CLIENT_USER|CLIENT_SERVER, 0, -1, 0, 0, 0 },
+	{ "WHOWAS", IRC_WHOWAS, CLIENT_USER|CLIENT_SERVER, 0, -1, 0, 0, 0 },
 
 #ifdef IRCPLUS
-	{ "CHANINFO", IRC_CHANINFO, CLIENT_SERVER, 0, 0, 0 },
+	{ "CHANINFO", IRC_CHANINFO, CLIENT_SERVER, 0, -1, 0, 0, 0 },
 # ifdef ICONV
-	{ "CHARCONV", IRC_CHARCONV, CLIENT_USER, 0, 0, 0 },
+	{ "CHARCONV", IRC_CHARCONV, CLIENT_USER, 1, 1, 0, 0, 0 },
 # endif
 #endif
 
 #ifndef STRICT_RFC
-	{ "GET",  IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, 0, 0 },
-	{ "POST", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, 0, 0 },
+	{ "GET",  IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, -1, 0, 0, 0 },
+	{ "POST", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, -1, 0, 0, 0 },
 #endif
-	{ NULL, NULL, 0x0, 0, 0, 0 } /* Ende-Marke */
+	{ NULL, NULL, 0x0, 0, 0, 0, 0, 0 } /* End-Mark */
 };
 
 static void Init_Request PARAMS(( REQUEST *Req ));
@@ -157,7 +161,7 @@ Parse_GetCommandStruct( void )
  * Parse a command ("request") received from a client.
  * 
  * This function is called after the connection layer received a valid CR+LF
- * terminated line of text: we asume that this is a valid IRC command and
+ * terminated line of text: we assume that this is a valid IRC command and
  * try to do something useful with it :-)
  *
  * All errors are reported to the client from which the command has been
@@ -169,7 +173,7 @@ Parse_GetCommandStruct( void )
  * @param Idx Index of the connection from which the command has been received.
  * @param Request NULL terminated line of text (the "command").
  * @return true on success (valid command or "regular" error), false if a
- * 	fatal error occured and the connection has been shut down.
+ * 	fatal error occurred and the connection has been shut down.
  */
 GLOBAL bool
 Parse_Request( CONN_ID Idx, char *Request )
@@ -271,8 +275,6 @@ Parse_Request( CONN_ID Idx, char *Request )
 static void
 Init_Request( REQUEST *Req )
 {
-	/* Neue Request-Struktur initialisieren */
-
 	int i;
 
 	assert( Req != NULL );
@@ -323,15 +325,20 @@ Validate_Prefix( CONN_ID Idx, REQUEST *Req, bool *Closed )
 	}
 
 	/* check if client in prefix is known */
-	c = Client_Search( Req->prefix );
+	c = Client_Search(Req->prefix);
 	if (!c) {
-		Log(LOG_ERR,
-		    "Invalid prefix \"%s\", client not known (connection %d, command \"%s\")!?",
-		    Req->prefix, Idx, Req->command);
-		if (!Conn_WriteStr(Idx,
-				   "ERROR :Invalid prefix \"%s\", client not known",
-				   Req->prefix))
-			*Closed = true;
+		if (Client_Type(client) != CLIENT_SERVER) {
+			Log(LOG_ERR,
+			    "Ignoring command with invalid prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
+			    Req->prefix, Client_ID(client), Idx, Req->command);
+			if (!Conn_WriteStr(Idx,
+					   "ERROR :Invalid prefix \"%s\"",
+					   Req->prefix))
+				*Closed = true;
+			IRC_SetPenalty(client, 2);
+		} else
+			LogDebug("Ignoring command with invalid prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
+			    Req->prefix, Client_ID(client), Idx, Req->command);
 		return false;
 	}
 
@@ -340,19 +347,16 @@ Validate_Prefix( CONN_ID Idx, REQUEST *Req, bool *Closed )
 	if (Client_NextHop(c) != client) {
 		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);
+			    "Spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\"), closing connection!",
+			    Req->prefix, Client_ID(client), 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);
+			Log(LOG_WARNING,
+			    "Ignoring command with spoofed prefix \"%s\" from \"%s\" (connection %d, command \"%s\")!",
+			    Req->prefix, Client_ID(client), Idx, Req->command);
 		}
 		return false;
-
 	}
 
 	return true;
@@ -367,7 +371,7 @@ Validate_Command( UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed )
 	*Closed = false;
 
 	return true;
-} /* Validate_Comman */
+} /* Validate_Command */
 
 
 static bool
@@ -419,7 +423,7 @@ Handle_Numeric(CLIENT *client, REQUEST *Req)
 		{ 376, IRC_Num_ENDOFMOTD }
 	};
 	int i, num;
-	char str[LINE_LEN];
+	char str[COMMAND_LEN];
 	CLIENT *prefix, *target = NULL;
 
 	/* Determine target */
@@ -517,15 +521,22 @@ Handle_Request( CONN_ID Idx, REQUEST *Req )
 		if (!(client_type & cmd->type)) {
 			if (client_type == CLIENT_USER
 			    && cmd->type & CLIENT_SERVER)
-				return IRC_WriteStrClient(client,
+				return IRC_WriteErrClient(client,
 						 ERR_NOTREGISTEREDSERVER_MSG,
 						 Client_ID(client));
 			else
-				return IRC_WriteStrClient(client,
+				return IRC_WriteErrClient(client,
 						ERR_NOTREGISTERED_MSG,
 						Client_ID(client));
 		}
 
+		if (Req->argc < cmd->min_argc ||
+		    (cmd->max_argc != -1 && Req->argc > cmd->max_argc)) {
+			IRC_SetPenalty(client, 2);
+			return IRC_WriteStrClient(client, ERR_NEEDMOREPARAMS_MSG,
+						  Client_ID(client), Req->command);
+		}
+
 		/* Command is allowed for this client: call it and count
 		 * generated bytes in output */
 		Conn_ResetWCounter();
@@ -551,11 +562,10 @@ Handle_Request( CONN_ID Idx, REQUEST *Req )
 			Req->argc == 1 ? "parameter" : "parameters",
 			Req->prefix ? "" : " no" );
 
-	if (Client_Type(client) != CLIENT_SERVER) {
-		result = IRC_WriteStrClient(client, ERR_UNKNOWNCOMMAND_MSG,
+	if (Client_Type(client) != CLIENT_SERVER)
+		result = IRC_WriteErrClient(client, ERR_UNKNOWNCOMMAND_MSG,
 				Client_ID(client), Req->command);
-		Conn_SetPenalty(Idx, 1);
-	}
+
 	return result;
 } /* Handle_Request */
 

+ 2 - 0
src/ngircd/parse.h

@@ -35,6 +35,8 @@ typedef struct _COMMAND
 	bool (*function) PARAMS(( CLIENT *Client, REQUEST *Request ));
 					/**< Function to handle this command */
 	CLIENT_TYPE type;		/**< Valid client types (bit mask) */
+	int min_argc;			/**< Min parameters */
+	int max_argc;			/**< Max parameters */
 	long lcount, rcount;		/**< Number of local and remote calls */
 	long bytes;			/**< Number of bytes created */
 } COMMAND;

+ 9 - 0
src/ngircd/proc.c

@@ -50,7 +50,9 @@ GLOBAL pid_t
 Proc_Fork(PROC_STAT *proc, int *pipefds, void (*cbfunc)(int, short), int timeout)
 {
 	pid_t pid;
+#ifndef HAVE_ARC4RANDOM
 	unsigned int seed;
+#endif
 
 	assert(proc != NULL);
 	assert(pipefds != NULL);
@@ -62,7 +64,9 @@ Proc_Fork(PROC_STAT *proc, int *pipefds, void (*cbfunc)(int, short), int timeout
 		return -1;
 	}
 
+#ifndef HAVE_ARC4RANDOM
 	seed = (unsigned int)rand();
+#endif
 	pid = fork();
 	switch (pid) {
 	case -1:
@@ -73,7 +77,12 @@ Proc_Fork(PROC_STAT *proc, int *pipefds, void (*cbfunc)(int, short), int timeout
 		return -1;
 	case 0:
 		/* New child process: */
+#ifdef HAVE_ARC4RANDOM_STIR
+		arc4random_stir();
+#endif
+#ifndef HAVE_ARC4RANDOM
 		srand(seed ^ (unsigned int)time(NULL) ^ getpid());
+#endif
 		Signals_Exit();
 		signal(SIGTERM, Proc_GenericSignalHandler);
 		signal(SIGALRM, Proc_GenericSignalHandler);

+ 12 - 16
src/ngircd/sighandlers.c

@@ -1,5 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2013 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
@@ -40,7 +41,6 @@ static const int signals_catch[] = {
        SIGINT, SIGQUIT, SIGTERM, SIGHUP, SIGCHLD, SIGUSR1, SIGUSR2
 };
 
-
 #ifdef DEBUG
 
 static void
@@ -57,7 +57,6 @@ Dump_State(void)
 
 #endif
 
-
 static void
 Signal_Block(int sig)
 {
@@ -73,7 +72,6 @@ Signal_Block(int sig)
 #endif
 }
 
-
 static void
 Signal_Unblock(int sig)
 {
@@ -90,7 +88,6 @@ Signal_Unblock(int sig)
 #endif
 }
 
-
 /**
  * Reload the server configuration file.
  */
@@ -117,18 +114,21 @@ Rehash(void)
 	 * be changed during run-time */
 	if (strcmp(old_name, Conf_ServerName) != 0 ) {
 		strlcpy(Conf_ServerName, old_name, sizeof Conf_ServerName);
-		Log(LOG_ERR, "Can't change \"ServerName\" on runtime! Ignored new name.");
+		Log(LOG_ERR,
+		    "Can't change \"ServerName\" on runtime! Ignored new name.");
 	}
 	if (old_nicklen != Conf_MaxNickLength) {
 		Conf_MaxNickLength = old_nicklen;
-		Log(LOG_ERR, "Can't change \"MaxNickLength\" on runtime! Ignored new value.");
+		Log(LOG_ERR,
+		    "Can't change \"MaxNickLength\" on runtime! Ignored new value.");
 	}
 
 	/* Create new pre-defined channels */
 	Channel_InitPredefined( );
 
 	if (!ConnSSL_InitLibrary())
-		Log(LOG_WARNING, "Re-Initializing SSL failed, using old keys");
+		Log(LOG_WARNING,
+		    "Re-Initializing of SSL failed, using old keys!");
 
 	/* Start listening on sockets */
 	Conn_InitListeners( );
@@ -139,7 +139,6 @@ Rehash(void)
 	Log( LOG_NOTICE|LOG_snotice, "Re-reading of configuration done." );
 } /* Rehash */
 
-
 /**
  * Signal handler of ngIRCd.
  * This function is called whenever ngIRCd catches a signal sent by the
@@ -198,7 +197,6 @@ Signal_Handler(int Signal)
 		Signal_Block(Signal);
 } /* Signal_Handler */
 
-
 /**
  * Signal processing handler of ngIRCd.
  * This function is called from the main conn event loop in (io_dispatch)
@@ -231,7 +229,6 @@ Signal_Handler_BH(int Signal)
 	Signal_Unblock(Signal);
 }
 
-
 static void
 Signal_Callback(int fd, short UNUSED what)
 {
@@ -248,19 +245,19 @@ Signal_Callback(int fd, short UNUSED what)
 		if (errno == EAGAIN || errno == EINTR)
 			return;
 
-		Log(LOG_EMERG, "read from signal pipe: %s", strerror(errno));
+		Log(LOG_EMERG, "Read from signal pipe: %s - Exiting!",
+		    strerror(errno));
 		exit(1);
 	}
 
-	Log(LOG_EMERG, "EOF on signal pipe");
+	Log(LOG_EMERG, "EOF on signal pipe!? - Exiting!");
 	exit(1);
 }
 
-
 /**
  * Initialize the signal handlers, catch
  * those signals we are interested in and sets SIGPIPE to be ignored.
- * @return true if initialization was sucessful.
+ * @return true if initialization was successful.
  */
 bool
 Signals_Init(void)
@@ -306,9 +303,8 @@ Signals_Init(void)
 	return io_event_create(signalpipe[0], IO_WANTREAD, Signal_Callback);
 } /* Signals_Init */
 
-
 /**
- * Restores signals to their default behaviour.
+ * Restores signals to their default behavior.
  *
  * This should be called after a fork() in the new
  * child prodcess, especially when we are about to call

+ 1 - 1
src/portab/Makefile.am

@@ -15,7 +15,7 @@ EXTRA_DIST = Makefile.ng
 
 noinst_LIBRARIES = libngportab.a
 
-libngportab_a_SOURCES = strdup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c
+libngportab_a_SOURCES = strdup.c strndup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c
 
 check_PROGRAMS = portabtest
 

+ 9 - 5
src/portab/Makefile.in

@@ -71,8 +71,9 @@ am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
 am__v_at_0 = @
 libngportab_a_AR = $(AR) $(ARFLAGS)
 libngportab_a_LIBADD =
-am_libngportab_a_OBJECTS = strdup$U.$(OBJEXT) strlcpy$U.$(OBJEXT) \
-	strtok_r$U.$(OBJEXT) vsnprintf$U.$(OBJEXT) waitpid$U.$(OBJEXT)
+am_libngportab_a_OBJECTS = strdup$U.$(OBJEXT) strndup$U.$(OBJEXT) \
+	strlcpy$U.$(OBJEXT) strtok_r$U.$(OBJEXT) vsnprintf$U.$(OBJEXT) \
+	waitpid$U.$(OBJEXT)
 libngportab_a_OBJECTS = $(am_libngportab_a_OBJECTS)
 am_portabtest_OBJECTS = portabtest$U.$(OBJEXT)
 portabtest_OBJECTS = $(am_portabtest_OBJECTS)
@@ -218,7 +219,7 @@ top_srcdir = @top_srcdir@
 AUTOMAKE_OPTIONS = ansi2knr
 EXTRA_DIST = Makefile.ng
 noinst_LIBRARIES = libngportab.a
-libngportab_a_SOURCES = strdup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c
+libngportab_a_SOURCES = strdup.c strndup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c
 portabtest_SOURCES = portabtest.c
 portabtest_LDFLAGS = -L.
 portabtest_LDADD = -lngportab
@@ -289,6 +290,7 @@ mostlyclean-kr:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/portabtest$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strdup$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcpy$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strndup$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strtok_r$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vsnprintf$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/waitpid$U.Po@am__quote@
@@ -314,6 +316,8 @@ strdup_.c: strdup.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/strdup.c; then echo $(srcdir)/strdup.c; else echo strdup.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 strlcpy_.c: strlcpy.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/strlcpy.c; then echo $(srcdir)/strlcpy.c; else echo strlcpy.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+strndup_.c: strndup.c $(ANSI2KNR)
+	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/strndup.c; then echo $(srcdir)/strndup.c; else echo strndup.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 strtok_r_.c: strtok_r.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/strtok_r.c; then echo $(srcdir)/strtok_r.c; else echo strtok_r.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 vsnprintf_.c: vsnprintf.c $(ANSI2KNR)
@@ -321,8 +325,8 @@ vsnprintf_.c: vsnprintf.c $(ANSI2KNR)
 waitpid_.c: waitpid.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/waitpid.c; then echo $(srcdir)/waitpid.c; else echo waitpid.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 portabtest_.$(OBJEXT) strdup_.$(OBJEXT) strlcpy_.$(OBJEXT) \
-strtok_r_.$(OBJEXT) vsnprintf_.$(OBJEXT) waitpid_.$(OBJEXT) : \
-$(ANSI2KNR)
+strndup_.$(OBJEXT) strtok_r_.$(OBJEXT) vsnprintf_.$(OBJEXT) \
+waitpid_.$(OBJEXT) : $(ANSI2KNR)
 
 ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
 	list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \

+ 1 - 1
src/portab/Makefile.ng

@@ -15,7 +15,7 @@ EXTRA_DIST = Makefile.ng
 
 noinst_LIBRARIES = libngportab.a
 
-libngportab_a_SOURCES = strdup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c
+libngportab_a_SOURCES = strdup.c strndup.c strlcpy.c strtok_r.c vsnprintf.c waitpid.c
 
 check_PROGRAMS = portabtest
 

+ 4 - 0
src/portab/portab.h

@@ -157,6 +157,10 @@ extern size_t strlcpy PARAMS(( char *dst, const char *src, size_t size ));
 extern char * strdup PARAMS(( const char *s ));
 #endif
 
+#ifndef HAVE_STRNDUP
+extern char * strndup PARAMS((const char *s, size_t maxlen));
+#endif
+
 #ifndef HAVE_STRTOK_R
 extern char * strtok_r PARAMS((char *str, const char *delim, char **saveptr));
 #endif

+ 37 - 0
src/portab/strndup.c

@@ -0,0 +1,37 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ */
+
+#include "portab.h"
+
+/**
+ * @file
+ * strndup() implementation. Public domain.
+ */
+
+#ifndef HAVE_STRNDUP
+
+#include "imp.h"
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "exp.h"
+
+GLOBAL char *
+strndup(const char *s, size_t maxlen)
+{
+	char *dup;
+	size_t len = strlen(s);
+
+	if (len > maxlen)
+		len = maxlen;
+	len++;
+	dup = malloc(len);
+	if (dup)
+		strlcpy(dup, s, len);
+	return dup;
+}
+
+#endif
+

+ 1 - 0
src/testsuite/README

@@ -97,3 +97,4 @@ server-link-test.e
 stress-A.e
 stress-B.e
 who-test.e
+whois-test.e

+ 2 - 1
src/testsuite/getpid.sh

@@ -1,10 +1,11 @@
 #!/bin/sh
 # ngIRCd Test Suite
-# $Id: getpid.sh,v 1.5 2006/08/05 00:15:28 alex Exp $
 
 # did we get a name?
 [ $# -ne 1 ] && exit 1
 
+[ -x /bin/pidof ] && exec /bin/pidof -s $@
+
 # detect flags for "ps" and "head"
 UNAME=`uname`
 if [ $UNAME = "FreeBSD" ]; then

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

@@ -16,6 +16,7 @@
 [Options]
 	OperCanUseMode = yes
 	Ident = no
+	IncludeDir = /var/empty
 	PAM = no
 
 [Operator]

+ 0 - 0
src/testsuite/ngircd-test2.conf


Some files were not shown because too many files changed in this diff