]> arthur.barton.de Git - ngircd-alex.git/blobdiff - src/ngircd/irc-info.c
IRC_Send_LUSERS(): Code cleanup
[ngircd-alex.git] / src / ngircd / irc-info.c
index d0dca7f3fdcde23646dd51aa4480c9432b03690a..cdd03bb1d3a24290bca78f8ec8f864f925185134 100644 (file)
@@ -1,19 +1,21 @@
 /*
  * ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2010 Alexander Barton <alex@barton.de>
+ * Copyright (c)2001-2011 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
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  * Please read the file COPYING, README and AUTHORS for more information.
- *
- * IRC info commands
  */
 
-
 #include "portab.h"
 
+/**
+ * @file
+ * IRC info commands
+ */
+
 #include "imp.h"
 #include <assert.h>
 #include <errno.h>
 #include "conn-func.h"
 #include "conn-zip.h"
 #include "channel.h"
+#include "class.h"
 #include "conf.h"
 #include "defines.h"
+#include "lists.h"
 #include "log.h"
 #include "messages.h"
 #include "match.h"
@@ -476,6 +480,8 @@ IRC_STATS( CLIENT *Client, REQUEST *Req )
        COMMAND *cmd;
        time_t time_now;
        unsigned int days, hrs, mins;
+       struct list_head *list;
+       struct list_elem *list_item;
 
        assert(Client != NULL);
        assert(Req != NULL);
@@ -514,6 +520,28 @@ IRC_STATS( CLIENT *Client, REQUEST *Req )
                query = '*';
 
        switch (query) {
+       case 'g':       /* Network-wide bans ("G-Lines") */
+       case 'G':
+       case 'k':       /* Server-local bans ("K-Lines") */
+       case 'K':
+               if (!Client_HasMode(from, 'o'))
+                   return IRC_WriteStrClient(from, ERR_NOPRIVILEGES_MSG,
+                                             Client_ID(from));
+               if (query == 'g' || query == 'G')
+                       list = Class_GetList(CLASS_GLINE);
+               else
+                       list = Class_GetList(CLASS_KLINE);
+                       list_item = Lists_GetFirst(list);
+                       while (list_item) {
+                               if (!IRC_WriteStrClient(from, RPL_STATSXLINE_MSG,
+                                               Client_ID(from), query,
+                                               Lists_GetMask(list_item),
+                                               Lists_GetValidity(list_item),
+                                               Lists_GetReason(list_item)))
+                                       return DISCONNECTED;
+                               list_item = Lists_GetNext(list_item);
+                       }
+               break;
        case 'l':       /* Link status (servers and own link) */
        case 'L':
                time_now = time(NULL);
@@ -665,7 +693,7 @@ IRC_USERHOST(CLIENT *Client, REQUEST *Req)
                                strlcat(rpl, "+", sizeof(rpl));
                        strlcat(rpl, Client_User(c), sizeof(rpl));
                        strlcat(rpl, "@", sizeof(rpl));
-                       strlcat(rpl, Client_Hostname(c), sizeof(rpl));
+                       strlcat(rpl, Client_HostnameCloaked(c), sizeof(rpl));
                        strlcat(rpl, " ", sizeof(rpl));
                }
        }
@@ -728,9 +756,11 @@ IRC_VERSION( CLIENT *Client, REQUEST *Req )
 static bool
 write_whoreply(CLIENT *Client, CLIENT *c, const char *channelname, const char *flags)
 {
-       return IRC_WriteStrClient(Client, RPL_WHOREPLY_MSG, Client_ID(Client), channelname,
-                       Client_User(c), Client_Hostname(c), Client_ID(Client_Introducer(c)), Client_ID(c),
-                       flags, Client_Hops(c), Client_Info(c));
+       return IRC_WriteStrClient(Client, RPL_WHOREPLY_MSG, Client_ID(Client),
+                                 channelname, Client_User(c),
+                                 Client_HostnameCloaked(c),
+                                 Client_ID(Client_Introducer(c)), Client_ID(c),
+                                 flags, Client_Hops(c), Client_Info(c));
 }
 
 
@@ -912,42 +942,21 @@ IRC_WHO( CLIENT *Client, REQUEST *Req )
 } /* IRC_WHO */
 
 
