Implement user mode "F" which disables "flood protection" (aka "throttling").

 doc/Modes.txt         |  3 +-
 src/ngircd/conn.c     | 76 +++++++++++++++++++++++++++++++++++----------------
 src/ngircd/defines.h  |  4 +--
 src/ngircd/irc-mode.c |  1 +
 4 files changed, 58 insertions(+), 26 deletions(-)

diff --git a/doc/Modes.txt b/doc/Modes.txt
index aee7491..7402e46 100644
--- a/doc/Modes.txt
+++ b/doc/Modes.txt
@@ -2,7 +2,7 @@
                      ngIRCd - Next Generation IRC Server
                            http://ngircd.barton.de/
 
-               (c)2001-2012 Alexander Barton and Contributors.
+               (c)2001-2014 Alexander Barton and Contributors.
                ngIRCd is free software and published under the
                    terms of the GNU General Public License.
 
@@ -26,6 +26,7 @@ channels he is using at the moment.
   B	20	User is flagged as a "bot".
   c	17	IRC operator wants to receive connect/disconnect NOTICEs.
   C	19	Only users that share a channel are allowed to send messages.
+  F	22	Disable flood protection (only settable by IRC Operators).
   i	0.0.1	User is "invisible".
   o	0.0.1	User is IRC operator.
   q	20	User is protected, can not be kicked from a channel.
diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c
index 4dfe62f..498cba2 100644
--- a/src/ngircd/conn.c
+++ b/src/ngircd/conn.c
@@ -74,6 +74,9 @@
 
 #define SD_LISTEN_FDS_START 3		/** systemd(8) socket activation offset */
 
+#define THROTTLE_CMDS 1			/** Throttling: max commands reached */
+#define THROTTLE_BPS 2			/** Throttling: max bps reached */
+
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
 static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
 static int New_Connection PARAMS(( int Sock, bool IsSSL ));
@@ -88,6 +91,8 @@ static void New_Server PARAMS(( int Server, ng_ipaddr_t *dest ));
 static void Simple_Message PARAMS(( int Sock, const char *Msg ));
 static int NewListener PARAMS(( const char *listen_addr, UINT16 Port ));
 static void Account_Connection PARAMS((void));
+static void Throttle_Connection PARAMS((const CONN_ID Idx, CLIENT *Client,
+					const int Reason, unsigned int Value));
 
 static array My_Listeners;
 static array My_ConnArray;
@@ -665,7 +670,7 @@ GLOBAL void
 Conn_Handler(void)
 {
 	int i;
-	size_t wdatalen, bytes_processed;
+	size_t wdatalen;
 	struct timeval tv;
 	time_t t;
 
@@ -684,17 +689,7 @@ Conn_Handler(void)
 			if ((My_Connections[i].sock > NONE)
 			    && (array_bytes(&My_Connections[i].rbuf) > 0)) {
 				/* ... and try to handle the received data */
-				bytes_processed = Handle_Buffer(i);
-				/* if we processed data, and there might be
-				 * more commands in the input buffer, do not
-				 * try to read any more data now */
-				if (bytes_processed &&
-				    array_bytes(&My_Connections[i].rbuf) > 2) {
-					LogDebug
-					    ("Throttling connection %d: command limit reached!",
-					     i);
-					Conn_SetPenalty(i, 1);
-				}
+				Handle_Buffer(i);
 			}
 		}
 
@@ -1573,8 +1568,8 @@ Read_Request( CONN_ID Idx )
 	{
 		/* Read buffer is full */
 		Log(LOG_ERR,
-		    "Receive buffer space exhausted (connection %d): %d bytes",
-		    Idx, array_bytes(&My_Connections[Idx].rbuf));
+		    "Receive buffer space exhausted (connection %d): %d/%d bytes",
+		    Idx, array_bytes(&My_Connections[Idx].rbuf), READBUFFER_LEN);
 		Conn_Close(Idx, "Receive buffer space exhausted", NULL, false);
 		return;
 	}
@@ -1626,6 +1621,8 @@ Read_Request( CONN_ID Idx )
 
 	/* Update connection statistics */
 	My_Connections[Idx].bytes_in += len;
+
+	/* Handle read buffer */
 	My_Connections[Idx].bps += Handle_Buffer(Idx);
 
 	/* Make sure that there is still a valid client registered */
