Browse Source

Import upstream version 19.2~rc1

Alexander Barton 12 years ago
parent
commit
e13e33193f

+ 42 - 14
ChangeLog

@@ -9,14 +9,42 @@
                                -- ChangeLog --
 
 
+ngIRCd Release 19.2
+
+  ngIRCd 19.2~rc1 (2012-06-13)
+  - New configuration option "CloakHostModeX" to configure the hostname
+    that gets used for IRC clients which have user mode "+x" enabled.
+    Up to now, the name of the IRC server itself has been used for this,
+    which still is the default when "CloakHostModeX" isn't set.
+  - Correctly handle asynchronously re-established server links: a race
+    condition could let the daemon loose track of an already re-established
+    incoming server link while preparing its own outgoing connection.
+    Peers that both try to connect each other could have been affected.
+  - Log a debug message when SIGUSR2 is handled in debug mode.
+  - Only allow alphanumeric characters in user-supplied user names of
+    USER command and IDENT replies.
+  - Change wording of "TLS initialized" message to make it more consistent.
+  - Don't leak file descriptors on error path when creating "PID files".
+  - Add missing mode "r" to CHANMODES in 005 "ISUPPORT" numeric.
+  - Update doc/Modes.txt and doc/Platforms.txt documents.
+  - contrib/platformtest.sh: correctly detect Open64 C compiler and handle
+    "CC=xxx MAKE=yyy ./platformtest.sh" calling convention.
+  - Add instructions for setting up Atheme IRC services.
+  - Implement support for IRC capability handling, the new "CAP" command,
+    and capablity "multi-prefix" which allows both the NAME and	WHO command
+    handlers to return more than one "class prefix" to the client.
+  - Update Xcode project files: reference missing documentation files.
+  - Fix: Don't ignore "permission denied" errors when enabling chroot.
+  - FAQ: enhance description of chroot setup.
+
 ngIRCd Release 19.1 (2012-03-19)
 
   - Fix gcc warning (v4.6.3), initialize "list" variable to NULL.
   - Fix typos: "recieved" -> "received", "Please not" -> "Please note",
     and fix lintian(1) warning ""hyphen-used-as-minus-sign", too.
-  - Really include _all_ patchtes to build the Anope module into the
+  - Really include _all_ patches to build the Anope module into the
     distribution archive ... ooops!
-  - getpid.sh: Fix testcase error for Debian using sbuild(1).
+  - getpid.sh: Fix test case error for Debian using sbuild(1).
   - Don't log "ngIRCd hello message" two times when starting up.
 
 ngIRCd Release 19 (2012-02-29)
@@ -28,7 +56,7 @@ ngIRCd Release 19 (2012-02-29)
   - Fix building ngIRCd with old gcc versions (e. g. 2.7.2).
   - Correctly re-open syslog logging after reading of configuration
     file: Syslog logging has been initialized before reading the
-    configuraton, so ngIRCd always used the default facility and ignored
+    configuration, so ngIRCd always used the default facility and ignored
     the "SyslogFacility" configuration option ...
     Thanks to Patrik Schindler for reporting this issue!
 
@@ -39,7 +67,7 @@ ngIRCd Release 19 (2012-02-29)
   - Log more information about server synchronization.
   - Update preliminary ngIRCd protocol module for Anope 1.9.6, which now
     is the only supported version.
-  - New numeric RPL_WHOISHOST_MSG(378), which returns the DNS hostname
+  - New numeric RPL_WHOISHOST_MSG(378), which returns the DNS host name
     (if available) and the IP address of a client in the WHOIS reply.
     Only the user itself and local IRC operators get this numeric.
   - Implement channel exception list (mode 'e'). This allows a channel
@@ -85,7 +113,7 @@ ngIRCd Release 19 (2012-02-29)
     commands: this reduces the possibility of flooding channels with
     commands like "PRIVMSG/NOTICE #a,#n,#c,... :message" a little bit.
     Problem noticed by Cahata, thanks!
-  - Display correct error message when "Server{UID|GID}" variabe in the
+  - Display correct error message when "Server{UID|GID}" variable in the
     configuration file is invalid (not a number and no existing user).
   - Update Copyright notices for 2012 :-)
   - JOIN command: don't stop handling of channel lists when a single
@@ -127,8 +155,8 @@ ngIRCd Release 19 (2012-02-29)
   - ./configure: Fix logic and quoting of poll() detection code: only use
     poll() when poll.h exists as well.
   - Suppress 'Can't create pre-defined channel: invalid name: ""' message.
-  - whois-test: handle local hostname = "localhost.localdomain" using the
-    pattern "localhost*" for valid local hostnames.
+  - whois-test: handle local host name = "localhost.localdomain" using the
+    pattern "localhost*" for valid local host names.
   - sample-ngircd.conf: show correct default for "PAM" variable: The
     default of "PAM" is "yes" when ngIRCd has been configured to use it,
     so show the correct default value in the sample configuration file.
@@ -242,8 +270,8 @@ ngIRCd Release 18 (2011-07-10)
     variable description.
   - Don't use "the.net" in sample-ngircd.conf, use "example.net".
   - Terminate incoming connections on HTTP commands "GET" and "POST".
-  - New configuration option "CloakHost": when set, this hostname is used for
-    every client instead of the real DNS hostname (or IP address).
+  - New configuration option "CloakHost": when set, this host name is used for
+    every client instead of the real DNS host name (or IP address).
   - New configuration option "CloakUserToNick": when enabled, ngIRCd sets
     every clients' user name to their nick name and hides the user name
     supplied by the IRC client.
@@ -348,7 +376,7 @@ ngIRCd Release 17 (2010-11-07)
   - configure script: correctly indent IPv6 yes/no summary output.
   - Don't reset My_Connections[Idx].lastping when reading data, so the
     client lag debug-output is working again.
-  - Implement user mode "x": hostname cloaking (closes: #102).
+  - Implement user mode "x": host name cloaking (closes: #102).
   - Make configure switch "--docdir" work (closes: #108).
   - Reformat and update FAQ.txt a little bit.
   - INSTALL: mention SSL, IPv6, and changed handling of MotdFile.
@@ -453,7 +481,7 @@ ngIRCd Release 14.1 (2009-05-05)
   - Allow ping timeout quit messages to show the timeout value.
   - Fix error handling on compressed links.
   - Fix server list announcement.
-  - Do not remove hostnames from info text.
+  - Do not remove host names from info text.
 
 ngIRCd Release 14 (2009-04-20)
 
@@ -549,7 +577,7 @@ ngIRCd 0.12.0 (2008-05-13)
   - RPL_WHOREPLY messages generated by IRC_WHO didn't include flags (*,@,+).
     (Dana Dahlstrom)
   - IRC_WHO now supports search patterns and will test this against user
-    nickname/servername/hostname, etc. as required by RFC 2812, Section 3.6.1.
+    nickname/server name/host name, etc. as required by RFC 2812, Section 3.6.1.
     (reported by Dana Dahlstrom)
   - Add test cases for "WHO" command. (Dana Dahlstrom)
   - Implement RFC 2812 handling of "0" argument to 'JOIN': must be treated
@@ -572,7 +600,7 @@ ngIRCd 0.11.0 (2008-01-15)
     ngircd to crash [from HEAD]. (CVE-2008-0285)
   
   ngIRCd 0.11.0-pre1 (2008-01-02)
-  - Use dotted-decimal IP address if hostname is >= 64.
+  - Use dotted-decimal IP address if host name is >= 64.
   - Add support for /STAT u (server uptime) command.
   - New [Server] configuration Option "Bind" allows to specify
     the source IP address to use when connecting to remote server.
@@ -798,7 +826,7 @@ ngIRCd 0.8.0 (2004-06-26)
     original ircd exactly: the unnecessary but missing ":" before the last
     parameter has been added.
   - Fixed TRACE: don't output "Serv" lines for ourself; display more info.
-  - Results of the resolver (hostnames and IDENT names) are discarded after
+  - Results of the resolver (host names and IDENT names) are discarded after
     the client is successfully registered with the server.
   - Better logging while establishing and shutting down connections.
   - The type of service (TOS) of all sockets is set to "interactive" now.

+ 26 - 1
INSTALL

@@ -93,7 +93,8 @@ automake ("configure") should be no problem.
 The normal installation procedure after getting (and expanding) the source
 files (using a distribution archive or GIT) is as following:
 
-  1) ./autogen.sh	[only necessary when using GIT]
+  0) Satisfy prerequisites
+  1) ./autogen.sh  [only necessary when using GIT]
   2) ./configure
   3) make
   4) make install
@@ -114,6 +115,30 @@ possible options will be installed there. You'll find its template in the
 doc/ directory: sample-ngircd.conf.
 
 
+0): Satisfy prerequisites
+
+When building from source, you'll need some other software to build ngIRCd:
+for example a working C compiler, make tool, GNU automake and autoconf (only
+when not using a distribution archive), and a few libraries depending on the
+features you want to compile in (like IDENT support, SSL, and PAM).
+
+If you are using one of the "big" operating systems or Linux distributions,
+you can use the following commands to install all the required packages to
+build the sources including all optional features and to run the test suite:
+
+* RedHat / Fedora based distributions:
+
+  yum install \
+    autoconf automake expect gcc glibc-devel gnutls-devel \
+    libident-devel make pam-devel tcp_wrappers-devel telnet zlib-devel
+
+* Debian / Ubuntu based distributions:
+
+  apt-get install \
+    autoconf automake build-essential expect libgnutls-dev \
+    libident-dev libpam-dev libwrap0-dev libz-dev telnet
+
+
 1): "autogen.sh"
 
 The first step, autogen.sh, is only necessary if the configure-script isn't

+ 18 - 6
NEWS

@@ -9,9 +9,21 @@
                                   -- NEWS --
 
 
+ngIRCd Release 19.2
+
+  ngIRCd 19.2~rc1 (2012-06-13)
+  - New configuration option "CloakHostModeX" to configure the hostname
+    that gets used for IRC clients which have user mode "+x" enabled.
+    Up to now, the name of the IRC server itself has been used for this,
+    which still is the default when "CloakHostModeX" isn't set.
+  - Add instructions for setting up Atheme IRC services.
+  - Implement support for IRC capability handling, the new "CAP" command,
+    and capablity "multi-prefix" which allows both the NAME and	WHO command
+    handlers to return more than one "class prefix" to the client.
+
 ngIRCd Release 19.1 (2012-03-19)
 
-  - Really include _all_ patchtes to build the Anope module into the
+  - Really include _all_ patches to build the Anope module into the
     distribution archive ... ooops!
 
 ngIRCd Release 19 (2012-02-29)
@@ -19,7 +31,7 @@ ngIRCd Release 19 (2012-02-29)
   ngIRCd 19~rc1 (2012-02-12)
   - Update preliminary ngIRCd protocol module for Anope 1.9.6, which now
     is the only supported version.
-  - New numeric RPL_WHOISHOST_MSG(378), which returns the DNS hostname
+  - New numeric RPL_WHOISHOST_MSG(378), which returns the DNS host name
     (if available) and the IP address of a client in the WHOIS reply.
     Only the user itself and local IRC operators get this numeric.
   - Implement channel exception list (mode 'e'). This allows a channel
@@ -134,8 +146,8 @@ ngIRCd Release 18 (2011-07-10)
     (booleans, text strings, integer numbers) and add type information to each
     variable description.
   - Terminate incoming connections on HTTP commands "GET" and "POST".
-  - New configuration option "CloakHost": when set, this hostname is used for
-    every client instead of the real DNS hostname (or IP address).
+  - New configuration option "CloakHost": when set, this host name is used for
+    every client instead of the real DNS host name (or IP address).
   - New configuration option "CloakUserToNick": when enabled, ngIRCd sets
     every clients' user name to their nick name and hides the user name
     supplied by the IRC client.
@@ -187,7 +199,7 @@ ngIRCd Release 17 (2010-11-07)
   - Enable the daemon to disable and enable "debug mode" on runtime using
     signal SIGUSR1, when debug code is compiled in, not only on startup
     using the command line parameters.
