]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/coremods/core_list.cpp
Use a duration string for the idle time in `/STATS P`.
[user/henk/code/inspircd.git] / src / coremods / core_list.cpp
index 67829a55edf472715d7075e5c592ede7c6f2612a..28ea91ce154a174a62a5ad56d69b67ff16c26e40 100644 (file)
@@ -1,8 +1,14 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
+ *   Copyright (C) 2017-2020 Sadie Powell <sadie@witchery.services>
+ *   Copyright (C) 2015 Daniel Vassdal <shutter@canternet.org>
+ *   Copyright (C) 2013-2016 Attila Molnar <attilamolnar@hush.com>
+ *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ *   Copyright (C) 2005-2007, 2010 Craig Edwards <brain@inspircd.org>
  *
  * This file is part of InspIRCd.  InspIRCd is free software: you can
  * redistribute it and/or modify it under the terms of the GNU General Public
  */
 class CommandList : public Command
 {
+ private:
        ChanModeReference secretmode;
        ChanModeReference privatemode;
 
- public:
-       /** Constructor for list.
+       /** Parses the creation time or topic set time out of a LIST parameter.
+        * @param value The parameter containing a minute count.
+        * @return The UNIX time at \p value minutes ago.
         */
+       time_t ParseMinutes(const std::string& value)
+       {
+               time_t minutes = ConvToNum<time_t>(value.c_str() + 2);
+               if (!minutes)
+                       return 0;
+               return ServerInstance->Time() - (minutes * 60);
+       }
+
+ public:
+       // Whether to show modes in the LIST response.
+       bool showmodes;
+
        CommandList(Module* parent)
                : Command(parent,"LIST", 0, 0)
                , secretmode(creator, "secret")
                , privatemode(creator, "private")
        {
+               allow_empty_last_param = false;
                Penalty = 5;
        }
 
@@ -43,51 +64,114 @@ class CommandList : public Command
         * @param user The user issuing the command
         * @return A value from CmdResult to indicate command success or failure.
         */
-       CmdResult Handle(const std::vector<std::string>& parameters, User *user);
+       CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
 };
 
 
 /** Handle /LIST
  */
-CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user)
+CmdResult CommandList::Handle(User* user, const Params& parameters)
 {
-       int minusers = 0, maxusers = 0;
-
-       user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
-
-       /* Work around mIRC suckyness. YOU SUCK, KHALED! */
-       if (parameters.size() == 1)
+       // C: Searching based on creation time, via the "C<val" and "C>val" modifiers
+       // to search for a channel creation time that is lower or higher than val
+       // respectively.
+       time_t mincreationtime = 0;
+       time_t maxcreationtime = 0;
+
+       // M: Searching based on mask.
+       // N: Searching based on !mask.
+       bool match_name_topic = false;
+       bool match_inverted = false;
+       const char* match = NULL;
+
+       // T: Searching based on topic time, via the "T<val" and "T>val" modifiers to
+       // search for a topic time that is lower or higher than val respectively.
+       time_t mintopictime = 0;
+       time_t maxtopictime = 0;
+
+       // U: Searching based on user count within the channel, via the "<val" and
+       // ">val" modifiers to search for a channel that has less than or more than
+       // val users respectively.
+       size_t minusers = 0;
+       size_t maxusers = 0;
+
+       for (Params::const_iterator iter = parameters.begin(); iter != parameters.end(); ++iter)
        {
-               if (parameters[0][0] == '<')
+               const std::string& constraint = *iter;
+               if (constraint[0] == '<')
+               {
+                       maxusers = ConvToNum<size_t>(constraint.c_str() + 1);
+               }
+               else if (constraint[0] == '>')
+               {
+                       minusers = ConvToNum<size_t>(constraint.c_str() + 1);
+               }
+               else if (!constraint.compare(0, 2, "C<", 2) || !constraint.compare(0, 2, "c<", 2))
+               {
+                       mincreationtime = ParseMinutes(constraint);
+               }
+               else if (!constraint.compare(0, 2, "C>", 2) || !constraint.compare(0, 2, "c>", 2))
+               {
+                       maxcreationtime = ParseMinutes(constraint);
+               }
+               else if (!constraint.compare(0, 2, "T<", 2) || !constraint.compare(0, 2, "t<", 2))
+               {
+                       mintopictime = ParseMinutes(constraint);
+               }
+               else if (!constraint.compare(0, 2, "T>", 2) || !constraint.compare(0, 2, "t>", 2))
                {
-                       maxusers = atoi((parameters[0].c_str())+1);
+                       maxtopictime = ParseMinutes(constraint);
                }
-               else if (parameters[0][0] == '>')
+               else
                {
-                       minusers = atoi((parameters[0].c_str())+1);
+                       // If the glob is prefixed with ! it is inverted.
+                       match = constraint.c_str();
+                       if (match[0] == '!')
+                       {
+                               match_inverted = true;
+                               match += 1;
+                       }
+
+                       // Ensure that the user didn't just run "LIST !".
+                       if (match[0])
+                               match_name_topic = true;
                }
        }
 
        const bool has_privs = user->HasPrivPermission("channels/auspex");
-       const bool match_name_topic = ((!parameters.empty()) && (!parameters[0].empty()) && (parameters[0][0] != '<') && (parameters[0][0] != '>'));
 
+       user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name");
        const chan_hash& chans = ServerInstance->GetChans();
        for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); ++i)
        {
                Channel* const chan = i->second;
 
-               // attempt to match a glob pattern
-               long users = chan->GetUserCounter();
+               // Check the user count if a search has been specified.
+               const size_t users = chan->GetUserCounter();
+               if ((minusers && users <= minusers) || (maxusers && users >= maxusers))
+                       continue;
 
-               bool too_few = (minusers && (users <= minusers));
-               bool too_many = (maxusers && (users >= maxusers));
+               // Check the creation ts if a search has been specified.
+               const time_t creationtime = chan->age;
+               if ((mincreationtime && creationtime <= mincreationtime) || (maxcreationtime && creationtime >= maxcreationtime))
+                       continue;
 
-               if (too_many || too_few)
+               // Check the topic ts if a search has been specified.
+               const time_t topictime = chan->topicset;
+               if ((mintopictime && (!topictime || topictime <= mintopictime)) || (maxtopictime && (!topictime || topictime >= maxtopictime)))
                        continue;
 
+               // Attempt to match a glob pattern.
                if (match_name_topic)
                {
-                       if (!InspIRCd::Match(chan->name, parameters[0]) && !InspIRCd::Match(chan->topic, parameters[0]))
+                       bool matches = InspIRCd::Match(chan->name, match) || InspIRCd::Match(chan->topic, match);
+
+                       // The user specified an match that we did not match.
+                       if (!matches && !match_inverted)
+                               continue;
+
+                       // The user specified an inverted match that we did match.
+                       if (matches && match_inverted)
                                continue;
                }
 
@@ -102,11 +186,16 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User
                                // Channel is private (+p) and user is outside/not privileged
                                user->WriteNumeric(RPL_LIST, '*', users, "");
                        }
-                       else
+                       else if (showmodes)
                        {
-                               /* User is in the channel/privileged, channel is not +s */
+                               // Show the list response with the modes and topic.
                                user->WriteNumeric(RPL_LIST, chan->name, users, InspIRCd::Format("[+%s] %s", chan->ChanModes(n), chan->topic.c_str()));
                        }
+                       else
+                       {
+                               // Show the list response with just the modes.
+                               user->WriteNumeric(RPL_LIST, chan->name, users, chan->topic);
+                       }
                }
        }
        user->WriteNumeric(RPL_LISTEND, "End of channel list.");
@@ -114,5 +203,33 @@ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User
        return CMD_SUCCESS;
 }
 
+class CoreModList : public Module
+{
+ private:
+       CommandList cmd;
+
+ public:
+       CoreModList()
+               : cmd(this)
+       {
+       }
+
+       void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+       {
+               ConfigTag* tag = ServerInstance->Config->ConfValue("options");
+               cmd.showmodes = tag->getBool("modesinlist", true);
+       }
+
+       void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
+       {
+               tokens["ELIST"] = "CMNTU";
+               tokens["SAFELIST"];
+       }
+
+       Version GetVersion() CXX11_OVERRIDE
+       {
+               return Version("Provides the LIST command", VF_VENDOR|VF_CORE);
+       }
+};
 
-COMMAND_INIT(CommandList)
+MODULE_INIT(CoreModList)