]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/irc.c
Don't allow SQUERY to send to "target masks" and channels; only services!
[ngircd-alex.git] / src / ngircd / irc.c
index 139531266fef48f3d80c37acd66ec5131031ed0d..b88d5d7db7cc7a1320eb94027ca6835518c9a4cc 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "portab.h"
 
-static char UNUSED id[] = "$Id: irc.c,v 1.126 2005/04/18 15:44:39 alex Exp $";
+static char UNUSED id[] = "$Id: irc.c,v 1.132 2008/01/15 22:28:14 fw Exp $";
 
 #include "imp.h"
 #include <assert.h>
@@ -22,23 +22,29 @@ static char UNUSED id[] = "$Id: irc.c,v 1.126 2005/04/18 15:44:39 alex Exp $";
 #include <string.h>
 
 #include "ngircd.h"
-#include "conn.h"
 #include "resolve.h"
-#include "conf.h"
 #include "conn-func.h"
+#include "conf.h"
 #include "client.h"
 #include "channel.h"
 #include "defines.h"
 #include "irc-write.h"
 #include "log.h"
+#include "match.h"
 #include "messages.h"
 #include "parse.h"
+#include "tool.h"
 
 #include "exp.h"
 #include "irc.h"
 
 
-LOCAL char *Option_String PARAMS(( CONN_ID Idx ));
+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 *command,
+                                     char *targetMask, char *message,
+                                     bool SendErrors));
 
 
 GLOBAL bool
@@ -151,7 +157,7 @@ IRC_KILL( CLIENT *Client, REQUEST *Req )
                }
 
                /* Kill client NOW! */
-               conn = Client_Conn( Client_NextHop( c ));
+               conn = Client_Conn( );
                Client_Destroy( c, NULL, reason, false );
                if( conn > NONE )
                        Conn_Close( conn, NULL, reason, true );
@@ -160,81 +166,41 @@ IRC_KILL( CLIENT *Client, REQUEST *Req )
                Log( LOG_NOTICE, "Client with nick \"%s\" is unknown here.", Req->argv[0] );
 
        /* Are we still connected or were we killed, too? */
-       if(( my_conn > NONE ) && ( Client_GetFromConn( my_conn )))
+       if(( my_conn > NONE ) && ( Conn_GetClient( my_conn )))
                return CONNECTED;
        else
                return DISCONNECTED;
 } /* IRC_KILL */
 
 
+/**
+ * Handler for the IRC command NOTICE.
+ */
 GLOBAL bool
-IRC_NOTICE( CLIENT *Client, REQUEST *Req )
+IRC_NOTICE(CLIENT *Client, REQUEST *Req)
 {
-       CLIENT *to, *from;
-
-       assert( Client != NULL );
-       assert( Req != NULL );
-
-       if(( Client_Type( Client ) != CLIENT_USER ) && ( Client_Type( Client ) != CLIENT_SERVER )) return CONNECTED;
-
-       /* Falsche Anzahl Parameter? */
-       if( Req->argc != 2 ) return CONNECTED;
-
-       if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
-       else from = Client;
-       if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
-
-       to = Client_Search( Req->argv[0] );
-       if(( to ) && ( Client_Type( to ) == CLIENT_USER ))
-       {
-               /* Okay, Ziel ist ein User */
-               return IRC_WriteStrClientPrefix( to, from, "NOTICE %s :%s", Client_ID( to ), Req->argv[1] );
-       }
-       else return CONNECTED;
+       return Send_Message(Client, Req, CLIENT_USER, false);
 } /* IRC_NOTICE */
 
 
+/**
+ * Handler for the IRC command PRIVMSG.
+ */
 GLOBAL bool
