diff options
author | Peter Powell <petpow@saberuk.com> | 2017-11-15 02:35:15 +0000 |
---|---|---|
committer | Peter Powell <petpow@saberuk.com> | 2017-11-17 18:17:55 +0000 |
commit | d8b7f36b3c402f2cb8b77410ac285d6f9066072d (patch) | |
tree | e3f02b1add4b89a8fa7b9dd467afaff2cb846a54 /src | |
parent | 36040be2952186d56a6646ee7d972aaafdd4e31a (diff) |
Improve LIST and implement more extended list features.
- Move the ISUPPORT token from the core into core_user and start
sending the SAFELIST token to let clients know that they can
safely run LIST on big servers.
- Add support for the channel creation time (C), topic set time
(T), and inverted glob match (N) filters as specified in the
draft-hardy-irc-isupport-00 specification.
- Clean up the logic for filtering channels by user count.
Diffstat (limited to 'src')
-rw-r--r-- | src/coremods/core_list.cpp | 124 | ||||
-rw-r--r-- | src/server.cpp | 1 |
2 files changed, 111 insertions, 14 deletions
diff --git a/src/coremods/core_list.cpp b/src/coremods/core_list.cpp index 3c29f7883..910ebb987 100644 --- a/src/coremods/core_list.cpp +++ b/src/coremods/core_list.cpp @@ -24,9 +24,22 @@ */ class CommandList : public Command { + private: ChanModeReference secretmode; ChanModeReference privatemode; + /** 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: /** Constructor for list. */ @@ -51,42 +64,105 @@ class CommandList : public Command */ CmdResult CommandList::Handle (const std::vector<std::string>& parameters, User *user) { - size_t minusers = 0, maxusers = 0; - - user->WriteNumeric(RPL_LISTSTART, "Channel", "Users Name"); + // 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; if ((parameters.size() == 1) && (!parameters[0].empty())) { if (parameters[0][0] == '<') { - maxusers = strtoul((parameters[0].c_str() + 1), NULL, 10); + maxusers = ConvToNum<size_t>(parameters[0].c_str() + 1); } else if (parameters[0][0] == '>') { - minusers = strtoul((parameters[0].c_str() + 1), NULL, 10); + minusers = ConvToNum<size_t>(parameters[0].c_str() + 1); + } + else if (!parameters[0].compare(0, 2, "C<", 2)) + { + mincreationtime = ParseMinutes(parameters[0]); + } + else if (!parameters[0].compare(0, 2, "C>", 2)) + { + maxcreationtime = ParseMinutes(parameters[0]); + } + else if (!parameters[0].compare(0, 2, "T<", 2)) + { + mintopictime = ParseMinutes(parameters[0]); + } + else if (!parameters[0].compare(0, 2, "T>", 2)) + { + maxtopictime = ParseMinutes(parameters[0]); + } + else + { + // If the glob is prefixed with ! it is inverted. + match = parameters[0].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 - size_t 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; } @@ -113,5 +189,27 @@ 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 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) diff --git a/src/server.cpp b/src/server.cpp index 1eebdd9e0..cd0889fb2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -190,7 +190,6 @@ void ISupportManager::Build() tokens["CHANMODES"] = ServerInstance->Modes->GiveModeList(MODETYPE_CHANNEL); tokens["CHANNELLEN"] = ConvToStr(ServerInstance->Config->Limits.ChanMax); tokens["CHANTYPES"] = "#"; - tokens["ELIST"] = "MU"; tokens["KICKLEN"] = ConvToStr(ServerInstance->Config->Limits.MaxKick); tokens["MAXTARGETS"] = ConvToStr(ServerInstance->Config->MaxTargets); tokens["MODES"] = ConvToStr(ServerInstance->Config->Limits.MaxModes); |