Add MaxNickLength config option and enable the server to deal with
configurable nick name lengths. The configured nick length must be > 0
and <= CLIENT_NICK_LEN (31 plus NULL byte). The default is 9 (plus NULL byte)
as defined in RFC 2812.

This patch enhances the IRC+ protocol as well and introduces a "server
handshake" indicated by the "H" flag which enables the server to receive
numerics from other servers. The numeric 005 (ISUPPORT) is used to verify
that the peer is using the same nick name length as the local server.

This patch is based on work of Florian Westphal.
Alexander Barton, <alex@barton.de>, 2007-11-20

 ChangeLog                                       |    5 
 NEWS                                            |    2 
 contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj |    6 
 doc/Protocol.txt                                |   50 +++
 doc/sample-ngircd.conf                          |    5 
 man/ngircd.conf.5.tmpl                          |    5 
 src/ngircd/Makefile.am                          |    4 
 src/ngircd/client.c                             |    5 
 src/ngircd/conf.c                               |   32 ++
 src/ngircd/conf.h                               |    2 
 src/ngircd/defines.h                            |    7 
 src/ngircd/irc-info.c                           |   18 +
 src/ngircd/irc-info.h                           |    1 
 src/ngircd/irc-login.c                          |    7 
 src/ngircd/irc-server.c                         |  209 +--------------
 src/ngircd/irc-server.h                         |    2 
 src/ngircd/ngircd.c                             |   18 -
 src/ngircd/numeric.c                            |  334 ++++++++++++++++++++++++
 src/ngircd/numeric.h                            |   24 +
 src/ngircd/parse.c                              |   57 +++-
 src/ngircd/parse.h                              |   11 
 21 files changed, 580 insertions(+), 224 deletions(-)

Index: ChangeLog
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/ChangeLog,v
retrieving revision 1.328
diff -u -p -r1.328 ChangeLog
--- ChangeLog	20 Nov 2007 21:39:35 -0000	1.328
+++ ChangeLog	20 Nov 2007 22:08:09 -0000
@@ -12,6 +12,11 @@
 
 ngIRCd HEAD
 
+  - New configuration option "MaxNickLength" to specify the allowed maximum
+    length of user nick names. Note: must be unique in an IRC network!
+  - Enhanced the IRC+ protocol to support an enhanced "server handshake" and
+    enable server to recognice numeric 005 (ISUPPORT) and 376 (ENDOFMOTD).
+    See doc/Protocol.txt for details.
   - Re-added doc/SSL.txt to distribution -- got lost somewhere!?
   - Fixes the wrong logging output when nested servers are introduced
     to the network as well as the wrong output of the LINKS command.
Index: NEWS
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/NEWS,v
retrieving revision 1.81
diff -u -p -r1.81 NEWS
--- NEWS	14 Oct 2007 14:17:32 -0000	1.81
+++ NEWS	20 Nov 2007 22:08:09 -0000
@@ -12,6 +12,8 @@
 
 ngIRCd HEAD
 
+  - New configuration option "MaxNickLength" to specify the allowed maximum
+    length of user nick names. Note: must be unique in an IRC network!
   - Numeric 317: implemented "signon time" (displayed in WHOIS result).
   - Added new server configuration option "Passive" for "Server" blocks to
     disable automatic outgoing connections (similar to -p option to ngircd,
Index: contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj,v
retrieving revision 1.6
diff -u -p -r1.6 project.pbxproj
--- contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj	19 Nov 2007 23:38:59 -0000	1.6
+++ contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj	20 Nov 2007 22:08:09 -0000
@@ -34,6 +34,7 @@
 		FA322D4D0CEF74B1001761B3 /* resolve.c in Sources */ = {isa = PBXBuildFile; fileRef = FA322D0C0CEF74B1001761B3 /* resolve.c */; };
 		FA322DBE0CEF7766001761B3 /* tool.c in Sources */ = {isa = PBXBuildFile; fileRef = FA322D330CEF74B1001761B3 /* tool.c */; };
 		FA322DC10CEF77CB001761B3 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FA322DC00CEF77CB001761B3 /* libz.dylib */; };