-  - Implement user mode "x": hostname cloaking (closes: #102).
+  - Implement user mode "x": host name cloaking (closes: #102).
   - Change MOTD file handling: ngIRCd now caches the contens of the MOTD
     file, so the daemon now requires a HUP signal or REHASH command to
     re-read the MOTD file when its content changed.
@@ -292,7 +304,7 @@ ngIRCd 0.12.0 (2008-05-13)
   - Implemented IRC commands INFO, SUMMON (dummy), and USERS (dummy) and
     enhanced test suite to check these commands. (Dana Dahlstrom)
   - IRC_WHO now supports search patterns and will test this against user
-    nickname/servername/hostname, etc. as required by RFC 2812, Section 3.6.1.
+    nickname/server name/host name, etc. as required by RFC 2812, Section 3.6.1.
     (reported by Dana Dahlstrom)
   - Implement RFC 2812 handling of "0" argument to 'JOIN': must be treated
     as if the user had sent PART commands for all channels the user is a

+ 10 - 10
configure

@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.67 for ngircd 19.1.
+# Generated by GNU Autoconf 2.67 for ngircd 19.2~rc1.
 #
 #
 # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -549,8 +549,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='ngircd'
 PACKAGE_TARNAME='ngircd'
-PACKAGE_VERSION='19.1'
-PACKAGE_STRING='ngircd 19.1'
+PACKAGE_VERSION='19.2~rc1'
+PACKAGE_STRING='ngircd 19.2~rc1'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1267,7 +1267,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures ngircd 19.1 to adapt to many kinds of systems.
+\`configure' configures ngircd 19.2~rc1 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1338,7 +1338,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of ngircd 19.1:";;
+     short | recursive ) echo "Configuration of ngircd 19.2~rc1:";;
    esac
   cat <<\_ACEOF
 
@@ -1448,7 +1448,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-ngircd configure 19.1
+ngircd configure 19.2~rc1
 generated by GNU Autoconf 2.67
 
 Copyright (C) 2010 Free Software Foundation, Inc.
@@ -1924,7 +1924,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 19.1, which was
+It was created by ngircd $as_me 19.2~rc1, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
   $ $0 $@
@@ -2850,7 +2850,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='ngircd'
- VERSION='19.1'
+ VERSION='19.2~rc1'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -7475,7 +7475,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 19.1, which was
+This file was extended by ngircd $as_me 19.2~rc1, which was
 generated by GNU Autoconf 2.67.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -7541,7 +7541,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 19.1
+ngircd config.status 19.2~rc1
 configured by $0, generated by GNU Autoconf 2.67,
   with options \\"\$ac_cs_config\\"
 

+ 6 - 0
contrib/Debian/changelog

@@ -1,3 +1,9 @@
+ngircd (19.2~rc1-0ab1) unstable; urgency=low
+
+  * New "upstream" release candidate 1 for ngIRC Release 19.2.
+
+ -- Alexander Barton <alex@barton.de>  Wed, 13 Jun 2012 10:59:34 +0200
+
 ngircd (19.1-0ab1) unstable; urgency=low
 
   * New "upstream" release: ngIRCd 19.1.

+ 40 - 8
contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj

@@ -41,6 +41,9 @@
 		FAA3D27B0F139CDC00B2447E /* conn-ssl.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA3D2790F139CDC00B2447E /* conn-ssl.c */; };
 		FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA97C55124A271400D5BBA9 /* sighandlers.c */; };
 		FAACD5F514A6099C006ED74F /* class.c in Sources */ = {isa = PBXBuildFile; fileRef = FAACD5F314A6099C006ED74F /* class.c */; };
+		FAD5853215271AAB00328741 /* client-cap.c in Sources */ = {isa = PBXBuildFile; fileRef = FAD5853015271AAB00328741 /* client-cap.c */; };
+		FAD5853515271AB800328741 /* irc-cap.c in Sources */ = {isa = PBXBuildFile; fileRef = FAD5853315271AB800328741 /* irc-cap.c */; };
+		FAD5853815272C2600328741 /* login.c in Sources */ = {isa = PBXBuildFile; fileRef = FAD5853615272C2500328741 /* login.c */; };
 		FAE5CC2E0CF2308A007D69B6 /* numeric.c in Sources */ = {isa = PBXBuildFile; fileRef = FAE5CC2D0CF2308A007D69B6 /* numeric.c */; };
 /* End PBXBuildFile section */
 
@@ -231,6 +234,19 @@
 		FAA97C56124A271400D5BBA9 /* sighandlers.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = sighandlers.h; sourceTree = "<group>"; };
 		FAACD5F314A6099C006ED74F /* class.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = class.c; sourceTree = "<group>"; };
 		FAACD5F414A6099C006ED74F /* class.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = class.h; sourceTree = "<group>"; };
+		FAD5852F15271A7800328741 /* Capabilities.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Capabilities.txt; sourceTree = "<group>"; };
+		FAD5853015271AAB00328741 /* client-cap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "client-cap.c"; sourceTree = "<group>"; };
+		FAD5853115271AAB00328741 /* client-cap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "client-cap.h"; sourceTree = "<group>"; };
+		FAD5853315271AB800328741 /* irc-cap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "irc-cap.c"; sourceTree = "<group>"; };
+		FAD5853415271AB800328741 /* irc-cap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "irc-cap.h"; sourceTree = "<group>"; };
+		FAD5853615272C2500328741 /* login.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = login.c; sourceTree = "<group>"; };
+		FAD5853715272C2500328741 /* login.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = login.h; sourceTree = "<group>"; };
+		FAE22BD215270EA300F1A5AB /* Bopm.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Bopm.txt; sourceTree = "<group>"; };
+		FAE22BD415270EA300F1A5AB /* Contributing.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Contributing.txt; sourceTree = "<group>"; };
+		FAE22BD515270EB500F1A5AB /* HowToRelease.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = HowToRelease.txt; sourceTree = "<group>"; };
+		FAE22BD615270EB500F1A5AB /* Modes.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Modes.txt; sourceTree = "<group>"; };
+		FAE22BD715270EB500F1A5AB /* PAM.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = PAM.txt; sourceTree = "<group>"; };
+		FAE22BD815270EC400F1A5AB /* README-Interix.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "README-Interix.txt"; sourceTree = "<group>"; };
 		FAE5CC2C0CF2308A007D69B6 /* numeric.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = numeric.h; sourceTree = "<group>"; };
 		FAE5CC2D0CF2308A007D69B6 /* numeric.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = numeric.c; sourceTree = "<group>"; };
 /* End PBXFileReference section */
@@ -298,8 +314,7 @@
 		FA322CD70CEF74B1001761B3 /* ngircd */ = {
 			isa = PBXGroup;
 			children = (
-				FAA3D2790F139CDC00B2447E /* conn-ssl.c */,
-				FAA3D27A0F139CDC00B2447E /* conn-ssl.h */,
+				FA322D020CEF74B1001761B3 /* Makefile.am */,
 				FA322CD90CEF74B1001761B3 /* array.c */,
 				FA322CDA0CEF74B1001761B3 /* array.h */,
 				FA322CDB0CEF74B1001761B3 /* channel.c */,
@@ -308,6 +323,8 @@
 				FAACD5F414A6099C006ED74F /* class.h */,
 				FA322CDD0CEF74B1001761B3 /* client.c */,
 				FA322CDE0CEF74B1001761B3 /* client.h */,
+				FAD5853015271AAB00328741 /* client-cap.c */,
+				FAD5853115271AAB00328741 /* client-cap.h */,
 				FA322CDF0CEF74B1001761B3 /* conf.c */,
 				FA322CE00CEF74B1001761B3 /* conf.h */,
 				FAA3D2780F139CDC00B2447E /* conf-ssl.h */,
@@ -317,11 +334,15 @@
 				FA322CE40CEF74B1001761B3 /* conn-zip.h */,
 				FA322CE50CEF74B1001761B3 /* conn.c */,
 				FA322CE60CEF74B1001761B3 /* conn.h */,
+				FAA3D2790F139CDC00B2447E /* conn-ssl.c */,
+				FAA3D27A0F139CDC00B2447E /* conn-ssl.h */,
 				FA322CE70CEF74B1001761B3 /* defines.h */,
 				FA322CE80CEF74B1001761B3 /* hash.c */,
 				FA322CE90CEF74B1001761B3 /* hash.h */,
 				FA322CEA0CEF74B1001761B3 /* io.c */,
 				FA322CEB0CEF74B1001761B3 /* io.h */,
+				FAD5853315271AB800328741 /* irc-cap.c */,
+				FAD5853415271AB800328741 /* irc-cap.h */,
 				FA322CEC0CEF74B1001761B3 /* irc-channel.c */,
 				FA322CED0CEF74B1001761B3 /* irc-channel.h */,
 				FA322CEE0CEF74B1001761B3 /* irc-info.c */,
@@ -344,7 +365,8 @@
 				FA322CFF0CEF74B1001761B3 /* lists.h */,
 				FA322D000CEF74B1001761B3 /* log.c */,
 				FA322D010CEF74B1001761B3 /* log.h */,
-				FA322D020CEF74B1001761B3 /* Makefile.am */,
+				FAD5853615272C2500328741 /* login.c */,
+				FAD5853715272C2500328741 /* login.h */,
 				FA322D030CEF74B1001761B3 /* match.c */,
 				FA322D040CEF74B1001761B3 /* match.h */,
 				FA322D050CEF74B1001761B3 /* messages.h */,
@@ -563,18 +585,25 @@
 		FA322D970CEF752C001761B3 /* doc */ = {
 			isa = PBXGroup;
 			children = (
-				FAA3D2800F139D1500B2447E /* Services.txt */,
-				FA407F380DB15AC700271AF1 /* GIT.txt */,
-				FA322D9A0CEF752C001761B3 /* FAQ.txt */,
 				FA322D9B0CEF752C001761B3 /* Makefile.am */,
+				FAE22BD215270EA300F1A5AB /* Bopm.txt */,
+				FAD5852F15271A7800328741 /* Capabilities.txt */,
+				FAE22BD415270EA300F1A5AB /* Contributing.txt */,
+				FA322D9A0CEF752C001761B3 /* FAQ.txt */,
+				FA407F380DB15AC700271AF1 /* GIT.txt */,
+				FAE22BD515270EB500F1A5AB /* HowToRelease.txt */,
+				FAE22BD615270EB500F1A5AB /* Modes.txt */,
+				FAE22BD715270EB500F1A5AB /* PAM.txt */,
 				FA322D9C0CEF752C001761B3 /* Platforms.txt */,
 				FA322D9D0CEF752C001761B3 /* Protocol.txt */,
 				FA322D9E0CEF752C001761B3 /* README-AUX.txt */,
 				FA322D9F0CEF752C001761B3 /* README-BeOS.txt */,
+				FAE22BD815270EC400F1A5AB /* README-Interix.txt */,
 				FA322DA00CEF752C001761B3 /* RFC.txt */,
+				FAA3D2800F139D1500B2447E /* Services.txt */,
+				FA322DA90CEF752C001761B3 /* SSL.txt */,
 				FA77849A133FB9FF00740057 /* sample-ngircd.conf.tmpl */,
 				FA322DA20CEF752C001761B3 /* src */,
-				FA322DA90CEF752C001761B3 /* SSL.txt */,
 			);
 			name = doc;
 			path = ../../doc;
@@ -653,7 +682,7 @@
 		08FB7793FE84155DC02AAC07 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 0420;
+				LastUpgradeCheck = 0430;
 			};
 			buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "ngIRCd" */;
 			compatibilityVersion = "Xcode 3.2";
@@ -718,6 +747,9 @@
 				FA2D564A11EA158B00D37A35 /* pam.c in Sources */,
 				FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */,
 				FAACD5F514A6099C006ED74F /* class.c in Sources */,
+				FAD5853215271AAB00328741 /* client-cap.c in Sources */,
+				FAD5853515271AB800328741 /* irc-cap.c in Sources */,
+				FAD5853815272C2600328741 /* login.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 1 - 1
contrib/ngircd.spec

@@ -1,5 +1,5 @@
 %define name    ngircd
-%define version 19.1
+%define version 19.2~rc1
 %define release 1
 %define prefix  %{_prefix}
 

+ 30 - 22
contrib/platformtest.sh

@@ -1,7 +1,7 @@
 #!/bin/sh
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2010 Alexander Barton <alex@barton.de>
+# Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -21,13 +21,16 @@ PLATFORM=
 COMPILER="unknown"
 VERSION="unknown"
 DATE=`date "+%y-%m-%d"`
-
-CONFIGURE=
-MAKE=
-CHECK=
-RUN=
 COMMENT=
 
+R_CONFIGURE=
+R_MAKE=
+R_CHECK=
+R_RUN=
+
+[ -n "$MAKE" ] || MAKE="make"
+export MAKE CC
+
 while [ $# -gt 0 ]; do
 	case "$1" in
 		"-v")
@@ -61,20 +64,20 @@ if [ -r ./configure ]; then
 	echo "$NAME: Running \"./configure\" script ..."
 	[ -n "$VERBOSE" ] && ./configure || ./configure >/dev/null
 	if [ $? -eq 0 -a -r ./Makefile ]; then
-		CONFIGURE=1
-		echo "$NAME: Running \"make\" ..."
-		[ -n "$VERBOSE" ] && make || make >/dev/null
+		R_CONFIGURE=1
+		echo "$NAME: Running \"$MAKE\" ..."
+		[ -n "$VERBOSE" ] && "$MAKE" || "$MAKE" >/dev/null
 		if [ $? -eq 0 -a -x src/ngircd/ngircd ]; then
-			MAKE=1
-			echo "$NAME: Running \"make check\" ..."
-			[ -n "$VERBOSE" ] && make check || make check >/dev/null
+			R_MAKE=1
+			echo "$NAME: Running \"$MAKE check\" ..."
+			[ -n "$VERBOSE" ] && "$MAKE" check || "$MAKE" check >/dev/null
 			if [ $? -eq 0 ]; then
-				CHECK=1
-				RUN=$CHECK
+				R_CHECK=1
+				R_RUN=$R_CHECK
 			else
 				./src/ngircd/ngircd --help 2>/dev/null \
 				 | grep "^ngircd" >/dev/null
-				[ $? -eq 0 ] && RUN=1
+				[ $? -eq 0 ] && R_RUN=1
 			fi
 		fi
 	fi
@@ -96,9 +99,14 @@ if [ -r "Makefile" ]; then
 	CC=$(grep "^CC = " Makefile | cut -d' ' -f3)
 	$CC --version 2>&1 | grep -i "GCC" >/dev/null
 	if [ $? -eq 0 ]; then
-		COMPILER=$($CC --version | head -1 \
-		  | cut -d')' -f2 | cut -d' ' -f2)
-		COMPILER="gcc $COMPILER"
+		$CC --version 2>&1 | grep -i "Open64" >/dev/null
+		if [ $? -eq 0 ]; then
+			COMPILER="Open64"
+		else
+			COMPILER=$($CC --version | head -1 \
+			  | cut -d')' -f2 | cut -d' ' -f2)
+			COMPILER="gcc $COMPILER"
+		fi
 	else
 		case "$CC" in
 		  gcc*)
@@ -130,10 +138,10 @@ else
 	[ $? -eq 0 ] && COMMENT="(3)"
 fi
 
-[ -n "$CONFIGURE" ] && C="Y" || C="N"
-[ -n "$MAKE" ] && M="Y" || M="N"
-[ -n "$CHECK" ] && T="Y" || T="N"
-[ -n "$RUN" ] && R="Y" || R="N"
+[ -n "$R_CONFIGURE" ] && C="Y" || C="N"
+[ -n "$R_MAKE" ] && M="Y" || M="N"
+[ -n "$R_CHECK" ] && T="Y" || T="N"
+[ -n "$R_RUN" ] && R="Y" || R="N"
 [ -n "$COMMENT" ] && COMMENT=" $COMMENT"
 
 echo

+ 23 - 0
doc/Capabilities.txt

@@ -0,0 +1,23 @@
+
+                     ngIRCd - Next Generation IRC Server
+                           http://ngircd.barton.de/
+
+               (c)2001-2012 Alexander Barton and Contributors.
+               ngIRCd is free software and published under the
+                   terms of the GNU General Public License.
+
+                            -- Capabilities.txt --
+
+
+This document lists and describes the "IRC capabilities" that ngIRCd supports
+and can be requested by a IRC/IRCv3 client that supports the "CAP" command.
+
+ngIRCd implements the "IRC Client Capabilities Extension" as described here:
+<http://www.leeh.co.uk/draft-mitchell-irc-capabilities-02.html>
+
+
+I. Supported Capabilities
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+None. At the moment, ngIRCd supports the "CAP" command and its sub-commands
+but offers no capabilities that could be requested by a client.

+ 11 - 3
doc/FAQ.txt

@@ -64,11 +64,19 @@ A: ngIRCd does not write its own log file. Instead, ngIRCd uses syslog(3).
 
 Q: I cannot connect to remote peers when I use the chroot option, the
    following is logged: "Can't resolve example.com: unknown error!".
+A: see next question blow ...
+
+Q: When running ngIRCd inside a chroot, no IP addresses can be translated
+   in DNS names, errors like "Name or service not known" are logged.
 A: On Linux/glibc with chroot enabled you need to put some libraries inside
    the chroot as well, notably libnss_dns; maybe others. Unfortunately, even
-   linking ngIRCd statically does not help this. The only known workaround
-   is to either disable chroot support or to link against dietlibc instead
-   of glibc. (tnx to Sebastian Siewior)
+   linking ngIRCd statically does not help this. So you can either copy
+   all the required files into the chroot directory:
+     $ mkdir -p ./chroot/etc ./chroot/lib
+     $ cp -a /etc/hosts /etc/resolv.conf /etc/nsswitch.conf ./chroot/etc/
+     $ cp -a /lib/libresolv* /lib/libnss_* ./chroot/lib/
+   Or you can try to link ngIRCd against an other C library (like dietlibc)
+   that doesn't depend on NSS modules and/or these files.
 
 Q: I have added an [Oper] section, how do i log on as IRC operator?
 A: You can use the /OPER command in your IRC client to become an IRC operator.

+ 17 - 4
doc/Makefile.am

@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -16,9 +16,22 @@
 
 SUFFIXES = .tmpl
 
-static_docs = Bopm.txt FAQ.txt GIT.txt HowToRelease.txt Modes.txt PAM.txt \
-	Platforms.txt Protocol.txt README-AUX.txt README-BeOS.txt \
-	README-Interix.txt RFC.txt SSL.txt Services.txt
+static_docs = \
+	Bopm.txt \
+	Capabilities.txt \
+	FAQ.txt \
+	GIT.txt \
+	HowToRelease.txt \
+	Modes.txt \
+	PAM.txt \
+	Platforms.txt \
+	Protocol.txt \
+	README-AUX.txt \
+	README-BeOS.txt \
+	README-Interix.txt \
+	RFC.txt \
+	Services.txt \
+	SSL.txt
 
 doc_templates = sample-ngircd.conf.tmpl
 

+ 17 - 4
doc/Makefile.in

@@ -17,7 +17,7 @@
 
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -207,9 +207,22 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 SUFFIXES = .tmpl
-static_docs = Bopm.txt FAQ.txt GIT.txt HowToRelease.txt Modes.txt PAM.txt \
-	Platforms.txt Protocol.txt README-AUX.txt README-BeOS.txt \
-	README-Interix.txt RFC.txt SSL.txt Services.txt
+static_docs = \
+	Bopm.txt \
+	Capabilities.txt \
+	FAQ.txt \
+	GIT.txt \
+	HowToRelease.txt \
+	Modes.txt \
+	PAM.txt \
+	Platforms.txt \
+	Protocol.txt \
+	README-AUX.txt \
+	README-BeOS.txt \
+	README-Interix.txt \
+	RFC.txt \
+	Services.txt \
+	SSL.txt
 
 doc_templates = sample-ngircd.conf.tmpl
 generated_docs = sample-ngircd.conf

+ 1 - 0
doc/Modes.txt

@@ -43,6 +43,7 @@ users to lists (e.g. "invite list", "ban list"), others have parameters
   mode	since	description
 
   b	0.5.0	Add/remove a host mask to the ban list.
+  e	19	Add/remove a host mask to the exception list.
   i	0.5.0	Channel is "invite only".
   I	0.5.0	Add/remove a host mask to the invite list.
   k	0.6.0	Channel has a "key" (a password).

+ 5 - 2
doc/Platforms.txt

@@ -26,6 +26,8 @@ list can be updated. Thanks for your help!
 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
@@ -35,7 +37,7 @@ 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    18         11-08-17 goetz  Y Y Y 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    19         12-02-26 alex   Y Y Y Y (3)
 i386/unknown/freebsd7.3     gcc 4.2.1    19         12-02-26 alex   Y Y Y Y (3)
@@ -49,6 +51,7 @@ 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/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)
@@ -62,7 +65,7 @@ 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      CVSHEAD    06-05-07 fw     Y Y Y Y (3)
+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

+ 22 - 2
doc/Services.txt

@@ -14,8 +14,9 @@ But services acting as a "regular server" are supported, either using the IRC
 protocol defined in RFC 1459 or RFC 2812.
 
 Support for Services has been tested using "IRC Services" version 5.x by
-Andrew Church (<http://achurch.org/services/>), and a Anope 1.9 using a
-preliminary protocol module for ngIRCd (<http://www.anope.org/>).
+Andrew Church (<http://achurch.org/services/>), Anope 1.9 using a
+preliminary protocol module for ngIRCd (<http://www.anope.org/>), and
+Atheme 7.0.2 or later.
 
 This document describes setting up ngIRCd and these services.
 
@@ -93,5 +94,24 @@ The documentation of IRC Services can be found here:
 <http://www.ircservices.za.net/docs/>
 
 
+Setting up Atheme 7.0.2 or later
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Atheme 7.0.2 or later may be used with ngIRCd using the "ngircd" protocol
+module.
+
+The following settings need to be in atheme.conf:
+
+loadmodule "modules/protocol/ngircd";
+
+uplink "server.irc.net" {
+	password = "123abc";
+	port = 6667;
+};
+
+The documentation of Atheme can be found in the doc/ directory of the
+Atheme source distribution.
+
+
 Please let us know if you are successfully using other IRC service packages or
 which problems you encounter, thanks!

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

@@ -127,7 +127,13 @@
 	# Set this hostname for every client instead of the real one.
 	# Please note: don't use the percentage sign ("%"), it is reserved for
 	# future extensions!
-	;CloakHost = irc.example.net
+	;CloakHost = cloaked.host
+
+	# Use this hostname for hostname cloaking on clients that have the
+	# user mode "+x" set, instead of the name of the server.
+	# Please note: don't use the percentage sign ("%"), it is reserved for
+	# future extensions!
+	;CloakHostModeX = cloaked.user
 
 	# Set every clients' user name to their nick name
 	;CloakUserToNick = yes

+ 11 - 0
man/ngircd.conf.5.tmpl

@@ -220,6 +220,17 @@ don't change.
 Don't use the percentage sign ("%"), it is reserved for future extensions!
 .RE
 .TP
+\fBCloakHostModeX\fR (string)
+Use this hostname for hostname cloaking on clients that have the user mode
+"+x" set, instead of the name of the server. Default: empty, use the name
+of the server.
+.PP
+.RS
+.B Please note:
+.br
+Don't use the percentage sign ("%"), it is reserved for future extensions!
+.RE
+.TP
 \fBCloakUserToNick\fR (boolean)
 Set every clients' user name to their nick name and hide the one supplied
 by the IRC client. Default: no.

+ 74 - 11
src/ngircd/Makefile.am

@@ -1,6 +1,6 @@
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -18,21 +18,84 @@ LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \
 
 sbin_PROGRAMS = ngircd
 
-ngircd_SOURCES = ngircd.c array.c channel.c class.c client.c conf.c conn.c \
-	conn-func.c conn-ssl.c conn-zip.c hash.c io.c irc.c irc-channel.c \
-	irc-info.c irc-login.c irc-mode.c irc-op.c irc-oper.c irc-server.c \
-	irc-write.c lists.c log.c match.c op.c numeric.c pam.c parse.c \
-	proc.c resolve.c sighandlers.c
+ngircd_SOURCES = \
+	ngircd.c \
+	array.c \
+	channel.c \
+	class.c \
+	client.c \
+	client-cap.c \
+	conf.c \
+	conn.c \
+	conn-func.c \
+	conn-ssl.c \
+	conn-zip.c \
+	hash.c \
+	io.c \
+	irc.c \
+	irc-cap.c \
+	irc-channel.c \
+	irc-info.c \
+	irc-login.c \
+	irc-mode.c \
+	irc-op.c \
+	irc-oper.c \
+	irc-server.c \
+	irc-write.c \
+	lists.c \
+	log.c \
+	login.c \
+	match.c \
+	numeric.c \
+	op.c \
+	pam.c \
+	parse.c \
+	proc.c \
+	resolve.c \
+	sighandlers.c
 
 ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr
 
 ngircd_LDADD = -lngportab -lngtool -lngipaddr
 
-noinst_HEADERS = ngircd.h array.h channel.h class.h client.h conf.h \
-	conf-ssl.h conn.h conn-func.h conn-ssl.h conn-zip.h hash.h io.h \
-	irc.h irc-channel.h irc-info.h irc-login.h irc-mode.h irc-op.h \
-	irc-oper.h irc-server.h irc-write.h lists.h log.h match.h numeric.h \
-	op.h pam.h parse.h proc.h resolve.h sighandlers.h defines.h messages.h
+noinst_HEADERS = \
+	ngircd.h \
+	array.h \
+	channel.h \
+	class.h \
+	client.h \
+	client-cap.h \
+	conf.h \
+	conf-ssl.h \
+	conn.h \
+	conn-func.h \
+	conn-ssl.h \
+	conn-zip.h \
+	defines.h \
+	hash.h \
+	io.h \
+	irc.h \
+	irc-cap.h \
+	irc-channel.h \
+	irc-info.h \
+	irc-login.h \
+	irc-mode.h \
+	irc-op.h \
+	irc-oper.h \
+	irc-server.h \
+	irc-write.h \
+	lists.h \
+	log.h \
+	login.h \
+	match.h \
+	messages.h \
+	numeric.h \
+	op.h \
+	pam.h \
+	parse.h \
+	proc.h \
+	resolve.h \
+	sighandlers.h
 
 clean-local:
 	rm -f check-version check-help lint.out

+ 98 - 23
src/ngircd/Makefile.in

@@ -17,7 +17,7 @@
 
 #
 # ngIRCd -- The Next Generation IRC Daemon
-# Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
+# Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -64,15 +64,17 @@ am__installdirs = "$(DESTDIR)$(sbindir)"
 PROGRAMS = $(sbin_PROGRAMS)
 am_ngircd_OBJECTS = ngircd$U.$(OBJEXT) array$U.$(OBJEXT) \
 	channel$U.$(OBJEXT) class$U.$(OBJEXT) client$U.$(OBJEXT) \
-	conf$U.$(OBJEXT) conn$U.$(OBJEXT) conn-func$U.$(OBJEXT) \
-	conn-ssl$U.$(OBJEXT) conn-zip$U.$(OBJEXT) hash$U.$(OBJEXT) \
-	io$U.$(OBJEXT) irc$U.$(OBJEXT) irc-channel$U.$(OBJEXT) \
+	client-cap$U.$(OBJEXT) conf$U.$(OBJEXT) conn$U.$(OBJEXT) \
+	conn-func$U.$(OBJEXT) conn-ssl$U.$(OBJEXT) \
+	conn-zip$U.$(OBJEXT) hash$U.$(OBJEXT) io$U.$(OBJEXT) \
+	irc$U.$(OBJEXT) irc-cap$U.$(OBJEXT) irc-channel$U.$(OBJEXT) \
 	irc-info$U.$(OBJEXT) irc-login$U.$(OBJEXT) \
 	irc-mode$U.$(OBJEXT) irc-op$U.$(OBJEXT) irc-oper$U.$(OBJEXT) \
 	irc-server$U.$(OBJEXT) irc-write$U.$(OBJEXT) lists$U.$(OBJEXT) \
-	log$U.$(OBJEXT) match$U.$(OBJEXT) op$U.$(OBJEXT) \
-	numeric$U.$(OBJEXT) pam$U.$(OBJEXT) parse$U.$(OBJEXT) \
-	proc$U.$(OBJEXT) resolve$U.$(OBJEXT) sighandlers$U.$(OBJEXT)
+	log$U.$(OBJEXT) login$U.$(OBJEXT) match$U.$(OBJEXT) \
+	numeric$U.$(OBJEXT) op$U.$(OBJEXT) pam$U.$(OBJEXT) \
+	parse$U.$(OBJEXT) proc$U.$(OBJEXT) resolve$U.$(OBJEXT) \
+	sighandlers$U.$(OBJEXT)
 ngircd_OBJECTS = $(am_ngircd_OBJECTS)
 ngircd_DEPENDENCIES =
 ngircd_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(ngircd_LDFLAGS) \
@@ -212,19 +214,82 @@ INCLUDES = -I$(srcdir)/../portab -I$(srcdir)/../tool -I$(srcdir)/../ipaddr
 LINTARGS = -weak -warnunixlib +unixlib -booltype BOOLEAN \
  -varuse -retvalother -emptyret -unrecog
 
-ngircd_SOURCES = ngircd.c array.c channel.c class.c client.c conf.c conn.c \
-	conn-func.c conn-ssl.c conn-zip.c hash.c io.c irc.c irc-channel.c \
-	irc-info.c irc-login.c irc-mode.c irc-op.c irc-oper.c irc-server.c \
-	irc-write.c lists.c log.c match.c op.c numeric.c pam.c parse.c \
-	proc.c resolve.c sighandlers.c
+ngircd_SOURCES = \
+	ngircd.c \
+	array.c \
+	channel.c \
+	class.c \
+	client.c \
+	client-cap.c \
+	conf.c \
+	conn.c \
+	conn-func.c \
+	conn-ssl.c \
+	conn-zip.c \
+	hash.c \
+	io.c \
+	irc.c \
+	irc-cap.c \
+	irc-channel.c \
+	irc-info.c \
+	irc-login.c \
+	irc-mode.c \
+	irc-op.c \
+	irc-oper.c \
+	irc-server.c \
+	irc-write.c \
+	lists.c \
+	log.c \
+	login.c \
+	match.c \
+	numeric.c \
+	op.c \
+	pam.c \
+	parse.c \
+	proc.c \
+	resolve.c \
+	sighandlers.c
 
 ngircd_LDFLAGS = -L../portab -L../tool -L../ipaddr
 ngircd_LDADD = -lngportab -lngtool -lngipaddr
-noinst_HEADERS = ngircd.h array.h channel.h class.h client.h conf.h \
-	conf-ssl.h conn.h conn-func.h conn-ssl.h conn-zip.h hash.h io.h \
-	irc.h irc-channel.h irc-info.h irc-login.h irc-mode.h irc-op.h \
-	irc-oper.h irc-server.h irc-write.h lists.h log.h match.h numeric.h \
-	op.h pam.h parse.h proc.h resolve.h sighandlers.h defines.h messages.h
+noinst_HEADERS = \
+	ngircd.h \
+	array.h \
+	channel.h \
+	class.h \
+	client.h \
+	client-cap.h \
+	conf.h \
+	conf-ssl.h \
+	conn.h \
+	conn-func.h \
+	conn-ssl.h \
+	conn-zip.h \
+	defines.h \
+	hash.h \
+	io.h \
+	irc.h \
+	irc-cap.h \
+	irc-channel.h \
+	irc-info.h \
+	irc-login.h \
+	irc-mode.h \
+	irc-op.h \
+	irc-oper.h \
+	irc-server.h \
+	irc-write.h \
+	lists.h \
+	log.h \
+	login.h \
+	match.h \
+	messages.h \
+	numeric.h \
+	op.h \
+	pam.h \
+	parse.h \
+	proc.h \
+	resolve.h \
+	sighandlers.h
 
 TESTS = check-version check-help
 all: all-am
@@ -317,6 +382,7 @@ mostlyclean-kr:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channel$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/class$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client-cap$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conn-func$U.Po@am__quote@
@@ -325,6 +391,7 @@ mostlyclean-kr:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/io$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irc$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irc-cap$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irc-channel$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irc-info$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irc-login$U.Po@am__quote@
@@ -335,6 +402,7 @@ mostlyclean-kr:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irc-write$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lists$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log$U.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/login$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/match$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ngircd$U.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/numeric$U.Po@am__quote@
@@ -368,6 +436,8 @@ class_.c: class.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/class.c; then echo $(srcdir)/class.c; else echo class.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 client_.c: client.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/client.c; then echo $(srcdir)/client.c; else echo client.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+client-cap_.c: client-cap.c $(ANSI2KNR)
+	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/client-cap.c; then echo $(srcdir)/client-cap.c; else echo client-cap.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 conf_.c: conf.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/conf.c; then echo $(srcdir)/conf.c; else echo conf.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 conn_.c: conn.c $(ANSI2KNR)
@@ -384,6 +454,8 @@ io_.c: io.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/io.c; then echo $(srcdir)/io.c; else echo io.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 irc_.c: irc.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/irc.c; then echo $(srcdir)/irc.c; else echo irc.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+irc-cap_.c: irc-cap.c $(ANSI2KNR)
+	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/irc-cap.c; then echo $(srcdir)/irc-cap.c; else echo irc-cap.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 irc-channel_.c: irc-channel.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/irc-channel.c; then echo $(srcdir)/irc-channel.c; else echo irc-channel.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 irc-info_.c: irc-info.c $(ANSI2KNR)
@@ -404,6 +476,8 @@ lists_.c: lists.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/lists.c; then echo $(srcdir)/lists.c; else echo lists.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 log_.c: log.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/log.c; then echo $(srcdir)/log.c; else echo log.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
+login_.c: login.c $(ANSI2KNR)
+	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/login.c; then echo $(srcdir)/login.c; else echo login.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 match_.c: match.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/match.c; then echo $(srcdir)/match.c; else echo match.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 ngircd_.c: ngircd.c $(ANSI2KNR)
@@ -423,12 +497,13 @@ resolve_.c: resolve.c $(ANSI2KNR)
 sighandlers_.c: sighandlers.c $(ANSI2KNR)
 	$(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) `if test -f $(srcdir)/sighandlers.c; then echo $(srcdir)/sighandlers.c; else echo sighandlers.c; fi` | sed 's/^# \([0-9]\)/#line \1/' | $(ANSI2KNR) > $@ || rm -f $@
 array_.$(OBJEXT) channel_.$(OBJEXT) class_.$(OBJEXT) client_.$(OBJEXT) \
-conf_.$(OBJEXT) conn_.$(OBJEXT) conn-func_.$(OBJEXT) \
-conn-ssl_.$(OBJEXT) conn-zip_.$(OBJEXT) hash_.$(OBJEXT) io_.$(OBJEXT) \
-irc_.$(OBJEXT) irc-channel_.$(OBJEXT) irc-info_.$(OBJEXT) \
-irc-login_.$(OBJEXT) irc-mode_.$(OBJEXT) irc-op_.$(OBJEXT) \
-irc-oper_.$(OBJEXT) irc-server_.$(OBJEXT) irc-write_.$(OBJEXT) \
-lists_.$(OBJEXT) log_.$(OBJEXT) match_.$(OBJEXT) ngircd_.$(OBJEXT) \
+client-cap_.$(OBJEXT) conf_.$(OBJEXT) conn_.$(OBJEXT) \
+conn-func_.$(OBJEXT) conn-ssl_.$(OBJEXT) conn-zip_.$(OBJEXT) \
+hash_.$(OBJEXT) io_.$(OBJEXT) irc_.$(OBJEXT) irc-cap_.$(OBJEXT) \
+irc-channel_.$(OBJEXT) irc-info_.$(OBJEXT) irc-login_.$(OBJEXT) \
+irc-mode_.$(OBJEXT) irc-op_.$(OBJEXT) irc-oper_.$(OBJEXT) \
+irc-server_.$(OBJEXT) irc-write_.$(OBJEXT) lists_.$(OBJEXT) \
+log_.$(OBJEXT) login_.$(OBJEXT) match_.$(OBJEXT) ngircd_.$(OBJEXT) \
 numeric_.$(OBJEXT) op_.$(OBJEXT) pam_.$(OBJEXT) parse_.$(OBJEXT) \
 proc_.$(OBJEXT) resolve_.$(OBJEXT) sighandlers_.$(OBJEXT) : \
 $(ANSI2KNR)

+ 73 - 0
src/ngircd/client-cap.c

@@ -0,0 +1,73 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#define __client_cap_c__
+
+#include "portab.h"
+
+/**
+ * @file
+ * Functions to deal with IRC Capabilities
+ */
+
+#include "imp.h"
+#include <assert.h>
+
+#include "defines.h"
+#include "conn.h"
+#include "client.h"
+#include "log.h"
+
+#include "exp.h"
+#include "client-cap.h"
+
+GLOBAL int
+Client_Cap(CLIENT *Client)
+{
+	assert (Client != NULL);
+
+	return Client->capabilities;
+}
+
+GLOBAL void
+Client_CapSet(CLIENT *Client, int Cap)
+{
+	assert(Client != NULL);
+	assert(Cap >= 0);
+
+	Client->capabilities = Cap;
+	LogDebug("Set new capability of \"%s\" to %d.",
+		 Client_ID(Client), Client->capabilities);
+}
+
+GLOBAL void
+Client_CapAdd(CLIENT *Client, int Cap)
+{
+	assert(Client != NULL);
+	assert(Cap > 0);
+
+	Client->capabilities |= Cap;
+	LogDebug("Add capability %d, new capability of \"%s\" is %d.",
+		 Cap, Client_ID(Client), Client->capabilities);
+}
+
+GLOBAL void
+Client_CapDel(CLIENT *Client, int Cap)
+{
+	assert(Client != NULL);
+	assert(Cap > 0);
+
+	Client->capabilities &= ~Cap;
+	LogDebug("Delete capability %d, new capability of \"%s\" is %d.",
+		 Cap, Client_ID(Client), Client->capabilities);
+}
+
+/* -eof- */

+ 31 - 0
src/ngircd/client-cap.h

@@ -0,0 +1,31 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#ifndef __client_cap_h__
+#define __client_cap_h__
+
+/**
+ * @file
+ * Functions to deal with IRC Capabilities (header)
+ */
+
+#define CLIENT_CAP_PENDING 1		/* Capability negotiation pending */
+#define CLIENT_CAP_SUPPORTED 2		/* Client supports IRC capabilities */
+
+#define CLIENT_CAP_MULTI_PREFIX 4	/* multi-prefix */
+
+GLOBAL int Client_Cap PARAMS((CLIENT *Client));
+
+GLOBAL void Client_CapSet PARAMS((CLIENT *Client, int Cap));
+GLOBAL void Client_CapAdd PARAMS((CLIENT *Client, int Cap));
+GLOBAL void Client_CapDel PARAMS((CLIENT *Client, int Cap));
+
+#endif

+ 99 - 1
src/ngircd/client.c

@@ -37,6 +37,7 @@
 #include "ngircd.h"
 #include "channel.h"
 #include "conf.h"
+#include "conn-func.h"
 #include "hash.h"
 #include "irc-write.h"
 #include "log.h"
@@ -69,6 +70,8 @@ static CLIENT *Init_New_Client PARAMS((CONN_ID Idx, CLIENT *Introducer,
 static void Destroy_UserOrService PARAMS((CLIENT *Client,const char *Txt, const char *FwdMsg,
 					bool SendQuit));
 
+static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix,
+				       void *i));
 
 GLOBAL void
 Client_Init( void )
@@ -822,7 +825,9 @@ Client_MaskCloaked(CLIENT *Client)
 	    return Client_Mask(Client);
 
 	snprintf(Mask_Buffer, GETID_LEN, "%s!%s@%s",
-		 Client->id, Client->user, Client_ID(Client->introducer));
+		 Client->id, Client->user,
+		 *Conf_CloakHostModeX ? Conf_CloakHostModeX
+				      : Client_ID(Client->introducer));
 	return Mask_Buffer;
 } /* Client_MaskCloaked */
 
@@ -1142,6 +1147,46 @@ Client_Reject(CLIENT *Client, const char *Reason, bool InformClient)
 }
 
 
+/**
+ * Introduce a new user or service client in the network.
+ *
+ * @param From Remote server introducing the client or NULL (local).
+ * @param Client New client.
+ * @param Type Type of the client (CLIENT_USER or CLIENT_SERVICE).
+ */
+GLOBAL void
+Client_Introduce(CLIENT *From, CLIENT *Client, int Type)
+{
+	/* Set client type (user or service) */
+	Client_SetType(Client, Type);
+
+	if (From) {
+		if (Conf_IsService(Conf_GetServer(Client_Conn(From)),
+				   Client_ID(Client)))
+			Client_SetType(Client, CLIENT_SERVICE);
+		LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).",
+			 Client_TypeText(Client), Client_Mask(Client),
+			 Client_Modes(Client), Client_ID(From),
+			 Client_ID(Client_Introducer(Client)),
+			 Client_Hops(Client), Client_Hops(Client) > 1 ? "s": "");
+	} else {
+		Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).",
+		    Client_TypeText(Client), Client_Mask(Client),
+		    Client_Conn(Client));
+		Log_ServerNotice('c', "Client connecting: %s (%s@%s) [%s] - %s",
+			         Client_ID(Client), Client_User(Client),
+				 Client_Hostname(Client),
+				 Conn_IPA(Client_Conn(Client)),
+				 Client_TypeText(Client));
+	}
+
+	/* Inform other servers */
+	IRC_WriteStrServersPrefixFlag_CB(From,
+				From != NULL ? From : Client_ThisServer(),
+				'\0', cb_introduceClient, (void *)Client);
+} /* Client_Introduce */
+
+
 static unsigned long
 Count( CLIENT_TYPE Type )
 {
@@ -1361,6 +1406,59 @@ Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool
 } /* Destroy_UserOrService */
 
 
+/**
+ * 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.
+ */
+static void
+cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
+{
+	CLIENT *c = (CLIENT *)data;
+	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) : "-";
+
+	conn = Client_Conn(To);
+	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);
+	} 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));
+	}
+} /* cb_introduceClient */
+
+
 #ifdef DEBUG
 
 GLOBAL void

+ 6 - 2
src/ngircd/client.h

@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2008 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,12 +29,13 @@
 #ifndef STRICT_RFC
 # define CLIENT_WAITAUTHPING 512	/* waiting for AUTH PONG from client */
 #endif
+#define CLIENT_WAITCAPEND 1024		/* waiting for "CAP END" command */
 
 #define CLIENT_TYPE int
 
 #include "defines.h"
 
-#if defined(__client_c__) | defined(S_SPLINT_S)
+#if defined(__client_c__) | defined(__client_cap_c__) | defined(S_SPLINT_S)
 
 typedef struct _CLIENT
 {
@@ -58,6 +59,7 @@ typedef struct _CLIENT
 	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 flags[CLIENT_FLAGS_LEN];	/* flags of the client */
+	int capabilities;		/* enabled IRC capabilities */
 } CLIENT;
 
 #else
@@ -165,6 +167,8 @@ GLOBAL const char *Client_TypeText PARAMS((CLIENT *Client));
 
 GLOBAL void Client_Reject PARAMS((CLIENT *Client, const char *Reason,
 				  bool InformClient));
+GLOBAL void Client_Introduce PARAMS((CLIENT *From, CLIENT *Client, int Type));
+
 
 #ifdef DEBUG
 GLOBAL void Client_DebugDump PARAMS((void));

+ 17 - 1
src/ngircd/conf.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-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -358,6 +358,7 @@ Conf_Test( void )
 	printf("  AllowRemoteOper = %s\n", yesno_to_str(Conf_AllowRemoteOper));
 	printf("  ChrootDir = %s\n", Conf_Chroot);
 	printf("  CloakHost = %s\n", Conf_CloakHost);
+	printf("  CloakHostModeX = %s\n", Conf_CloakHostModeX);
 	printf("  CloakUserToNick = %s\n", yesno_to_str(Conf_CloakUserToNick));
 #ifdef WANT_IPV6
 	printf("  ConnectIPv4 = %s\n", yesno_to_str(Conf_ConnectIPv6));
@@ -492,6 +493,14 @@ Conf_SetServer( int ConfServer, CONN_ID Idx )
 	assert( ConfServer > NONE );
 	assert( Idx > NONE );
 
+	if (Conf_Server[ConfServer].conn_id > NONE &&
+	    Conf_Server[ConfServer].conn_id != Idx) {
+		Log(LOG_ALERT,
+			"Trying to update connection index for already registered server \"%s\": %d/%d - ignored.",
+			Conf_Server[ConfServer].name,
+			Conf_Server[ConfServer].conn_id, Idx);
+		return;
+	}
 	Conf_Server[ConfServer].conn_id = Idx;
 }
 
@@ -676,6 +685,7 @@ Set_Defaults(bool InitServers)
 #endif
 	strlcpy(Conf_Chroot, CHROOT_DIR, sizeof(Conf_Chroot));
 	strcpy(Conf_CloakHost, "");
+	strcpy(Conf_CloakHostModeX, "");
 	Conf_CloakUserToNick = false;
 	Conf_ConnectIPv4 = true;
 #ifdef WANT_IPV6
@@ -1469,6 +1479,12 @@ Handle_OPTIONS(int Line, char *Var, char *Arg)
 			Config_Error_TooLong(Line, Var);
 		return;
 	}
+	if (strcasecmp(Var, "CloakHostModeX") == 0) {
+		len = strlcpy(Conf_CloakHostModeX, Arg, sizeof(Conf_CloakHostModeX));
+		if (len >= sizeof(Conf_CloakHostModeX))
+			Config_Error_TooLong(Line, Var);
+		return;
+	}
 	if (strcasecmp(Var, "CloakUserToNick") == 0) {
 		Conf_CloakUserToNick = Check_ArgIsTrue(Arg);
 		return;

+ 3 - 0
src/ngircd/conf.h

@@ -166,6 +166,9 @@ GLOBAL bool Conf_AllowRemoteOper;
 /** Cloaked hostname of the clients */
 GLOBAL char Conf_CloakHost[CLIENT_ID_LEN];
 
+/** Cloaked hostname for clients that did +x */
+GLOBAL char Conf_CloakHostModeX[CLIENT_ID_LEN];
+
 /** Use nick name as user name? */
 GLOBAL bool Conf_CloakUserToNick;
 

+ 5 - 4
src/ngircd/conn-ssl.c

@@ -549,17 +549,18 @@ ConnSSL_LogCertInfo( CONNECTION *c )
 
 	assert(ssl);
 
-	Log(LOG_INFO, "New %s connection using cipher %s on socket %d.",
-		SSL_get_version(ssl), SSL_get_cipher(ssl), c->sock);
+	Log(LOG_INFO, "Connection %d: initialized %s using cipher %s.",
+		c->sock, SSL_get_version(ssl), SSL_get_cipher(ssl));
 #endif
 #ifdef HAVE_LIBGNUTLS
 	gnutls_session_t sess = c->ssl_state.gnutls_session;
 	gnutls_cipher_algorithm_t cipher = gnutls_cipher_get(sess);
 
-	Log(LOG_INFO, "New %s connection using cipher %s-%s on socket %d.",
+	Log(LOG_INFO, "Connection %d: initialized %s using cipher %s-%s.",
+	    c->sock,
 	    gnutls_protocol_get_name(gnutls_protocol_get_version(sess)),
 	    gnutls_cipher_get_name(cipher),
-	    gnutls_mac_get_name(gnutls_mac_get(sess)), c->sock);
+	    gnutls_mac_get_name(gnutls_mac_get(sess)));
 #endif
 }
 

+ 34 - 6
src/ngircd/conn.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-2012 Alexander Barton (alex@barton.de) and Contributors.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -1935,6 +1935,14 @@ New_Server( int Server , ng_ipaddr_t *dest)
 
 	assert( Server > NONE );
 
+	/* Make sure that the remote server hasn't re-linked to this server
+	 * asynchronously on its own */
+	if (Conf_Server[Server].conn_id > NONE) {
+		Log(LOG_INFO,
+			"Connection to \"%s\" meanwhile re-established, aborting preparation.");
+		return;
+	}
+
 	if (!ng_ipaddr_tostr_r(dest, ip_str)) {
 		Log(LOG_WARNING, "New_Server: Could not convert IP to string");
 		return;
@@ -2008,7 +2016,7 @@ New_Server( int Server , ng_ipaddr_t *dest)
 	Client_SetToken( c, TOKEN_OUTBOUND );
 
 	/* Register connection */
-	Conf_Server[Server].conn_id = new_sock;
+	Conf_SetServer(Server, new_sock);
 	My_Connections[new_sock].sock = new_sock;
 	My_Connections[new_sock].addr = *dest;
 	My_Connections[new_sock].client = c;
@@ -2174,6 +2182,7 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 	char *identptr;
 #ifdef IDENTAUTH
 	char readbuf[HOST_LEN + 2 + CLIENT_USER_LEN];
+	char *ptr;
 #else
 	char readbuf[HOST_LEN + 1];
 #endif
@@ -2226,11 +2235,30 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 #ifdef IDENTAUTH
 		++identptr;
 		if (*identptr) {
-			Log(LOG_INFO, "IDENT lookup for connection %d: \"%s\".", i, identptr);
-			Client_SetUser(c, identptr, true);
-			if (Conf_NoticeAuth)
+			ptr = identptr;
+			while (*ptr) {
+				if ((*ptr < '0' || *ptr > '9') &&
+				    (*ptr < 'A' || *ptr > 'Z') &&
+				    (*ptr < 'a' || *ptr > 'z'))
+					break;
+				ptr++;
+			}
+			if (*ptr) {
+				/* Erroneous IDENT reply */
+				Log(LOG_NOTICE,
+				    "Got invalid IDENT reply for connection %d! Ignored.",
+				    i);
+			} else {
+				Log(LOG_INFO,
+				    "IDENT lookup for connection %d: \"%s\".",
+				    i, identptr);
+				Client_SetUser(c, identptr, true);
+			}
+			if (Conf_NoticeAuth) {
 				(void)Conn_WriteStr(i,
-					"NOTICE AUTH :*** Got ident response");
+					"NOTICE AUTH :*** Got %sident response",
+					*ptr ? "invalid " : "");
+			}
 		} else {
 			Log(LOG_INFO, "IDENT lookup for connection %d: no result.", i);
 			if (Conf_NoticeAuth && Conf_Ident)

+ 1 - 1
src/ngircd/defines.h

@@ -161,7 +161,7 @@
 #define USERMODES "acCiorRswx"
 
 /** Supported channel modes. */
-#define CHANMODES "beiIklmnoOPRstvz"
+#define CHANMODES "beiIklmnoOPrRstvz"
 
 /** Away message for users connected to linked servers. */
 #define DEFAULT_AWAY_MSG "Away"

+ 291 - 0
src/ngircd/irc-cap.c

@@ -0,0 +1,291 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#include "portab.h"
+
+/**
+ * @file
+ * Handler for IRC capability ("CAP") commands
+ */
+
+#include "imp.h"
+#include <assert.h>
+#include <string.h>
+
+#include "defines.h"
+#include "conn.h"
+#include "channel.h"
+#include "client-cap.h"
+#include "irc-write.h"
+#include "log.h"
+#include "login.h"
+#include "messages.h"
+#include "parse.h"
+
+#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));
+
+void Set_CAP_Negotiation PARAMS((CLIENT *Client));
+
+int Parse_CAP PARAMS((int Capabilities, char *Args));
+char *Get_CAP_String PARAMS((int Capabilities));
+
+/**
+ * Handler for the IRCv3 "CAP" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+IRC_CAP(CLIENT *Client, REQUEST *Req)
+{
+	assert(Client != NULL);
+	assert(Req != NULL);
+
+	/* Bad number of prameters? */
+	if (Req->argc < 1 || Req->argc > 2)
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
+
+	LogDebug("Got \"%s %s\" command from \"%s\" ...",
+		 Req->command, Req->argv[0], Client_ID(Client));
+
+	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 IRC_WriteStrClient(Client, ERR_INVALIDCAP_MSG,
+				  Client_ID(Client), Req->argv[0]);
+}
+
+/**
+ * Handler for the "CAP LS" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Arg Command argument or NULL.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+bool
+Handle_CAP_LS(CLIENT *Client, UNUSED char *Arg)
+{
+	assert(Client != NULL);
+
+	Set_CAP_Negotiation(Client);
+
+	return IRC_WriteStrClient(Client,
+				  "CAP %s LS :multi-prefix",
+				  Client_ID(Client));
+}
+
+/**
+ * Handler for the "CAP LIST" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Arg Command argument or NULL.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+bool
+Handle_CAP_LIST(CLIENT *Client, UNUSED char *Arg)
+{
+	assert(Client != NULL);
+
+	return IRC_WriteStrClient(Client, "CAP %s LIST :%s", Client_ID(Client),
+				  Get_CAP_String(Client_Cap(Client)));
+}
+
+/**
+ * Handler for the "CAP REQ" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Arg Command argument.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+bool
+Handle_CAP_REQ(CLIENT *Client, char *Arg)
+{
+	int new_cap;
+
+	assert(Client != NULL);
+	assert(Arg != NULL);
+
+	Set_CAP_Negotiation(Client);
+
+	new_cap = Parse_CAP(Client_Cap(Client), Arg);
+
+	if (new_cap < 0)
+		return IRC_WriteStrClient(Client, "CAP %s NAK :%s",
+					  Client_ID(Client), Arg);
+
+	Client_CapSet(Client, new_cap);
+	return IRC_WriteStrClient(Client, "CAP %s ACK :%s",
+				  Client_ID(Client), Arg);
+}
+
+/**
+ * Handler for the "CAP ACK" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Arg Command argument.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+bool
+Handle_CAP_ACK(UNUSED CLIENT *Client, UNUSED char *Arg)
+{
+	assert(Client != NULL);
+	assert(Arg != NULL);
+
+	return CONNECTED;
+}
+
+/**
+ * Handler for the "CAP CLEAR" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+bool
+Handle_CAP_CLEAR(CLIENT *Client)
+{
+	int cap_old;
+
+	assert(Client != NULL);
+
+	cap_old = Client_Cap(Client);
+	if (cap_old & CLIENT_CAP_MULTI_PREFIX)
+		Client_CapDel(Client, CLIENT_CAP_MULTI_PREFIX);
+
+	return IRC_WriteStrClient(Client, "CAP %s ACK :%s", Client_ID(Client),
+				  Get_CAP_String(cap_old));
+}
+
+/**
+ * Handler for the "CAP END" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+bool
+Handle_CAP_END(CLIENT *Client)
+{
+	assert(Client != NULL);
+
+	if (Client_Type(Client) != CLIENT_USER) {
+		/* User is still logging in ... */
+		Client_CapDel(Client, CLIENT_CAP_PENDING);
+
+		if (Client_Type(Client) == CLIENT_WAITCAPEND) {
+			/* Only "CAP END" was missing: log in! */
+			return Login_User(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);
+}
+
+/**
+ * Parse capability string and return numeric flag value.
+ *
+ * @param Args The string containing space-separated capability names.
+ * @return Changed capability flags or 0 on error.
+ */
+int
+Parse_CAP(int Capabilities, char *Args)
+{
+	static char tmp[COMMAND_LEN];
+	char *ptr;
+
+	assert(Args != NULL);
+
+	strlcpy(tmp, Args, sizeof(tmp));
+
+	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 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;
+}
+
+/* -eof- */

+ 24 - 0
src/ngircd/irc-cap.h

@@ -0,0 +1,24 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2010 Alexander Barton (alex@barton.de).
+ *
+ * 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_cap_h__
+#define __irc_cap_h__
+
+/**
+ * @file
+ * Handler for IRC capability ("CAP") commands (header)
+ */
+
+GLOBAL bool IRC_CAP PARAMS((CLIENT *Client, REQUEST *Req));
+
+#endif
+
+/* -eof- */

+ 61 - 35
src/ngircd/irc-info.c

@@ -39,6 +39,7 @@
 #include "parse.h"
 #include "irc.h"
 #include "irc-write.h"
+#include "client-cap.h"
 
 #include "exp.h"
 #include "irc-info.h"
@@ -807,8 +808,16 @@ who_flags_status(const char *client_modes)
 
 
 static const char *
-who_flags_qualifier(const char *chan_user_modes)
+who_flags_qualifier(CLIENT *Client, const char *chan_user_modes)
 {
+	assert(Client != NULL);
+
+	if (Client_Cap(Client) & CLIENT_CAP_MULTI_PREFIX) {
+		if (strchr(chan_user_modes, 'o') &&
+		    strchr(chan_user_modes, 'v'))
+			return "@+";
+	}
+
 	if (strchr(chan_user_modes, 'o'))
 		return "@";
 	else if (strchr(chan_user_modes, 'v'))
@@ -865,7 +874,7 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 				strlcat(flags, "*", sizeof(flags));
 
 			chan_user_modes = Channel_UserModes(Chan, c);
-			strlcat(flags, who_flags_qualifier(chan_user_modes),
+			strlcat(flags, who_flags_qualifier(c, chan_user_modes),
 				sizeof(flags));
 
 			if (!write_whoreply(Client, c, Channel_Name(Chan),
@@ -1078,7 +1087,7 @@ IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
 		if (str[strlen(str) - 1] != ':')
 			strlcat(str, " ", sizeof(str));
 
-		strlcat(str, who_flags_qualifier(Channel_UserModes(chan, c)),
+		strlcat(str, who_flags_qualifier(c, Channel_UserModes(chan, c)),
 						 sizeof(str));
 		strlcat(str, Channel_Name(chan), sizeof(str));
 
@@ -1524,60 +1533,77 @@ IRC_Show_MOTD( CLIENT *Client )
 } /* IRC_Show_MOTD */
 
 
+/**
+ * Send NAMES reply for a specific client and channel.
+ *
+ * @param Client The client requesting the NAMES information.
+ * @param Chan The channel
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
-IRC_Send_NAMES( CLIENT *Client, CHANNEL *Chan )
+IRC_Send_NAMES(CLIENT * Client, CHANNEL * Chan)
 {
 	bool is_visible, is_member;
 	char str[LINE_LEN + 1];
 	CL2CHAN *cl2chan;
 	CLIENT *cl;
 
-	assert( Client != NULL );
-	assert( Chan != NULL );
+	assert(Client != NULL);
+	assert(Chan != NULL);
 
-	if( Channel_IsMemberOf( Chan, Client )) is_member = true;
-	else is_member = false;
+	if (Channel_IsMemberOf(Chan, Client))
+		is_member = true;
+	else
+		is_member = false;
 
 	/* Do not print info on channel memberships to anyone that is not member? */
 	if (Conf_MorePrivacy && !is_member)
 		return CONNECTED;
 
 	/* Secret channel? */
-	if( ! is_member && strchr( Channel_Modes( Chan ), 's' )) return CONNECTED;
+	if (!is_member && strchr(Channel_Modes(Chan), 's'))
+		return CONNECTED;
 
-	/* Alle Mitglieder suchen */
-	snprintf( str, sizeof( str ), RPL_NAMREPLY_MSG, Client_ID( Client ), "=", Channel_Name( Chan ));
-	cl2chan = Channel_FirstMember( Chan );
-	while( cl2chan )
-	{
-		cl = Channel_GetClient( cl2chan );
+	snprintf(str, sizeof(str), RPL_NAMREPLY_MSG, Client_ID(Client), "=",
+		 Channel_Name(Chan));
+	cl2chan = Channel_FirstMember(Chan);
+	while (cl2chan) {
+		cl = Channel_GetClient(cl2chan);
 
-		if( strchr( Client_Modes( cl ), 'i' )) is_visible = false;
-		else is_visible = true;
+		if (strchr(Client_Modes(cl), 'i'))
+			is_visible = false;
+		else
+			is_visible = true;
 
-		if( is_member || is_visible )
-		{
-			/* Nick anhaengen */
-			if( str[strlen( str ) - 1] != ':' ) strlcat( str, " ", sizeof( str ));
-			if( strchr( Channel_UserModes( Chan, cl ), 'o' )) strlcat( str, "@", sizeof( str ));
-			else if( strchr( Channel_UserModes( Chan, cl ), 'v' )) strlcat( str, "+", sizeof( str ));
-			strlcat( str, Client_ID( cl ), sizeof( str ));
+		if (is_member || is_visible) {
+			if (str[strlen(str) - 1] != ':')
+				strlcat(str, " ", sizeof(str));
+			if (Client_Cap(cl) & CLIENT_CAP_MULTI_PREFIX) {
+				if (strchr(Channel_UserModes(Chan, cl), 'o') &&
+				    strchr(Channel_UserModes(Chan, cl), 'v'))
+					strlcat(str, "@+", sizeof(str));
+			} else {
+				if (strchr(Channel_UserModes(Chan, cl), 'o'))
+					strlcat(str, "@", sizeof(str));
+				else if (strchr(Channel_UserModes(Chan, cl), 'v'))
+					strlcat(str, "+", sizeof(str));
+			}
+			strlcat(str, Client_ID(cl), sizeof(str));
 
-			if( strlen( str ) > ( LINE_LEN - CLIENT_NICK_LEN - 4 ))
-			{
-				/* Zeile wird zu lang: senden! */
-				if( ! IRC_WriteStrClient( Client, "%s", str )) return DISCONNECTED;
-				snprintf( str, sizeof( str ), RPL_NAMREPLY_MSG, Client_ID( Client ), "=", Channel_Name( Chan ));
+			if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 4)) {
+				if (!IRC_WriteStrClient(Client, "%s", str))
+					return DISCONNECTED;
+				snprintf(str, sizeof(str), RPL_NAMREPLY_MSG,
+					 Client_ID(Client), "=",
+					 Channel_Name(Chan));
 			}
 		}
 
-		/* naechstes Mitglied suchen */
-		cl2chan = Channel_NextMember( Chan, cl2chan );
+		cl2chan = Channel_NextMember(Chan, cl2chan);
 	}
-	if( str[strlen( str ) - 1] != ':')
-	{
-		/* Es sind noch Daten da, die gesendet werden muessen */
-		if( ! IRC_WriteStrClient( Client, "%s", str )) return DISCONNECTED;
+	if (str[strlen(str) - 1] != ':') {
+		if (!IRC_WriteStrClient(Client, "%s", str))
+			return DISCONNECTED;
 	}
 
 	return CONNECTED;

+ 20 - 309
src/ngircd/irc-login.c

@@ -18,22 +18,17 @@
 
 #include "imp.h"
 #include <assert.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
-#include <signal.h>
-#include <unistd.h>
 
-#include "ngircd.h"
 #include "conn-func.h"
 #include "class.h"
 #include "conf.h"
 #include "channel.h"
-#include "io.h"
 #include "log.h"
+#include "login.h"
 #include "messages.h"
-#include "pam.h"
 #include "parse.h"
 #include "irc.h"
 #include "irc-info.h"
@@ -42,18 +37,7 @@
 #include "exp.h"
 #include "irc-login.h"
 
-
-static bool Hello_User PARAMS(( CLIENT *Client ));
-static bool Hello_User_PostAuth PARAMS(( CLIENT *Client ));
 static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
-static void Introduce_Client PARAMS((CLIENT *To, CLIENT *Client, int Type));
-
-static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix,
-				       void *i));
-
-#ifdef PAM
-static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events));
-#endif
 
 /**
  * Handler for the IRC "PASS" command.
@@ -285,7 +269,7 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 			/* If we received a valid USER command already then
 			 * register the new client! */
 			if( Client_Type( Client ) == CLIENT_GOTUSER )
-				return Hello_User( Client );
+				return Login_User( Client );
 			else
 				Client_SetType( Client, CLIENT_GOTNICK );
 		} else {
@@ -395,7 +379,7 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 				 Client_Mask(c));
 			Client_SetType(c, CLIENT_GOTNICK);
 		} else
-			Introduce_Client(Client, c, CLIENT_USER);
+			Client_Introduce(Client, c, CLIENT_USER);
 
 		return CONNECTED;
 	}
@@ -416,9 +400,7 @@ GLOBAL bool
 IRC_USER(CLIENT * Client, REQUEST * Req)
 {
 	CLIENT *c;
-#ifdef IDENTAUTH
 	char *ptr;
-#endif
 
 	assert(Client != NULL);
 	assert(Req != NULL);
@@ -436,7 +418,19 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 						  Client_ID(Client),
 						  Req->command);
 
-		/* User name */
+		/* User name: only alphanumeric characters are allowed! */
+		ptr = Req->argv[0];
+		while (*ptr) {
+			if ((*ptr < '0' || *ptr > '9') &&
+			    (*ptr < 'A' || *ptr > 'Z') &&
+			    (*ptr < 'a' || *ptr > 'z')) {
+				Conn_Close(Client_Conn(Client), NULL,
+					   "Invalid user name", true);
+				return DISCONNECTED;
+			}
+			ptr++;
+		}
+
 #ifdef IDENTAUTH
 		ptr = Client_User(Client);
 		if (!ptr || !*ptr || *ptr == '~')
@@ -457,7 +451,7 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 		LogDebug("Connection %d: got valid USER command ...",
 		    Client_Conn(Client));
 		if (Client_Type(Client) == CLIENT_GOTNICK)
-			return Hello_User(Client);
+			return Login_User(Client);
 		else
 			Client_SetType(Client, CLIENT_GOTUSER);
 		return CONNECTED;
@@ -487,7 +481,7 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 		/* RFC 1459 style user registration?
 		 * Introduce client to network: */
 		if (Client_Type(c) == CLIENT_GOTNICK)
-			Introduce_Client(Client, c, CLIENT_USER);
+			Client_Introduce(Client, c, CLIENT_USER);
 
 		return CONNECTED;
 	} else if (Client_Type(Client) == CLIENT_USER) {
@@ -601,7 +595,7 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req)
 		return CONNECTED;
 	}
 
-	Introduce_Client(Client, c, CLIENT_SERVICE);
+	Client_Introduce(Client, c, CLIENT_SERVICE);
 	return CONNECTED;
 } /* IRC_SERVICE */
 
@@ -880,7 +874,7 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 		if (auth_ping == atoi(Req->argv[0])) {
 			Conn_SetAuthPing(conn, 0);
 			if (Client_Type(Client) == CLIENT_WAITAUTHPING)
-				Hello_User(Client);
+				Login_User(Client);
 		} else
 			if (!IRC_WriteStrClient(Client,
 					"To connect, type /QUOTE PONG %ld",
@@ -904,196 +898,6 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 
 
 /**
- * Initiate client registration.
- *
- * This function is called after the daemon received the required NICK and
- * USER commands of a new client. If the daemon is compiled with support for
- * PAM, the authentication sub-processs is forked; otherwise the global server
- * password is checked.
- *
- * @param Client	The client logging in.
- * @returns		CONNECTED or DISCONNECTED.
- */
-static bool
-Hello_User(CLIENT * Client)
-{
-#ifdef PAM
-	int pipefd[2], result;
-	pid_t pid;
-#endif
-	CONN_ID conn;
-
-	assert(Client != NULL);
-	conn = Client_Conn(Client);
-
-#ifndef STRICT_RFC
-	if (Conf_AuthPing) {
-		/* Did we receive the "auth PONG" already? */
-		if (Conn_GetAuthPing(conn)) {
-			Client_SetType(Client, CLIENT_WAITAUTHPING);
-			LogDebug("Connection %d: Waiting for AUTH PONG ...", conn);
-			return CONNECTED;
-		}
-	}
-#endif
-
-#ifdef PAM
-	if (!Conf_PAM) {
-		/* Don't do any PAM authentication at all, instead emulate
-		 * the beahiour of the daemon compiled without PAM support:
-		 * because there can't be any "server password", all
-		 * passwords supplied are classified as "wrong". */
-		if(Client_Password(Client)[0] == '\0')
-			return Hello_User_PostAuth(Client);
-		Client_Reject(Client, "Non-empty password", false);
-		return DISCONNECTED;
-	}
-
-	if (Conf_PAMIsOptional && strcmp(Client_Password(Client), "") == 0) {
-		/* Clients are not required to send a password and to be PAM-
-		 * authenticated at all. If not, they won't become "identified"
-		 * and keep the "~" in their supplied user name.
-		 * Therefore it is sensible to either set Conf_PAMisOptional or
-		 * to enable IDENT lookups -- not both. */
-		return Hello_User_PostAuth(Client);
-	}
-
-	/* Fork child process for PAM authentication; and make sure that the
-	 * process timeout is set higher than the login timeout! */
-	pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
-			cb_Read_Auth_Result, Conf_PongTimeout + 1);
-	if (pid > 0) {
-		LogDebug("Authenticator for connection %d created (PID %d).",
-			 conn, pid);
-		return CONNECTED;
-	} else {
-		/* Sub process */
-		Log_Init_Subprocess("Auth");
-		Conn_CloseAllSockets(NONE);
-		result = PAM_Authenticate(Client);
-		if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
-			Log_Subprocess(LOG_ERR,
-				       "Failed to pipe result to parent!");
-		Log_Exit_Subprocess("Auth");
-		exit(0);
-	}
-#else
-	/* Check global server password ... */
-	if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
-		/* Bad password! */
-		Client_Reject(Client, "Bad server password", false);
-		return DISCONNECTED;
-	}
-	return Hello_User_PostAuth(Client);
-#endif
-}
-
-
-#ifdef PAM
-
-/**
- * Read result of the authenticatior sub-process from pipe
- *
- * @param r_fd		File descriptor of the pipe.
- * @param events	(ignored IO specification)
- */
-static void
-cb_Read_Auth_Result(int r_fd, UNUSED short events)
-{
-	CONN_ID conn;
-	CLIENT *client;
-	int result;
-	size_t len;
-	PROC_STAT *proc;
-
-	LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
-	conn = Conn_GetFromProc(r_fd);
-	if (conn == NONE) {
-		/* Ops, none found? Probably the connection has already
-		 * been closed!? We'll ignore that ... */
-		io_close(r_fd);
-		LogDebug("Auth: Got callback for unknown connection!?");
-		return;
-	}
-	proc = Conn_GetProcStat(conn);
-	client = Conn_GetClient(conn);
-
-	/* Read result from pipe */
-	len = Proc_Read(proc, &result, sizeof(result));
-	Proc_Close(proc);
-	if (len == 0)
-		return;
-
-	if (len != sizeof(result)) {
-		Log(LOG_CRIT, "Auth: Got malformed result!");
-		Client_Reject(client, "Internal error", false);
-		return;
-	}
-
-	if (result == true) {
-		Client_SetUser(client, Client_OrigUser(client), true);
-		(void)Hello_User_PostAuth(client);
-	} else
-		Client_Reject(client, "Bad password", false);
-}
-
-#endif
-
-
-/**
- * Finish client registration.
- *
- * Introduce the new client to the network and send all "hello messages"
- * to it after authentication has been succeeded.
- *
- * @param Client	The client logging in.
- * @returns		CONNECTED or DISCONNECTED.
- */
-static bool
-Hello_User_PostAuth(CLIENT *Client)
-{
-	assert(Client != NULL);
-
-	if (Class_HandleServerBans(Client) != CONNECTED)
-		return DISCONNECTED;
-
-	Introduce_Client(NULL, Client, CLIENT_USER);
-
-	if (!IRC_WriteStrClient
-	    (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
-		return false;
-	if (!IRC_WriteStrClient
-	    (Client, RPL_YOURHOST_MSG, Client_ID(Client),
-	     Client_ID(Client_ThisServer()), PACKAGE_VERSION, TARGET_CPU,
-	     TARGET_VENDOR, TARGET_OS))
-		return false;
-	if (!IRC_WriteStrClient
-	    (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
-		return false;
-	if (!IRC_WriteStrClient
-	    (Client, RPL_MYINFO_MSG, Client_ID(Client),
-	     Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
-	     CHANMODES))
-		return false;
-
-	/* Features supported by this server (005 numeric, ISUPPORT),
-	 * see <http://www.irc.org/tech_docs/005.html> for details. */
-	if (!IRC_Send_ISUPPORT(Client))
-		return DISCONNECTED;
-
-	if (!IRC_Send_LUSERS(Client))
-		return DISCONNECTED;
-	if (!IRC_Show_MOTD(Client))
-		return DISCONNECTED;
-
-	/* Suspend the client for a second ... */
-	IRC_SetPenalty(Client, 1);
-
-	return CONNECTED;
-}
-
-
-/**
  * Kill all users with a specific nick name in the network.
  *
  * @param Nick		Nick name.
@@ -1119,97 +923,4 @@ Kill_Nick(char *Nick, char *Reason)
 } /* Kill_Nick */
 
 
-/**
- * Introduce a new user or service client in the network.
- *
- * @param From		Remote server introducing the client or NULL (local).
- * @param Client	New client.
- * @param Type		Type of the client (CLIENT_USER or CLIENT_SERVICE).
- */
-static void
-Introduce_Client(CLIENT *From, CLIENT *Client, int Type)
-{
-	/* Set client type (user or service) */
-	Client_SetType(Client, Type);
-
-	if (From) {
-		if (Conf_IsService(Conf_GetServer(Client_Conn(From)),
-				   Client_ID(Client)))
-			Client_SetType(Client, CLIENT_SERVICE);
-		LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).",
-			 Client_TypeText(Client), Client_Mask(Client),
-			 Client_Modes(Client), Client_ID(From),
-			 Client_ID(Client_Introducer(Client)),
-			 Client_Hops(Client), Client_Hops(Client) > 1 ? "s": "");
-	} else {
-		Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).",
-		    Client_TypeText(Client), Client_Mask(Client),
-		    Client_Conn(Client));
-		Log_ServerNotice('c', "Client connecting: %s (%s@%s) [%s] - %s",
-			         Client_ID(Client), Client_User(Client),
-				 Client_Hostname(Client),
-				 Conn_IPA(Client_Conn(Client)),
-				 Client_TypeText(Client));
-	}
-
-	/* Inform other servers */
-	IRC_WriteStrServersPrefixFlag_CB(From,
-				From != NULL ? From : Client_ThisServer(),
-				'\0', cb_introduceClient, (void *)Client);
-} /* Introduce_Client */
-
-
-/**
- * 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.
- */
-static void
-cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
-{
-	CLIENT *c = (CLIENT *)data;
-	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) : "-";
-
-	conn = Client_Conn(To);
-	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);
-	} 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));
-	}
-} /* cb_introduceClient */
-
-
 /* -eof- */

+ 2 - 4
src/ngircd/lists.c

@@ -147,10 +147,8 @@ Lists_Add(struct list_head *h, const char *Mask, time_t ValidUntil,
 
 	strlcpy(newelem->mask, Mask, sizeof(newelem->mask));
 	if (Reason) {
-		newelem->reason = malloc(strlen(Reason) + 1);
-		if (newelem->reason)
-			strlcpy(newelem->reason, Reason, strlen(Reason) + 1);
-		else
+		newelem->reason = strdup(Reason);
+		if (!newelem->reason)
 			Log(LOG_EMERG,
 			    "Can't allocate memory for new list reason text!");
 	}

+ 243 - 0
src/ngircd/login.c

@@ -0,0 +1,243 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#include "portab.h"
+
+/**
+ * @file
+ * Functions to deal with client logins
+ */
+
+#include "imp.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "defines.h"
+#include "conn.h"
+#include "class.h"
+#include "client.h"
+#include "client-cap.h"
+#include "channel.h"
+#include "conf.h"
+#include "io.h"
+#include "parse.h"
+#include "log.h"
+#include "messages.h"
+#include "ngircd.h"
+#include "pam.h"
+#include "irc-info.h"
+#include "irc-write.h"
+
+#include "exp.h"
+#include "login.h"
+
+#ifdef PAM
+static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events));
+#endif
+
+/**
+ * Initiate client login.
+ *
+ * This function is called after the daemon received the required NICK and
+ * USER commands of a new client. If the daemon is compiled with support for
+ * PAM, the authentication sub-processs is forked; otherwise the global server
+ * password is checked.
+ *
+ * @param Client The client logging in.
+ * @returns CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+Login_User(CLIENT * Client)
+{
+#ifdef PAM
+	int pipefd[2], result;
+	pid_t pid;
+#endif
+	CONN_ID conn;
+
+	assert(Client != NULL);
+	conn = Client_Conn(Client);
+
+#ifndef STRICT_RFC
+	if (Conf_AuthPing) {
+		/* Did we receive the "auth PONG" already? */
+		if (Conn_GetAuthPing(conn)) {
+			Client_SetType(Client, CLIENT_WAITAUTHPING);
+			LogDebug("Connection %d: Waiting for AUTH PONG ...", conn);
+			return CONNECTED;
+		}
+	}
+#endif
+
+	/* Still waiting for "CAP END" command? */
+	if (Client_Cap(Client) & CLIENT_CAP_PENDING) {
+		Client_SetType(Client, CLIENT_WAITCAPEND);
+		LogDebug("Connection %d: Waiting for CAP END ...", conn);
+		return CONNECTED;
+	}
+
+#ifdef PAM
+	if (!Conf_PAM) {
+		/* Don't do any PAM authentication at all, instead emulate
+		 * the beahiour of the daemon compiled without PAM support:
+		 * because there can't be any "server password", all
+		 * passwords supplied are classified as "wrong". */
+		if(Client_Password(Client)[0] == '\0')
+			return Login_User_PostAuth(Client);
+		Client_Reject(Client, "Non-empty password", false);
+		return DISCONNECTED;
+	}
+
+	if (Conf_PAMIsOptional && strcmp(Client_Password(Client), "") == 0) {
+		/* Clients are not required to send a password and to be PAM-
+		 * authenticated at all. If not, they won't become "identified"
+		 * and keep the "~" in their supplied user name.
+		 * Therefore it is sensible to either set Conf_PAMisOptional or
+		 * to enable IDENT lookups -- not both. */
+		return Login_User_PostAuth(Client);
+	}
+
+	/* Fork child process for PAM authentication; and make sure that the
+	 * process timeout is set higher than the login timeout! */
+	pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
+			cb_Read_Auth_Result, Conf_PongTimeout + 1);
+	if (pid > 0) {
+		LogDebug("Authenticator for connection %d created (PID %d).",
+			 conn, pid);
+		return CONNECTED;
+	} else {
+		/* Sub process */
+		Log_Init_Subprocess("Auth");
+		Conn_CloseAllSockets(NONE);
+		result = PAM_Authenticate(Client);
+		if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
+			Log_Subprocess(LOG_ERR,
+				       "Failed to pipe result to parent!");
+		Log_Exit_Subprocess("Auth");
+		exit(0);
+	}
+#else
+	/* Check global server password ... */
+	if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
+		/* Bad password! */
+		Client_Reject(Client, "Bad server password", false);
+		return DISCONNECTED;
+	}
+	return Login_User_PostAuth(Client);
+#endif
+}
+
+/**
+ * Finish client registration.
+ *
+ * Introduce the new client to the network and send all "hello messages"
+ * to it after authentication has been succeeded.
+ *
+ * @param Client The client logging in.
+ * @return CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+Login_User_PostAuth(CLIENT *Client)
+{
+	assert(Client != NULL);
+
+	if (Class_HandleServerBans(Client) != CONNECTED)
+		return DISCONNECTED;
+
+	Client_Introduce(NULL, Client, CLIENT_USER);
+
+	if (!IRC_WriteStrClient
+	    (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
+		return false;
+	if (!IRC_WriteStrClient
+	    (Client, RPL_YOURHOST_MSG, Client_ID(Client),
+	     Client_ID(Client_ThisServer()), PACKAGE_VERSION, TARGET_CPU,
+	     TARGET_VENDOR, TARGET_OS))
+		return false;
+	if (!IRC_WriteStrClient
+	    (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
+		return false;
+	if (!IRC_WriteStrClient
+	    (Client, RPL_MYINFO_MSG, Client_ID(Client),
+	     Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
+	     CHANMODES))
+		return false;
+
+	/* Features supported by this server (005 numeric, ISUPPORT),
+	 * see <http://www.irc.org/tech_docs/005.html> for details. */
+	if (!IRC_Send_ISUPPORT(Client))
+		return DISCONNECTED;
+
+	if (!IRC_Send_LUSERS(Client))
+		return DISCONNECTED;
+	if (!IRC_Show_MOTD(Client))
+		return DISCONNECTED;
+
+	/* Suspend the client for a second ... */
+	IRC_SetPenalty(Client, 1);
+
+	return CONNECTED;
+}
+
+#ifdef PAM
+
+/**
+ * Read result of the authenticatior sub-process from pipe
+ *
+ * @param r_fd		File descriptor of the pipe.
+ * @param events	(ignored IO specification)
+ */
+static void
+cb_Read_Auth_Result(int r_fd, UNUSED short events)
+{
+	CONN_ID conn;
+	CLIENT *client;
+	int result;
+	size_t len;
+	PROC_STAT *proc;
+
+	LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
+	conn = Conn_GetFromProc(r_fd);
+	if (conn == NONE) {
+		/* Ops, none found? Probably the connection has already
+		 * been closed!? We'll ignore that ... */
+		io_close(r_fd);
+		LogDebug("Auth: Got callback for unknown connection!?");
+		return;
+	}
+	proc = Conn_GetProcStat(conn);
+	client = Conn_GetClient(conn);
+
+	/* Read result from pipe */
+	len = Proc_Read(proc, &result, sizeof(result));
+	Proc_Close(proc);
+	if (len == 0)
+		return;
+
+	if (len != sizeof(result)) {
+		Log(LOG_CRIT, "Auth: Got malformed result!");
+		Client_Reject(client, "Internal error", false);
+		return;
+	}
+
+	if (result == true) {
+		Client_SetUser(client, Client_OrigUser(client), true);
+		(void)Login_User_PostAuth(client);
+	} else
+		Client_Reject(client, "Bad password", false);
+}
+
+#endif
+
+/* -eof- */

+ 25 - 0
src/ngircd/login.h

@@ -0,0 +1,25 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2012 Alexander Barton (alex@barton.de) and Contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ */
+
+#ifndef __login_h__
+#define __login_h__
+
+/**
+ * @file
+ * Functions to deal with client logins (header)
+ */
+
+GLOBAL bool Login_User PARAMS((CLIENT * Client));
+GLOBAL bool Login_User_PostAuth PARAMS((CLIENT *Client));
+
+#endif
+
+/* -eof- */

+ 1 - 0
src/ngircd/messages.h

@@ -103,6 +103,7 @@
 #define ERR_TOOMANYCHANNELS_MSG		"405 %s %s :You have joined too many channels"
 #define ERR_WASNOSUCHNICK_MSG		"406 %s %s :There was no such nickname"
 #define ERR_NOORIGIN_MSG		"409 %s :No origin specified"
+#define ERR_INVALIDCAP_MSG		"410 %s %s :Invalid CAP subcommand"
 #define ERR_NORECIPIENT_MSG		"411 %s :No recipient given (%s)"
 #define ERR_NOTEXTTOSEND_MSG		"412 %s :No text to send"
 #define ERR_WILDTOPLEVEL		"414 %s :Wildcard in toplevel domain"

+ 6 - 7
src/ngircd/ngircd.c

@@ -498,7 +498,8 @@ 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 pid");
+		close(pidfd);
 		return;
 	}
 	
@@ -642,12 +643,10 @@ NGIRCd_Init(bool NGIRCd_NoDaemon)
 		}
 
 		if (chroot(Conf_Chroot) != 0) {
-			if (errno != EPERM) {
-				Log(LOG_ERR,
-				    "Can't change root directory to \"%s\": %s",
-				    Conf_Chroot, strerror(errno));
-				goto out;
-			}
+			Log(LOG_ERR,
+			    "Can't change root directory to \"%s\": %s",
+			    Conf_Chroot, strerror(errno));
+			goto out;
 		} else {
 			chrooted = true;
 			Log(LOG_INFO,

+ 2 - 0
src/ngircd/parse.c

@@ -36,6 +36,7 @@
 
 #include "imp.h"
 #include "irc.h"
+#include "irc-cap.h"
 #include "irc-channel.h"
 #include "irc-info.h"
 #include "irc-login.h"
@@ -113,6 +114,7 @@ static COMMAND My_Commands[] =
 	{ "CHANINFO", IRC_CHANINFO, CLIENT_SERVER, 0, 0, 0 },
 #endif
 #ifndef STRICT_RFC
+	{ "CAP", IRC_CAP, 0xFFFF, 0, 0, 0 },
 	{ "GET",  IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, 0, 0 },
 	{ "POST", IRC_QUIT_HTTP, CLIENT_UNKNOWN, 0, 0, 0 },
 #endif

+ 4 - 1
src/ngircd/sighandlers.c

@@ -218,8 +218,11 @@ Signal_Handler_BH(int Signal)
 		break;
 #ifdef DEBUG
 	case SIGUSR2:
-		if (NGIRCd_Debug)
+		if (NGIRCd_Debug) {
+			Log(LOG_INFO|LOG_snotice,
+			    "Got SIGUSR2, dumping internal state ...");
 			Dump_State();
+		}
 		break;
 	default:
 		Log(LOG_DEBUG, "Got signal %d! Ignored.", Signal);