@@ -1651,14 +1648,8 @@ Read_Request( CONN_ID Idx )
 	}
 
 	/* Look at the data in the (read-) buffer of this connection */
-	if (Client_Type(c) != CLIENT_SERVER
-	    && Client_Type(c) != CLIENT_UNKNOWNSERVER
-	    && Client_Type(c) != CLIENT_SERVICE
-	    && My_Connections[Idx].bps >= maxbps) {
-		LogDebug("Throttling connection %d: BPS exceeded! (%u >= %u)",
-			 Idx, My_Connections[Idx].bps, maxbps);
-		Conn_SetPenalty(Idx, 1);
-	}
+	if (My_Connections[Idx].bps >= maxbps)
+		Throttle_Connection(Idx, c, THROTTLE_BPS, maxbps);
 } /* Read_Request */
 
 /**
@@ -1702,7 +1693,12 @@ Handle_Buffer(CONN_ID Idx)
 			maxcmd *= 5;
 		break;
 	    case CLIENT_SERVICE:
-		maxcmd = MAX_COMMANDS_SERVICE; break;
+		maxcmd = MAX_COMMANDS_SERVICE;
+		break;
+	    case CLIENT_USER:
+		if (Client_HasMode(c, 'F'))
+			maxcmd = MAX_COMMANDS_SERVICE;
+		break;
 	}
 
 	for (i=0; i < maxcmd; i++) {
@@ -1827,6 +1823,11 @@ Handle_Buffer(CONN_ID Idx)
 		 array_bytes(&My_Connections[Idx].rbuf));
 #endif
 
+	/* If data has been processed but there is still data in the read
+	 * buffer, the command limit triggered. Enforce the penalty time: */
+	if (len_processed && array_bytes(&My_Connections[Idx].rbuf) > 2)
+		Throttle_Connection(Idx, c, THROTTLE_CMDS, maxcmd);
+
 	return len_processed;
 } /* Handle_Buffer */
 
@@ -2410,6 +2411,35 @@ Conn_GetFromProc(int fd)
 	return NONE;
 } /* Conn_GetFromProc */
 
+/**
+ * Throttle a connection because of excessive usage.
+ *
+ * @param Reason The reason, see THROTTLE_xxx constants.
+ * @param Idx The connection index.
+ * @param Client The client of this connection.
+ * @param Seconds The time to delay this connection.
+ */
+static void
+Throttle_Connection(const CONN_ID Idx, CLIENT *Client, const int Reason,
+		    unsigned int Value)
+{
+	assert(Idx > NONE);
+	assert(Client != NULL);
+
+	/* Never throttle servers or services */
+	if (Client_Type(Client) == CLIENT_SERVER
+	    || Client_Type(Client) == CLIENT_UNKNOWNSERVER
+	    || Client_Type(Client) == CLIENT_SERVICE)
+		return;
+
+	/* Don't throttle clients with user mode 'F' set */
+	if (Client_HasMode(Client, 'F'))
+		return;
+
+	LogDebug("Throttling connection %d: code %d, value %d!", Idx,
+		 Reason, Value);
+	Conn_SetPenalty(Idx, 1);
+}
 
 #ifndef STRICT_RFC
 
diff --git a/src/ngircd/defines.h b/src/ngircd/defines.h
index 361564f..4acdc47 100644
--- a/src/ngircd/defines.h
+++ b/src/ngircd/defines.h
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
+ * Copyright (c)2001-2014 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
@@ -173,7 +173,7 @@
 #endif
 
 /** Supported user modes. */
-#define USERMODES "abBcCioqrRswx"
+#define USERMODES "abBcCFioqrRswx"
 
 /** Supported channel modes. */
 #define CHANMODES "abehiIklmMnoOPqQrRstvVz"
diff --git a/src/ngircd/irc-mode.c b/src/ngircd/irc-mode.c
index 6a67007..fe98121 100644
--- a/src/ngircd/irc-mode.c
+++ b/src/ngircd/irc-mode.c
@@ -222,6 +222,7 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 			break;
 		case 'c': /* Receive connect notices */
 		case 'q': /* KICK-protected user */
+		case 'F': /* disable flood protection */
 			  /* (only settable by IRC operators!) */
 			if (!set || Client_Type(Client) == CLIENT_SERVER
 			    || Client_HasMode(Origin, 'o'))