+		FAE5CC2E0CF2308A007D69B6 /* numeric.c in Sources */ = {isa = PBXBuildFile; fileRef = FAE5CC2D0CF2308A007D69B6 /* numeric.c */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -198,6 +199,8 @@
 		FA322DB10CEF7565001761B3 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
 		FA322DBB0CEF773C001761B3 /* cvs-version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "cvs-version.h"; sourceTree = "<group>"; };
 		FA322DC00CEF77CB001761B3 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = /usr/lib/libz.dylib; sourceTree = "<absolute>"; };
+		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 */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -309,6 +312,8 @@
 				FA322D050CEF74B1001761B3 /* messages.h */,
 				FA322D060CEF74B1001761B3 /* ngircd.c */,
 				FA322D070CEF74B1001761B3 /* ngircd.h */,
+				FAE5CC2D0CF2308A007D69B6 /* numeric.c */,
+				FAE5CC2C0CF2308A007D69B6 /* numeric.h */,
 				FA322D080CEF74B1001761B3 /* parse.c */,
 				FA322D090CEF74B1001761B3 /* parse.h */,
 				FA322D0A0CEF74B1001761B3 /* rendezvous.c */,
@@ -622,6 +627,7 @@
 				FA322D4C0CEF74B1001761B3 /* rendezvous.c in Sources */,
 				FA322D4D0CEF74B1001761B3 /* resolve.c in Sources */,
 				FA322DBE0CEF7766001761B3 /* tool.c in Sources */,
+				FAE5CC2E0CF2308A007D69B6 /* numeric.c in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
Index: doc/Protocol.txt
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/doc/Protocol.txt,v
retrieving revision 1.13
diff -u -p -r1.13 Protocol.txt
--- doc/Protocol.txt	27 Aug 2005 19:00:06 -0000	1.13
+++ doc/Protocol.txt	20 Nov 2007 22:08:09 -0000
@@ -1,7 +1,7 @@
 
                      ngIRCd - Next Generation IRC Server
 
-                      (c)2001-2003 by Alexander Barton,
+                        (c)2001-2007 Alexander Barton,
                     alex@barton.de, http://www.barton.de/
 
                ngIRCd is free software and published under the
@@ -79,6 +79,9 @@ The following <serverflags> are defined 
      peer understands this flag, it will send "MODE +I" and "MODE +b"
      commands after the server link has been established.
 
+- H: The server supports the "enhanced server handshake", see section II.2
+     for a detailed description.
+
 - o: IRC operators are allowed to change channel- and channel-user-modes
      even if they aren't channel-operator of the affected channel.
 
@@ -90,7 +93,50 @@ The optional parameter <options> is used
 defined in RFC 2813, section 4.1.1.
 
 
-II.2 Exchange channel-modes, topics, and persistent channels
+II.2 Enhanced Server Handshake
+
+The "enhanced server handshake" is used when both servers support this IRC+
+extension, which is indicated by the 'H' flag in the <serverflags> sent with
+the PASS command, see section II.1.
+
+It basically means, that after exchanging the PASS and SERVER commands the
+server is not registered in the network (as usual), but that IRC numerics
+are exchanged until the numeric 376 (ENDOFMOTD) is received. Afterwards the
+peer is registered in the network as with the regular IRC protocol.
+
+A server implementing the enhanced server handshake (and indicating this
+using 'H' in the <serverflags>) MUST ignore all unknown numerics to it
+silently.
+
+In addition, such a server should at least send the numeric 005 (ISUPPORT)
+to its peer, containing the following information. Syntax: <key>=<value>,
+one token per IRC parameter. If the server has to send more than 12 token
+it must send separate ISUPPORT numerics (this is a limitation of the IRC
+protocol which allows at max 15 arguments per command).
+
+ - NICKLEN: Maximum nickname length. Default: 9.
+ - CASEMAPPING: Case mapping used for nick- and channel name comparing.
+   Default: "ascii", the chars [a-z] are lowercase of [A-Z].
+ - PREFIX: List of channel modes a person can get and the respective prefix
+   a channel or nickname will get in case the person has it. The order of the
+   modes goes from most powerful to least powerful. Default: "(ov)@+"
+ - CHANTYPES: Supported channel prefixes. Default: "#".
+ - CHANMODES: List of channel modes for 4 types, separated by comma (","):
+   Mode that adds or removes a nick or address to a list, mode that changes
+   a setting (both have always has a parameter), mode that changes a setting
+   and only has a parameter when set, and mode that changes a setting and
+   never has a parameter. For example "bI,k,l,imnPst".
+ - CHANLIMIT: Maximum number of channels allowed to join by channel prefix,
+   for example "#:10".
+
+Please see <http://www.irc.org/tech_docs/005.html> for details.
+
+The information exchanged using ISUPPORT can be used to detect configuration
+incompatibilities (different maximum nick name length, for example) and
+therefore to disconnect the peer prior to registering it in the network.
+
+
+II.3 Exchange channel-modes, topics, and persistent channels
 
      Command: CHANINFO
   Parameters: <channel> +<modes> <key> <limit> [<topic>]
Index: doc/sample-ngircd.conf
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/doc/sample-ngircd.conf,v
retrieving revision 1.41
diff -u -p -r1.41 sample-ngircd.conf
--- doc/sample-ngircd.conf	13 Oct 2007 20:45:11 -0000	1.41
+++ doc/sample-ngircd.conf	20 Nov 2007 22:08:09 -0000
@@ -111,6 +111,11 @@
 	# Maximum number of channels a user can be member of (0: no limit):
 	;MaxJoins = 10
 
+	# Maximum length of an user nick name (Default: 9, as in RFC 2812).
+	# Please note that all servers in an IRC network MUST use the same
+	# maximum nick name length!
+	;MaxNickLength = 9
+
 [Operator]
 	# [Operator] sections are used to define IRC Operators. There may be
 	# more than one [Operator] block, one for each local operator.
Index: man/ngircd.conf.5.tmpl
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/man/ngircd.conf.5.tmpl,v
retrieving revision 1.5
diff -u -p -r1.5 ngircd.conf.5.tmpl
--- man/ngircd.conf.5.tmpl	25 Oct 2007 11:01:19 -0000	1.5
+++ man/ngircd.conf.5.tmpl	20 Nov 2007 22:08:09 -0000
@@ -170,6 +170,11 @@ the risk of denial of service attacks (D
 \fBMaxJoins\fR
 Maximum number of channels a user can be member of (0: no limit).
 Default: 10.
+.TP
+\fBMaxNickLength\fR
+Maximum length of an user nick name (Default: 9, as in RFC 2812). Please
+note that all servers in an IRC network MUST use the same maximum nick name
+length!
 .SH [OPERATOR]
 .I [Operator]
 sections are used to define IRC Operators. There may be more than one
Index: src/ngircd/Makefile.am
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/Makefile.am,v
retrieving revision 1.49
diff -u -p -r1.49 Makefile.am
--- src/ngircd/Makefile.am	11 Mar 2006 01:48:50 -0000	1.49
+++ src/ngircd/Makefile.am	20 Nov 2007 22:08:09 -0000
@@ -23,7 +23,7 @@ sbin_PROGRAMS = ngircd
 ngircd_SOURCES = ngircd.c array.c channel.c client.c conf.c conn.c conn-func.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 parse.c rendezvous.c resolve.c
+	match.c numeric.c parse.c rendezvous.c resolve.c
 
 ngircd_LDFLAGS = -L../portab -L../tool
 
@@ -32,7 +32,7 @@ ngircd_LDADD = -lngportab -lngtool
 noinst_HEADERS = ngircd.h array.h channel.h client.h conf.h conn.h conn-func.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 parse.h rendezvous.h resolve.h \
+	match.h numeric.h parse.h rendezvous.h resolve.h \
 	defines.h messages.h
 
 clean-local:
Index: src/ngircd/client.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/client.c,v
retrieving revision 1.96
diff -u -p -r1.96 client.c
--- src/ngircd/client.c	25 Oct 2007 11:01:19 -0000	1.96
+++ src/ngircd/client.c	20 Nov 2007 22:08:09 -0000
@@ -609,7 +609,8 @@ Client_ID( CLIENT *Client )
 	assert( Client != NULL );
 
 #ifdef DEBUG
-	if( Client->type == CLIENT_USER ) assert( strlen( Client->id ) < CLIENT_NICK_LEN );
+	if(Client->type == CLIENT_USER)
+		assert(strlen(Client->id) < Conf_MaxNickLength);
 #endif
 						   
 	if( Client->id[0] ) return Client->id;
@@ -952,7 +953,7 @@ Client_IsValidNick( const char *Nick )
 
 	if( Nick[0] == '#' ) return false;
 	if( strchr( goodchars, Nick[0] )) return false;
-	if( strlen( Nick ) >= CLIENT_NICK_LEN ) return false;
+	if( strlen( Nick ) >= Conf_MaxNickLength) return false;
 
 	ptr = Nick;
 	while( *ptr )
Index: src/ngircd/conf.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/conf.c,v
retrieving revision 1.101
diff -u -p -r1.101 conf.c
--- src/ngircd/conf.c	25 Oct 2007 11:01:19 -0000	1.101
+++ src/ngircd/conf.c	20 Nov 2007 22:08:09 -0000
@@ -208,7 +208,8 @@ Conf_Test( void )
 	printf( "  NoDNS = %s\n", Conf_NoDNS ? "yes" : "no");
 	printf( "  MaxConnections = %ld\n", Conf_MaxConnections);
 	printf( "  MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP);
-	printf( "  MaxJoins = %d\n\n", Conf_MaxJoins);
+	printf( "  MaxJoins = %d\n", Conf_MaxJoins>0 ? Conf_MaxJoins : -1);
+	printf( "  MaxNickLength = %u\n\n", Conf_MaxNickLength - 1);
 
 	for( i = 0; i < Conf_Oper_Count; i++ ) {
 		if( ! Conf_Oper[i].name[0] ) continue;
@@ -452,6 +453,7 @@ Set_Defaults( bool InitServers )
 	Conf_MaxConnections = 0;
 	Conf_MaxConnectionsIP = 5;
 	Conf_MaxJoins = 10;
+	Conf_MaxNickLength = CLIENT_NICK_LEN_DEFAULT;
 
 	/* Initialize server configuration structures */
 	if( InitServers ) for( i = 0; i < MAX_SERVERS; Init_Server_Struct( &Conf_Server[i++] ));
@@ -638,6 +640,27 @@ Check_ArgIsTrue( const char *Arg )
 } /* Check_ArgIsTrue */
 
 