-IRC_PRIVMSG( CLIENT *Client, REQUEST *Req )
+IRC_PRIVMSG(CLIENT *Client, REQUEST *Req)
 {
-       CLIENT *cl, *from;
-       CHANNEL *chan;
-       
-       assert( Client != NULL );
-       assert( Req != NULL );
-
-       /* Falsche Anzahl Parameter? */
-       if( Req->argc == 0 ) return IRC_WriteStrClient( Client, ERR_NORECIPIENT_MSG, Client_ID( Client ), Req->command );
-       if( Req->argc == 1 ) return IRC_WriteStrClient( Client, ERR_NOTEXTTOSEND_MSG, Client_ID( Client ));
-       if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
-
-       if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
-       else from = Client;
-       if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
-
-       cl = Client_Search( Req->argv[0] );
-       if( cl )
-       {
-               /* Okay, Ziel ist ein Client. Aber ist es auch ein User? */
-               if( Client_Type( cl ) != CLIENT_USER ) return IRC_WriteStrClient( from, ERR_NOSUCHNICK_MSG, Client_ID( from ), Req->argv[0] );
-
-               /* Okay, Ziel ist ein User */
-               if(( Client_Type( Client ) != CLIENT_SERVER ) && ( strchr( Client_Modes( cl ), 'a' )))
-               {
-                       /* Ziel-User ist AWAY: Meldung verschicken */
-                       if( ! IRC_WriteStrClient( from, RPL_AWAY_MSG, Client_ID( from ), Client_ID( cl ), Client_Away( cl ))) return DISCONNECTED;
-               }
-
-               /* Text senden */
-               if( Client_Conn( from ) > NONE ) Conn_UpdateIdle( Client_Conn( from ));
-               return IRC_WriteStrClientPrefix( cl, from, "PRIVMSG %s :%s", Client_ID( cl ), Req->argv[1] );
-       }
+       return Send_Message(Client, Req, CLIENT_USER, true);
+} /* IRC_PRIVMSG */
 
-       chan = Channel_Search( Req->argv[0] );
-       if( chan ) return Channel_Write( chan, from, Client, Req->argv[1] );
 
-       return IRC_WriteStrClient( from, ERR_NOSUCHNICK_MSG, Client_ID( from ), Req->argv[0] );
-} /* IRC_PRIVMSG */
+/**
+ * Handler for the IRC command SQUERY.
+ */
+GLOBAL bool
+IRC_SQUERY(CLIENT *Client, REQUEST *Req)
+{
+       return Send_Message(Client, Req, CLIENT_SERVICE, true);
+} /* IRC_SQUERY */
 
 
 GLOBAL bool
@@ -325,21 +291,238 @@ IRC_HELP( CLIENT *Client, REQUEST *Req )
 } /* IRC_HELP */
 
 
-LOCAL char *
+static char *
 Option_String( CONN_ID Idx )
 {
        static char option_txt[8];
-       int options;
+       UINT16 options;
 
-       options = Conn_Options( Idx );
+       options = Conn_Options(Idx);
 
-       strcpy( option_txt, "F" );      /* No idea what this means but the original ircd sends it ... */
+       strcpy(option_txt, "F");        /* No idea what this means, but the
+                                        * original ircd sends it ... */
 #ifdef ZLIB
-       if( options & CONN_ZIP ) strcat( option_txt, "z" );
+       if(options & CONN_ZIP)          /* zlib compression supported. */
+               strcat(option_txt, "z");
 #endif
 
        return option_txt;
 } /* Option_String */
 
 