-GLOBAL bool
-IRC_WHOIS( CLIENT *Client, REQUEST *Req )
+/**
+ * Generate WHOIS reply of one actual client.
+ *
+ * @param Client       The client from which this command has been received.
+ * @param from         The client requesting the information ("originator").
+ * @param c            The client of which information should be returned.
+ * @returns            CONNECTED or DISCONNECTED.
+ */
+static bool
+IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
 {
-       CLIENT *from, *target, *c;
        char str[LINE_LEN + 1];
        CL2CHAN *cl2chan;
        CHANNEL *chan;
 
-       assert( Client != NULL );
-       assert( Req != NULL );
-
-       /* Bad number of parameters? */
-       if(( Req->argc < 1 ) || ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
-
-       /* Search client */
-       c = Client_Search( Req->argv[Req->argc - 1] );
-       if(( ! c ) || ( Client_Type( c ) != CLIENT_USER )) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[Req->argc - 1] );
-
-       /* Search sender of the WHOIS */
-       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 );
-
-       /* Forward to other server? */
-       if( Req->argc > 1 )
-       {
-               /* Search target server (can be specified as nick of that server!) */
-               target = Client_Search( Req->argv[0] );
-               if( ! target ) return IRC_WriteStrClient( from, ERR_NOSUCHSERVER_MSG, Client_ID( from ), Req->argv[0] );
-       }
-       else target = Client_ThisServer( );
-
-       assert( target != NULL );
-
-       if(( Client_NextHop( target ) != Client_ThisServer( )) && ( Client_Type( Client_NextHop( target )) == CLIENT_SERVER )) return IRC_WriteStrClientPrefix( target, from, "WHOIS %s :%s", Req->argv[0], Req->argv[1] );
-
        /* Nick, user, hostname and client info */
        if (!IRC_WriteStrClient(from, RPL_WHOISUSER_MSG, Client_ID(from),
                                Client_ID(c), Client_User(c),
@@ -955,18 +964,21 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
                return DISCONNECTED;
 
        /* Server */
-       if( ! IRC_WriteStrClient( from, RPL_WHOISSERVER_MSG, Client_ID( from ), Client_ID( c ), Client_ID( Client_Introducer( c )), Client_Info( Client_Introducer( c )))) return DISCONNECTED;
+       if (!IRC_WriteStrClient(from, RPL_WHOISSERVER_MSG, Client_ID(from),
+                               Client_ID(c), Client_ID(Client_Introducer(c)),
+                               Client_Info(Client_Introducer(c))))
+               return DISCONNECTED;
 
        /* Channels */
-       snprintf( str, sizeof( str ), RPL_WHOISCHANNELS_MSG, Client_ID( from ), Client_ID( c ));
-       cl2chan = Channel_FirstChannelOf( c );
-       while( cl2chan )
-       {
-               chan = Channel_GetChannel( cl2chan );
-               assert( chan != NULL );
+       snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
+                Client_ID(from), Client_ID(c));
+       cl2chan = Channel_FirstChannelOf(c);
+       while (cl2chan) {
+               chan = Channel_GetChannel(cl2chan);
+               assert(chan != NULL);
 
                /* next */
-               cl2chan = Channel_NextChannelOf( c, cl2chan );
+               cl2chan = Channel_NextChannelOf(c, cl2chan);
 
                /* Secret channel? */
                if (strchr(Channel_Modes(chan), 's')
@@ -979,54 +991,168 @@ IRC_WHOIS( CLIENT *Client, REQUEST *Req )
                        continue;
 
                /* Concatenate channel names */
-               if( str[strlen( str ) - 1] != ':' ) strlcat( str, " ", sizeof( str ));
-               if( strchr( Channel_UserModes( chan, c ), 'o' )) strlcat( str, "@", sizeof( str ));
-               else if( strchr( Channel_UserModes( chan, c ), 'v' )) strlcat( str, "+", sizeof( str ));
-               strlcat( str, Channel_Name( chan ), sizeof( str ));
+               if (str[strlen(str) - 1] != ':')
+                       strlcat(str, " ", sizeof(str));
 
-               if( strlen( str ) > ( LINE_LEN - CHANNEL_NAME_LEN - 4 ))
-               {
+               strlcat(str, who_flags_qualifier(Channel_UserModes(chan, c)),
+                                                sizeof(str));
+               strlcat(str, Channel_Name(chan), sizeof(str));
+
+               if (strlen(str) > (LINE_LEN - CHANNEL_NAME_LEN - 4)) {
                        /* Line becomes too long: send it! */
-                       if( ! IRC_WriteStrClient( Client, "%s", str )) return DISCONNECTED;
-                       snprintf( str, sizeof( str ), RPL_WHOISCHANNELS_MSG, Client_ID( from ), Client_ID( c ));
+                       if (!IRC_WriteStrClient(Client, "%s", str))
+                               return DISCONNECTED;
+                       snprintf(str, sizeof(str), RPL_WHOISCHANNELS_MSG,
+                                Client_ID(from), Client_ID(c));
                }
        }
-       if( str[strlen( str ) - 1] != ':')
-       {
+       if(str[strlen(str) - 1] != ':') {
                /* There is data left to send: */
-               if( ! IRC_WriteStrClient( Client, "%s", str )) return DISCONNECTED;
+               if (!IRC_WriteStrClient(Client, "%s", str))
+                       return DISCONNECTED;
        }
 
        /* IRC-Operator? */
-       if( Client_HasMode( c, 'o' ))
-       {
-               if( ! IRC_WriteStrClient( from, RPL_WHOISOPERATOR_MSG, Client_ID( from ), Client_ID( c ))) return DISCONNECTED;
-       }
+       if (Client_HasMode(c, 'o') &&
+               !IRC_WriteStrClient(from, RPL_WHOISOPERATOR_MSG,
+                                   Client_ID(from), Client_ID(c)))
+                       return DISCONNECTED;
 
        /* Connected using SSL? */
-       if (Conn_UsesSSL(Client_Conn(c))) {
-               if (!IRC_WriteStrClient
-                   (from, RPL_WHOISSSL_MSG, Client_ID(from), Client_ID(c)))
+       if (Conn_UsesSSL(Client_Conn(c)) &&
+               !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG,
+                                   Client_ID(from), Client_ID(c)))
                        return DISCONNECTED;
-       }
 
        /* Idle and signon time (local clients only!) */
-       if (Client_Conn(c) > NONE ) {
-               if (! IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
-                       Client_ID(from), Client_ID(c),
-                       (unsigned long)Conn_GetIdle(Client_Conn(c)),
-                       (unsigned long)Conn_GetSignon(Client_Conn(c))))
-                               return DISCONNECTED;
-       }
+       if (!Conf_MorePrivacy && Client_Conn(c) > NONE &&
+               !IRC_WriteStrClient(from, RPL_WHOISIDLE_MSG,
+                                   Client_ID(from), Client_ID(c),
+                                   (unsigned long)Conn_GetIdle(Client_Conn(c)),
+                                   (unsigned long)Conn_GetSignon(Client_Conn(c))))
+                       return DISCONNECTED;
 
        /* Away? */
-       if( Client_HasMode( c, 'a' ))
-       {
-               if( ! IRC_WriteStrClient( from, RPL_AWAY_MSG, Client_ID( from ), Client_ID( c ), Client_Away( c ))) return DISCONNECTED;
+       if (Client_HasMode(c, 'a') &&
+               !IRC_WriteStrClient(from, RPL_AWAY_MSG,
+                                   Client_ID(from), Client_ID(c),
+                                   Client_Away(c)))
+                       return DISCONNECTED;
+
+       return IRC_WriteStrClient(from, RPL_ENDOFWHOIS_MSG,
+                                 Client_ID(from), Client_ID(c));
+} /* IRC_WHOIS_SendReply */
+
+
+/**
+ * Handler for the IRC "WHOIS" command.
+ *
+ * See RFC 2812, 3.6.2 "Whois query".
+ *
+ * @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_WHOIS( CLIENT *Client, REQUEST *Req )
+{
+       CLIENT *from, *target, *c;
+       unsigned int match_count = 0, found = 0;
+       bool has_wildcards, is_remote;
+       bool got_wildcard = false;
+       const char *query;
+
+       assert( Client != NULL );
+       assert( Req != NULL );
+
+       /* Bad number of parameters? */
+       if (Req->argc < 1 || Req->argc > 2)
+               return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
+                                         Client_ID(Client), Req->command);
+
+       /* Search sender of the WHOIS */
+       if (Client_Type(Client) == CLIENT_SERVER) {
+               from = Client_Search(Req->prefix);
+       } else {
+               IRC_SetPenalty(Client, 1);
+               from = Client;
        }
+       if (!from)
+               return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+                                         Client_ID(Client), Req->prefix);
+
+       /* Get target server for this command */
+       if (Req->argc > 1) {
+               /* Search the target server, which can be specified as a
+                * nick name on that server as well: */
+               target = Client_Search(Req->argv[0]);
+               if (!target)
+                       return IRC_WriteStrClient(from, ERR_NOSUCHSERVER_MSG,
+                                               Client_ID(from), Req->argv[0]);
+       } else
+               target = Client_ThisServer();
+       assert(target != NULL);
+
+       /* Forward to other server? */
+       if (Client_NextHop(target) != Client_ThisServer() &&
+           Client_Type(Client_NextHop(target)) == CLIENT_SERVER)
+               return IRC_WriteStrClientPrefix(target, from,
+                                               "WHOIS %s :%s",
+                                               Req->argv[0], Req->argv[1]);
+
+       is_remote = Client_Conn(from) < 0;
+       for (query = strtok(Req->argv[Req->argc - 1], ",");
+                       query && found < 3;
+                       query = strtok(NULL, ","), found++)
+       {
+               has_wildcards = query[strcspn(query, "*?")] != 0;
+               /*
+                * follows ircd 2.10 implementation:
+                *  - handle up to 3 targets
+                *  - no wildcards for remote clients
+                *  - only one wildcard target per local client
+                *
+                *  also, at most ten matches are returned.
+                */
+               if (!has_wildcards || is_remote) {
+                       c = Client_Search(query);
+                       if (c) {
+                               if (!IRC_WHOIS_SendReply(Client, from, c))
+                                       return DISCONNECTED;
+                       } else {
+                               if (!IRC_WriteStrClient(Client,
+                                                       ERR_NOSUCHNICK_MSG,
+                                                       Client_ID(Client),
+                                                       query))
+                                       return DISCONNECTED;
+                       }
+                       continue;
+               }
+               if (got_wildcard) {
+                       /* we already handled one wildcard query */
+                       if (!IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+                            Client_ID(Client), query))
+                               return DISCONNECTED;
+                       continue;
+               }
+               got_wildcard = true;
+               IRC_SetPenalty(Client, 3);
 
-       /* End of Whois */
-       return IRC_WriteStrClient( from, RPL_ENDOFWHOIS_MSG, Client_ID( from ), Client_ID( c ));
+               for (c = Client_First(); c && match_count < 10; c = Client_Next(c)) {
+                       if (Client_Type(c) != CLIENT_USER)
+                               continue;
+                       if (!MatchCaseInsensitive(query, Client_ID(c)))
+                               continue;
+                       if (!IRC_WHOIS_SendReply(Client, from, c))
+                               return DISCONNECTED;
+                       match_count++;
+               }
+
+               if (match_count == 0)
+                       return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
+                               Client_ID(Client), Req->argv[Req->argc - 1]);
+       }
+       return CONNECTED;
 } /* IRC_WHOIS */
 
 