+static unsigned int Handle_MaxNickLength(int Line, const char *Arg)
+{
+	unsigned new;
+
+	new = (unsigned) atoi(Arg) + 1;
+	if (new > CLIENT_NICK_LEN) {
+		Config_Error(LOG_WARNING,
+			     "%s, line %d: Value of \"MaxNickLength\" exceeds %u!",
+			     NGIRCd_ConfFile, Line, CLIENT_NICK_LEN - 1);
+		return CLIENT_NICK_LEN;
+	}
+	if (new < 2) {
+		Config_Error(LOG_WARNING,
+			     "%s, line %d: Value of \"MaxNickLength\" must be at least 1!",
+			     NGIRCd_ConfFile, Line);
+		return 2;
+	}
+	return new;
+} /* Handle_MaxNickLength */
+
+
 static void
 Handle_GLOBAL( int Line, char *Var, char *Arg )
 {
@@ -827,6 +850,13 @@ Handle_GLOBAL( int Line, char *Var, char
 		Conf_MaxJoins = atoi( Arg );
 		return;
 	}
+	if( strcasecmp( Var, "MaxNickLength" ) == 0 ) {
+		/* Maximum length of a nick name; must be same on all servers
+		 * within the IRC network! */
+		Conf_MaxNickLength = Handle_MaxNickLength(Line, Arg);
+		return;
+	}
+
 	if( strcasecmp( Var, "Listen" ) == 0 ) {
 		/* IP-Address to bind sockets */
 		len = strlcpy( Conf_ListenAddress, Arg, sizeof( Conf_ListenAddress ));
Index: src/ngircd/conf.h
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/conf.h,v
retrieving revision 1.44
diff -u -p -r1.44 conf.h
--- src/ngircd/conf.h	25 Oct 2007 11:01:19 -0000	1.44
+++ src/ngircd/conf.h	20 Nov 2007 22:08:09 -0000
@@ -135,6 +135,8 @@ GLOBAL int Conf_MaxJoins;
 /* Maximum number of connections per IP address */
 GLOBAL int Conf_MaxConnectionsIP;
 
+/* Maximum length of a nick name */
+GLOBAL unsigned int Conf_MaxNickLength;
 
 GLOBAL void Conf_Init PARAMS((void));
 GLOBAL void Conf_Rehash PARAMS((void));
Index: src/ngircd/defines.h
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/defines.h,v
retrieving revision 1.61
diff -u -p -r1.61 defines.h
--- src/ngircd/defines.h	2 Aug 2007 10:14:26 -0000	1.61
+++ src/ngircd/defines.h	20 Nov 2007 22:08:09 -0000
@@ -47,8 +47,9 @@
 
 #define CLIENT_ID_LEN 64		/* Max. length of an IRC ID; see RFC
 					   RFC 2812 section 1.1 and 1.2.1 */
-#define CLIENT_NICK_LEN 10		/* Max. nick length, see. RFC 2812
-					   section 1.2.1 */
+#define CLIENT_NICK_LEN_DEFAULT 10	/* Default nick length, see. RFC 2812
+					 * section 1.2.1 */
+#define CLIENT_NICK_LEN 32		/* Maximum nick name length */
 #define CLIENT_PASS_LEN 21		/* Max. password length */
 #define CLIENT_USER_LEN 10		/* Max. length of user name ("login")
 					   see RFC 2812, section 1.2.1 */
@@ -81,7 +82,7 @@
 					   protocol, see doc/Protocol.txt */
 
 #ifdef IRCPLUS
-# define IRCPLUSFLAGS "CL"		/* Standard IRC+ flags */
+# define IRCPLUSFLAGS "CHL"		/* Standard IRC+ flags */
 #endif
 
 #define STARTUP_DELAY 1			/* Delay outgoing connections n seconds
Index: src/ngircd/irc-info.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/irc-info.c,v
retrieving revision 1.39
diff -u -p -r1.39 irc-info.c
--- src/ngircd/irc-info.c	18 Nov 2007 15:05:35 -0000	1.39
+++ src/ngircd/irc-info.c	20 Nov 2007 22:08:09 -0000
@@ -1050,4 +1050,22 @@ IRC_Send_WHO( CLIENT *Client, CHANNEL *C
 } /* IRC_Send_WHO */
 
 
+/**
+ * Send the ISUPPORT numeric (005).
+ * This numeric indicates the features that are supported by this server.
+ * See <http://www.irc.org/tech_docs/005.html> for details.
+ */
+GLOBAL bool
+IRC_Send_ISUPPORT PARAMS((CLIENT * Client))
+{
+	if (!IRC_WriteStrClient(Client, RPL_ISUPPORT1_MSG, Client_ID(Client),
+				Conf_MaxJoins))
+		return DISCONNECTED;
+	return IRC_WriteStrClient(Client, RPL_ISUPPORT2_MSG, Client_ID(Client),
+				  CHANNEL_NAME_LEN - 1, Conf_MaxNickLength - 1,
+				  COMMAND_LEN - 23, CLIENT_AWAY_LEN - 1,
+				  COMMAND_LEN - 113);
+} /* IRC_Send_ISUPPORT */
+
+
 /* -eof- */
Index: src/ngircd/irc-info.h
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/irc-info.h,v
retrieving revision 1.3
diff -u -p -r1.3 irc-info.h
--- src/ngircd/irc-info.h	19 Mar 2005 18:43:48 -0000	1.3
+++ src/ngircd/irc-info.h	20 Nov 2007 22:08:09 -0000
@@ -36,6 +36,7 @@ GLOBAL bool IRC_Send_LUSERS PARAMS(( CLI
 GLOBAL bool IRC_Send_NAMES PARAMS(( CLIENT *Client, CHANNEL *Chan ));
 GLOBAL bool IRC_Show_MOTD PARAMS(( CLIENT *Client ));
 GLOBAL bool IRC_Send_WHO PARAMS(( CLIENT *Client, CHANNEL *Chan, bool OnlyOps ));
+GLOBAL bool IRC_Send_ISUPPORT PARAMS(( CLIENT *Client ));
 
 
 #endif
Index: src/ngircd/irc-login.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/irc-login.c,v
retrieving revision 1.53
diff -u -p -r1.53 irc-login.c
--- src/ngircd/irc-login.c	3 Oct 2006 10:28:38 -0000	1.53
+++ src/ngircd/irc-login.c	20 Nov 2007 22:08:09 -0000
@@ -618,12 +618,7 @@ Hello_User( CLIENT *Client )
 
 	/* Features supported by this server (005 numeric, ISUPPORT),
 	 * see <http://www.irc.org/tech_docs/005.html> for details. */
-	if (! IRC_WriteStrClient(Client, RPL_ISUPPORT1_MSG, Client_ID(Client),
-			Conf_MaxJoins))
-		return DISCONNECTED;
-	if (! IRC_WriteStrClient(Client, RPL_ISUPPORT2_MSG, Client_ID(Client),
-			CHANNEL_NAME_LEN-1, CLIENT_NICK_LEN-1, COMMAND_LEN-23,
-			CLIENT_AWAY_LEN-1, COMMAND_LEN-113))
+	if (! IRC_Send_ISUPPORT(Client))
 		return DISCONNECTED;
 
 	Client_SetType( Client, CLIENT_USER );
Index: src/ngircd/irc-server.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/irc-server.c,v
retrieving revision 1.45
diff -u -p -r1.45 irc-server.c
--- src/ngircd/irc-server.c	20 Nov 2007 20:02:41 -0000	1.45
+++ src/ngircd/irc-server.c	20 Nov 2007 22:08:09 -0000
@@ -35,54 +35,14 @@ static char UNUSED id[] = "$Id: irc-serv
 #include "log.h"
 #include "messages.h"
 #include "parse.h"
+#include "numeric.h"
 #include "ngircd.h"
+#include "irc-info.h"
 
 #include "exp.h"
 #include "irc-server.h"
 
 
-#ifdef IRCPLUS
-static bool
-Synchronize_Lists( CLIENT *Client )
-{
-	CHANNEL *c;
-	struct list_head *head;
-	struct list_elem *elem;
-
-	assert( Client != NULL );
-
-	c = Channel_First();
-
-	while (c) {
-		head = Channel_GetListBans(c);
-
-		elem = Lists_GetFirst(head);
-		while (elem) {
-			if( ! IRC_WriteStrClient( Client, "MODE %s +b %s",
-					Channel_Name(c), Lists_GetMask(elem)))
-			{
-				return false;
-			}
-			elem = Lists_GetNext(elem);
-		}
-
-		head = Channel_GetListInvites(c);
-		elem = Lists_GetFirst(head);
-		while (elem) {
-			if( ! IRC_WriteStrClient( Client, "MODE %s +I %s",
-					Channel_Name( c ), Lists_GetMask(elem)))
-			{
-				return false;
-			}
-			elem = Lists_GetNext(elem);
-		}
-		c = Channel_Next(c);
-	}
-	return true;
-}
-#endif
-
-
 /**
  * Handler for the IRC command "SERVER".
  * See RFC 2813 section 4.1.2.
@@ -90,12 +50,10 @@ Synchronize_Lists( CLIENT *Client )
 GLOBAL bool
 IRC_SERVER( CLIENT *Client, REQUEST *Req )
 {
-	char str[LINE_LEN], *ptr, *modes, *topic;
-	CLIENT *from, *c, *cl;
-	CL2CHAN *cl2chan;
-	int max_hops, i;
-	CHANNEL *chan;
+	char str[LINE_LEN], *ptr;
+	CLIENT *from, *c;
 	bool ok;
+	int i;
 	CONN_ID con;
 	
 	assert( Client != NULL );
@@ -164,10 +122,10 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req
 			Client_SetToken( Client, atoi( Req->argv[1] ));
 		}
 
-		Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" registered (connection %d, 1 hop - direct link).", Client_ID( Client ), con );
-
-		Client_SetType( Client, CLIENT_SERVER );
-		Conf_SetServer( i, con );
+		/* Mark this connection as belonging to an configured server */
+		Conf_SetServer(i, con);
+		
+		Client_SetType(Client, CLIENT_UNKNOWNSERVER);
 
 #ifdef ZLIB
 		/* Kompression initialisieren, wenn erforderlich */
@@ -182,145 +140,23 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req
 		}
 #endif
 
-		/* maximalen Hop Count ermitteln */
-		max_hops = 0;
-		c = Client_First( );
-		while( c )
-		{
-			if( Client_Hops( c ) > max_hops ) max_hops = Client_Hops( c );
-			c = Client_Next( c );
-		}
-		
-		/* Alle bisherigen Server dem neuen Server bekannt machen,
-		 * die bisherigen Server ueber den neuen informierenn */
-		for( i = 0; i < ( max_hops + 1 ); i++ )
-		{
-			c = Client_First( );
-			while( c )
-			{
-				if(( Client_Type( c ) == CLIENT_SERVER ) && ( c != Client ) && ( c != Client_ThisServer( )) && ( Client_Hops( c ) == i ))
-				{
-					if( Client_Conn( c ) > NONE )
-					{
-						/* Dem gefundenen Server gleich den neuen
-						 * Server bekannt machen */
-						if( ! IRC_WriteStrClient( c, "SERVER %s %d %d :%s", Client_ID( Client ), Client_Hops( Client ) + 1, Client_MyToken( Client ), Client_Info( Client ))) return DISCONNECTED;
-					}
-					
-					/* Inform the new server about this one */
-					if (! IRC_WriteStrClientPrefix(Client,
-							Client_Hops(c) == 1 ? Client_ThisServer() : Client_TopServer(c),
-							"SERVER %s %d %d :%s",
-							Client_ID(c), Client_Hops(c) + 1,
-							Client_MyToken(c), Client_Info(c)))
-						return DISCONNECTED;
-				}
-				c = Client_Next( c );
-			}
-		}
-
-		/* alle User dem neuen Server bekannt machen */
-		c = Client_First( );
-		while( c )
-		{
-			if( Client_Type( c ) == CLIENT_USER )
-			{
-				/* User an neuen Server melden */
-				if( ! IRC_WriteStrClient( Client, "NICK %s %d %s %s %d +%s :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_User( c ), Client_Hostname( c ), Client_MyToken( Client_Introducer( c )), Client_Modes( c ), Client_Info( c ))) return DISCONNECTED;
-			}
-			c = Client_Next( c );
-		}
-
-		/* Channels dem neuen Server bekannt machen */
-		chan = Channel_First( );
-		while( chan )
-		{
 #ifdef IRCPLUS
-			/* Send CHANINFO if the peer supports it */
-			if( strchr( Client_Flags( Client ), 'C' ))
-			{
-#ifdef DEBUG
-				Log( LOG_DEBUG, "Sending CHANINFO commands ..." );
-#endif
-				modes = Channel_Modes( chan );
-				topic = Channel_Topic( chan );
-
-				if( *modes || *topic )
-				{
-					/* send CHANINFO */
-					if(( ! strchr( Channel_Modes( chan ), 'k' )) && ( ! strchr( Channel_Modes( chan ), 'l' )) && ( ! *topic ))
-					{
-						/* "CHANINFO <chan> +<modes>" */
-						if( ! IRC_WriteStrClient( Client, "CHANINFO %s +%s", Channel_Name( chan ), modes )) return DISCONNECTED;
-					}
-					else if(( ! strchr( Channel_Modes( chan ), 'k' )) && ( ! strchr( Channel_Modes( chan ), 'l' )))
-					{
-						/* "CHANINFO <chan> +<modes> :<topic>" */
-						if( ! IRC_WriteStrClient( Client, "CHANINFO %s +%s :%s", Channel_Name( chan ), modes, topic )) return DISCONNECTED;
-					}
-					else
-					{
-						/* "CHANINFO <chan> +<modes> <key> <limit> :<topic>" */
-						if( ! IRC_WriteStrClient( Client, "CHANINFO %s +%s %s %lu :%s",
-							Channel_Name( chan ), modes,
-							strchr( Channel_Modes( chan ), 'k' ) ? Channel_Key( chan ) : "*",
-							strchr( Channel_Modes( chan ), 'l' ) ? Channel_MaxUsers( chan ) : 0, topic ))
-						{
-							return DISCONNECTED;
-						}
-					}
-				}
-			}
+		if (strchr(Client_Flags(Client), 'H')) {
+			LogDebug("Peer supports IRC+ extended server handshake ...");
+			if (!IRC_Send_ISUPPORT(Client))
+				return DISCONNECTED;
+			return IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG,
+						  Client_ID(Client));
+		} else {
 #endif
-
-			/* alle Member suchen */
-			cl2chan = Channel_FirstMember( chan );
-			snprintf( str, sizeof( str ), "NJOIN %s :", Channel_Name( chan ));
-			while( cl2chan )
-			{
-				cl = Channel_GetClient( cl2chan );
-				assert( cl != NULL );
-
-				/* Nick, ggf. mit Modes, anhaengen */
-				if( str[strlen( str ) - 1] != ':' ) strlcat( str, ",", sizeof( str ));
-				if( strchr( Channel_UserModes( chan, cl ), 'v' )) strlcat( str, "+", sizeof( str ));
-				if( strchr( Channel_UserModes( chan, cl ), 'o' )) strlcat( str, "@", sizeof( str ));
-				strlcat( str, Client_ID( cl ), sizeof( str ));
-
-				if( strlen( str ) > ( LINE_LEN - CLIENT_NICK_LEN - 8 ))
-				{
-					/* Zeile senden */
-					if( ! IRC_WriteStrClient( Client, "%s", str )) return DISCONNECTED;
-					snprintf( str, sizeof( str ), "NJOIN %s :", Channel_Name( chan ));
-				}
-				
-				cl2chan = Channel_NextMember( chan, cl2chan );
-			}
-
-			/* noch Daten da? */
-			if( str[strlen( str ) - 1] != ':')
-			{
-				/* Ja; Also senden ... */
-				if( ! IRC_WriteStrClient( Client, "%s", str )) return DISCONNECTED;
-			}
-
-			/* Get next channel ... */
-			chan = Channel_Next(chan);
-		}
-
+			if (Conf_MaxNickLength != CLIENT_NICK_LEN_DEFAULT)
+				Log(LOG_CRIT,
+				    "Attention: this server uses a non-standard nick length, but the peer doesn't support the IRC+ extended server handshake!");
 #ifdef IRCPLUS
-		if (strchr(Client_Flags(Client), 'L')) {
-#ifdef DEBUG
-			Log(LOG_DEBUG,
-			    "Synchronizing INVITE- and BAN-lists ...");
-#endif
-			/* Synchronize INVITE- and BAN-lists */
-			if (!Synchronize_Lists(Client))
-				return DISCONNECTED;
 		}
 #endif
 
-		return CONNECTED;
+		return IRC_Num_ENDOFMOTD(Client, Req);
 	}
 	else if( Client_Type( Client ) == CLIENT_SERVER )
 	{
@@ -364,8 +200,9 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req
 		IRC_WriteStrServersPrefix( Client, from, "SERVER %s %d %d :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_MyToken( c ), Client_Info( c ));
 
 		return CONNECTED;
-	}
-	else return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
+	} else
+		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+					  Client_ID(Client), Req->command);
 } /* IRC_SERVER */
 
 
Index: src/ngircd/irc-server.h
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/irc-server.h,v
retrieving revision 1.5
diff -u -p -r1.5 irc-server.h
--- src/ngircd/irc-server.h	19 Mar 2005 18:43:49 -0000	1.5
+++ src/ngircd/irc-server.h	20 Nov 2007 22:08:09 -0000
@@ -22,6 +22,8 @@ GLOBAL bool IRC_SERVER PARAMS((CLIENT *C
 GLOBAL bool IRC_NJOIN PARAMS((CLIENT *Client, REQUEST *Req ));
 GLOBAL bool IRC_SQUIT PARAMS((CLIENT *Client, REQUEST *Req ));
 
+GLOBAL bool IRC_ENDOFMOTD_Server PARAMS((CLIENT *Client));
+
 
 #endif
 
Index: src/ngircd/ngircd.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/ngircd.c,v
retrieving revision 1.116
diff -u -p -r1.116 ngircd.c
--- src/ngircd/ngircd.c	15 Nov 2007 01:03:01 -0000	1.116
+++ src/ngircd/ngircd.c	20 Nov 2007 22:08:09 -0000
@@ -422,6 +422,7 @@ GLOBAL void
 NGIRCd_Rehash( void )
 {
 	char old_name[CLIENT_ID_LEN];
+	unsigned old_nicklen;
 
 	Log( LOG_NOTICE|LOG_snotice, "Re-reading configuration NOW!" );
 	NGIRCd_SignalRehash = false;
@@ -429,17 +430,22 @@ NGIRCd_Rehash( void )
 	/* Close down all listening sockets */
 	Conn_ExitListeners( );
 
-	/* Remember old server name */
+	/* Remember old server name and nick name length */
 	strlcpy( old_name, Conf_ServerName, sizeof old_name );
+	old_nicklen = Conf_MaxNickLength;
 
 	/* Re-read configuration ... */
 	Conf_Rehash( );
 
-	/* Recover old server name: it can't be changed during run-time */
-	if( strcmp( old_name, Conf_ServerName ) != 0 )
-	{
-		strlcpy( Conf_ServerName, old_name, sizeof Conf_ServerName );
-		Log( LOG_ERR, "Can't change \"ServerName\" on runtime! Ignored new name." );
+	/* Recover old server name and nick name length: these values can't
+	 * be changed during run-time */
+	if (strcmp(old_name, Conf_ServerName) != 0 ) {
+		strlcpy(Conf_ServerName, old_name, sizeof Conf_ServerName);
+		Log(LOG_ERR, "Can't change \"ServerName\" on runtime! Ignored new name.");
+	}
+	if (old_nicklen != Conf_MaxNickLength) {
+		Conf_MaxNickLength = old_nicklen;
+		Log(LOG_ERR, "Can't change \"MaxNickLength\" on runtime! Ignored new value.");
 	}
 
 	/* Create new pre-defined channels */
Index: src/ngircd/numeric.c
===================================================================
RCS file: src/ngircd/numeric.c
diff -N src/ngircd/numeric.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/ngircd/numeric.c	20 Nov 2007 22:08:09 -0000
@@ -0,0 +1,334 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2007 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.
+ *
+ * Handlers for IRC numerics sent to the server
+ */
+
+#include "portab.h"
+
+static char UNUSED id[] = "$Id$";
+
+#include "imp.h"
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "defines.h"
+#include "resolve.h"
+#include "conn.h"
+#include "conf.h"
+#include "conn.h"
+#include "client.h"
+#include "channel.h"
+#include "irc-write.h"
+#include "lists.h"
+#include "log.h"
+#include "messages.h"
+#include "parse.h"
+
+#include "exp.h"
+#include "numeric.h"
+
+
+/**
+ * Announce new server in the network
+ * @param Client New server
+ * @param Server Existing server in the network
+ */
+static bool
+Announce_Server(CLIENT * Client, CLIENT * Server)
+{
+	CLIENT *c;
+
+	if (Client_Conn(Server) > NONE) {
+		/* Announce the new server to the one already registered
+		 * which is directly connected to the local server */
+		if (!IRC_WriteStrClient
+		    (Server, "SERVER %s %d %d :%s", Client_ID(Client),
+		     Client_Hops(Client) + 1, Client_MyToken(Client),
+		     Client_Info(Client)))
+			return DISCONNECTED;
+	}
+
+	if (Client_Hops(Server) == 1)
+		c = Client_ThisServer();
+	else
+		c = Client_Introducer(Server);
+
+	/* Inform new server about the one already registered in the network */
+	return IRC_WriteStrClientPrefix(Client, c, "SERVER %s %d %d :%s",
+		Client_ID(Server), Client_Hops(Server) + 1,
+		Client_MyToken(Server), Client_Info(Server));
+} /* Announce_Server */
+
+
+/**
+ * Announce existing user to a new server
+ * @param Client New server
+ * @param User Existing user in the network
+ */
+static bool
+Announce_User(CLIENT * Client, CLIENT * User)
+{
+	return IRC_WriteStrClient(Client, "NICK %s %d %s %s %d +%s :%s",
+		Client_ID(User), Client_Hops(User) + 1, Client_User(User),
+		Client_Hostname(User), Client_MyToken(Client_Introducer(User)),
+		Client_Modes(User), Client_Info(User));
+} /* Announce_User */
+
+
+#ifdef IRCPLUS
+
+/**
+ * Synchronize invite and ban lists between servers
+ * @param Client New server
+ */
+static bool
+Synchronize_Lists(CLIENT * Client)
+{
+	CHANNEL *c;
+	struct list_head *head;
+	struct list_elem *elem;
+
+	assert(Client != NULL);
+
+	c = Channel_First();
+	while (c) {
+		/* ban list */
+		head = Channel_GetListBans(c);
+		elem = Lists_GetFirst(head);
+		while (elem) {
+			if (!IRC_WriteStrClient(Client, "MODE %s +b %s",
+						Channel_Name(c),
+						Lists_GetMask(elem))) {
+				return DISCONNECTED;
+			}
+			elem = Lists_GetNext(elem);
+		}
+
+		/* invite list */
+		head = Channel_GetListInvites(c);
+		elem = Lists_GetFirst(head);
+		while (elem) {
+			if (!IRC_WriteStrClient(Client, "MODE %s +I %s",
+						Channel_Name(c),
+						Lists_GetMask(elem))) {
+				return DISCONNECTED;
+			}
+			elem = Lists_GetNext(elem);
+		}
+
+		c = Channel_Next(c);
+	}
+	return CONNECTED;
+}
+
+
+/**
+ * Send CHANINFO commands to a new server (inform it about existing channels).
+ * @param Client New server
+ * @param Chan Channel
+ */
+static bool
+Send_CHANINFO(CLIENT * Client, CHANNEL * Chan)
+{
+	char *modes, *topic;
+	bool has_k, has_l;
+	
+#ifdef DEBUG
+	Log(LOG_DEBUG, "Sending CHANINFO commands ...");
+#endif
+	
+	modes = Channel_Modes(Chan);
+	topic = Channel_Topic(Chan);
+	
+	if (!*modes && !*topic)
+		return CONNECTED;
+	
+	has_k = strchr(modes, 'k') != NULL;
+	has_l = strchr(modes, 'l') != NULL;
+	
+	/* send CHANINFO */
+	if (!has_k && !has_l) {
+		if (!*topic) {
+			/* "CHANINFO <chan> +<modes>" */
+			return IRC_WriteStrClient(Client, "CHANINFO %s +%s",
+						  Channel_Name(Chan), modes);
+		}
+		/* "CHANINFO <chan> +<modes> :<topic>" */
+		return IRC_WriteStrClient(Client, "CHANINFO %s +%s :%s",
+					  Channel_Name(Chan), modes, topic);
+	}
+	/* "CHANINFO <chan> +<modes> <key> <limit> :<topic>" */
+	return IRC_WriteStrClient(Client, "CHANINFO %s +%s %s %lu :%s",
+				  Channel_Name(Chan), modes,
+				  has_k ? Channel_Key(Chan) : "*",
+				  has_l ? Channel_MaxUsers(Chan) : 0, topic);
+} /* Send_CHANINFO */
+
+#endif /* IRCPLUS */
+
+
+/**
+ * Handle ENDOFMOTD (376) numeric and login remote server.
+ * The peer is either an IRC server (no IRC+ protocol), or we got the
+ * ENDOFMOTD numeric from an IRC+ server. We have to register the new server.
+ */
+GLOBAL bool
+IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
+{
+	char str[LINE_LEN];
+	int max_hops, i;
+	CLIENT *c, *cl;
+	CHANNEL *chan;
+	CL2CHAN *cl2chan;
+
+	Client_SetType(Client, CLIENT_SERVER);
+
+	Log(LOG_NOTICE | LOG_snotice,
+	    "Server \"%s\" registered (connection %d, 1 hop - direct link).",
+	    Client_ID(Client), Client_Conn(Client));
+
+	/* Get highest hop count */
+	max_hops = 0;
+	c = Client_First();
+	while (c) {
+		if (Client_Hops(c) > max_hops)
+			max_hops = Client_Hops(c);
+		c = Client_Next(c);
+	}
+
+	/* Inform the new server about all other servers, and announce the
+	 * new server to all the already registered ones. Important: we have
+	 * to do this "in order" and can't introduce servers of which the
+	 * "toplevel server" isn't known already. */
+	for (i = 0; i < (max_hops + 1); i++) {
+		for (c = Client_First(); c != NULL; c = Client_Next(c)) {
+			if (Client_Type(c) != CLIENT_SERVER)
+				continue;	/* not a server */
+			if (Client_Hops(c) != i)
+				continue;	/* not actual "nesting level" */
+			if (c == Client || c == Client_ThisServer())
+				continue;	/* that's us or the peer! */
+
+			if (!Announce_Server(Client, c))
+				return DISCONNECTED;
+		}
+	}
+
+	/* Announce all the users to the new server */
+	c = Client_First();
+	while (c) {
+		if (Client_Type(c) == CLIENT_USER) {
+			if (!Announce_User(Client, c))
+				return DISCONNECTED;
+		}
+		c = Client_Next(c);
+	}
+
+	/* Announce all channels to the new server */
+	chan = Channel_First();
+	while (chan) {
+#ifdef IRCPLUS
+		/* Send CHANINFO if the peer supports it */
+		if (strchr(Client_Flags(Client), 'C')) {
+			if (!Send_CHANINFO(Client, chan))
+				return DISCONNECTED;
+		}
+#endif
+
+		/* Get all the members of this channel */
+		cl2chan = Channel_FirstMember(chan);
+		snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(chan));
+		while (cl2chan) {
+			cl = Channel_GetClient(cl2chan);
+			assert(cl != NULL);
+
+			/* Nick name, with modes (if applicable) */
+			if (str[strlen(str) - 1] != ':')
+				strlcat(str, ",", sizeof(str));
+			if (strchr(Channel_UserModes(chan, cl), 'v'))
+				strlcat(str, "+", sizeof(str));
+			if (strchr(Channel_UserModes(chan, cl), 'o'))
+				strlcat(str, "@", sizeof(str));
+			strlcat(str, Client_ID(cl), sizeof(str));
+
+			/* Send the data if the buffer is "full" */
+			if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 8)) {
+				if (!IRC_WriteStrClient(Client, "%s", str))
+					return DISCONNECTED;
+				snprintf(str, sizeof(str), "NJOIN %s :",
+					 Channel_Name(chan));
+			}
+
+			cl2chan = Channel_NextMember(chan, cl2chan);
+		}
+
+		/* Data left in the buffer? */
+		if (str[strlen(str) - 1] != ':') {
+			/* Yes, send it ... */
+			if (!IRC_WriteStrClient(Client, "%s", str))
+				return DISCONNECTED;
+		}
+
+		/* Get next channel ... */
+		chan = Channel_Next(chan);
+	}
+
+#ifdef IRCPLUS
+	if (strchr(Client_Flags(Client), 'L')) {
+		LogDebug("Synchronizing INVITE- and BAN-lists ...");
+		if (!Synchronize_Lists(Client))
+			return DISCONNECTED;
+	}
+#endif
+
+	return CONNECTED;
+} /* IRC_Num_ENDOFMOTD */
+
+
+/**
+ * Handle ISUPPORT (005) numeric.
+ */
+GLOBAL bool
+IRC_Num_ISUPPORT(CLIENT * Client, REQUEST * Req)
+{
+	int i;
+	char *key, *value;
+
+	for (i = 1; i < Req->argc - 1; i++) {
+		key = Req->argv[i];
+		value = strchr(key, '=');
+		if (value)
+			*value++ = '\0';
+		else
+			value = "";
+
+		if (strcmp("NICKLEN", key) == 0) {
+			if ((unsigned int)atol(value) == Conf_MaxNickLength - 1)
+				continue;
+
+			/* Nick name length settings are different! */
+			Log(LOG_ERR,
+			    "Peer uses incompatible nick name length (%d/%d)! Disconnecting ...",
+			    Conf_MaxNickLength - 1, atoi(value));
+			Conn_Close(Client_Conn(Client),
+				   "Incompatible nick name length",
+				   NULL, false);
+			return DISCONNECTED;
+		}
+	}
+
+	return CONNECTED;
+} /* IRC_Num_ISUPPORT */
+
+
+/* -eof- */
Index: src/ngircd/numeric.h
===================================================================
RCS file: src/ngircd/numeric.h
diff -N src/ngircd/numeric.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/ngircd/numeric.h	20 Nov 2007 22:08:09 -0000
@@ -0,0 +1,24 @@
+/*
+ * ngIRCd -- The Next Generation IRC Daemon
+ * Copyright (c)2001-2007 by 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.
+ *
+ * $Id$
+ *
+ * Handlers for IRC numerics sent to the server (header)
+ */
+
+#ifndef __numeric_h__
+#define __numeric_h__
+
+GLOBAL bool IRC_Num_ENDOFMOTD PARAMS((CLIENT *Client, UNUSED REQUEST *Req));
+GLOBAL bool IRC_Num_ISUPPORT PARAMS((CLIENT *Client, REQUEST *Req));
+
+#endif
+
+/* -eof- */
Index: src/ngircd/parse.c
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/parse.c,v
retrieving revision 1.68
diff -u -p -r1.68 parse.c
--- src/ngircd/parse.c	2 Aug 2007 10:14:26 -0000	1.68
+++ src/ngircd/parse.c	20 Nov 2007 22:08:09 -0000
@@ -48,6 +48,7 @@ static char UNUSED id[] = "$Id: parse.c,
 #include "irc-oper.h"
 #include "irc-server.h"
 #include "irc-write.h"
+#include "numeric.h"
 
 #include "exp.h"
 
@@ -103,6 +104,13 @@ COMMAND My_Commands[] =
 	{ NULL, NULL, 0x0, 0, 0, 0 } /* Ende-Marke */
 };
 
+NUMERIC My_Numerics[] =
+{
+	{ 005, IRC_Num_ISUPPORT },
+	{ 376, IRC_Num_ENDOFMOTD },
+	{ 0, NULL } /* end marker */
+};
+
 
 static void Init_Request PARAMS(( REQUEST *Req ));
 
@@ -349,6 +357,7 @@ Handle_Request( CONN_ID Idx, REQUEST *Re
 	char str[LINE_LEN];
 	bool result;
 	COMMAND *cmd;
+	NUMERIC *num;
 	int i;
 
 	assert( Idx >= 0 );
@@ -358,25 +367,45 @@ Handle_Request( CONN_ID Idx, REQUEST *Re
 	client = Conn_GetClient( Idx );
 	assert( client != NULL );
 
-	/* Statuscode? */
-	if(( Client_Type( client ) == CLIENT_SERVER ) && ( strlen( Req->command ) == 3 ) && ( atoi( Req->command ) > 100 ))
-	{
-		/* Command is a status code from an other server */
+	/* Numeric? */
+	if ((Client_Type(client) == CLIENT_SERVER ||
+	     Client_Type(client) == CLIENT_UNKNOWNSERVER)
+	    && strlen(Req->command) == 3 && atoi(Req->command) > 1) {
+		/* Command is a status code ("numeric") from an other server */
 
 		/* Determine target */
-		if( Req->argc > 0 ) target = Client_Search( Req->argv[0] );
-		else target = NULL;
-		if( ! target )
-		{
+		if (Req->argc > 0)
+			target = Client_Search( Req->argv[0] );
+		else
+			target = NULL;
+		if (!target) {
 			/* Status code without target!? */
-			if( Req->argc > 0 ) Log( LOG_WARNING, "Unknown target for status code %s: \"%s\"", Req->command, Req->argv[0] );
-			else Log( LOG_WARNING, "Unknown target for status code %s!", Req->command );
+			if (Req->argc > 0)
+				Log(LOG_WARNING,
+				    "Unknown target for status code %s: \"%s\"",
+				    Req->command, Req->argv[0]);
+			else
+				Log(LOG_WARNING,
+				    "Unknown target for status code %s!",
+				    Req->command);
 			return true;
 		}
-		if( target == Client_ThisServer( ))
-		{
-			/* This server is the target, ignore it */
-			Log( LOG_DEBUG, "Ignored status code %s from \"%s\".", Req->command, Client_ID( client ));
+		if (target == Client_ThisServer()) {
+			/* This server is the target of the numeric */
+			i = atoi(Req->command);
+
+			num = My_Numerics;
+			while (num->numeric > 0) {
+				if (i != num->numeric) {
+					num++;
+					continue;
+				}
+				result = (num->function)(client, Req);
+				return result;
+			}
+			
+			LogDebug("Ignored status code %s from \"%s\".",
+				 Req->command, Client_ID(client));
 			return true;
 		}
 
Index: src/ngircd/parse.h
===================================================================
RCS file: /srv/cvs/ngircd/ngircd/src/ngircd/parse.h,v
retrieving revision 1.11
diff -u -p -r1.11 parse.h
--- src/ngircd/parse.h	19 Mar 2005 18:43:49 -0000	1.11
+++ src/ngircd/parse.h	20 Nov 2007 22:08:09 -0000
@@ -33,11 +33,18 @@ typedef struct _COMMAND
 	char *name;			/* command name */
 	bool (*function) PARAMS(( CLIENT *Client, REQUEST *Request ));
 	CLIENT_TYPE type;		/* valid client types (bit mask) */
-	long lcount, rcount;	/* number of local and remote calls */
-	long bytes;		/* number of bytes created */
+	long lcount, rcount;		/* number of local and remote calls */
+	long bytes;			/* number of bytes created */
 } COMMAND;
 
 
+typedef struct _NUMERIC
+{
+	int numeric;			/* numeric */
+	bool (*function) PARAMS(( CLIENT *Client, REQUEST *Request ));
+} NUMERIC;
+
+
 GLOBAL bool Parse_Request PARAMS((CONN_ID Idx, char *Request ));
 
 GLOBAL COMMAND *Parse_GetCommandStruct PARAMS(( void ));
