]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/irc-login.c
Fix some compiler warnings of Apple Xcode/Clang
[ngircd-alex.git] / src / ngircd / irc-login.c
index 9e1abdd59e8b0349b535b9b1299989f994a3827c..846b10d6e8aa2f08951c393f545aba0deb8ae331 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-2018 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
  * Login and logout
  */
 
-#include "imp.h"
 #include <assert.h>
+#include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
+#include <time.h>
 
 #include "conn-func.h"
-#include "class.h"
 #include "conf.h"
 #include "channel.h"
 #include "log.h"
 #include "messages.h"
 #include "parse.h"
 #include "irc.h"
-#include "irc-info.h"
+#include "irc-macros.h"
 #include "irc-write.h"
 
-#include "exp.h"
 #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 InformClient));
 
 /**
  * Handler for the IRC "PASS" command.
  *
- * See RFC 2813 section 4.1.1, and RFC 2812 section 3.1.1.
- *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED.
+ * @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_PASS( CLIENT *Client, REQUEST *Req )
@@ -59,7 +57,7 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
 
        /* Return an error if this is not a local client */
        if (Client_Conn(Client) <= NONE)
-               return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
+               return IRC_WriteErrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
                                          Client_ID(Client), Req->command);
 
        if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) {
@@ -79,11 +77,11 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
        } else if (Client_Type(Client) == CLIENT_UNKNOWN ||
                   Client_Type(Client) == CLIENT_UNKNOWNSERVER) {
                /* Unregistered connection, but wrong number of arguments: */
-               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+               return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
                                          Client_ID(Client), Req->command);
        } else {
                /* Registered connection, PASS command is not allowed! */
-               return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
+               return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
                                          Client_ID(Client));
        }
 
@@ -91,7 +89,7 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
 
        /* Protocol version */
        if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) {
-               int c2, c4;
+               char c2, c4;
 
                c2 = Req->argv[1][2];
                c4 = Req->argv[1][4];
@@ -142,7 +140,7 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
                        } else
                                flags = "";
                        Log(LOG_INFO,
-                           "Peer on conenction %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
+                           "Peer on connection %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
                            Client_Conn(Client), impl, serverver,
                            protohigh, protolow, flags);
                } else {
@@ -163,24 +161,18 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
        return CONNECTED;
 } /* IRC_PASS */
 
-
 /**
  * Handler for the IRC "NICK" command.
  *
- * See RFC 2812, 3.1.2 "Nick message", and RFC 2813, 4.1.3 "Nick".
- *
- * This function implements the IRC command "NICK" which is used to register
- * with the server, to change already registered nicknames and to introduce
- * new users which are connected to other servers.
- *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED.
+ * @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_NICK( CLIENT *Client, REQUEST *Req )
 {
        CLIENT *intr_c, *target, *c;
+       CHANNEL *chan;
        char *nick, *user, *hostname, *modes, *info;
        int token, hops;
 
@@ -200,31 +192,23 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
            || (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1))
        {
                /* User registration or change of nickname */
-
-               /* Wrong number of arguments? */
-               if( Req->argc != 1 )
-                       return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
-                                                  Client_ID( Client ),
-                                                  Req->command );
+               _IRC_ARGC_EQ_OR_RETURN_(Client, Req, 1)
 
                /* Search "target" client */
