]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/irc.c
Added missing modes to USERMODES #define
[ngircd-alex.git] / src / ngircd / irc.c
index 2e3af757584b0d9630640da16029a6fe9e18b03f..2466b6bf9e47ef9ed7c084fe0a2af93a6570cf0f 100644 (file)
@@ -33,15 +33,18 @@ static char UNUSED id[] = "$Id: irc.c,v 1.132 2008/01/15 22:28:14 fw Exp $";
 #include "match.h"
 #include "messages.h"
 #include "parse.h"
+#include "tool.h"
 
 #include "exp.h"
 #include "irc.h"
 
 
 static char *Option_String PARAMS((CONN_ID Idx));
-static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType, bool SendErrors));
-static bool Send_Message_Mask PARAMS((CLIENT *from, char *targetMask, char *message, bool SendErrors));
-static bool MatchCaseInsensitive PARAMS((const char *pattern, const char *searchme));
+static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType,
+                                bool SendErrors));
+static bool Send_Message_Mask PARAMS((CLIENT *from, char *command,
+                                     char *targetMask, char *message,
+                                     bool SendErrors));
 
 
 GLOBAL bool
@@ -170,8 +173,11 @@ IRC_KILL( CLIENT *Client, REQUEST *Req )
 } /* IRC_KILL */
 
 
+/**
+ * Handler for the IRC command NOTICE.
+ */
 GLOBAL bool
-IRC_NOTICE( CLIENT *Client, REQUEST *Req )
+IRC_NOTICE(CLIENT *Client, REQUEST *Req)
 {
        return Send_Message(Client, Req, CLIENT_USER, false);
 } /* IRC_NOTICE */
@@ -309,28 +315,27 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
 {
        CLIENT *cl, *from;
        CHANNEL *chan;
-       char *targetList = Req->argv[0];
        char *currentTarget = Req->argv[0];
-       unsigned int targetCount = 1;
+       char *lastCurrentTarget = NULL;
 
-       assert( Client != NULL );
-       assert( Req != NULL );
+       assert(Client != NULL);
+       assert(Req != NULL);
 
        if (Req->argc == 0) {
                if (!SendErrors)
-                       return true;
+                       return CONNECTED;
                return IRC_WriteStrClient(Client, ERR_NORECIPIENT_MSG,
                                          Client_ID(Client), Req->command);
        }
        if (Req->argc == 1) {
                if (!SendErrors)
-                       return true;
+                       return CONNECTED;
                return IRC_WriteStrClient(Client, ERR_NOTEXTTOSEND_MSG,
                                          Client_ID(Client));
        }
        if (Req->argc > 2) {
                if (!SendErrors)
-                       return true;
+                       return CONNECTED;
                return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
                                          Client_ID(Client), Req->command);
        }
@@ -339,33 +344,40 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
                from = Client_Search(Req->prefix);
        else
                from = Client;
-       if (!from) {
+       if (!from)
                return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
                                          Client_ID(Client), Req->prefix);
-       }
-
-       while (*targetList) {
-               if (*targetList == ',') {
-                       *targetList = '\0';
-                       targetCount++;
-               }
-               targetList++;
-       }
 
