]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/parse.c
Implement WEBIRC command
[ngircd-alex.git] / src / ngircd / parse.c
index ef2dbbba0bc6d874f886e6a311bef9a233887d6d..3710d70c494d07972d3198245a00e0dd445bb719 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
  *
  * 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
@@ -9,11 +9,8 @@
  * Please read the file COPYING, README and AUTHORS for more information.
  */
 
-
 #include "portab.h"
 
-static char UNUSED id[] = "$Id: parse.c,v 1.72 2008/02/17 13:26:42 alex Exp $";
-
 /**
  * @file
  * IRC command parser and validator.
@@ -62,7 +59,7 @@ static COMMAND My_Commands[] =
 {
        { "ADMIN", IRC_ADMIN, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "AWAY", IRC_AWAY, CLIENT_USER, 0, 0, 0 },
-       { "CONNECT", IRC_CONNECT, CLIENT_USER, 0, 0, 0 },
+       { "CONNECT", IRC_CONNECT, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "DIE", IRC_DIE, CLIENT_USER, 0, 0, 0 },
        { "DISCONNECT", IRC_DISCONNECT, CLIENT_USER, 0, 0, 0 },
        { "ERROR", IRC_ERROR, 0xFFFF, 0, 0, 0 },
@@ -95,7 +92,7 @@ static COMMAND My_Commands[] =
        { "SERVICE", IRC_SERVICE, 0xFFFF, 0, 0, 0 },
        { "SERVLIST", IRC_SERVLIST, CLIENT_USER, 0, 0, 0 },
        { "SQUERY", IRC_SQUERY, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-       { "SQUIT", IRC_SQUIT, CLIENT_SERVER, 0, 0, 0 },
+       { "SQUIT", IRC_SQUIT, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "TIME", IRC_TIME, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
@@ -106,6 +103,7 @@ static COMMAND My_Commands[] =
        { "USERS", IRC_USERS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "VERSION", IRC_VERSION, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "WALLOPS", IRC_WALLOPS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
+       { "WEBIRC", IRC_WEBIRC, CLIENT_UNKNOWN, 0, 0, 0 },
        { "WHO", IRC_WHO, CLIENT_USER, 0, 0, 0 },
        { "WHOIS", IRC_WHOIS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
        { "WHOWAS", IRC_WHOWAS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
@@ -172,51 +170,45 @@ Parse_Request( CONN_ID Idx, char *Request )
 
        Init_Request( &req );
 
-       /* Fuehrendes und folgendes "Geraffel" verwerfen */
+       /* remove leading & trailing whitespace */
        ngt_TrimStr( Request );
 
-       /* gibt es ein Prefix? */
        if( Request[0] == ':' )
        {
-               /* Prefix vorhanden */
+               /* Prefix */
                req.prefix = Request + 1;
                ptr = strchr( Request, ' ' );
                if( ! ptr )
                {
-                       Log( LOG_DEBUG, "Connection %d: Parse error: prefix without command!?", Idx );
+                       LogDebug("Connection %d: Parse error: prefix without command!?", Idx);
                        return Conn_WriteStr( Idx, "ERROR :Prefix without command!?" );
                }
                *ptr = '\0';
 #ifndef STRICT_RFC
-               /* multiple Leerzeichen als Trenner zwischen
-                * Prefix und Befehl ignorieren */
+               /* ignore multiple spaces between prefix and command */
                while( *(ptr + 1) == ' ' ) ptr++;
 #endif
                start = ptr + 1;
        }
        else start = Request;
 
-       /* Befehl */
        ptr = strchr( start, ' ' );
        if( ptr )
        {
                *ptr = '\0';
 #ifndef STRICT_RFC
-               /* multiple Leerzeichen als Trenner vor
-                * Parametern ignorieren */
+               /* ignore multiple spaces between parameters */
                while( *(ptr + 1) == ' ' ) ptr++;
 #endif
        }
        req.command = start;
 