-               if( Client_Type( Client ) == CLIENT_SERVER )
-               {
-                       target = Client_Search( Req->prefix );
-                       if( ! target )
-                               return IRC_WriteStrClient( Client,
-                                                          ERR_NOSUCHNICK_MSG,
-                                                          Client_ID( Client ),
-                                                          Req->argv[0] );
-               }
-               else
-               {
+               if (Client_Type(Client) == CLIENT_SERVER) {
+                       _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
+                       target = Client_Search(Req->prefix);
+                       if (!target)
+                               return IRC_WriteErrClient(Client,
+                                                         ERR_NOSUCHNICK_MSG,
+                                                         Client_ID(Client),
+                                                         Req->argv[0]);
+               } else {
                        /* Is this a restricted client? */
-                       if( Client_HasMode( Client, 'r' ))
-                               return IRC_WriteStrClient( Client,
-                                                          ERR_RESTRICTED_MSG,
-                                                          Client_ID( Client ));
-
+                       if (Client_HasMode(Client, 'r'))
+                               return IRC_WriteErrClient(Client,
+                                                         ERR_RESTRICTED_MSG,
+                                                         Client_ID(Client));
                        target = Client;
                }
 
@@ -233,15 +217,14 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
                 * do anything. This is how the original ircd behaves and some
                 * clients (for example Snak) expect it to be like this.
                 * But I doubt that this is "really the right thing" ... */
-               if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 )
+               if (strcmp(Client_ID(target), Req->argv[0]) == 0)
                        return CONNECTED;
 #endif
 
                /* Check that the new nickname is available. Special case:
                 * the client only changes from/to upper to lower case. */
-               if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
-               {
-                       if( ! Client_CheckNick( target, Req->argv[0] ))
+               if (strcasecmp(Client_ID(target), Req->argv[0]) != 0) {
+                       if (!Client_CheckNick(target, Req->argv[0]))
                                return CONNECTED;
                }
 
@@ -257,8 +240,12 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 
 #ifndef STRICT_RFC
                        if (Conf_AuthPing) {
+#ifdef HAVE_ARC4RANDOM
+                               Conn_SetAuthPing(Client_Conn(Client), arc4random());
+#else
                                Conn_SetAuthPing(Client_Conn(Client), rand());
-                               IRC_WriteStrClient(Client, "PING :%ld",
+#endif
+                               Conn_WriteStr(Client_Conn(Client), "PING :%ld",
                                        Conn_GetAuthPing(Client_Conn(Client)));
                                LogDebug("Connection %d: sent AUTH PING %ld ...",
                                        Client_Conn(Client),
@@ -274,40 +261,25 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
                                Client_SetType( Client, CLIENT_GOTNICK );
                } else {
                        /* Nickname change */
-                       if (Client_Conn(target) > NONE) {
-                               /* Local client */
-                               Log(LOG_INFO,
-                                   "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
-                                   Client_TypeText(target), Client_Mask(target),
-                                   Client_Conn(target), Client_ID(target),
-                                   Req->argv[0]);
-                               Conn_UpdateIdle(Client_Conn(target));
-                       } else {
-                               /* Remote client */
-                               LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
-                                        Client_TypeText(target),
-                                        Client_Mask(target), Client_ID(target),
-                                        Req->argv[0]);
-                       }
 
-                       /* Inform all users and servers (which have to know)
-                        * of this nickname change */
-                       if( Client_Type( Client ) == CLIENT_USER )
-                               IRC_WriteStrClientPrefix( Client, Client,
-                                                         "NICK :%s",
-                                                         Req->argv[0] );
-                       IRC_WriteStrServersPrefix( Client, target,
-                                                  "NICK :%s", Req->argv[0] );
-                       IRC_WriteStrRelatedPrefix( target, target, false,
-                                                  "NICK :%s", Req->argv[0] );
-
-                       /* Register old nickname for WHOWAS queries */
-                       Client_RegisterWhowas( target );
-
-                       /* Save new nickname */
-                       Client_SetID( target, Req->argv[0] );
+                       /* Check that the user isn't on any channels set +N */
+                       if(Client_Type(Client) == CLIENT_USER &&
+                          !Client_HasMode(Client, 'o')) {
+                               chan = Channel_First();
+                               while (chan) {
+                                       if(Channel_HasMode(chan, 'N') &&
+                                          Channel_IsMemberOf(chan, Client))
+                                               return IRC_WriteErrClient(Client,
+                                                                         ERR_NONICKCHANGE_MSG,
+                                                                         Client_ID(Client),
+                                                                         Channel_Name(chan));
+                                       chan = Channel_Next(chan);
+                               }
+                       }
 
-                       IRC_SetPenalty( target, 2 );
+                       Change_Nick(Client, target, Req->argv[0],
+                                   Client_Type(Client) == CLIENT_USER ? true : false);
+                       IRC_SetPenalty(target, 2);
                }
 
                return CONNECTED;
@@ -317,7 +289,7 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 
                /* Bad number of parameters? */
                if (Req->argc != 2 && Req->argc != 7)
-                       return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+                       return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
                                                  Client_ID(Client), Req->command);
 
                if (Req->argc >= 7) {
@@ -346,28 +318,33 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
                         * the new nick is already present on this server:
                         * the new and the old one have to be disconnected now.
                         */
-                       Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
-                       Kill_Nick( Req->argv[0], "Nick collision" );
-                       return CONNECTED;
+                       Log(LOG_ERR,
+                           "Server %s introduces already registered nick \"%s\"!",
+                           Client_ID(Client), Req->argv[0]);
+                       return IRC_KillClient(Client, NULL, Req->argv[0],
+                                             "Nick collision");
                }
 
                /* Find the Server this client is connected to */
                intr_c = Client_GetFromToken(Client, token);
-               if( ! intr_c )
-               {
-                       Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
-                       Kill_Nick( Req->argv[0], "Unknown server" );
-                       return CONNECTED;
+               if (!intr_c) {
+                       Log(LOG_ERR,
+                           "Server %s introduces nick \"%s\" on unknown server!?",
+                           Client_ID(Client), Req->argv[0]);
+                       return IRC_KillClient(Client, NULL, Req->argv[0],
+                                             "Unknown server");
                }
 
                c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
                                         token, modes, info, true);
-               if( ! c )
-               {
-                       /* out of memory, need to disconnect client to keep network state consistent */
-                       Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
-                       Kill_Nick( Req->argv[0], "Server error" );
-                       return CONNECTED;
+               if (!c) {
+                       /* Out of memory, we need to disconnect client to keep
+                        * network state consistent! */
+                       Log(LOG_ALERT,
+                           "Can't create client structure! (on connection %d)",
+                           Client_Conn(Client));
+                       return IRC_KillClient(Client, NULL, Req->argv[0],
+                                             "Server error");
                }
 
                /* RFC 2813: client is now fully registered, inform all the
@@ -383,18 +360,62 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 
                return CONNECTED;
        }
-       else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
+       else
+               return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
+                                         Client_ID(Client));
 } /* 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);
+
+       _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
+
+       /* 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_WriteErrClient(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.
  *
- * See RFC 2812, 3.1.3 "User message".
- *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED.
+ * @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_USER(CLIENT * Client, REQUEST * Req)
@@ -412,18 +433,15 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
            Client_Type(Client) == CLIENT_GOTPASS)
        {
                /* New connection */
-               if (Req->argc != 4)
-                       return IRC_WriteStrClient(Client,
-                                                 ERR_NEEDMOREPARAMS_MSG,
-                                                 Client_ID(Client),
-                                                 Req->command);
+               _IRC_ARGC_EQ_OR_RETURN_(Client, Req, 4)
 
-               /* User name: only alphanumeric characters are allowed! */
+               /* User name: only alphanumeric characters and limited
+                  punctuation is allowed.*/
                ptr = Req->argv[0];
                while (*ptr) {
-                       if ((*ptr < '0' || *ptr > '9') &&
-                           (*ptr < 'A' || *ptr > 'Z') &&
-                           (*ptr < 'a' || *ptr > 'z')) {
+                       if (!isalnum((int)*ptr) &&
+                           *ptr != '+' && *ptr != '-' && *ptr != '@' &&
+                           *ptr != '.' && *ptr != '_') {
                                Conn_Close(Client_Conn(Client), NULL,
                                           "Invalid user name", true);
                                return DISCONNECTED;
@@ -431,6 +449,13 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
                        ptr++;
                }
 
+               /* Save the received username for authentication, and use
+                * it up to the first '@' as default user name (like ircd2.11,
+                * bahamut, ircd-seven, ...), prefixed with '~', if needed: */
+               Client_SetOrigUser(Client, Req->argv[0]);
+               ptr = strchr(Req->argv[0], '@');
+               if (ptr)
+                       *ptr = '\0';
 #ifdef IDENTAUTH
                ptr = Client_User(Client);
                if (!ptr || !*ptr || *ptr == '~')
@@ -438,7 +463,6 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
 #else
                Client_SetUser(Client, Req->argv[0], false);
 #endif
-               Client_SetOrigUser(Client, Req->argv[0]);
 
                /* "Real name" or user info text: Don't set it to the empty
                 * string, the original ircd can't deal with such "real names"
@@ -459,14 +483,12 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
        } else if (Client_Type(Client) == CLIENT_SERVER ||
                   Client_Type(Client) == CLIENT_SERVICE) {
                /* Server/service updating an user */
-               if (Req->argc != 4)
-                       return IRC_WriteStrClient(Client,
-                                                 ERR_NEEDMOREPARAMS_MSG,
-                                                 Client_ID(Client),
-                                                 Req->command);
+               _IRC_ARGC_EQ_OR_RETURN_(Client, Req, 4)
+               _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
+
                c = Client_Search(Req->prefix);
                if (!c)
-                       return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+                       return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
                                                  Client_ID(Client),
                                                  Req->prefix);
 
@@ -486,29 +508,25 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
                return CONNECTED;
        } else if (Client_Type(Client) == CLIENT_USER) {
                /* Already registered connection */
-               return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
+               return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
                                          Client_ID(Client));
        } else {
                /* Unexpected/invalid connection state? */
-               return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
+               return IRC_WriteErrClient(Client, ERR_NOTREGISTERED_MSG,
                                          Client_ID(Client));
        }
 } /* IRC_USER */
 
