]> arthur.barton.de Git - ngircd-alex.git/commitdiff
Implement SVSNICK command to change remote nicknames
authorAlexander Barton <alex@barton.de>
Fri, 2 Nov 2012 16:50:31 +0000 (17:50 +0100)
committerAlexander Barton <alex@barton.de>
Fri, 2 Nov 2012 16:50:31 +0000 (17:50 +0100)
The SVSNICK command allows other servers (and services on
"pseudo-servers") to forcefully change nicknames of remote users.

Syntax: ":<from> SVSNICK <oldnick> <newnick>"

The SVSNICK command itself doesn't change the nickname, but it becomes
forwarded to the server to which the user is connected to. And then this
server initiates the real nickname changing using regular NICK commands.

This allows to still run networks with old servers not supporting the
SVSNICK command, because SVSNICK commands for nicknames on such servers
are silently ignored and don't cause a desync of the network.

src/ngircd/irc-login.c
src/ngircd/irc-login.h
src/ngircd/parse.c

index c3f32b7e8b2516fe383b6482341817ce668269a6..977fc54666e75ee8e5d753104cd46512f8471382 100644 (file)
@@ -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
@@ -39,8 +39,8 @@
 #include "irc-login.h"
 
 static void Kill_Nick PARAMS((char *Nick, char *Reason));
-static void Change_Nick PARAMS((CLIENT * Origin, CLIENT * Target,
-                               char *NewNick, bool RegisterWhowas));
+static void Change_Nick PARAMS((CLIENT * Origin, CLIENT * Target, char *NewNick,
+                               bool InformClient));
 
 
 /**
@@ -278,7 +278,8 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
                                Client_SetType( Client, CLIENT_GOTNICK );
                } else {
                        /* Nickname change */
-                       Change_Nick(Client, target, Req->argv[0], true);
+                       Change_Nick(Client, target, Req->argv[0],
+                                   Client_Type(Client) == CLIENT_USER ? true : false);
                        IRC_SetPenalty(target, 2);
                }
 
@@ -359,6 +360,54 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 } /* IRC_NICK */
 
 
+/**
+ * Handler for the IRC "SVSNICK" command.
+ *
+ * @param Client The client from which this command has been received.
+ * @param Req Request structure with prefix and all parameters.
+ * @return CONNECTED or DISCONNECTED.
+ */
+GLOBAL bool
+IRC_SVSNICK(CLIENT *Client, REQUEST *Req)
+{
+       CLIENT *from, *target;
+
+       assert(Client != NULL);
+       assert(Req != NULL);
+
+       if (Req->argc != 2)
+               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+                                         Client_ID(Client), Req->command);
+
+       /* Search the originator */
+       from = Client_Search(Req->prefix);
+       if (!from)
+               from = Client;
+
+       /* Search the target */
+       target = Client_Search(Req->argv[0]);
+       if (!target || Client_Type(target) != CLIENT_USER) {
+               return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+                                         Client_ID(Client), Req->argv[0]);
+       }
+
+       if (Client_Conn(target) <= NONE) {
+               /* We have to forward the message to the server handling
+                * this user; this is required to make sure all servers
+                * in the network do follow the nick name change! */
+               return IRC_WriteStrClientPrefix(Client_NextHop(target), from,
+                                               "SVSNICK %s %s",
+                                               Req->argv[0], Req->argv[1]);
+       }
+
+       /* Make sure that the new nickname is valid */
+       if (!Client_CheckNick(from, Req->argv[1]))
+               return CONNECTED;
+
+       Change_Nick(from, target, Req->argv[1], true);
+       return CONNECTED;
+}
+
 /**
  * Handler for the IRC "USER" command.
  *
@@ -904,7 +953,7 @@ Kill_Nick(char *Nick, char *Reason)
  * @param NewNick The new nickname.
  */
 static void
-Change_Nick(CLIENT *Origin, CLIENT *Target, char *NewNick, bool RegisterWhowas)
+Change_Nick(CLIENT *Origin, CLIENT *Target, char *NewNick, bool InformClient)
 {
        if (Client_Conn(Target) > NONE) {
                /* Local client */
@@ -921,14 +970,15 @@ Change_Nick(CLIENT *Origin, CLIENT *Target, char *NewNick, bool RegisterWhowas)
        }
 
        /* Inform all servers and users (which have to know) of the new name */
-       if (Client_Type(Origin) == CLIENT_USER)
-               IRC_WriteStrClientPrefix(Origin, Origin, "NICK :%s", NewNick);
-       IRC_WriteStrServersPrefix(Origin, Target, "NICK :%s", NewNick);
+       if (InformClient) {
+               IRC_WriteStrClientPrefix(Target, Origin, "NICK :%s", NewNick);
+               IRC_WriteStrServersPrefix(NULL, Target, "NICK :%s", NewNick);
+       } else
+               IRC_WriteStrServersPrefix(Origin, Target, "NICK :%s", NewNick);
        IRC_WriteStrRelatedPrefix(Target, Target, false, "NICK :%s", NewNick);
 
-       /* Register old nickname for WHOWAS queries, if required */
-       if (RegisterWhowas)
-               Client_RegisterWhowas(Target);
+       /* Register old nickname for WHOWAS queries */
+       Client_RegisterWhowas(Target);
 
        /* Save new nickname */
        Client_SetID(Target, NewNick);
index f3138f6efaa0a8ddb55fa37910aec3f56850f7b3..15be7b4c56c22f54015a212fc0b055a3151006f6 100644 (file)
@@ -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) 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
@@ -26,6 +26,7 @@ GLOBAL bool IRC_PING PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_PONG PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_QUIT PARAMS((CLIENT *Client, REQUEST *Req));
 GLOBAL bool IRC_QUIT_HTTP PARAMS((CLIENT *Client, REQUEST *Req));
+GLOBAL bool IRC_SVSNICK PARAMS(( CLIENT *Client, REQUEST *Req ));
 
 #endif
 
index f3b04d0c2ee8617ceb21d32ce919859e640b55f4..446180b523653cf85e5e747026819e03272b531f 100644 (file)
@@ -99,6 +99,7 @@ static COMMAND My_Commands[] =
        { "SQUERY", IRC_SQUERY, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "SQUIT", IRC_SQUIT, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
+       { "SVSNICK", IRC_SVSNICK, CLIENT_SERVER, 0, 0, 0 },
        { "SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "TIME", IRC_TIME, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "TOPIC", IRC_TOPIC, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },