]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/irc-login.c
Streamline punctuation of log messages
[ngircd-alex.git] / src / ngircd / irc-login.c
index 53069f00ff24c7e749db0e18a53f0d65ccbfc1d4..e7d83eff301c3491c83d71f9892944c0f6e5c480 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
 
 #include "imp.h"
 #include <assert.h>
-#include <stdio.h>
+#include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
-#include <signal.h>
-#include <unistd.h>
 
-#include "ngircd.h"
 #include "conn-func.h"
 #include "class.h"
 #include "conf.h"
 #include "channel.h"
-#include "io.h"
 #include "log.h"
+#include "login.h"
 #include "messages.h"
-#include "pam.h"
 #include "parse.h"
 #include "irc.h"
 #include "irc-info.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));
 
-static bool Hello_User PARAMS(( CLIENT *Client ));
-static bool Hello_User_PostAuth PARAMS(( CLIENT *Client ));
-static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
-#ifdef PAM
-static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events));
-#endif
 
 /**
  * Handler for the IRC "PASS" command.
@@ -98,7 +91,7 @@ IRC_PASS( CLIENT *Client, REQUEST *Req )
                                          Client_ID(Client));
        }
 
-       Client_SetPassword(Client, Req->argv[0]);
+       Conn_SetPassword(Client_Conn(Client), Req->argv[0]);
 
        /* Protocol version */
        if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) {
@@ -280,45 +273,14 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
                        /* If we received a valid USER command already then
                         * register the new client! */
                        if( Client_Type( Client ) == CLIENT_GOTUSER )
-                               return Hello_User( Client );
+                               return Login_User( Client );
                        else
                                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] );
-
-                       IRC_SetPenalty( target, 2 );
+                       Change_Nick(Client, target, Req->argv[0],
+                                   Client_Type(Client) == CLIENT_USER ? true : false);
+                       IRC_SetPenalty(target, 2);
                }
 
                return CONNECTED;
@@ -398,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.
  *
@@ -411,9 +421,7 @@ GLOBAL bool
 IRC_USER(CLIENT * Client, REQUEST * Req)
 {
        CLIENT *c;
-#ifdef IDENTAUTH
        char *ptr;
-#endif
 
        assert(Client != NULL);
        assert(Req != NULL);
@@ -431,7 +439,20 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
                                                  Client_ID(Client),
                                                  Req->command);
 
-               /* User name */
+               /* User name: only alphanumeric characters and limited
+                  punctuation is allowed.*/
+               ptr = Req->argv[0];
+               while (*ptr) {
+                       if (!isalnum((int)*ptr) &&
+                           *ptr != '+' && *ptr != '-' &&
+                           *ptr != '.' && *ptr != '_') {
+                               Conn_Close(Client_Conn(Client), NULL,
+                                          "Invalid user name", true);
+                               return DISCONNECTED;
+                       }
+                       ptr++;
+               }
+
 #ifdef IDENTAUTH
                ptr = Client_User(Client);
                if (!ptr || !*ptr || *ptr == '~')
@@ -452,7 +473,7 @@ IRC_USER(CLIENT * Client, REQUEST * Req)
                LogDebug("Connection %d: got valid USER command ...",
                    Client_Conn(Client));
                if (Client_Type(Client) == CLIENT_GOTNICK)
-                       return Hello_User(Client);
+                       return Login_User(Client);
                else
                        Client_SetType(Client, CLIENT_GOTUSER);
                return CONNECTED;
@@ -545,10 +566,10 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req)
        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! */
+               /* Nickname collission: disconnect (KILL) both clients! */
                Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
                    Client_ID(Client), nick);
                Kill_Nick(nick, "Nick collision");
@@ -670,11 +691,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;
                }