-       /* Argumente, Parameter */
+       /* Arguments, Parameters */
        if( ptr )
        {
-               /* Prinzipiell gibt es welche :-) */
                start = ptr + 1;
                while( start )
                {
-                       /* Parameter-String "zerlegen" */
                        if( start[0] == ':' )
                        {
                                req.argv[req.argc] = start + 1;
@@ -230,8 +222,6 @@ Parse_Request( CONN_ID Idx, char *Request )
                                {
                                        *ptr = '\0';
 #ifndef STRICT_RFC
-                                       /* multiple Leerzeichen als
-                                        * Parametertrenner ignorieren */
                                        while( *(ptr + 1) == ' ' ) ptr++;
 #endif
                                }
@@ -247,7 +237,6 @@ Parse_Request( CONN_ID Idx, char *Request )
                }
        }
 
-       /* Daten validieren */
        if( ! Validate_Prefix( Idx, &req, &closed )) return ! closed;
        if( ! Validate_Command( Idx, &req, &closed )) return ! closed;
        if( ! Validate_Args( Idx, &req, &closed )) return ! closed;
@@ -286,39 +275,32 @@ Validate_Prefix( CONN_ID Idx, REQUEST *Req, bool *Closed )
 
        *Closed = false;
 
-       /* ist ueberhaupt ein Prefix vorhanden? */
        if( ! Req->prefix ) return true;
 
-       /* Client-Struktur der Connection ermitteln */
        client = Conn_GetClient( Idx );
        assert( client != NULL );
 
-       /* nur validieren, wenn bereits registrierte Verbindung */
+       /* only validate if this connection is already registered */
        if(( Client_Type( client ) != CLIENT_USER ) && ( Client_Type( client ) != CLIENT_SERVER ) && ( Client_Type( client ) != CLIENT_SERVICE ))
        {
-               /* noch nicht registrierte Verbindung.
-                * Das Prefix wird ignoriert. */
+               /* not registered, ignore prefix */
                Req->prefix = NULL;
                return true;
        }
 
-       /* pruefen, ob der im Prefix angegebene Client bekannt ist */
+       /* check if client in prefix is known */
        c = Client_Search( Req->prefix );
        if( ! c )
        {
-               /* im Prefix angegebener Client ist nicht bekannt */
                Log( LOG_ERR, "Invalid prefix \"%s\", client not known (connection %d, command %s)!?", Req->prefix, Idx, Req->command );
                if( ! Conn_WriteStr( Idx, "ERROR :Invalid prefix \"%s\", client not known!?", Req->prefix )) *Closed = true;
                return false;
        }
 
-       /* pruefen, ob der Client mit dem angegebenen Prefix in Richtung
-        * des Senders liegt, d.h. sicherstellen, dass das Prefix nicht
-        * gefaelscht ist */
+       /* check if the client named in the prefix is expected
+        * to come from that direction */
        if( Client_NextHop( c ) != client )
        {
-               /* das angegebene Prefix ist aus dieser Richtung, also
-                * aus der gegebenen Connection, ungueltig! */
                Log( LOG_ERR, "Spoofed prefix \"%s\" from \"%s\" (connection %d, command %s)!", Req->prefix, Client_Mask( Conn_GetClient( Idx )), Idx, Req->command );
                Conn_Close( Idx, NULL, "Spoofed prefix", true);
                *Closed = true;
@@ -341,11 +323,39 @@ Validate_Command( UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed )
 
 
 static bool
-Validate_Args( UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed )
+#ifdef STRICT_RFC
+Validate_Args(CONN_ID Idx, REQUEST *Req, bool *Closed)
+#else
+Validate_Args(UNUSED CONN_ID Idx, UNUSED REQUEST *Req, bool *Closed)
+#endif
 {
+#ifdef STRICT_RFC
+       int i;
+#endif
+
+       *Closed = false;
+
+#ifdef STRICT_RFC
        assert( Idx >= 0 );
        assert( Req != NULL );
-       *Closed = false;
+
+       /* CR and LF are never allowed in command parameters.
+        * But since we do accept lines terminated only with CR or LF in
+        * "non-RFC-compliant mode" (besides the correct CR+LF combination),
+        * this check can only trigger in "strict RFC" mode; therefore we
+        * optimize it away otherwise ... */
+       for (i = 0; i < Req->argc; i++) {
+               if (strchr(Req->argv[i], '\r') || strchr(Req->argv[i], '\n')) {
+                       Log(LOG_ERR,
+                           "Invalid character(s) in parameter (connection %d, command %s)!?",
+                           Idx, Req->command);
+                       if (!Conn_WriteStr(Idx,
+                                          "ERROR :Invalid character(s) in parameter!"))
+                               *Closed = true;
+                       return false;
+               }
+       }
+#endif
 
        return true;
 } /* Validate_Args */
@@ -356,7 +366,8 @@ static bool
 Handle_Numeric(CLIENT *client, REQUEST *Req)
 {
        static const struct _NUMERIC Numerics[] = {
-               { 005, IRC_Num_ISUPPORT },
+               {   5, IRC_Num_ISUPPORT },
+               {  20, NULL },
                { 376, IRC_Num_ENDOFMOTD }
        };
        int i, num;
@@ -364,8 +375,12 @@ Handle_Numeric(CLIENT *client, REQUEST *Req)
        CLIENT *prefix, *target = NULL;
 
        /* Determine target */
-       if (Req->argc > 0)
-               target = Client_Search(Req->argv[0]);
+       if (Req->argc > 0) {
+               if (strcmp(Req->argv[0], "*") != 0)
+                       target = Client_Search(Req->argv[0]);
+               else
+                       target = Client_ThisServer();
+       }
 
        if (!target) {
                /* Status code without target!? */
@@ -384,8 +399,11 @@ Handle_Numeric(CLIENT *client, REQUEST *Req)
                num = atoi(Req->command);
 
                for (i = 0; i < (int) ARRAY_SIZE(Numerics); i++) {
-                       if (num == Numerics[i].numeric)
+                       if (num == Numerics[i].numeric) {
+                               if (!Numerics[i].function)
+                                       return CONNECTED;
                                return Numerics[i].function(client, Req);
+                       }
                }
 
                LogDebug("Ignored status code %s from \"%s\".",
@@ -423,8 +441,6 @@ Handle_Numeric(CLIENT *client, REQUEST *Req)
 static bool
 Handle_Request( CONN_ID Idx, REQUEST *Req )
 {
-       /* Client-Request verarbeiten. Bei einem schwerwiegenden Fehler
-        * wird die Verbindung geschlossen und false geliefert. */
        CLIENT *client;
        bool result = true;
        int client_type;
@@ -446,7 +462,6 @@ Handle_Request( CONN_ID Idx, REQUEST *Req )
 
        cmd = My_Commands;
        while (cmd->name) {
-               /* Befehl suchen */
                if (strcasecmp(Req->command, cmd->name) != 0) {
                        cmd++;
                        continue;