-       while (targetCount > 0) {
+       /* handle msgtarget = msgto *("," msgto) */
+       currentTarget = strtok_r(currentTarget, ",", &lastCurrentTarget);
+       ngt_UpperStr(Req->command);
+
+       while (currentTarget) {
+               /* Check for and handle valid <msgto> of form:
+                * RFC 2812 2.3.1:
+                *   msgto =  channel / ( user [ "%" host ] "@" servername )
+                *   msgto =/ ( user "%" host ) / targetmask
+                *   msgto =/ nickname / ( nickname "!" user "@" host )
+                */
                if (strchr(currentTarget, '!') == NULL)
+                       /* nickname */
                        cl = Client_Search(currentTarget);
                else
                        cl = NULL;
+
                if (cl == NULL) {
-                       char target[513]; // max mesage length plus null terminator
+                       /* If currentTarget isn't a nickname check for:
+                        * user ["%" host] "@" servername
+                        * user "%" host
+                        * nickname "!" user "@" host
+                        */
+                       char target[COMMAND_LEN];
                        char * nick = NULL;
                        char * user = NULL;
                        char * host = NULL;
                        char * server = NULL;
 
-                       strncpy(target, currentTarget, 512);
-                       target[512] = '\0';
+                       strlcpy(target, currentTarget, COMMAND_LEN);
                        server = strchr(target, '@');
                        if (server) {
                                *server = '\0';
@@ -378,116 +390,163 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
                        }
                        user = strchr(target, '!');
                        if (user) {
+                               /* msgto form: nick!user@host */
                                *user = '\0';
                                user++;
                                nick = target;
-                               host = server; // <msgto> form: nick!user@host
+                               host = server; /* not "@server" but "@host" */
                        } else {
                                user = target;
                        }
 
-                       if (user != NULL) {
-                               for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
-                                       if (Client_Type(cl) != CLIENT_USER)
-                                               continue;
-                                       if (nick != NULL) {
-                                               if (strcmp(nick, Client_ID(cl)) == 0 && strcmp(user, Client_User(cl)) == 0 && strcasecmp(host, Client_Hostname(cl)) == 0)
-                                                       break;
-                                               else
-                                                       continue;
-                                       }
-                                       if (strcasecmp(user, Client_User(cl)) != 0)
-                                               continue;
-                                       if (host != NULL && strcasecmp(host, Client_Hostname(cl)) != 0)
-                                               continue;
-                                       if (server != NULL && strcasecmp(server, Client_ID(Client_Introducer(cl))) != 0)
+                       for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
+                               if (Client_Type(cl) != CLIENT_USER &&
+                                   Client_Type(cl) != CLIENT_SERVICE)
+                                       continue;
+                               if (nick != NULL && host != NULL) {
+                                       if (strcmp(nick, Client_ID(cl)) == 0 &&
+                                           strcmp(user, Client_User(cl)) == 0 &&
+                                           strcasecmp(host, Client_Hostname(cl)) == 0)
+                                               break;
+                                       else
                                                continue;
-                                       break;
                                }
+                               if (strcasecmp(user, Client_User(cl)) != 0)
+                                       continue;
+                               if (host != NULL && strcasecmp(host,
+                                               Client_Hostname(cl)) != 0)
+                                       continue;
+                               if (server != NULL && strcasecmp(server,
+                                               Client_ID(Client_Introducer(cl))) != 0)
+                                       continue;
+                               break;
                        }
                }
 
                if (cl) {
                        /* Target is a user, enforce type */
+#ifndef STRICT_RFC
+                       if (Client_Type(cl) != ForceType &&
+                           !(ForceType == CLIENT_USER &&
+                             (Client_Type(cl) == CLIENT_USER ||
+                              Client_Type(cl) == CLIENT_SERVICE))) {
+#else
                        if (Client_Type(cl) != ForceType) {
+#endif
                                if (!SendErrors)
-                                       return true;
-                               if (!IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
+                                       return CONNECTED;
+                               return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
                                                          Client_ID(from),
-                                                         currentTarget))
-                                       return false;
-                       } else if ((Client_Type(Client) != CLIENT_SERVER
-                                               && (strchr(Client_Modes(cl), 'a')))) {
+                                                         currentTarget);
+                       }
+
+#ifndef STRICT_RFC
+                       if (ForceType == CLIENT_SERVICE &&
+                           (Conn_Options(Client_Conn(Client_NextHop(cl)))
+                            & CONN_RFC1459)) {
+                               /* SQUERY command but RFC 1459 link: convert
+                                * request to PRIVMSG command */
+                               Req->command = "PRIVMSG";
+                       }
+#endif
+
+                       if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
+                           && strchr(Client_Modes(cl), 'a')) {
                                /* Target is away */
-                               if (!SendErrors)
-                                       return true;
-                               if (!IRC_WriteStrClient
-                                   (from, RPL_AWAY_MSG, Client_ID(from), Client_ID(cl),
-                                    Client_Away(cl)))
+                               if (!IRC_WriteStrClient(from, RPL_AWAY_MSG,
+                                                       Client_ID(from),
+                                                       Client_ID(cl),
+                                                       Client_Away(cl)))
                                        return DISCONNECTED;
-                       }       if (Client_Conn(from) > NONE) {
+                       }
+                       if (Client_Conn(from) > NONE) {
                                Conn_UpdateIdle(Client_Conn(from));
                        }
-                       if (!IRC_WriteStrClientPrefix(cl, from, "PRIVMSG %s :%s",
-                                               Client_ID(cl), Req->argv[1]))
-                               return false;
-               } else if (strchr("$#", currentTarget[0]) && strchr(currentTarget, '.')) {
-                       if (!Send_Message_Mask(from, currentTarget, Req->argv[1], SendErrors))
-                               return false;
-               } else if ((chan = Channel_Search(currentTarget))) {
-                               if (!Channel_Write(chan, from, Client, Req->argv[1]))
-                                       return false;
+                       if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
+                                                     Req->command, Client_ID(cl),
+                                                     Req->argv[1]))
+                               return DISCONNECTED;
+               } else if (ForceType != CLIENT_SERVICE
+                          && (chan = Channel_Search(currentTarget))) {
+                       if (!Channel_Write(chan, from, Client, Req->command,
+                                          SendErrors, Req->argv[1]))
+                                       return DISCONNECTED;
+               } else if (ForceType != CLIENT_SERVICE
+                       /* $#: server/target mask, RFC 2812, sec. 3.3.1 */
+                          && strchr("$#", currentTarget[0])
+                          && strchr(currentTarget, '.')) {
+                       /* targetmask */
+                       if (!Send_Message_Mask(from, Req->command, currentTarget,
+                                              Req->argv[1], SendErrors))
+                               return DISCONNECTED;
                } else {
                        if (!SendErrors)
-                               return true;
+                               return CONNECTED;
                        if (!IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
                                                Client_ID(from), currentTarget))