@@ -1063,6 +1189,10 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req )
        assert( Client != NULL );
        assert( Req != NULL );
 
+       /* Do not reveal any info on disconnected users? */
+       if (Conf_MorePrivacy)
+               return CONNECTED;
+
        /* Wrong number of parameters? */
        if (Req->argc > 3)
                return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
@@ -1147,38 +1277,54 @@ IRC_WHOWAS( CLIENT *Client, REQUEST *Req )
 } /* IRC_WHOWAS */
 
 
+/**
+ * Send LUSERS reply to a client.
+ *
+ * @param Client The receipient of the information.
+ * @return CONNECTED or DISCONNECTED.
+ */
 GLOBAL bool
-IRC_Send_LUSERS( CLIENT *Client )
+IRC_Send_LUSERS(CLIENT *Client)
 {
        unsigned long cnt;
 #ifndef STRICT_RFC
        unsigned long max;
 #endif
 
-       assert( Client != NULL );
+       assert(Client != NULL);
 
        /* Users, services and serevers in the network */
-       if( ! IRC_WriteStrClient( Client, RPL_LUSERCLIENT_MSG, Client_ID( Client ), Client_UserCount( ), Client_ServiceCount( ), Client_ServerCount( ))) return DISCONNECTED;
+       if (!IRC_WriteStrClient(Client, RPL_LUSERCLIENT_MSG, Client_ID(Client),
+                               Client_UserCount(), Client_ServiceCount(),
+                               Client_ServerCount()))
+               return DISCONNECTED;
 
        /* Number of IRC operators */
        cnt = Client_OperCount( );
-       if( cnt > 0 )
-       {
-               if( ! IRC_WriteStrClient( Client, RPL_LUSEROP_MSG, Client_ID( Client ), cnt )) return DISCONNECTED;
+       if (cnt > 0) {
+               if (!IRC_WriteStrClient(Client, RPL_LUSEROP_MSG,
+                                       Client_ID(Client), cnt))
+                       return DISCONNECTED;
        }
 
        /* Unknown connections */
        cnt = Client_UnknownCount( );
-       if( cnt > 0 )
-       {
-               if( ! IRC_WriteStrClient( Client, RPL_LUSERUNKNOWN_MSG, Client_ID( Client ), cnt )) return DISCONNECTED;
+       if (cnt > 0) {
+               if (!IRC_WriteStrClient(Client, RPL_LUSERUNKNOWN_MSG,
+                                       Client_ID(Client), cnt))
+                       return DISCONNECTED;
        }
 
        /* Number of created channels */
-       if( ! IRC_WriteStrClient( Client, RPL_LUSERCHANNELS_MSG, Client_ID( Client ), Channel_Count( ))) return DISCONNECTED;
+       if (!IRC_WriteStrClient(Client, RPL_LUSERCHANNELS_MSG,
+                               Client_ID(Client), Channel_Count()))
+               return DISCONNECTED;
 
        /* Number of local users, services and servers */
-       if( ! IRC_WriteStrClient( Client, RPL_LUSERME_MSG, Client_ID( Client ), Client_MyUserCount( ), Client_MyServiceCount( ), Client_MyServerCount( ))) return DISCONNECTED;
+       if (!IRC_WriteStrClient(Client, RPL_LUSERME_MSG, Client_ID(Client),
+                               Client_MyUserCount(), Client_MyServiceCount(),
+                               Client_MyServerCount()))
+               return DISCONNECTED;
 
 #ifndef STRICT_RFC
        /* Maximum number of local users */
@@ -1237,7 +1383,9 @@ static bool Show_MOTD_SSLInfo(CLIENT *Client)
        return ret;
 }
 #else
-static inline bool Show_MOTD_SSLInfo(UNUSED CLIENT *c) { return true; }
+static inline bool
+Show_MOTD_SSLInfo(UNUSED CLIENT *c)
+{ return true; }
 #endif
 
 GLOBAL bool
@@ -1287,6 +1435,10 @@ IRC_Send_NAMES( CLIENT *Client, CHANNEL *Chan )
        if( Channel_IsMemberOf( Chan, Client )) is_member = true;
        else is_member = false;
 
+       /* Do not print info on channel memberships to anyone that is not member? */
+       if (Conf_MorePrivacy && !is_member)
+               return CONNECTED;
+
        /* Secret channel? */
        if( ! is_member && strchr( Channel_Modes( Chan ), 's' )) return CONNECTED;
 
@@ -1329,14 +1481,13 @@ IRC_Send_NAMES( CLIENT *Client, CHANNEL *Chan )
 } /* IRC_Send_NAMES */
 
 
-
 /**
  * Send the ISUPPORT numeric (005).
  * This numeric indicates the features that are supported by this server.
  * See <http://www.irc.org/tech_docs/005.html> for details.
  */
 GLOBAL bool
-IRC_Send_ISUPPORT PARAMS((CLIENT * Client))
+IRC_Send_ISUPPORT(CLIENT * Client)
 {
        if (!IRC_WriteStrClient(Client, RPL_ISUPPORT1_MSG, Client_ID(Client),
                                Conf_MaxJoins))