diff options
author | Attila Molnar <attilamolnar@hush.com> | 2016-01-03 14:50:25 +0100 |
---|---|---|
committer | Attila Molnar <attilamolnar@hush.com> | 2016-01-03 14:50:25 +0100 |
commit | 69df6199ef8e9693060906037ac9a23880bf4acc (patch) | |
tree | e5d2195999b34117d7097ce700c1efb48f463bb1 | |
parent | 7bc9c25b8b9a04cb85fa0c00f304fe489ad9148d (diff) | |
parent | a956c5d10879f9286c9878ea2e35d5eb469f901f (diff) |
Merge branch 'master+numericbuilder'
-rw-r--r-- | include/inspircd.h | 1 | ||||
-rw-r--r-- | include/numericbuilder.h | 119 | ||||
-rw-r--r-- | src/coremods/core_channel/cmd_names.cpp | 28 | ||||
-rw-r--r-- | src/coremods/core_channel/core_channel.h | 6 | ||||
-rw-r--r-- | src/coremods/core_ison.cpp | 69 | ||||
-rw-r--r-- | src/coremods/core_whois.cpp | 128 |
6 files changed, 233 insertions, 118 deletions
diff --git a/include/inspircd.h b/include/inspircd.h index 91b70fbd8..20a6508c9 100644 --- a/include/inspircd.h +++ b/include/inspircd.h @@ -664,4 +664,5 @@ inline void stdalgo::culldeleter::operator()(classbase* item) ServerInstance->GlobalCulls.AddItem(item); } +#include "numericbuilder.h" #include "modules/whois.h" diff --git a/include/numericbuilder.h b/include/numericbuilder.h new file mode 100644 index 000000000..9f4cfd7dd --- /dev/null +++ b/include/numericbuilder.h @@ -0,0 +1,119 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com> + * + * 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 + * License as published by the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#pragma once + +namespace Numeric +{ + class WriteNumericSink; + + template <char Sep, bool SendEmpty, typename Sink> + class GenericBuilder; + + template <char Sep = ',', bool SendEmpty = false> + class Builder; +} + +class Numeric::WriteNumericSink +{ + LocalUser* const user; + + public: + WriteNumericSink(LocalUser* u) + : user(u) + { + } + + void operator()(unsigned int numeric, const std::string& params) const + { + user->WriteNumeric(numeric, params); + } +}; + +template <char Sep, bool SendEmpty, typename Sink> +class Numeric::GenericBuilder +{ + Sink sink; + std::string data; + const unsigned int numeric; + const std::string::size_type max; + std::string::size_type beginpos; + + bool HasRoom(const std::string::size_type additional) const + { + return (data.size() + additional <= max); + } + + public: + GenericBuilder(Sink s, unsigned int num, bool addparam = true, size_t additionalsize = 0) + : sink(s) + , numeric(num) + , max(ServerInstance->Config->Limits.MaxLine - ServerInstance->Config->ServerName.size() - additionalsize - 9) + { + if (addparam) + data.push_back(':'); + SaveBeginPos(); + } + + std::string& GetNumeric() { return data; } + + void Add(const std::string& entry) + { + if (!HasRoom(entry.size())) + Flush(); + data.append(entry).push_back(Sep); + } + + void Add(const std::string& entry1, const std::string& entry2) + { + if (!HasRoom(entry1.size() + entry2.size())) + Flush(); + data.append(entry1).append(entry2).push_back(Sep); + } + + void Flush() + { + if (IsEmpty()) + { + if (!SendEmpty) + return; + } + else + { + data.erase(data.size()-1); + } + + sink(numeric, data); + if (data.size() > beginpos) + data.erase(beginpos); + } + + bool IsEmpty() const { return (data.size() == beginpos); } + void SaveBeginPos() { beginpos = data.size(); } +}; + +template <char Sep, bool SendEmpty> +class Numeric::Builder : public GenericBuilder<Sep, SendEmpty, WriteNumericSink> +{ + public: + Builder(LocalUser* user, unsigned int num, bool addparam = true, size_t additionalsize = 0) + : GenericBuilder<Sep, SendEmpty, WriteNumericSink>(WriteNumericSink(user), num, addparam, additionalsize + user->nick.size()) + { + } +}; diff --git a/src/coremods/core_channel/cmd_names.cpp b/src/coremods/core_channel/cmd_names.cpp index 3af99ed2b..986dbe018 100644 --- a/src/coremods/core_channel/cmd_names.cpp +++ b/src/coremods/core_channel/cmd_names.cpp @@ -22,7 +22,7 @@ #include "core_channel.h" CommandNames::CommandNames(Module* parent) - : Command(parent, "NAMES", 0, 0) + : SplitCommand(parent, "NAMES", 0, 0) , secretmode(parent, "secret") , privatemode(parent, "private") , invisiblemode(parent, "invisible") @@ -32,7 +32,7 @@ CommandNames::CommandNames(Module* parent) /** Handle /NAMES */ -CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User *user) +CmdResult CommandNames::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) { Channel* c; @@ -66,9 +66,10 @@ CmdResult CommandNames::Handle (const std::vector<std::string>& parameters, User return CMD_FAILURE; } -void CommandNames::SendNames(User* user, Channel* chan, bool show_invisible) +void CommandNames::SendNames(LocalUser* user, Channel* chan, bool show_invisible) { - std::string list; + Numeric::Builder<' '> reply(user, RPL_NAMREPLY, false); + std::string& list = reply.GetNumeric(); if (chan->IsModeSet(secretmode)) list.push_back('@'); else if (chan->IsModeSet(privatemode)) @@ -78,9 +79,8 @@ void CommandNames::SendNames(User* user, Channel* chan, bool show_invisible) list.push_back(' '); list.append(chan->name).append(" :"); - std::string::size_type pos = list.size(); + reply.SaveBeginPos(); - const size_t maxlen = ServerInstance->Config->Limits.MaxLine - 10 - ServerInstance->Config->ServerName.size() - user->nick.size(); std::string prefixlist; std::string nick; const Channel::MemberMap& members = chan->GetUsers(); @@ -107,21 +107,9 @@ void CommandNames::SendNames(User* user, Channel* chan, bool show_invisible) if (res == MOD_RES_DENY) continue; - if (list.size() + prefixlist.length() + nick.length() + 1 > maxlen) - { - // List overflowed into multiple numerics - user->WriteNumeric(RPL_NAMREPLY, list); - - // Erase all nicks, keep the constant part - list.erase(pos); - } - - list.append(prefixlist).append(nick).push_back(' '); + reply.Add(prefixlist, nick); } - // Only send the user list numeric if there is at least one user in it - if (list.size() != pos) - user->WriteNumeric(RPL_NAMREPLY, list); - + reply.Flush(); user->WriteNumeric(RPL_ENDOFNAMES, "%s :End of /NAMES list.", chan->name.c_str()); } diff --git a/src/coremods/core_channel/core_channel.h b/src/coremods/core_channel/core_channel.h index 2a2800523..0dafde8cb 100644 --- a/src/coremods/core_channel/core_channel.h +++ b/src/coremods/core_channel/core_channel.h @@ -90,7 +90,7 @@ class CommandTopic : public SplitCommand /** Handle /NAMES. */ -class CommandNames : public Command +class CommandNames : public SplitCommand { ChanModeReference secretmode; ChanModeReference privatemode; @@ -106,14 +106,14 @@ class CommandNames : 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 HandleLocal(const std::vector<std::string>& parameters, LocalUser* user); /** Spool the NAMES list for a given channel to the given user * @param user User to spool the NAMES list to * @param chan Channel whose nicklist to send * @param show_invisible True to show invisible (+i) members to the user, false to omit them from the list */ - void SendNames(User* user, Channel* chan, bool show_invisible); + void SendNames(LocalUser* user, Channel* chan, bool show_invisible); }; /** Handle /KICK. diff --git a/src/coremods/core_ison.cpp b/src/coremods/core_ison.cpp index ebb43bdf9..8deeefc59 100644 --- a/src/coremods/core_ison.cpp +++ b/src/coremods/core_ison.cpp @@ -22,20 +22,14 @@ /** Handle /ISON. */ -class CommandIson : public Command +class CommandIson : public SplitCommand { - /** Helper function to append a nick to an ISON reply - * @param user User doing the /ISON - * @param toadd User to append to the ISON reply - * @param reply Reply string to append the nick to - * @param pos If the reply gets too long it is sent to the user and truncated from this position - */ - static bool AddNick(User* user, User* toadd, std::string& reply, const std::string::size_type pos); - public: /** Constructor for ison. */ - CommandIson ( Module* parent) : Command(parent,"ISON", 1) { + CommandIson(Module* parent) + : SplitCommand(parent, "ISON", 1) + { syntax = "<nick> {nick}"; } /** Handle command. @@ -43,52 +37,43 @@ class CommandIson : 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 HandleLocal(const std::vector<std::string>& parameters, LocalUser* user); }; -bool CommandIson::AddNick(User* user, User* toadd, std::string& reply, const std::string::size_type pos) +class IsonReplyBuilder : public Numeric::Builder<' ', true> { - if ((toadd) && (toadd->registered == REG_ALL)) + public: + IsonReplyBuilder(LocalUser* user) + : Builder<' ', true>(user, 303) { - reply.append(toadd->nick).push_back(' '); - if (reply.length() > 450) - { - user->WriteServ(reply); - reply.erase(pos); - } - return true; } - return false; -} + + void AddNick(const std::string& nickname) + { + User* const user = ServerInstance->FindNickOnly(nickname); + if ((user) && (user->registered == REG_ALL)) + Add(user->nick); + } +}; /** Handle /ISON */ -CmdResult CommandIson::Handle (const std::vector<std::string>& parameters, User *user) +CmdResult CommandIson::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) { - std::string reply = "303 " + user->nick + " :"; - const std::string::size_type pos = reply.size(); + IsonReplyBuilder reply(user); - for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end(); ++i) + for (std::vector<std::string>::const_iterator i = parameters.begin(); i != parameters.end()-1; ++i) { const std::string& targetstr = *i; - - User* const u = ServerInstance->FindNickOnly(targetstr); - if (!AddNick(user, u, reply, pos)) - { - if ((i == parameters.end() - 1) && (targetstr.find(' ') != std::string::npos)) - { - /* Its a space seperated list of nicks (RFC1459 says to support this) - */ - irc::spacesepstream list(targetstr); - std::string item; - - while (list.GetToken(item)) - AddNick(user, ServerInstance->FindNickOnly(item), reply, pos); - } - } + reply.AddNick(targetstr); } - user->WriteServ(reply); + // Last parameter can be a space separated list + irc::spacesepstream ss(parameters.back()); + for (std::string token; ss.GetToken(token); ) + reply.AddNick(token); + + reply.Flush(); return CMD_SUCCESS; } diff --git a/src/coremods/core_whois.cpp b/src/coremods/core_whois.cpp index 1cd622092..703ebbed3 100644 --- a/src/coremods/core_whois.cpp +++ b/src/coremods/core_whois.cpp @@ -59,9 +59,8 @@ class CommandWhois : public SplitCommand Events::ModuleEventProvider evprov; Events::ModuleEventProvider lineevprov; - void SplitChanList(WhoisContextImpl& whois, const std::string& cl); void DoWhois(LocalUser* user, User* dest, unsigned long signon, unsigned long idle); - std::string ChannelList(User* source, User* dest, bool spy); + void SendChanList(WhoisContextImpl& whois); public: /** Constructor for whois. @@ -87,56 +86,94 @@ class CommandWhois : public SplitCommand CmdResult HandleRemote(const std::vector<std::string>& parameters, RemoteUser* target); }; -std::string CommandWhois::ChannelList(User* source, User* dest, bool spy) +class WhoisNumericSink { - std::string list; + WhoisContextImpl& whois; + public: + WhoisNumericSink(WhoisContextImpl& whoisref) + : whois(whoisref) + { + } - for (User::ChanList::iterator i = dest->chans.begin(); i != dest->chans.end(); i++) + void operator()(unsigned int numeric, const std::string& text) const { - Membership* memb = *i; - Channel* c = memb->chan; - /* If the target is the sender, neither +p nor +s is set, or - * the channel contains the user, it is not a spy channel - */ - if (spy != (source == dest || !(c->IsModeSet(privatemode) || c->IsModeSet(secretmode)) || c->HasUser(source))) - { - char prefix = memb->GetPrefixChar(); - if (prefix) - list.push_back(prefix); - list.append(c->name).push_back(' '); - } + whois.SendLine(numeric, text); } +}; - return list; -} +class WhoisChanListNumericBuilder : public Numeric::GenericBuilder<' ', false, WhoisNumericSink> +{ + public: + WhoisChanListNumericBuilder(WhoisContextImpl& whois) + : GenericBuilder<' ', false, WhoisNumericSink>(WhoisNumericSink(whois), 319, true, whois.GetSource()->nick.size() + whois.GetTarget()->nick.size() + 1) + { + } +}; -void CommandWhois::SplitChanList(WhoisContextImpl& whois, const std::string& cl) +class WhoisChanList { - std::string line(1, ':'); - std::string::size_type start, pos; + const ServerConfig::OperSpyWhoisState spywhois; + WhoisChanListNumericBuilder num; + WhoisChanListNumericBuilder spynum; + std::string prefixstr; - // ":server.name 319 source target " ... "\r\n" - const std::string::size_type maxlen = ServerInstance->Config->Limits.MaxLine - 10 - ServerInstance->Config->ServerName.length() - whois.GetTarget()->nick.length() - whois.GetSource()->nick.length(); + void AddMember(Membership* memb, WhoisChanListNumericBuilder& out) + { + prefixstr.clear(); + const char prefix = memb->GetPrefixChar(); + if (prefix) + prefixstr.push_back(prefix); + out.Add(prefixstr, memb->chan->name); + } - for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1) + public: + WhoisChanList(WhoisContextImpl& whois) + : spywhois(whois.GetSource()->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE) + , num(whois) + , spynum(whois) { - if (line.length() + pos - start > maxlen) - { - // Erase last ' ' and send - line.erase(line.length()-1); - whois.SendLine(319, line); - line.erase(1); - } + } + + void AddVisible(Membership* memb) + { + AddMember(memb, num); + } + + void AddHidden(Membership* memb) + { + if (spywhois == ServerConfig::SPYWHOIS_NONE) + return; + AddMember(memb, (spywhois == ServerConfig::SPYWHOIS_SPLITMSG ? spynum : num)); + } - line.append(cl, start, pos - start + 1); + void Flush(WhoisContextImpl& whois) + { + num.Flush(); + if (!spynum.IsEmpty()) + whois.SendLine(336, ":is on private/secret channels:"); + spynum.Flush(); } +}; + +void CommandWhois::SendChanList(WhoisContextImpl& whois) +{ + WhoisChanList chanlist(whois); - if (line.length() > 1) + User* const target = whois.GetTarget(); + for (User::ChanList::iterator i = target->chans.begin(); i != target->chans.end(); ++i) { - // Erase last ' ' and send - line.erase(line.length()-1); - whois.SendLine(319, line); + Membership* memb = *i; + Channel* c = memb->chan; + /* If the target is the sender, neither +p nor +s is set, or + * the channel contains the user, it is not a spy channel + */ + if ((whois.IsSelfWhois()) || ((!c->IsModeSet(privatemode)) && (!c->IsModeSet(secretmode))) || (c->HasUser(whois.GetSource()))) + chanlist.AddVisible(memb); + else + chanlist.AddHidden(memb); } + + chanlist.Flush(whois); } void CommandWhois::DoWhois(LocalUser* user, User* dest, unsigned long signon, unsigned long idle) @@ -149,23 +186,8 @@ void CommandWhois::DoWhois(LocalUser* user, User* dest, unsigned long signon, un whois.SendLine(378, ":is connecting from %s@%s %s", dest->ident.c_str(), dest->host.c_str(), dest->GetIPString().c_str()); } - std::string cl = ChannelList(user, dest, false); - const ServerConfig::OperSpyWhoisState state = user->HasPrivPermission("users/auspex") ? ServerInstance->Config->OperSpyWhois : ServerConfig::SPYWHOIS_NONE; - - if (state == ServerConfig::SPYWHOIS_SINGLEMSG) - cl.append(ChannelList(user, dest, true)); + SendChanList(whois); - SplitChanList(whois, cl); - - if (state == ServerConfig::SPYWHOIS_SPLITMSG) - { - std::string scl = ChannelList(user, dest, true); - if (scl.length()) - { - whois.SendLine(336, ":is on private/secret channels:"); - SplitChanList(whois, scl); - } - } if (!whois.IsSelfWhois() && !ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex")) { whois.SendLine(312, "%s :%s", ServerInstance->Config->HideWhoisServer.c_str(), ServerInstance->Config->Network.c_str()); |