@@ -687,7 +708,7 @@ 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;
@@ -875,7 +896,7 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
                if (auth_ping == atoi(Req->argv[0])) {
                        Conn_SetAuthPing(conn, 0);
                        if (Client_Type(Client) == CLIENT_WAITAUTHPING)
-                               Hello_User(Client);
+                               Login_User(Client);
                } else
                        if (!IRC_WriteStrClient(Client,
                                        "To connect, type /QUOTE PONG %ld",
@@ -886,8 +907,9 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 
        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]",
+                   "Synchronization with \"%s\" done (connection %d): %ld second%s [%ld users, %ld channels].",
                    Client_ID(Client), conn, time(NULL) - Conn_GetSignon(conn),
+                   time(NULL) - Conn_GetSignon(conn) == 1 ? "" : "s",
                    Client_UserCount(), Channel_CountVisible(NULL));
                Conn_UpdatePing(conn);
        } else
@@ -899,199 +921,9 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 
 
 /**
- * Initiate client registration.
- *
- * This function is called after the daemon received the required NICK and
- * USER commands of a new client. If the daemon is compiled with support for
- * PAM, the authentication sub-processs is forked; otherwise the global server
- * password is checked.
- *
- * @param Client       The client logging in.
- * @returns            CONNECTED or DISCONNECTED.
- */
-static bool
-Hello_User(CLIENT * Client)
-{
-#ifdef PAM
-       int pipefd[2], result;
-       pid_t pid;
-#endif
-       CONN_ID conn;
-
-       assert(Client != NULL);
-       conn = Client_Conn(Client);
-
-#ifndef STRICT_RFC
-       if (Conf_AuthPing) {
-               /* Did we receive the "auth PONG" already? */
-               if (Conn_GetAuthPing(conn)) {
-                       Client_SetType(Client, CLIENT_WAITAUTHPING);
-                       LogDebug("Connection %d: Waiting for AUTH PONG ...", conn);
-                       return CONNECTED;
-               }
-       }
-#endif
-
-#ifdef PAM
-       if (!Conf_PAM) {
-               /* Don't do any PAM authentication at all, instead emulate
-                * the beahiour of the daemon compiled without PAM support:
-                * because there can't be any "server password", all
-                * passwords supplied are classified as "wrong". */
-               if(Client_Password(Client)[0] == '\0')
-                       return Hello_User_PostAuth(Client);
-               Client_Reject(Client, "Non-empty password", false);
-               return DISCONNECTED;
-       }
-
-       if (Conf_PAMIsOptional && strcmp(Client_Password(Client), "") == 0) {
-               /* Clients are not required to send a password and to be PAM-
-                * authenticated at all. If not, they won't become "identified"
-                * and keep the "~" in their supplied user name.
-                * Therefore it is sensible to either set Conf_PAMisOptional or
-                * to enable IDENT lookups -- not both. */
-               return Hello_User_PostAuth(Client);
-       }
-
-       /* Fork child process for PAM authentication; and make sure that the
-        * process timeout is set higher than the login timeout! */
-       pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
-                       cb_Read_Auth_Result, Conf_PongTimeout + 1);
-       if (pid > 0) {
-               LogDebug("Authenticator for connection %d created (PID %d).",
-                        conn, pid);
-               return CONNECTED;
-       } else {
-               /* Sub process */
-               Log_Init_Subprocess("Auth");
-               Conn_CloseAllSockets(NONE);
-               result = PAM_Authenticate(Client);
-               if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
-                       Log_Subprocess(LOG_ERR,
-                                      "Failed to pipe result to parent!");
-               Log_Exit_Subprocess("Auth");
-               exit(0);
-       }
-#else
-       /* Check global server password ... */
-       if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
-               /* Bad password! */
-               Client_Reject(Client, "Bad server password", false);
-               return DISCONNECTED;
-       }
-       return Hello_User_PostAuth(Client);
-#endif
-}
-
-
-#ifdef PAM
-
-/**
- * Read result of the authenticatior sub-process from pipe
- *
- * @param r_fd         File descriptor of the pipe.
- * @param events       (ignored IO specification)
- */
-static void
-cb_Read_Auth_Result(int r_fd, UNUSED short events)
-{
-       CONN_ID conn;
-       CLIENT *client;
-       int result;
-       size_t len;
-       PROC_STAT *proc;
-
-       LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
-       conn = Conn_GetFromProc(r_fd);
-       if (conn == NONE) {
-               /* Ops, none found? Probably the connection has already
-                * been closed!? We'll ignore that ... */
-               io_close(r_fd);
-               LogDebug("Auth: Got callback for unknown connection!?");
-               return;
-       }
-       proc = Conn_GetProcStat(conn);
-       client = Conn_GetClient(conn);
-
-       /* Read result from pipe */
-       len = Proc_Read(proc, &result, sizeof(result));
-       Proc_Close(proc);
-       if (len == 0)
-               return;
-
-       if (len != sizeof(result)) {
-               Log(LOG_CRIT, "Auth: Got malformed result!");
-               Client_Reject(client, "Internal error", false);
-               return;
-       }
-
-       if (result == true) {
-               Client_SetUser(client, Client_OrigUser(client), true);
-               (void)Hello_User_PostAuth(client);
-       } else
-               Client_Reject(client, "Bad password", false);
-}
-
-#endif
-
-
-/**
- * Finish client registration.
- *
- * Introduce the new client to the network and send all "hello messages"
- * to it after authentication has been succeeded.
+ * Kill all users with a specific nickname in the network.
  *
- * @param Client       The client logging in.
- * @returns            CONNECTED or DISCONNECTED.
- */
-static bool
-Hello_User_PostAuth(CLIENT *Client)
-{
-       assert(Client != NULL);
-
-       if (Class_HandleServerBans(Client) != CONNECTED)
-               return DISCONNECTED;
-
-       Client_Introduce(NULL, Client, CLIENT_USER);
-
-       if (!IRC_WriteStrClient
-           (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
-               return false;
-       if (!IRC_WriteStrClient
-           (Client, RPL_YOURHOST_MSG, Client_ID(Client),
-            Client_ID(Client_ThisServer()), PACKAGE_VERSION, TARGET_CPU,
-            TARGET_VENDOR, TARGET_OS))
-               return false;
-       if (!IRC_WriteStrClient
-           (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
-               return false;
-       if (!IRC_WriteStrClient
-           (Client, RPL_MYINFO_MSG, Client_ID(Client),
-            Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
-            CHANMODES))
-               return false;
-
-       /* Features supported by this server (005 numeric, ISUPPORT),
-        * see <http://www.irc.org/tech_docs/005.html> for details. */
-       if (!IRC_Send_ISUPPORT(Client))
-               return DISCONNECTED;
-
-       if (!IRC_Send_LUSERS(Client))
-               return DISCONNECTED;
-       if (!IRC_Show_MOTD(Client))
-               return DISCONNECTED;
-
-       /* Suspend the client for a second ... */
-       IRC_SetPenalty(Client, 1);
-
-       return CONNECTED;
-}
-
-
-/**
- * Kill all users with a specific nick name in the network.
- *
- * @param Nick         Nick name.
+ * @param Nick         Nickname.
  * @param Reason       Reason for the KILL.
  */
 static void
@@ -1107,11 +939,51 @@ Kill_Nick(char *Nick, char *Reason)
        r.argv[1] = Reason;
        r.argc = 2;
 
-       Log(LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s",
+       Log(LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s!",
            Nick, Reason);
 
        IRC_KILL(Client_ThisServer(), &r);
 } /* Kill_Nick */
 
 
+/**
+ * Change the nickname of a client.
+ *
+ * @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
+Change_Nick(CLIENT *Origin, CLIENT *Target, char *NewNick, bool InformClient)
+{
+       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);
+       }
+
+       /* 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);
+
+       /* Register old nickname for WHOWAS queries */
+       Client_RegisterWhowas(Target);
+
+       /* Save new nickname */
+       Client_SetID(Target, NewNick);
+}
+
+
 /* -eof- */