-
 /**
  * Handler for the IRC "SERVICE" command.
  *
- * This function implements IRC Services registration using the SERVICE command
- * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
- *
  * At the moment ngIRCd doesn't support directly linked services, so this
  * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
  * received from a peer server.
  *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED..
+ * @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_SERVICE(CLIENT *Client, REQUEST *Req)
@@ -522,45 +540,36 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req)
 
        if (Client_Type(Client) != CLIENT_GOTPASS &&
            Client_Type(Client) != CLIENT_SERVER)
-               return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
+               return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
                                          Client_ID(Client));
 
-       if (Req->argc != 6)
-               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-                                         Client_ID(Client), Req->command);
-
        if (Client_Type(Client) != CLIENT_SERVER)
-               return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
+               return IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
                                  Client_ID(Client), Req->argv[0]);
 
-       /* Bad number of parameters? */
-       if (Req->argc != 6)
-               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-                                         Client_ID(Client), Req->command);
-
        nick = Req->argv[0];
        user = NULL; host = NULL;
        token = atoi(Req->argv[1]);
        hops = atoi(Req->argv[4]);
        info = Req->argv[5];
 
-       /* Validate service name ("nick name") */
+       /* Validate service name ("nickname") */
        c = Client_Search(nick);
        if(c) {
-               /* Nick name collission: disconnect (KILL) both clients! */
-               Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
+               /* Nickname collision: disconnect (KILL) both clients! */
+               Log(LOG_ERR,
+                   "Server %s introduces already registered service \"%s\"!",
                    Client_ID(Client), nick);
-               Kill_Nick(nick, "Nick collision");
-               return CONNECTED;
+               return IRC_KillClient(Client, NULL, nick, "Nick collision");
        }
 
        /* Get the server to which the service is connected */
        intr_c = Client_GetFromToken(Client, token);
        if (! intr_c) {
-               Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
+               Log(LOG_ERR,
+                   "Server %s introduces service \"%s\" on unknown server!?",
                    Client_ID(Client), nick);
-               Kill_Nick(nick, "Unknown server");
-               return CONNECTED;
+               return IRC_KillClient(Client, NULL, nick, "Unknown server");
        }
 
        /* Get user and host name */
@@ -589,37 +598,28 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req)
        if (! c) {
                /* Couldn't create client structure, so KILL the service to
                 * keep network status consistent ... */
-               Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
+               Log(LOG_ALERT,
+                   "Can't create client structure! (on connection %d)",
                    Client_Conn(Client));
-               Kill_Nick(nick, "Server error");
-               return CONNECTED;
+               return IRC_KillClient(Client, NULL, nick, "Server error");
        }
 
        Client_Introduce(Client, c, CLIENT_SERVICE);
        return CONNECTED;
 } /* IRC_SERVICE */
 
-
 /**
  * Handler for the IRC "WEBIRC" command.
  *
- * See doc/Protocol.txt, section II.4:
- * "Update webchat/proxy client information".
- *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED.
+ * @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_WEBIRC(CLIENT *Client, REQUEST *Req)
 {
-       /* Exactly 4 parameters are requited */
-       if (Req->argc != 4)
-               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-                                         Client_ID(Client), Req->command);
-
        if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
-               return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
+               return IRC_WriteErrClient(Client, ERR_PASSWDMISMATCH_MSG,
                                          Client_ID(Client));
 
        LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
@@ -627,39 +627,38 @@ IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
 
        Client_SetUser(Client, Req->argv[1], true);
        Client_SetOrigUser(Client, Req->argv[1]);
-       Client_SetHostname(Client, Req->argv[2]);
+       if (Conf_DNS)
+               Client_SetHostname(Client, Req->argv[2]);
+       else
+               Client_SetHostname(Client, Req->argv[3]);
+       Client_SetIPAText(Client, Req->argv[3]);
+
        return CONNECTED;
 } /* IRC_WEBIRC */
 
