repo: ngircd
action: commit
revision: 
path_from: 
revision_from: 294320ed62bdb7dac546cea43fac3b4c916788a4:
path_to: 
revision_to: 
git.thebackupbox.net
ngircd
git clone git://git.thebackupbox.net/ngircd
commit 294320ed62bdb7dac546cea43fac3b4c916788a4
Author: Alexander Barton 
Date:   Tue May 19 22:41:45 2009 +0200

    Enable SQUIT command for IRC Operators

    This patch enables IRC Operators to use the SQUIT command as specified in
    RFC 2812, section 3.1.8 "Squit".

    When forwarding SQUIT commands, the server connected to the target will
    drop the connection (not the target server itself!).

    Please note:

     - the configuration option "AllowRemoteOper" mus be enabled on the
       server disconnecting the target to allow forwarding of SQUIT commands.
     - if the remote server is configured to establish the connection, it
       will just do this; so the disconnect is not permanent in this case!

diff --git a/src/ngircd/irc-server.c b/src/ngircd/irc-server.c
index b75a34f9834abf6267d5ebb60faae753cff51a17..
index ..ca2502dc62414008abdd558a715d458436aa8b2c 100644
--- a/src/ngircd/irc-server.c
+++ b/src/ngircd/irc-server.c
@@ -37,6 +37,7 @@
 #include "numeric.h"
 #include "ngircd.h"
 #include "irc-info.h"
+#include "op.h"

 #include "exp.h"
 #include "irc-server.h"
@@ -273,21 +274,40 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req )
 GLOBAL bool
 IRC_SQUIT(CLIENT * Client, REQUEST * Req)
 {
-	CLIENT *target;
-	char msg[LINE_LEN + 64];
+	char msg[COMMAND_LEN], logmsg[COMMAND_LEN];
+	CLIENT *from, *target;
+	CONN_ID con;

 	assert(Client != NULL);
 	assert(Req != NULL);

+	if (Client_Type(Client) != CLIENT_SERVER
+	    && !Client_HasMode(Client, 'o'))
+		return Op_NoPrivileges(Client, Req);
+
 	/* Bad number of arguments? */
 	if (Req->argc != 2)
 		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
 					  Client_ID(Client), Req->command);

+	if (Client_Type(Client) == CLIENT_SERVER && Req->prefix) {
+		from = Client_Search(Req->prefix);
+		if (Client_Type(from) != CLIENT_SERVER
+		    && !Op_Check(Client, Req))
+			return Op_NoPrivileges(Client, Req);
+	} else
+		from = Client;
+	if (!from)
+		return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+					  Client_ID(Client), Req->prefix);
+
 	Log(LOG_DEBUG, "Got SQUIT from %s for \"%s\": \"%s\" ...",
-	    Client_ID(Client), Req->argv[0], Req->argv[1]);
+	    Client_ID(from), Req->argv[0], Req->argv[1]);

 	target = Client_Search(Req->argv[0]);
+	if (Client_Type(Client) != CLIENT_SERVER &&
+	    target == Client_ThisServer())
+		return Op_NoPrivileges(Client, Req);
 	if (!target) {
 		/* The server is (already) unknown */
 		Log(LOG_WARNING,
@@ -296,27 +316,46 @@ IRC_SQUIT(CLIENT * Client, REQUEST * Req)
 		return CONNECTED;
 	}

-	if (Req->argv[1][0]) {
-		if (strlen(Req->argv[1]) > LINE_LEN)
-			Req->argv[1][LINE_LEN] = '\0';
-		snprintf(msg, sizeof(msg), "%s (SQUIT from %s).", Req->argv[1],
-			 Client_ID(Client));
-	} else
-		snprintf(msg, sizeof(msg), "Got SQUIT from %s.",
-			 Client_ID(Client));
-
-	if (Client_Conn(target) > NONE) {
-		/* We are directly connected to this server */
-		if (Req->argv[1][0])
-			Conn_Close(Client_Conn(target), msg, Req->argv[1],
-				   true);
+	con = Client_Conn(target);
+
+	if (Req->argv[1][0])
+		if (Client_NextHop(from) != Client || con > NONE)
+			snprintf(msg, sizeof(msg), "%s (SQUIT from %s)",
+				 Req->argv[1], Client_ID(from));
 		else
-			Conn_Close(Client_Conn(target), msg, NULL, true);
-		return DISCONNECTED;
+			strlcpy(msg, Req->argv[1], sizeof(msg));
+	else
+		snprintf(msg, sizeof(msg), "Got SQUIT from %s",
+			 Client_ID(from));
+
+	if (con > NONE) {
+		/* We are directly connected to the target server, so we
+		 * have to tear down the connection and to inform all the
+		 * other remaining servers in the network */
+		Conn_Close(con, NULL, msg, true);
+		if (con == Client_Conn(Client))
+			return DISCONNECTED;
 	} else {
-		Client_Destroy(target, msg, Req->argv[1], false);
-		return CONNECTED;
+		/* This server is not directly connected, so the SQUIT must
+		 * be forwarded ... */
+		if (Client_Type(from) != CLIENT_SERVER) {
+			/* The origin is not an IRC server, so don't evaluate
+			 * this SQUIT but simply forward it */
+			IRC_WriteStrClientPrefix(Client_NextHop(target),
+			    from, "SQUIT %s :%s", Req->argv[0], Req->argv[1]);
+		} else {
+			/* SQUIT has been generated by another server, so
+			 * remove the target server from the network! */
+			logmsg[0] = '\0';
+			if (!strchr(msg, '('))
+				snprintf(logmsg, sizeof(logmsg),
+					 "%s (SQUIT from %s)", Req->argv[1],
+					 Client_ID(from));
+			Client_Destroy(target, logmsg[0] ? logmsg : msg,
+				       msg, false);
+		}
 	}
+	return CONNECTED;
 } /* IRC_SQUIT */

 /* -eof- */
diff --git a/src/ngircd/parse.c b/src/ngircd/parse.c
index 6d53525b6f2754875eae94a9baa565800156ac0a..
index ..2c28a309a0bebf39076b06450a98ac0d5fdc1265 100644
--- a/src/ngircd/parse.c
+++ b/src/ngircd/parse.c
@@ -92,7 +92,7 @@ static COMMAND My_Commands[] =
 	{ "SERVICE", IRC_SERVICE, 0xFFFF, 0, 0, 0 },
 	{ "SERVLIST", IRC_SERVLIST, CLIENT_USER, 0, 0, 0 },
 	{ "SQUERY", IRC_SQUERY, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "SQUIT", IRC_SQUIT, CLIENT_SERVER, 0, 0, 0 },
+	{ "SQUIT", IRC_SQUIT, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "TIME", IRC_TIME, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },

-----END OF PAGE-----