summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAttila Molnar <attilamolnar@hush.com>2016-01-03 14:50:25 +0100
committerAttila Molnar <attilamolnar@hush.com>2016-01-03 14:50:25 +0100
commit69df6199ef8e9693060906037ac9a23880bf4acc (patch)
treee5d2195999b34117d7097ce700c1efb48f463bb1
parent7bc9c25b8b9a04cb85fa0c00f304fe489ad9148d (diff)
parenta956c5d10879f9286c9878ea2e35d5eb469f901f (diff)
Merge branch 'master+numericbuilder'
-rw-r--r--include/inspircd.h1
-rw-r--r--include/numericbuilder.h119
-rw-r--r--src/coremods/core_channel/cmd_names.cpp28
-rw-r--r--src/coremods/core_channel/core_channel.h6
-rw-r--r--src/coremods/core_ison.cpp69
-rw-r--r--src/coremods/core_whois.cpp128
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());