-
 /**
  * Handler for the IRC "QUIT" command.
  *
- * See RFC 2812, 3.1.7 "Quit", and RFC 2813, 4.1.5 "Quit".
- *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED.
+ * @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_QUIT( CLIENT *Client, REQUEST *Req )
 {
        CLIENT *target;
-       char quitmsg[LINE_LEN];
+       char quitmsg[COMMAND_LEN];
 
        assert(Client != NULL);
        assert(Req != NULL);
 
-       /* Wrong number of arguments? */
-       if (Req->argc > 1)
-               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-                                         Client_ID(Client), Req->command);
-
        if (Req->argc == 1)
                strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
 
        if (Client_Type(Client) == CLIENT_SERVER) {
                /* Server */
+               _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
+
                target = Client_Search(Req->prefix);
                if (!target) {
                        Log(LOG_WARNING,
@@ -669,11 +668,11 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
                }
 
                if (target != Client) {
-                       Client_Destroy(target, "Got QUIT command.",
+                       Client_Destroy(target, "Got QUIT command",
                                       Req->argc == 1 ? quitmsg : NULL, true);
                        return CONNECTED;
                } else {
-                       Conn_Close(Client_Conn(Client), "Got QUIT command.",
+                       Conn_Close(Client_Conn(Client), "Got QUIT command",
                                   Req->argc == 1 ? quitmsg : NULL, true);
                        return DISCONNECTED;
                }
@@ -686,14 +685,13 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
                }
 
                /* User, Service, or not yet registered */
-               Conn_Close(Client_Conn(Client), "Got QUIT command.",
+               Conn_Close(Client_Conn(Client), "Got QUIT command",
                           Req->argc == 1 ? quitmsg : NULL, true);
 
                return DISCONNECTED;
        }
 } /* IRC_QUIT */
 
-
 #ifndef STRICT_RFC
 
 /**
@@ -702,9 +700,9 @@ IRC_QUIT( CLIENT *Client, REQUEST *Req )
  * We handle these commands here to avoid the quite long timeout when
  * some user tries to access this IRC daemon using an web browser ...
  *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED.
+ * @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_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
@@ -716,15 +714,12 @@ IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
 
 #endif
 
-
 /**
  * Handler for the IRC "PING" command.
  *
- * See RFC 2812, 3.7.2 "Ping message".
- *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED.
+ * @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_PING(CLIENT *Client, REQUEST *Req)
@@ -735,13 +730,11 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
        assert(Req != NULL);
 
        if (Req->argc < 1)
-               return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
+               return IRC_WriteErrClient(Client, ERR_NOORIGIN_MSG,
                                          Client_ID(Client));
 #ifdef STRICT_RFC
        /* Don't ignore additional arguments when in "strict" mode */
-       if (Req->argc > 2)
-                return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-                                          Client_ID(Client), Req->command);
+       _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
 #endif
 
        if (Req->argc > 1) {
@@ -749,17 +742,18 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
                target = Client_Search(Req->argv[1]);
 
                if (!target || Client_Type(target) != CLIENT_SERVER)
-                       return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
+                       return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
                                        Client_ID(Client), Req->argv[1]);
 
                if (target != Client_ThisServer()) {
                        /* Ok, we have to forward the PING */
-                       if (Client_Type(Client) == CLIENT_SERVER)
+                       if (Client_Type(Client) == CLIENT_SERVER) {
+                               _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
                                from = Client_Search(Req->prefix);
-                       else
+                       else
                                from = Client;
                        if (!from)
-                               return IRC_WriteStrClient(Client,
+                               return IRC_WriteErrClient(Client,
                                                ERR_NOSUCHSERVER_MSG,
                                                Client_ID(Client), Req->prefix);
 
@@ -777,7 +771,7 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
        } else
                from = Client_ThisServer();
        if (!from)
-               return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
+               return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
                                        Client_ID(Client), Req->prefix);
 
        Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
@@ -794,15 +788,12 @@ IRC_PING(CLIENT *Client, REQUEST *Req)
 #endif
 } /* IRC_PING */
 
-
 /**
  * Handler for the IRC "PONG" command.
  *
- * See RFC 2812, 3.7.3 "Pong message".
- *
- * @param Client       The client from which this command has been received.
- * @param Req          Request structure with prefix and all parameters.
- * @returns            CONNECTED or DISCONNECTED.
+ * @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_PONG(CLIENT *Client, REQUEST *Req)
@@ -820,26 +811,22 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
        /* Wrong number of arguments? */
        if (Req->argc < 1) {
                if (Client_Type(Client) == CLIENT_USER)
-                       return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
+                       return IRC_WriteErrClient(Client, ERR_NOORIGIN_MSG,
                                                  Client_ID(Client));
                else
                        return CONNECTED;
        }
-       if (Req->argc > 2) {
-               if (Client_Type(Client) == CLIENT_USER)
-                       return IRC_WriteStrClient(Client,
-                                                 ERR_NEEDMOREPARAMS_MSG,
-                                                 Client_ID(Client),
-                                                 Req->command);
-               else
-                       return CONNECTED;
+       if (Client_Type(Client) == CLIENT_USER) {
+               _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
        }
 
        /* Forward? */
        if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
+               _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
+
                target = Client_Search(Req->argv[0]);
                if (!target)
-                       return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
+                       return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
                                        Client_ID(Client), Req->argv[0]);
 
                from = Client_Search(Req->prefix);
@@ -847,7 +834,7 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
                if (target != Client_ThisServer() && target != from) {
                        /* Ok, we have to forward the message. */
                        if (!from)
-                               return IRC_WriteStrClient(Client,
+                               return IRC_WriteErrClient(Client,
                                                ERR_NOSUCHSERVER_MSG,
                                                Client_ID(Client), Req->prefix);
 
@@ -871,56 +858,70 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
        if (auth_ping) {
                LogDebug("AUTH PONG: waiting for token \"%ld\", got \"%s\" ...",
                         auth_ping, Req->argv[0]);
-               if (auth_ping == atoi(Req->argv[0])) {
+               if (auth_ping == atol(Req->argv[0])) {
                        Conn_SetAuthPing(conn, 0);
                        if (Client_Type(Client) == CLIENT_WAITAUTHPING)
                                Login_User(Client);
                } else
                        if (!IRC_WriteStrClient(Client,
-                                       "To connect, type /QUOTE PONG %ld",
-                                       auth_ping))
+                                       "NOTICE %s :To connect, type /QUOTE PONG %ld",
+                                       Client_ID(Client), auth_ping))
                                return DISCONNECTED;
        }
 #endif
 
        if (Client_Type(Client) == CLIENT_SERVER && Conn_LastPing(conn) == 0) {
                Log(LOG_INFO,
-                   "Synchronization with \"%s\" done (connection %d): %ld seconds [%ld users, %ld channels]",
-                   Client_ID(Client), conn, time(NULL) - Conn_GetSignon(conn),
+                   "Synchronization with \"%s\" done (connection %d): %ld second%s [%ld users, %ld channels].",
+                   Client_ID(Client), conn,
+                   (long)(time(NULL) - Conn_GetSignon(conn)),
+                   time(NULL) - Conn_GetSignon(conn) == 1 ? "" : "s",
                    Client_UserCount(), Channel_CountVisible(NULL));
                Conn_UpdatePing(conn);
        } else
                LogDebug("Connection %d: received PONG. Lag: %ld seconds.",
-                        conn, time(NULL) - Conn_LastPing(conn));
+                        conn, (long)(time(NULL) - Conn_LastPing(conn)));
 
        return CONNECTED;
 } /* IRC_PONG */
 