-                               return false;
+                               return DISCONNECTED;
                }
 
-               while (*currentTarget)
-                       currentTarget++;
-
-               currentTarget++;
-               targetCount--;
+               currentTarget = strtok_r(NULL, ",", &lastCurrentTarget);
        }
 
        return CONNECTED;
-
 } /* Send_Message */
 
 
 static bool
-Send_Message_Mask(CLIENT * from, char * targetMask, char * message, bool SendErrors)
+Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
+                 char * message, bool SendErrors)
 {
        CLIENT *cl;
        bool client_match;
        char *mask = targetMask + 1;
+       const char *check_wildcards;
 
        cl = NULL;
 
        if (strchr(Client_Modes(from), 'o') == NULL) {
                if (!SendErrors)
                        return true;
-               return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG, Client_ID(from));
+               return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
+                                         Client_ID(from));
        }
 
+       /*
+        * RFC 2812, sec. 3.3.1 requires that targetMask have at least one
+        * dot (".") and no wildcards ("*", "?") following the last one.
+        */
+       check_wildcards = strrchr(targetMask, '.');
+       assert(check_wildcards != NULL);
+       if (check_wildcards &&
+               check_wildcards[strcspn(check_wildcards, "*?")])
+       {
+               if (!SendErrors)
+                       return true;
+               return IRC_WriteStrClient(from, ERR_WILDTOPLEVEL, targetMask);
+       }
+
+       /* #: hostmask, see RFC 2812, sec. 3.3.1 */
        if (targetMask[0] == '#') {
                for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
                        if (Client_Type(cl) != CLIENT_USER)
                                continue;
                        client_match = MatchCaseInsensitive(mask, Client_Hostname(cl));
                        if (client_match)
-                               if (!IRC_WriteStrClientPrefix(cl, from, "PRIVMSG %s :%s", Client_ID(cl), message))
+                               if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
+                                               command, Client_ID(cl), message))
                                        return false;
                }
        } else {
+               assert(targetMask[0] == '$'); /* $: server mask, see RFC 2812, sec. 3.3.1 */
                for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
                        if (Client_Type(cl) != CLIENT_USER)
                                continue;
-                       client_match = MatchCaseInsensitive(mask, Client_ID(Client_Introducer(cl)));
+                       client_match = MatchCaseInsensitive(mask,
+                                       Client_ID(Client_Introducer(cl)));
                        if (client_match)
-                               if (!IRC_WriteStrClientPrefix(cl, from, "PRIVMSG %s :%s", Client_ID(cl), message))
+                               if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
+                                               command, Client_ID(cl), message))
                                        return false;
                }
        }
@@ -495,16 +554,4 @@ Send_Message_Mask(CLIENT * from, char * targetMask, char * message, bool SendErr
 } /* Send_Message_Mask */
 
 
-static bool
-MatchCaseInsensitive(const char *pattern, const char *searchme)
-{
-       char haystack[COMMAND_LEN];
-
-       strlcpy(haystack, searchme, sizeof(haystack));
-
-       ngt_LowerStr(haystack);
-
-       return Match(pattern, haystack);
-}
-
 /* -eof- */