+static bool
+Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors)
+{
+       CLIENT *cl, *from;
+       CHANNEL *chan;
+       char *currentTarget = Req->argv[0];
+       char *lastCurrentTarget = NULL;
+
+       assert(Client != NULL);
+       assert(Req != NULL);
+
+       if (Req->argc == 0) {
+               if (!SendErrors)
+                       return CONNECTED;
+               return IRC_WriteStrClient(Client, ERR_NORECIPIENT_MSG,
+                                         Client_ID(Client), Req->command);
+       }
+       if (Req->argc == 1) {
+               if (!SendErrors)
+                       return CONNECTED;
+               return IRC_WriteStrClient(Client, ERR_NOTEXTTOSEND_MSG,
+                                         Client_ID(Client));
+       }
+       if (Req->argc > 2) {
+               if (!SendErrors)
+                       return CONNECTED;
+               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+                                         Client_ID(Client), Req->command);
+       }
+
+       if (Client_Type(Client) == CLIENT_SERVER)
+               from = Client_Search(Req->prefix);
+       else
+               from = Client;
+       if (!from)
+               return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+                                         Client_ID(Client), Req->prefix);
+
+       /* 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) {
+                       /* 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;
+
+                       strlcpy(target, currentTarget, COMMAND_LEN);
+                       server = strchr(target, '@');
+                       if (server) {
+                               *server = '\0';
+                               server++;
+                       }
+                       host = strchr(target, '%');
+                       if (host) {
+                               *host = '\0';
+                               host++;
+                       }
+                       user = strchr(target, '!');
+                       if (user) {
+                               /* msgto form: nick!user@host */
+                               *user = '\0';
+                               user++;
+                               nick = target;
+                               host = server; /* not "@server" but "@host" */
+                       } else {
+                               user = target;
+                       }
+
+                       for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) {
+                               if (Client_Type(cl) != CLIENT_USER)
+                                       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;
+                               }
+                               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 CONNECTED;
+                               return IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
+                                                         Client_ID(from),
+                                                         currentTarget);
+                       }
+                       if (SendErrors && (Client_Type(Client) != CLIENT_SERVER)
+                           && strchr(Client_Modes(cl), 'a')) {
+                               /* Target is away */
+                               if (!IRC_WriteStrClient(from, RPL_AWAY_MSG,
+                                                       Client_ID(from),
+                                                       Client_ID(cl),
+                                                       Client_Away(cl)))
+                                       return DISCONNECTED;
+                       }
+                       if (Client_Conn(from) > NONE) {
+                               Conn_UpdateIdle(Client_Conn(from));
+                       }
+                       if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
+                                                     Req->command, Client_ID(cl),
+                                                     Req->argv[1]))
+                               return DISCONNECTED;
+               } else if (ForceType != CLIENT_SERVICE
+                          && strchr("$#", currentTarget[0])
+                          && strchr(currentTarget, '.')) {
+                       /* targetmask */
+                       if (!Send_Message_Mask(from, Req->command, currentTarget,
+                                              Req->argv[1], SendErrors))
+                               return DISCONNECTED;
+               } else if (ForceType != CLIENT_SERVICE
+                          && (chan = Channel_Search(currentTarget))) {
+                       /* channel */
+                       if (!Channel_Write(chan, from, Client, Req->command,
+                                          SendErrors, Req->argv[1]))
+                                       return DISCONNECTED;
+               } else {
+                       if (!SendErrors)
+                               return CONNECTED;
+                       if (!IRC_WriteStrClient(from, ERR_NOSUCHNICK_MSG,
+                                               Client_ID(from), currentTarget))
+                               return DISCONNECTED;
+               }
+
+               currentTarget = strtok_r(NULL, ",", &lastCurrentTarget);
+       }
+
+       return CONNECTED;
+} /* Send_Message */
+
+
+static bool
+Send_Message_Mask(CLIENT * from, char * command, char * targetMask,
+                 char * message, bool SendErrors)
+{
+       CLIENT *cl;
+       bool client_match;
+       char *mask = targetMask + 1;
+
+       cl = NULL;
+
+       if (strchr(Client_Modes(from), 'o') == NULL) {
+               if (!SendErrors)
+                       return true;
+               return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
+                                         Client_ID(from));
+       }
+
+       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, "%s %s :%s",
+                                               command, Client_ID(cl), message))
+                                       return false;
+               }
+       } else {
+               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)));
+                       if (client_match)
+                               if (!IRC_WriteStrClientPrefix(cl, from, "%s %s :%s",
+                                               command, Client_ID(cl), message))
+                                       return false;
+               }
+       }
+       return CONNECTED;
+} /* Send_Message_Mask */
+
+
 /* -eof- */