-
 /**
- * Kill all users with a specific nick name in the network.
+ * Change the nickname of a client.
  *
- * @param Nick         Nick name.
- * @param Reason       Reason for the KILL.
+ * @param Origin The client which caused the nickname change.
+ * @param Target The client of which the nickname should be changed.
+ * @param NewNick The new nickname.
  */
 static void
-Kill_Nick(char *Nick, char *Reason)
+Change_Nick(CLIENT *Origin, CLIENT *Target, char *NewNick, bool InformClient)
 {
-       REQUEST r;
-
-       assert (Nick != NULL);
-       assert (Reason != NULL);
-
-       r.prefix = NULL;
-       r.argv[0] = Nick;
-       r.argv[1] = Reason;
-       r.argc = 2;
+       if (Client_Conn(Target) > NONE) {
+               /* Local client */
+               Log(LOG_INFO,
+                   "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
+                   Client_TypeText(Target), Client_Mask(Target),
+                   Client_Conn(Target), Client_ID(Target), NewNick);
+               Conn_UpdateIdle(Client_Conn(Target));
+       } else {
+               /* Remote client */
+               LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
+                        Client_TypeText(Target),
+                        Client_Mask(Target), Client_ID(Target), NewNick);
+       }
 
-       Log(LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s",
-           Nick, Reason);
+       /* Inform all servers and users (which have to know) of the new name */
+       if (InformClient) {
+               IRC_WriteStrClientPrefix(Target, Target, "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);
 
-       IRC_KILL(Client_ThisServer(), &r);
-} /* Kill_Nick */
+       /* Register old nickname for WHOWAS queries */
+       Client_RegisterWhowas(Target);
 
+       /* Save new nickname */
+       Client_SetID(Target, NewNick);
+}
 
 /* -eof- */