summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPeter Powell <petpow@saberuk.com>2018-08-13 20:17:46 +0100
committerPeter Powell <petpow@saberuk.com>2018-08-13 21:51:11 +0100
commit58a0a7e01422e62de1565a8eb0a1febdc463d04d (patch)
tree8861789deefe9df3524690de8ccd11e5366f1f2e /src
parente2a820cce21342478653a34cf8ce2b593128d035 (diff)
Implement IRCv3 message tag support.
Co-authored-by: Attila Molnar <attilamolnar@hush.com>
Diffstat (limited to 'src')
-rw-r--r--src/channels.cpp113
-rw-r--r--src/clientprotocol.cpp105
-rw-r--r--src/command_parse.cpp49
-rw-r--r--src/configreader.cpp1
-rw-r--r--src/coremods/core_channel/cmd_invite.cpp14
-rw-r--r--src/coremods/core_channel/core_channel.cpp71
-rw-r--r--src/coremods/core_oper/cmd_die.cpp5
-rw-r--r--src/coremods/core_oper/cmd_kill.cpp24
-rw-r--r--src/coremods/core_oper/core_oper.h17
-rw-r--r--src/coremods/core_privmsg.cpp53
-rw-r--r--src/coremods/core_reloadmodule.cpp139
-rw-r--r--src/coremods/core_serialize_rfc.cpp222
-rw-r--r--src/coremods/core_user/core_user.cpp9
-rw-r--r--src/coremods/core_wallops.cpp13
-rw-r--r--src/inspircd.cpp10
-rw-r--r--src/mode.cpp42
-rw-r--r--src/modules.cpp3
-rw-r--r--src/modules/m_alias.cpp2
-rw-r--r--src/modules/m_auditorium.cpp51
-rw-r--r--src/modules/m_cap.cpp37
-rw-r--r--src/modules/m_chanhistory.cpp25
-rw-r--r--src/modules/m_chanlog.cpp3
-rw-r--r--src/modules/m_cloaking.cpp8
-rw-r--r--src/modules/m_conn_waitpong.cpp6
-rw-r--r--src/modules/m_delayjoin.cpp63
-rw-r--r--src/modules/m_hostcycle.cpp34
-rw-r--r--src/modules/m_ircv3.cpp230
-rw-r--r--src/modules/m_ircv3_capnotify.cpp54
-rw-r--r--src/modules/m_ircv3_chghost.cpp10
-rw-r--r--src/modules/m_ircv3_echomessage.cpp24
-rw-r--r--src/modules/m_ircv3_invitenotify.cpp8
-rw-r--r--src/modules/m_knock.cpp8
-rw-r--r--src/modules/m_ldapoper.cpp3
-rw-r--r--src/modules/m_passforward.cpp4
-rw-r--r--src/modules/m_samode.cpp42
-rw-r--r--src/modules/m_sasl.cpp17
-rw-r--r--src/modules/m_showfile.cpp7
-rw-r--r--src/modules/m_spanningtree/main.cpp6
-rw-r--r--src/modules/m_spanningtree/main.h2
-rw-r--r--src/modules/m_spanningtree/treesocket2.cpp3
-rw-r--r--src/modules/m_sqloper.cpp3
-rw-r--r--src/modules/m_timedbans.cpp8
-rw-r--r--src/usermanager.cpp34
-rw-r--r--src/users.cpp179
44 files changed, 1199 insertions, 562 deletions
diff --git a/src/channels.cpp b/src/channels.cpp
index b293e7fad..c9816db4b 100644
--- a/src/channels.cpp
+++ b/src/channels.cpp
@@ -49,7 +49,8 @@ void Channel::SetTopic(User* u, const std::string& ntopic, time_t topicts, const
if (this->topic != ntopic)
{
this->topic = ntopic;
- this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
+ ClientProtocol::Messages::Topic topicmsg(u, this, this->topic);
+ Write(ServerInstance->GetRFCEvents().topic, topicmsg);
}
// Always update setter and set time
@@ -287,18 +288,8 @@ Membership* Channel::ForceJoin(User* user, const std::string* privs, bool bursti
CUList except_list;
FOREACH_MOD(OnUserJoin, (memb, bursting, created_by_local, except_list));
- this->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", this->name.c_str());
-
- /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
- if ((GetUserCounter() > 1) && (!memb->modes.empty()))
- {
- std::string ms = memb->modes;
- for(unsigned int i=0; i < memb->modes.length(); i++)
- ms.append(" ").append(user->nick);
-
- except_list.insert(user);
- this->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", this->name.c_str(), ms.c_str());
- }
+ ClientProtocol::Events::Join joinevent(memb);
+ this->Write(joinevent, 0, except_list);
FOREACH_MOD(OnPostJoin, (memb));
return memb;
@@ -397,7 +388,8 @@ bool Channel::PartUser(User* user, std::string& reason)
CUList except_list;
FOREACH_MOD(OnUserPart, (memb, reason, except_list));
- WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
+ ClientProtocol::Messages::Part partmsg(memb, reason);
+ Write(ServerInstance->GetRFCEvents().part, partmsg, 0, except_list);
// Remove this channel from the user's chanlist
user->chans.erase(memb);
@@ -413,73 +405,14 @@ void Channel::KickUser(User* src, const MemberMap::iterator& victimiter, const s
CUList except_list;
FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
- User* victim = memb->user;
- WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), victim->nick.c_str(), reason.c_str());
+ ClientProtocol::Messages::Kick kickmsg(src, memb, reason);
+ Write(ServerInstance->GetRFCEvents().kick, kickmsg, 0, except_list);
- victim->chans.erase(memb);
+ memb->user->chans.erase(memb);
this->DelUser(victimiter);
}
-void Channel::WriteChannel(User* user, const char* text, ...)
-{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- this->WriteChannel(user, textbuffer);
-}
-
-void Channel::WriteChannel(User* user, const std::string &text)
-{
- const std::string message = ":" + user->GetFullHost() + " " + text;
-
- for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
- {
- if (IS_LOCAL(i->first))
- i->first->Write(message);
- }
-}
-
-void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
-{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- this->WriteChannelWithServ(ServName, textbuffer);
-}
-
-void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
-{
- const std::string message = ":" + (ServName.empty() ? ServerInstance->Config->ServerName : ServName) + " " + text;
-
- for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
- {
- if (IS_LOCAL(i->first))
- i->first->Write(message);
- }
-}
-
-/* write formatted text from a source user to all users on a channel except
- * for the sender (for privmsg etc) */
-void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
-{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- this->WriteAllExceptSender(user, serversource, status, textbuffer);
-}
-
-void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
-{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- textbuffer = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + textbuffer;
- this->RawWriteAllExcept(user, serversource, status, except_list, textbuffer);
-}
-
-void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
-{
- const std::string message = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + text;
- this->RawWriteAllExcept(user, serversource, status, except_list, message);
-}
-
-void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
+void Channel::Write(ClientProtocol::Event& protoev, char status, const CUList& except_list)
{
unsigned int minrank = 0;
if (status)
@@ -490,24 +423,18 @@ void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CULi
}
for (MemberMap::iterator i = userlist.begin(); i != userlist.end(); i++)
{
- if (IS_LOCAL(i->first) && (except_list.find(i->first) == except_list.end()))
+ LocalUser* user = IS_LOCAL(i->first);
+ if ((user) && (!except_list.count(user)))
{
/* User doesn't have the status we're after */
if (minrank && i->second->getRank() < minrank)
continue;
- i->first->Write(out);
+ user->Send(protoev);
}
}
}
-void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const std::string& text)
-{
- CUList except_list;
- except_list.insert(user);
- this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
-}
-
const char* Channel::ChanModes(bool showkey)
{
static std::string scratch;
@@ -545,9 +472,8 @@ const char* Channel::ChanModes(bool showkey)
void Channel::WriteNotice(const std::string& text)
{
- std::string rawmsg = "NOTICE ";
- rawmsg.append(this->name).append(" :").append(text);
- WriteChannelWithServ(ServerInstance->Config->ServerName, rawmsg);
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this, text, MSG_NOTICE);
+ Write(ServerInstance->GetRFCEvents().privmsg, privmsg);
}
/* returns the status character for a given user on a channel, e.g. @ for op,
@@ -628,7 +554,10 @@ bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding)
void Membership::WriteNotice(const std::string& text) const
{
- std::string rawmsg = "NOTICE ";
- rawmsg.append(chan->name).append(" :").append(text);
- user->WriteServ(rawmsg);
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (!localuser)
+ return;
+
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this->chan, text, MSG_NOTICE);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
}
diff --git a/src/clientprotocol.cpp b/src/clientprotocol.cpp
new file mode 100644
index 000000000..a732855a5
--- /dev/null
+++ b/src/clientprotocol.cpp
@@ -0,0 +1,105 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+
+#include "inspircd.h"
+
+ClientProtocol::Serializer::Serializer(Module* mod, const char* Name)
+ : DataProvider(mod, std::string("serializer/") + Name)
+ , evprov(mod, "event/messagetag")
+{
+}
+
+bool ClientProtocol::Serializer::HandleTag(LocalUser* user, const std::string& tagname, std::string& tagvalue, TagMap& tags) const
+{
+ // Catch and block empty tags
+ if (tagname.empty())
+ return false;
+
+ const ::Events::ModuleEventProvider::SubscriberList& list = evprov.GetSubscribers();
+ for (::Events::ModuleEventProvider::SubscriberList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ MessageTagProvider* const tagprov = static_cast<MessageTagProvider*>(*i);
+ const ModResult res = tagprov->OnClientProtocolProcessTag(user, tagname, tagvalue);
+ if (res == MOD_RES_ALLOW)
+ return tags.insert(std::make_pair(tagname, MessageTagData(tagprov, tagvalue))).second;
+ else if (res == MOD_RES_DENY)
+ break;
+ }
+
+ // No module handles the tag but that's not an error
+ return true;
+}
+
+ClientProtocol::TagSelection ClientProtocol::Serializer::MakeTagWhitelist(LocalUser* user, const TagMap& tagmap) const
+{
+ TagSelection tagwl;
+ for (TagMap::const_iterator i = tagmap.begin(); i != tagmap.end(); ++i)
+ {
+ const MessageTagData& tagdata = i->second;
+ if (tagdata.tagprov->ShouldSendTag(user, tagdata))
+ tagwl.Select(tagmap, i);
+ }
+ return tagwl;
+}
+
+const ClientProtocol::SerializedMessage& ClientProtocol::Serializer::SerializeForUser(LocalUser* user, Message& msg)
+{
+ if (!msg.msginit_done)
+ {
+ msg.msginit_done = true;
+ FOREACH_MOD_CUSTOM(evprov, MessageTagProvider, OnClientProtocolPopulateTags, (msg));
+ }
+ return msg.GetSerialized(Message::SerializedInfo(this, MakeTagWhitelist(user, msg.GetTags())));
+}
+
+const ClientProtocol::SerializedMessage& ClientProtocol::Message::GetSerialized(const SerializedInfo& serializeinfo) const
+{
+ // First check if the serialized line they're asking for is in the cache
+ for (SerializedList::const_iterator i = serlist.begin(); i != serlist.end(); ++i)
+ {
+ const SerializedInfo& curr = i->first;
+ if (curr == serializeinfo)
+ return i->second;
+ }
+
+ // Not cached, generate it and put it in the cache for later use
+ serlist.push_back(std::make_pair(serializeinfo, serializeinfo.serializer->Serialize(*this, serializeinfo.tagwl)));
+ return serlist.back().second;
+}
+
+void ClientProtocol::Event::GetMessagesForUser(LocalUser* user, MessageList& messagelist)
+{
+ if (!eventinit_done)
+ {
+ eventinit_done = true;
+ FOREACH_MOD_CUSTOM(*event, EventHook, OnEventInit, (*this));
+ }
+
+ // Most of the time there's only a single message but in rare cases there are more
+ if (initialmsg)
+ messagelist.assign(1, initialmsg);
+ else
+ messagelist = *initialmsglist;
+
+ // Let modules modify the message list
+ ModResult res;
+ FIRST_MOD_RESULT_CUSTOM(*event, EventHook, OnPreEventSend, res, (user, *this, messagelist));
+ if (res == MOD_RES_DENY)
+ messagelist.clear();
+}
diff --git a/src/command_parse.cpp b/src/command_parse.cpp
index c133c475e..503630d53 100644
--- a/src/command_parse.cpp
+++ b/src/command_parse.cpp
@@ -93,7 +93,8 @@ bool CommandParser::LoopCall(User* user, Command* handler, const CommandBase::Pa
new_parameters[extra] = item;
}
- CmdResult result = handler->Handle(user, new_parameters);
+ CommandBase::Params params(new_parameters, parameters.GetTags());
+ CmdResult result = handler->Handle(user, params);
if (localuser)
{
// Run the OnPostCommand hook with the last parameter (original line) being empty
@@ -152,14 +153,16 @@ CmdResult CommandParser::CallHandler(const std::string& commandname, const Comma
{
if (cmd)
*cmd = n->second;
- return n->second->Handle(user, parameters);
+
+ ClientProtocol::TagMap tags;
+ return n->second->Handle(user, CommandBase::Params(parameters, tags));
}
}
}
return CMD_INVALID;
}
-void CommandParser::ProcessCommand(LocalUser* user, std::string& command, Command::Params& command_p)
+void CommandParser::ProcessCommand(LocalUser* user, std::string& command, CommandBase::Params& command_p)
{
/* find the command, check it exists */
Command* handler = GetHandler(command);
@@ -369,44 +372,14 @@ void Command::RegisterService()
void CommandParser::ProcessBuffer(LocalUser* user, const std::string& buffer)
{
- size_t start = buffer.find_first_not_of(" ");
- if (start == std::string::npos)
- {
- // Discourage the user from flooding the server.
- user->CommandFloodPenalty += 2000;
+ ClientProtocol::ParseOutput parseoutput;
+ if (!user->serializer->Parse(user, buffer, parseoutput))
return;
- }
-
- ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I %s", user->uuid.c_str(), buffer.c_str());
-
- irc::tokenstream tokens(buffer, start);
- std::string command;
- CommandBase::Params parameters;
-
- // Get the command name. This will always exist because of the check
- // at the start of the function.
- tokens.GetMiddle(command);
- // If this exists then the client sent a prefix as part of their
- // message. Section 2.3 of RFC 1459 technically says we should only
- // allow the nick of the client here but in practise everyone just
- // ignores it so we will copy them.
- if (command[0] == ':' && !tokens.GetMiddle(command))
- {
- // Discourage the user from flooding the server.
- user->CommandFloodPenalty += 2000;
- return;
- }
-
- // We upper-case the command name to ensure consistency internally.
+ std::string& command = parseoutput.cmd;
std::transform(command.begin(), command.end(), command.begin(), ::toupper);
- // Build the parameter map. We intentionally do not respect the RFC 1459
- // thirteen parameter limit here.
- std::string parameter;
- while (tokens.GetTrailing(parameter))
- parameters.push_back(parameter);
-
+ CommandBase::Params parameters(parseoutput.params, parseoutput.tags);
ProcessCommand(user, command, parameters);
}
@@ -425,7 +398,7 @@ CommandParser::CommandParser()
{
}
-std::string CommandParser::TranslateUIDs(const std::vector<TranslateType>& to, const std::vector<std::string>& source, bool prefix_final, CommandBase* custom_translator)
+std::string CommandParser::TranslateUIDs(const std::vector<TranslateType>& to, const CommandBase::Params& source, bool prefix_final, CommandBase* custom_translator)
{
std::vector<TranslateType>::const_iterator types = to.begin();
std::string dest;
diff --git a/src/configreader.cpp b/src/configreader.cpp
index 52d349f5c..6c7cb492a 100644
--- a/src/configreader.cpp
+++ b/src/configreader.cpp
@@ -415,7 +415,6 @@ void ServerConfig::Fill()
HideULineKills = security->getBool("hideulinekills");
GenericOper = security->getBool("genericoper");
SyntaxHints = options->getBool("syntaxhints");
- CycleHostsFromUser = options->getBool("cyclehostsfromuser");
FullHostInTopic = options->getBool("hostintopic");
MaxTargets = security->getUInt("maxtargets", 20, 1, 31);
DefaultModes = options->getString("defaultmodes", "not");
diff --git a/src/coremods/core_channel/cmd_invite.cpp b/src/coremods/core_channel/cmd_invite.cpp
index 89a2f6b69..1b480aa20 100644
--- a/src/coremods/core_channel/cmd_invite.cpp
+++ b/src/coremods/core_channel/cmd_invite.cpp
@@ -114,10 +114,12 @@ CmdResult CommandInvite::Handle(User* user, const Params& parameters)
}
}
- if (IS_LOCAL(u))
+ LocalUser* const localtargetuser = IS_LOCAL(u);
+ if (localtargetuser)
{
- invapi.Create(IS_LOCAL(u), c, timeout);
- u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
+ invapi.Create(localtargetuser, c, timeout);
+ ClientProtocol::Messages::Invite invitemsg(user, localtargetuser, c);
+ localtargetuser->Send(ServerInstance->GetRFCEvents().invite, invitemsg);
}
if (IS_LOCAL(user))
@@ -156,7 +158,11 @@ CmdResult CommandInvite::Handle(User* user, const Params& parameters)
FOREACH_MOD(OnUserInvite, (user, u, c, timeout, minrank, excepts));
if (announceinvites != Invite::ANNOUNCE_NONE)
- c->WriteAllExcept(user, true, prefix, excepts, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str());
+ {
+ excepts.insert(user);
+ ClientProtocol::Messages::Privmsg privmsg(ServerInstance->FakeClient, c, InspIRCd::Format("*** %s invited %s into the channel", user->nick.c_str(), u->nick.c_str()), MSG_NOTICE);
+ c->Write(ServerInstance->GetRFCEvents().privmsg, privmsg, prefix, excepts);
+ }
}
else if (IS_LOCAL(user))
{
diff --git a/src/coremods/core_channel/core_channel.cpp b/src/coremods/core_channel/core_channel.cpp
index ccf4d1a6e..4e49ba2b4 100644
--- a/src/coremods/core_channel/core_channel.cpp
+++ b/src/coremods/core_channel/core_channel.cpp
@@ -22,6 +22,71 @@
#include "invite.h"
#include "listmode.h"
+namespace
+{
+/** Hook that sends a MODE after a JOIN if the user in the JOIN has some modes prefix set.
+ * This happens e.g. when modules such as operprefix explicitly set prefix modes on the joining
+ * user, or when a member with prefix modes does a host cycle.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+ ClientProtocol::Messages::Mode modemsg;
+ Modes::ChangeList modechangelist;
+ const User* joininguser;
+
+ public:
+ /** If true, MODE changes after JOIN will be sourced from the user, rather than the server
+ */
+ bool modefromuser;
+
+ JoinHook(Module* mod)
+ : ClientProtocol::EventHook(mod, "JOIN")
+ {
+ }
+
+ void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
+ {
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+ const Membership& memb = *join.GetMember();
+
+ modechangelist.clear();
+ for (std::string::const_iterator i = memb.modes.begin(); i != memb.modes.end(); ++i)
+ {
+ PrefixMode* const pm = ServerInstance->Modes.FindPrefixMode(*i);
+ if (!pm)
+ continue; // Shouldn't happen
+ modechangelist.push_add(pm, memb.user->nick);
+ }
+
+ if (modechangelist.empty())
+ {
+ // Member got no modes on join
+ joininguser = NULL;
+ return;
+ }
+
+ joininguser = memb.user;
+
+ // Prepare a mode protocol event that we can append to the message list in OnPreEventSend()
+ modemsg.SetParams(memb.chan, NULL, modechangelist);
+ if (modefromuser)
+ modemsg.SetSource(join);
+ else
+ modemsg.SetSourceUser(ServerInstance->FakeClient);
+ }
+
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
+ {
+ // If joininguser is NULL then they didn't get any modes on join, skip.
+ // Also don't show their own modes to them, they get that in the NAMES list not via MODE.
+ if ((joininguser) && (user != joininguser))
+ messagelist.push_back(&modemsg);
+ return MOD_RES_PASSTHRU;
+ }
+};
+
+}
+
class CoreModChannel : public Module, public CheckExemption::EventListener
{
Invite::APIImpl invapi;
@@ -30,6 +95,8 @@ class CoreModChannel : public Module, public CheckExemption::EventListener
CommandKick cmdkick;
CommandNames cmdnames;
CommandTopic cmdtopic;
+ Events::ModuleEventProvider evprov;
+ JoinHook joinhook;
ModeChannelBan banmode;
SimpleChannelModeHandler inviteonlymode;
@@ -62,6 +129,8 @@ class CoreModChannel : public Module, public CheckExemption::EventListener
, cmdkick(this)
, cmdnames(this)
, cmdtopic(this)
+ , evprov(this, "event/channel")
+ , joinhook(this)
, banmode(this)
, inviteonlymode(this, "inviteonly", 'i')
, keymode(this)
@@ -88,6 +157,8 @@ class CoreModChannel : public Module, public CheckExemption::EventListener
ServerInstance->Modules.Detach(events[i], this);
}
+ joinhook.modefromuser = optionstag->getBool("cyclehostsfromuser");
+
std::string current;
irc::spacesepstream defaultstream(optionstag->getString("exemptchanops"));
insp::flat_map<std::string, char> exempts;
diff --git a/src/coremods/core_oper/cmd_die.cpp b/src/coremods/core_oper/cmd_die.cpp
index d10732952..b25fe2407 100644
--- a/src/coremods/core_oper/cmd_die.cpp
+++ b/src/coremods/core_oper/cmd_die.cpp
@@ -39,7 +39,8 @@ static void QuitAll()
void DieRestart::SendError(const std::string& message)
{
- const std::string unregline = "ERROR :" + message;
+ ClientProtocol::Messages::Error errormsg(message);
+ ClientProtocol::Event errorevent(ServerInstance->GetRFCEvents().error, errormsg);
const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
@@ -51,7 +52,7 @@ void DieRestart::SendError(const std::string& message)
else
{
// Unregistered connections receive ERROR, not a NOTICE
- user->Write(unregline);
+ user->Send(errorevent);
}
}
}
diff --git a/src/coremods/core_oper/cmd_kill.cpp b/src/coremods/core_oper/cmd_kill.cpp
index fd6729f53..5572e5789 100644
--- a/src/coremods/core_oper/cmd_kill.cpp
+++ b/src/coremods/core_oper/cmd_kill.cpp
@@ -24,12 +24,28 @@
CommandKill::CommandKill(Module* parent)
: Command(parent, "KILL", 2, 2)
+ , protoev(parent, name)
{
flags_needed = 'o';
syntax = "<nickname> <reason>";
TRANSLATE2(TR_CUSTOM, TR_CUSTOM);
}
+class KillMessage : public ClientProtocol::Message
+{
+ public:
+ KillMessage(ClientProtocol::EventProvider& protoev, User* user, LocalUser* target, const std::string& text)
+ : ClientProtocol::Message("KILL", NULL)
+ {
+ if (ServerInstance->Config->HideKillsServer.empty())
+ SetSourceUser(user);
+ else
+ SetSource(ServerInstance->Config->HideKillsServer);
+
+ PushParamRef(target->nick);
+ PushParamRef(text);
+ }
+};
/** Handle /KILL
*/
@@ -100,10 +116,10 @@ CmdResult CommandKill::Handle(User* user, const Params& parameters)
if (IS_LOCAL(target))
{
- target->Write(":%s KILL %s :%s",
- ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(),
- target->nick.c_str(),
- parameters[1].c_str());
+ LocalUser* localu = IS_LOCAL(target);
+ KillMessage msg(protoev, user, localu, killreason);
+ ClientProtocol::Event killevent(protoev, msg);
+ localu->Send(killevent);
this->lastuuid.clear();
}
diff --git a/src/coremods/core_oper/core_oper.h b/src/coremods/core_oper/core_oper.h
index b069c34b2..db8c4161c 100644
--- a/src/coremods/core_oper/core_oper.h
+++ b/src/coremods/core_oper/core_oper.h
@@ -60,6 +60,7 @@ class CommandKill : public Command
{
std::string lastuuid;
std::string killreason;
+ ClientProtocol::EventProvider protoev;
public:
/** Constructor for kill.
@@ -67,8 +68,8 @@ class CommandKill : public Command
CommandKill(Module* parent);
/** Handle command.
- * @param parameters The parameters to the command
- * @param user The user issuing the command
+ * @param user User issuing the command
+ * @param parameters Parameters to the command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
@@ -87,8 +88,8 @@ class CommandOper : public SplitCommand
CommandOper(Module* parent);
/** Handle command.
- * @param parameters The parameters to the command
- * @param user The user issuing the command
+ * @param user User issuing the command
+ * @param parameters Parameters to the command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE;
@@ -104,8 +105,8 @@ class CommandRehash : public Command
CommandRehash(Module* parent);
/** Handle command.
- * @param parameters The parameters to the command
- * @param user The user issuing the command
+ * @param user User issuing the command
+ * @param parameters Parameters to the command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
@@ -121,8 +122,8 @@ class CommandRestart : public Command
CommandRestart(Module* parent);
/** Handle command.
- * @param parameters The parameters to the command
- * @param user The user issuing the command
+ * @param user User issuing the command
+ * @param parameters Parameters to the command
* @return A value from CmdResult to indicate command success or failure.
*/
CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE;
diff --git a/src/coremods/core_privmsg.cpp b/src/coremods/core_privmsg.cpp
index 6078f5297..2daeef3ad 100644
--- a/src/coremods/core_privmsg.cpp
+++ b/src/coremods/core_privmsg.cpp
@@ -21,11 +21,6 @@
#include "inspircd.h"
-namespace
-{
- const char* MessageTypeString[] = { "PRIVMSG", "NOTICE" };
-}
-
class MessageCommandBase : public Command
{
ChanModeReference moderatedmode;
@@ -35,12 +30,13 @@ class MessageCommandBase : public Command
* @param user User sending the message
* @param msg The message to send
* @param mt Type of the message (MSG_PRIVMSG or MSG_NOTICE)
+ * @param tags Message tags to include in the outgoing protocol message
*/
- static void SendAll(User* user, const std::string& msg, MessageType mt);
+ static void SendAll(User* user, const std::string& msg, MessageType mt, const ClientProtocol::TagMap& tags);
public:
MessageCommandBase(Module* parent, MessageType mt)
- : Command(parent, MessageTypeString[mt], 2, 2)
+ : Command(parent, ClientProtocol::Messages::Privmsg::CommandStrFromMsgType(mt), 2, 2)
, moderatedmode(parent, "moderated")
, noextmsgmode(parent, "noextmsg")
{
@@ -52,7 +48,7 @@ class MessageCommandBase : public Command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult HandleMessage(User* user, const CommandBase::Params& parameters, MessageType mt);
+ CmdResult HandleMessage(User* user, const Params& parameters, MessageType mt);
RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
@@ -64,18 +60,22 @@ class MessageCommandBase : public Command
}
};
-void MessageCommandBase::SendAll(User* user, const std::string& msg, MessageType mt)
+void MessageCommandBase::SendAll(User* user, const std::string& msg, MessageType mt, const ClientProtocol::TagMap& tags)
{
- const std::string message = ":" + user->GetFullHost() + " " + MessageTypeString[mt] + " $* :" + msg;
+ ClientProtocol::Messages::Privmsg message(ClientProtocol::Messages::Privmsg::nocopy, user, "$*", msg, mt);
+ message.AddTags(tags);
+ message.SetSideEffect(true);
+ ClientProtocol::Event messageevent(ServerInstance->GetRFCEvents().privmsg, message);
+
const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
if ((*i)->registered == REG_ALL)
- (*i)->Write(message);
+ (*i)->Send(messageevent);
}
}
-CmdResult MessageCommandBase::HandleMessage(User* user, const CommandBase::Params& parameters, MessageType mt)
+CmdResult MessageCommandBase::HandleMessage(User* user, const Params& parameters, MessageType mt)
{
User *dest;
Channel *chan;
@@ -94,7 +94,7 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const CommandBase::Param
std::string servername(parameters[0], 1);
MessageTarget msgtarget(&servername);
- MessageDetails msgdetails(mt, parameters[1]);
+ MessageDetails msgdetails(mt, parameters[1], parameters.GetTags());
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
@@ -107,7 +107,7 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const CommandBase::Param
FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
if (InspIRCd::Match(ServerInstance->Config->ServerName, servername, NULL))
{
- SendAll(user, msgdetails.text, mt);
+ SendAll(user, msgdetails.text, mt, msgdetails.tags_out);
}
FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
return CMD_SUCCESS;
@@ -153,7 +153,7 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const CommandBase::Param
}
MessageTarget msgtarget(chan, status);
- MessageDetails msgdetails(mt, parameters[1]);
+ MessageDetails msgdetails(mt, parameters[1], parameters.GetTags());
msgdetails.exemptions.insert(user);
ModResult MOD_RESULT;
@@ -173,14 +173,10 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const CommandBase::Param
FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
- if (status)
- {
- chan->WriteAllExcept(user, false, status, msgdetails.exemptions, "%s %c%s :%s", MessageTypeString[mt], status, chan->name.c_str(), msgdetails.text.c_str());
- }
- else
- {
- chan->WriteAllExcept(user, false, status, msgdetails.exemptions, "%s %s :%s", MessageTypeString[mt], chan->name.c_str(), msgdetails.text.c_str());
- }
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, chan, msgdetails.text, msgdetails.type, msgtarget.status);
+ privmsg.AddTags(msgdetails.tags_out);
+ privmsg.SetSideEffect(true);
+ chan->Write(ServerInstance->GetRFCEvents().privmsg, privmsg, msgtarget.status, msgdetails.exemptions);
FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
}
@@ -233,7 +229,8 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const CommandBase::Param
}
MessageTarget msgtarget(dest);
- MessageDetails msgdetails(mt, parameters[1]);
+ MessageDetails msgdetails(mt, parameters[1], parameters.GetTags());
+
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
@@ -245,10 +242,14 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const CommandBase::Param
FOREACH_MOD(OnUserMessage, (user, msgtarget, msgdetails));
- if (IS_LOCAL(dest))
+ LocalUser* const localtarget = IS_LOCAL(dest);
+ if (localtarget)
{
// direct write, same server
- dest->WriteFrom(user, "%s %s :%s", MessageTypeString[mt], dest->nick.c_str(), msgdetails.text.c_str());
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, localtarget->nick, msgdetails.text, mt);
+ privmsg.AddTags(msgdetails.tags_out);
+ privmsg.SetSideEffect(true);
+ localtarget->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
}
FOREACH_MOD(OnUserPostMessage, (user, msgtarget, msgdetails));
diff --git a/src/coremods/core_reloadmodule.cpp b/src/coremods/core_reloadmodule.cpp
index 23be33af8..383d574bf 100644
--- a/src/coremods/core_reloadmodule.cpp
+++ b/src/coremods/core_reloadmodule.cpp
@@ -24,18 +24,42 @@
#include "modules/reload.h"
static Events::ModuleEventProvider* reloadevprov;
+static ClientProtocol::Serializer* dummyserializer;
+
+class DummySerializer : public ClientProtocol::Serializer
+{
+ bool Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput) CXX11_OVERRIDE
+ {
+ return false;
+ }
+
+ ClientProtocol::SerializedMessage Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const CXX11_OVERRIDE
+ {
+ return ClientProtocol::SerializedMessage();
+ }
+
+ public:
+ DummySerializer(Module* mod)
+ : ClientProtocol::Serializer(mod, "dummy")
+ {
+ }
+};
class CommandReloadmodule : public Command
{
Events::ModuleEventProvider evprov;
+ DummySerializer dummyser;
+
public:
/** Constructor for reloadmodule.
*/
CommandReloadmodule(Module* parent)
: Command(parent, "RELOADMODULE", 1)
, evprov(parent, "event/reloadmodule")
+ , dummyser(parent)
{
reloadevprov = &evprov;
+ dummyserializer = &dummyser;
flags_needed = 'o';
syntax = "<modulename>";
}
@@ -62,6 +86,7 @@ class DataKeeper
{
ModeHandler* mh;
ExtensionItem* extitem;
+ ClientProtocol::Serializer* serializer;
};
ProviderInfo(ModeHandler* mode)
@@ -75,6 +100,12 @@ class DataKeeper
, extitem(ei)
{
}
+
+ ProviderInfo(ClientProtocol::Serializer* ser)
+ : itemname(ser->name)
+ , serializer(ser)
+ {
+ }
};
struct InstanceData
@@ -143,7 +174,17 @@ class DataKeeper
};
// Data saved for each user
- typedef OwnedModesExts UserData;
+ struct UserData : public OwnedModesExts
+ {
+ static const size_t UNUSED_INDEX = (size_t)-1;
+ size_t serializerindex;
+
+ UserData(User* user, size_t serializeridx)
+ : OwnedModesExts(user->uuid)
+ , serializerindex(serializeridx)
+ {
+ }
+ };
/** Module being reloaded
*/
@@ -157,6 +198,10 @@ class DataKeeper
*/
std::vector<ProviderInfo> handledexts;
+ /** Stores all serializers provided by the module
+ */
+ std::vector<ProviderInfo> handledserializers;
+
/** Stores all of the module data related to users
*/
std::vector<UserData> userdatalist;
@@ -172,6 +217,14 @@ class DataKeeper
void SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdatalist);
void SaveMemberData(Channel* chan, std::vector<ChanData::MemberData>& memberdatalist);
static void SaveListModes(Channel* chan, ListModeBase* lm, size_t index, ModesExts& currdata);
+ size_t SaveSerializer(User* user);
+
+ /** Get the index of a ProviderInfo representing the serializer in the handledserializers list.
+ * If the serializer is not already in the list it is added.
+ * @param serializer Serializer to get an index to.
+ * @return Index of the ProviderInfo representing the serializer.
+ */
+ size_t GetSerializerIndex(ClientProtocol::Serializer* serializer);
void CreateModeList(ModeType modetype);
void DoSaveUsers();
@@ -186,6 +239,10 @@ class DataKeeper
*/
void LinkModes(ModeType modetype);
+ /** Link previously saved serializer names to currently available Serializers
+ */
+ void LinkSerializers();
+
void DoRestoreUsers();
void DoRestoreChans();
void DoRestoreModules();
@@ -213,6 +270,15 @@ class DataKeeper
*/
void RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange);
+ /** Restore previously saved serializer on a User.
+ * Quit the user if the serializer cannot be restored.
+ * @param serializerindex Saved serializer index to restore.
+ * @param user User whose serializer to restore. If not local then calling this method is a no-op.
+ * @return True if the serializer didn't need restoring or was restored successfully.
+ * False if the serializer should have been restored but the required serializer is unavailable and the user was quit.
+ */
+ bool RestoreSerializer(size_t serializerindex, User* user);
+
/** Restore all modes and extensions of all members on a channel
* @param chan Channel whose members are being restored
* @param memberdata Data to restore
@@ -262,16 +328,44 @@ void DataKeeper::DoSaveUsers()
// Serialize all extensions attached to the User
SaveExtensions(user, currdata.extlist);
+ // Save serializer name if applicable and get an index to it
+ size_t serializerindex = SaveSerializer(user);
+
// Add to list if the user has any modes or extensions set that we are interested in, otherwise we don't
// have to do anything with this user when restoring
- if (!currdata.empty())
+ if ((!currdata.empty()) || (serializerindex != UserData::UNUSED_INDEX))
{
- userdatalist.push_back(UserData(user->uuid));
+ userdatalist.push_back(UserData(user, serializerindex));
userdatalist.back().swap(currdata);
}
}
}
+size_t DataKeeper::GetSerializerIndex(ClientProtocol::Serializer* serializer)
+{
+ for (size_t i = 0; i < handledserializers.size(); i++)
+ {
+ if (handledserializers[i].serializer == serializer)
+ return i;
+ }
+
+ handledserializers.push_back(ProviderInfo(serializer));
+ return handledserializers.size()-1;
+}
+
+size_t DataKeeper::SaveSerializer(User* user)
+{
+ LocalUser* const localuser = IS_LOCAL(user);
+ if ((!localuser) || (!localuser->serializer))
+ return UserData::UNUSED_INDEX;
+ if (localuser->serializer->creator != mod)
+ return UserData::UNUSED_INDEX;
+
+ const size_t serializerindex = GetSerializerIndex(localuser->serializer);
+ localuser->serializer = dummyserializer;
+ return serializerindex;
+}
+
void DataKeeper::SaveExtensions(Extensible* extensible, std::vector<InstanceData>& extdata)
{
const Extensible::ExtensibleStore& setexts = extensible->GetExtList();
@@ -456,6 +550,16 @@ void DataKeeper::LinkExtensions()
}
}
+void DataKeeper::LinkSerializers()
+{
+ for (std::vector<ProviderInfo>::iterator i = handledserializers.begin(); i != handledserializers.end(); ++i)
+ {
+ ProviderInfo& item = *i;
+ item.serializer = ServerInstance->Modules.FindDataService<ClientProtocol::Serializer>(item.itemname);
+ VerifyServiceProvider(item.serializer, "Serializer");
+ }
+}
+
void DataKeeper::Restore(Module* newmod)
{
this->mod = newmod;
@@ -464,6 +568,7 @@ void DataKeeper::Restore(Module* newmod)
LinkExtensions();
LinkModes(MODETYPE_USER);
LinkModes(MODETYPE_CHANNEL);
+ LinkSerializers();
// Restore
DoRestoreUsers();
@@ -505,6 +610,30 @@ void DataKeeper::RestoreModes(const std::vector<InstanceData>& list, ModeType mo
}
}
+bool DataKeeper::RestoreSerializer(size_t serializerindex, User* user)
+{
+ if (serializerindex == UserData::UNUSED_INDEX)
+ return true;
+
+ // The following checks are redundant
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (!localuser)
+ return true;
+ if (localuser->serializer != dummyserializer)
+ return true;
+
+ const ProviderInfo& provinfo = handledserializers[serializerindex];
+ if (!provinfo.serializer)
+ {
+ // Users cannot exist without a serializer
+ ServerInstance->Users.QuitUser(user, "Serializer lost in reload");
+ return false;
+ }
+
+ localuser->serializer = provinfo.serializer;
+ return true;
+}
+
void DataKeeper::DoRestoreUsers()
{
ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring user data");
@@ -520,6 +649,10 @@ void DataKeeper::DoRestoreUsers()
continue;
}
+ // Attempt to restore serializer first, if it fails it's a fatal error and RestoreSerializer() quits them
+ if (!RestoreSerializer(userdata.serializerindex, user))
+ continue;
+
RestoreObj(userdata, user, MODETYPE_USER, modechange);
ServerInstance->Modes.Process(ServerInstance->FakeClient, NULL, user, modechange, ModeParser::MODE_LOCALONLY);
modechange.clear();
diff --git a/src/coremods/core_serialize_rfc.cpp b/src/coremods/core_serialize_rfc.cpp
new file mode 100644
index 000000000..afa914f3d
--- /dev/null
+++ b/src/coremods/core_serialize_rfc.cpp
@@ -0,0 +1,222 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+
+#include "inspircd.h"
+
+class RFCSerializer : public ClientProtocol::Serializer
+{
+ /** Maximum size of the message tags portion of the message, including the `@` and the trailing space characters.
+ */
+ static const std::string::size_type MAX_MESSAGE_TAG_LENGTH = 512;
+
+ static void SerializeTags(const ClientProtocol::TagMap& tags, const ClientProtocol::TagSelection& tagwl, std::string& line);
+
+ public:
+ RFCSerializer(Module* mod)
+ : ClientProtocol::Serializer(mod, "rfc")
+ {
+ }
+
+ bool Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput) CXX11_OVERRIDE;
+ ClientProtocol::SerializedMessage Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const CXX11_OVERRIDE;
+};
+
+bool RFCSerializer::Parse(LocalUser* user, const std::string& line, ClientProtocol::ParseOutput& parseoutput)
+{
+ size_t start = line.find_first_not_of(" ");
+ if (start == std::string::npos)
+ {
+ // Discourage the user from flooding the server.
+ user->CommandFloodPenalty += 2000;
+ return false;
+ }
+
+ ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I %s", user->uuid.c_str(), line.c_str());
+
+ irc::tokenstream tokens(line, start);
+ std::string token;
+
+ // This will always exist because of the check at the start of the function.
+ tokens.GetMiddle(token);
+ if (token[0] == '@')
+ {
+ // Line begins with message tags, parse them.
+ std::string tagval;
+ irc::sepstream ss(token.substr(1), ';');
+ while (ss.GetToken(token))
+ {
+ // Two or more tags with the same key must not be sent, but if a client violates that we accept
+ // the first occurence of duplicate tags and ignore all later occurences.
+ //
+ // Another option is to reject the message entirely but there is no standard way of doing that.
+ const std::string::size_type p = token.find('=');
+ if (p != std::string::npos)
+ {
+ // Tag has a value
+ tagval.assign(token, p+1, std::string::npos);
+ token.erase(p);
+ }
+ else
+ tagval.clear();
+
+ HandleTag(user, token, tagval, parseoutput.tags);
+ }
+
+
+ // Try to read the prefix or command name.
+ if (!tokens.GetMiddle(token))
+ {
+ // Discourage the user from flooding the server.
+ user->CommandFloodPenalty += 2000;
+ return false;
+ }
+ }
+
+ if (token[0] == ':')
+ {
+ // If this exists then the client sent a prefix as part of their
+ // message. Section 2.3 of RFC 1459 technically says we should only
+ // allow the nick of the client here but in practise everyone just
+ // ignores it so we will copy them.
+
+ // Try to read the command name.
+ if (!tokens.GetMiddle(token))
+ {
+ // Discourage the user from flooding the server.
+ user->CommandFloodPenalty += 2000;
+ return false;
+ }
+ }
+
+ parseoutput.cmd.assign(token);
+
+ // Build the parameter map. We intentionally do not respect the RFC 1459
+ // thirteen parameter limit here.
+ while (tokens.GetTrailing(token))
+ parseoutput.params.push_back(token);
+
+ return true;
+}
+
+void RFCSerializer::SerializeTags(const ClientProtocol::TagMap& tags, const ClientProtocol::TagSelection& tagwl, std::string& line)
+{
+ char prefix = '@'; // First tag name is prefixed with a '@'
+ for (ClientProtocol::TagMap::const_iterator i = tags.begin(); i != tags.end(); ++i)
+ {
+ if (!tagwl.IsSelected(tags, i))
+ continue;
+
+ const std::string::size_type prevsize = line.size();
+ line.push_back(prefix);
+ prefix = ';'; // Remaining tags are prefixed with ';'
+ line.append(i->first);
+ const std::string& val = i->second.value;
+ if (!val.empty())
+ {
+ line.push_back('=');
+ line.append(val);
+ }
+
+ // The tags part of the message mustn't grow longer than what is allowed by the spec. If it does,
+ // remove last tag and stop adding more tags.
+ //
+ // One is subtracted from the limit before comparing because there must be a ' ' char after the last tag
+ // which also counts towards the limit.
+ if (line.size() > MAX_MESSAGE_TAG_LENGTH-1)
+ {
+ line.erase(prevsize);
+ break;
+ }
+ }
+
+ if (!line.empty())
+ line.push_back(' ');
+}
+
+ClientProtocol::SerializedMessage RFCSerializer::Serialize(const ClientProtocol::Message& msg, const ClientProtocol::TagSelection& tagwl) const
+{
+ std::string line;
+ SerializeTags(msg.GetTags(), tagwl, line);
+
+ // Save position for length calculation later
+ const std::string::size_type rfcmsg_begin = line.size();
+
+ if (msg.GetSource())
+ {
+ line.push_back(':');
+ line.append(*msg.GetSource());
+ line.push_back(' ');
+ }
+ line.append(msg.GetCommand());
+
+ const ClientProtocol::Message::ParamList& params = msg.GetParams();
+ if (!params.empty())
+ {
+ for (ClientProtocol::Message::ParamList::const_iterator i = params.begin(); i != params.end()-1; ++i)
+ {
+ const std::string& param = *i;
+ line.push_back(' ');
+ line.append(param);
+ }
+
+ line.append(" :", 2).append(params.back());
+ }
+
+ // Truncate if too long
+ std::string::size_type maxline = ServerInstance->Config->Limits.MaxLine - 2;
+ if (line.length() - rfcmsg_begin > maxline)
+ line.erase(rfcmsg_begin + maxline);
+
+ line.append("\r\n", 2);
+ return line;
+}
+
+class ModuleCoreRFCSerializer : public Module
+{
+ RFCSerializer rfcserializer;
+
+ public:
+ ModuleCoreRFCSerializer()
+ : rfcserializer(this)
+ {
+ }
+
+ void OnCleanup(ExtensionItem::ExtensibleType type, Extensible* item) CXX11_OVERRIDE
+ {
+ if (type != ExtensionItem::EXT_USER)
+ return;
+
+ LocalUser* const user = IS_LOCAL(static_cast<User*>(item));
+ if ((user) && (user->serializer == &rfcserializer))
+ ServerInstance->Users.QuitUser(user, "Protocol serializer module unloading");
+ }
+
+ void OnUserInit(LocalUser* user) CXX11_OVERRIDE
+ {
+ if (!user->serializer)
+ user->serializer = &rfcserializer;
+ }
+
+ Version GetVersion()
+ {
+ return Version("RFC client protocol serializer and unserializer", VF_CORE|VF_VENDOR);
+ }
+};
+
+MODULE_INIT(ModuleCoreRFCSerializer)
diff --git a/src/coremods/core_user/core_user.cpp b/src/coremods/core_user/core_user.cpp
index 6e4e547c1..e4e7a5056 100644
--- a/src/coremods/core_user/core_user.cpp
+++ b/src/coremods/core_user/core_user.cpp
@@ -57,13 +57,13 @@ class CommandPass : public SplitCommand
/** Handle /PING.
*/
-class CommandPing : public Command
+class CommandPing : public SplitCommand
{
public:
/** Constructor for ping.
*/
CommandPing(Module* parent)
- : Command(parent, "PING", 1, 2)
+ : SplitCommand(parent, "PING", 1, 2)
{
syntax = "<servername> [:<servername>]";
}
@@ -73,9 +73,10 @@ class CommandPing : public Command
* @param user The user issuing the command
* @return A value from CmdResult to indicate command success or failure.
*/
- CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
{
- user->WriteServ("PONG %s :%s", ServerInstance->Config->ServerName.c_str(), parameters[0].c_str());
+ ClientProtocol::Messages::Pong pong(parameters[0]);
+ user->Send(ServerInstance->GetRFCEvents().pong, pong);
return CMD_SUCCESS;
}
};
diff --git a/src/coremods/core_wallops.cpp b/src/coremods/core_wallops.cpp
index 856fcea74..26a00a47d 100644
--- a/src/coremods/core_wallops.cpp
+++ b/src/coremods/core_wallops.cpp
@@ -25,6 +25,7 @@
class CommandWallops : public Command
{
SimpleUserModeHandler wallopsmode;
+ ClientProtocol::EventProvider protoevprov;
public:
/** Constructor for wallops.
@@ -32,6 +33,7 @@ class CommandWallops : public Command
CommandWallops(Module* parent)
: Command(parent, "WALLOPS", 1, 1)
, wallopsmode(parent, "wallops", 'w')
+ , protoevprov(parent, name)
{
flags_needed = 'o';
syntax = "<any-text>";
@@ -52,15 +54,16 @@ class CommandWallops : public Command
CmdResult CommandWallops::Handle(User* user, const Params& parameters)
{
- std::string wallop("WALLOPS :");
- wallop.append(parameters[0]);
+ ClientProtocol::Message msg("WALLOPS", user);
+ msg.PushParamRef(parameters[0]);
+ ClientProtocol::Event wallopsevent(protoevprov, msg);
const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
{
- User* t = *i;
- if (t->IsModeSet(wallopsmode))
- t->WriteFrom(user, wallop);
+ LocalUser* curr = *i;
+ if (curr->IsModeSet(wallopsmode))
+ curr->Send(wallopsevent);
}
return CMD_SUCCESS;
diff --git a/src/inspircd.cpp b/src/inspircd.cpp
index bb27c718e..aa3fb9612 100644
--- a/src/inspircd.cpp
+++ b/src/inspircd.cpp
@@ -271,6 +271,16 @@ InspIRCd::InspIRCd(int argc, char** argv) :
srandom(TIME.tv_nsec ^ TIME.tv_sec);
#endif
+ {
+ ServiceProvider* provs[] =
+ {
+ &rfcevents.numeric, &rfcevents.join, &rfcevents.part, &rfcevents.kick, &rfcevents.quit, &rfcevents.nick,
+ &rfcevents.mode, &rfcevents.topic, &rfcevents.privmsg, &rfcevents.invite, &rfcevents.ping, &rfcevents.pong,
+ &rfcevents.error
+ };
+ Modules.AddServices(provs, sizeof(provs)/sizeof(provs[0]));
+ }
+
struct option longopts[] =
{
{ "nofork", no_argument, &do_nofork, 1 },
diff --git a/src/mode.cpp b/src/mode.cpp
index 9d17f5be8..71fce24d8 100644
--- a/src/mode.cpp
+++ b/src/mode.cpp
@@ -438,14 +438,9 @@ void ModeParser::Process(User* user, Channel* targetchannel, User* targetuser, M
unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags, unsigned int beginindex)
{
- LastParse.clear();
LastChangeList.clear();
unsigned int modes_processed = 0;
- std::string output_mode;
- std::string output_parameters;
-
- char output_pm = '\0'; // current output state, '+' or '-'
Modes::ChangeList::List& list = changelist.getlist();
for (Modes::ChangeList::List::iterator i = list.begin()+beginindex; i != list.end(); ++i)
{
@@ -478,43 +473,30 @@ unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User*
if (ma != MODEACTION_ALLOW)
continue;
- char needed_pm = item.adding ? '+' : '-';
- if (needed_pm != output_pm)
- {
- output_pm = needed_pm;
- output_mode.append(1, output_pm);
- }
- output_mode.push_back(mh->GetModeChar());
-
- if (!item.param.empty())
- {
- output_parameters.push_back(' ');
- output_parameters.append(item.param);
- }
LastChangeList.push(mh, item.adding, item.param);
- if ((output_mode.length() + output_parameters.length() > 450)
- || (output_mode.length() > 100)
- || (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes))
+ if (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes)
{
/* mode sequence is getting too long */
break;
}
}
- if (!output_mode.empty())
+ if (!LastChangeList.empty())
{
- LastParse = targetchannel ? targetchannel->name : targetuser->nick;
- LastParse.append(" ");
- LastParse.append(output_mode);
- LastParse.append(output_parameters);
-
+ ClientProtocol::Events::Mode modeevent(user, targetchannel, targetuser, LastChangeList);
if (targetchannel)
- targetchannel->WriteChannel(user, "MODE " + LastParse);
+ {
+ targetchannel->Write(modeevent);
+ }
else
- targetuser->WriteFrom(user, "MODE " + LastParse);
+ {
+ LocalUser* localtarget = IS_LOCAL(targetuser);
+ if (localtarget)
+ localtarget->Send(modeevent);
+ }
- FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags, output_mode));
+ FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags));
}
return modes_processed;
diff --git a/src/modules.cpp b/src/modules.cpp
index 8f2d874dc..7912fb569 100644
--- a/src/modules.cpp
+++ b/src/modules.cpp
@@ -79,7 +79,7 @@ void Module::OnUserPart(Membership*, std::string&, CUList&) { DetachEvent(I_OnU
void Module::OnPreRehash(User*, const std::string&) { DetachEvent(I_OnPreRehash); }
void Module::OnModuleRehash(User*, const std::string&) { DetachEvent(I_OnModuleRehash); }
ModResult Module::OnUserPreJoin(LocalUser*, Channel*, const std::string&, std::string&, const std::string&) { DetachEvent(I_OnUserPreJoin); return MOD_RES_PASSTHRU; }
-void Module::OnMode(User*, User*, Channel*, const Modes::ChangeList&, ModeParser::ModeProcessFlag, const std::string&) { DetachEvent(I_OnMode); }
+void Module::OnMode(User*, User*, Channel*, const Modes::ChangeList&, ModeParser::ModeProcessFlag) { DetachEvent(I_OnMode); }
void Module::OnOper(User*, const std::string&) { DetachEvent(I_OnOper); }
void Module::OnPostOper(User*, const std::string&, const std::string &) { DetachEvent(I_OnPostOper); }
void Module::OnPostDeoper(User*) { DetachEvent(I_OnPostDeoper); }
@@ -138,6 +138,7 @@ ModResult Module::OnSendWhoLine(User*, const std::vector<std::string>&, User*, M
void Module::OnSetUserIP(LocalUser*) { DetachEvent(I_OnSetUserIP); }
void Module::OnServiceAdd(ServiceProvider&) { DetachEvent(I_OnServiceAdd); }
void Module::OnServiceDel(ServiceProvider&) { DetachEvent(I_OnServiceDel); }
+ModResult Module::OnUserWrite(LocalUser*, ClientProtocol::Message&) { DetachEvent(I_OnUserWrite); return MOD_RES_PASSTHRU; }
#ifdef INSPIRCD_ENABLE_TESTSUITE
void Module::OnRunTestSuite() { }
diff --git a/src/modules/m_alias.cpp b/src/modules/m_alias.cpp
index 76ccc6ebc..75ab57e94 100644
--- a/src/modules/m_alias.cpp
+++ b/src/modules/m_alias.cpp
@@ -129,7 +129,7 @@ class ModuleAlias : public Module
return word;
}
- std::string CreateRFCMessage(const std::string& command, Command::Params& parameters)
+ std::string CreateRFCMessage(const std::string& command, CommandBase::Params& parameters)
{
std::string message(command);
for (CommandBase::Params::const_iterator iter = parameters.begin(); iter != parameters.end();)
diff --git a/src/modules/m_auditorium.cpp b/src/modules/m_auditorium.cpp
index 7acbd2fff..8485f1d7a 100644
--- a/src/modules/m_auditorium.cpp
+++ b/src/modules/m_auditorium.cpp
@@ -32,6 +32,29 @@ class AuditoriumMode : public SimpleChannelModeHandler
}
};
+class ModuleAuditorium;
+
+namespace
+{
+
+/** Hook handler for join client protocol events.
+ * This allows us to block join protocol events completely, including all associated messages (e.g. MODE, away-notify AWAY).
+ * This is not the same as OnUserJoin() because that runs only when a real join happens but this runs also when a module
+ * such as delayjoin or hostcycle generates a join.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+ ModuleAuditorium* const parentmod;
+ bool active;
+
+ public:
+ JoinHook(ModuleAuditorium* mod);
+ void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE;
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE;
+};
+
+}
+
class ModuleAuditorium : public Module
{
CheckExemption::EventProvider exemptionprov;
@@ -39,11 +62,13 @@ class ModuleAuditorium : public Module
bool OpsVisible;
bool OpsCanSee;
bool OperCanSee;
+ JoinHook joinhook;
public:
ModuleAuditorium()
: exemptionprov(this)
, aum(this)
+ , joinhook(this)
{
}
@@ -115,11 +140,6 @@ class ModuleAuditorium : public Module
}
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
- {
- BuildExcept(memb, excepts);
- }
-
void OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) CXX11_OVERRIDE
{
BuildExcept(memb, excepts);
@@ -165,4 +185,25 @@ class ModuleAuditorium : public Module
}
};
+JoinHook::JoinHook(ModuleAuditorium* mod)
+ : ClientProtocol::EventHook(mod, "JOIN", 10)
+ , parentmod(mod)
+{
+}
+
+void JoinHook::OnEventInit(const ClientProtocol::Event& ev)
+{
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+ active = !parentmod->IsVisible(join.GetMember());
+}
+
+ModResult JoinHook::OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist)
+{
+ if (!active)
+ return MOD_RES_PASSTHRU;
+
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+ return ((parentmod->CanSee(user, join.GetMember())) ? MOD_RES_PASSTHRU : MOD_RES_DENY);
+}
+
MODULE_INIT(ModuleAuditorium)
diff --git a/src/modules/m_cap.cpp b/src/modules/m_cap.cpp
index 80e70d3e5..922061757 100644
--- a/src/modules/m_cap.cpp
+++ b/src/modules/m_cap.cpp
@@ -341,16 +341,35 @@ void Cap::ExtItem::unserialize(SerializeFormat format, Extensible* container, co
managerimpl->HandleReq(user, caplist);
}
+class CapMessage : public Cap::MessageBase
+{
+ public:
+ CapMessage(LocalUser* user, const std::string& subcmd, const std::string& result)
+ : Cap::MessageBase(subcmd)
+ {
+ SetUser(user);
+ PushParamRef(result);
+ }
+};
+
class CommandCap : public SplitCommand
{
Events::ModuleEventProvider evprov;
Cap::ManagerImpl manager;
+ ClientProtocol::EventProvider protoevprov;
- static void DisplayResult(LocalUser* user, std::string& result)
+ void DisplayResult(LocalUser* user, const std::string& subcmd, std::string& result)
{
if (*result.rbegin() == ' ')
result.erase(result.end()-1);
- user->WriteCommand("CAP", result);
+ DisplayResult2(user, subcmd, result);
+ }
+
+ void DisplayResult2(LocalUser* user, const std::string& subcmd, const std::string& result)
+ {
+ CapMessage msg(user, subcmd, result);
+ ClientProtocol::Event ev(protoevprov, msg);
+ user->Send(ev);
}
public:
@@ -360,6 +379,7 @@ class CommandCap : public SplitCommand
: SplitCommand(mod, "CAP", 1)
, evprov(mod, "event/cap")
, manager(mod, evprov)
+ , protoevprov(mod, name)
, holdext("cap_hold", ExtensionItem::EXT_USER, mod)
{
works_before_reg = true;
@@ -378,9 +398,8 @@ class CommandCap : public SplitCommand
if (parameters.size() < 2)
return CMD_FAILURE;
- std::string result = (manager.HandleReq(user, parameters[1]) ? "ACK :" : "NAK :");
- result.append(parameters[1]);
- user->WriteCommand("CAP", result);
+ const std::string replysubcmd = (manager.HandleReq(user, parameters[1]) ? "ACK" : "NAK");
+ DisplayResult2(user, replysubcmd, parameters[1]);
}
else if (subcommand == "END")
{
@@ -392,16 +411,16 @@ class CommandCap : public SplitCommand
if ((is_ls) && (parameters.size() > 1) && (parameters[1] == "302"))
manager.Set302Protocol(user);
- std::string result = subcommand + " :";
+ std::string result;
// Show values only if supports v3.2 and doing LS
manager.HandleList(result, user, is_ls, ((is_ls) && (manager.GetProtocol(user) != Cap::CAP_LEGACY)));
- DisplayResult(user, result);
+ DisplayResult(user, subcommand, result);
}
else if ((subcommand == "CLEAR") && (manager.GetProtocol(user) == Cap::CAP_LEGACY))
{
- std::string result = "ACK :";
+ std::string result;
manager.HandleClear(user, result);
- DisplayResult(user, result);
+ DisplayResult(user, "ACK", result);
}
else
{
diff --git a/src/modules/m_chanhistory.cpp b/src/modules/m_chanhistory.cpp
index 081731126..0c3945346 100644
--- a/src/modules/m_chanhistory.cpp
+++ b/src/modules/m_chanhistory.cpp
@@ -22,8 +22,15 @@
struct HistoryItem
{
time_t ts;
- std::string line;
- HistoryItem(const std::string& Line) : ts(ServerInstance->Time()), line(Line) {}
+ std::string text;
+ std::string sourcemask;
+
+ HistoryItem(User* source, const std::string& Text)
+ : ts(ServerInstance->Time())
+ , text(Text)
+ , sourcemask(source->GetFullHost())
+ {
+ }
};
struct HistoryList
@@ -136,8 +143,7 @@ class ModuleChanHistory : public Module
HistoryList* list = m.ext.get(c);
if (list)
{
- const std::string line = ":" + user->GetFullHost() + " PRIVMSG " + c->name + " :" + details.text;
- list->lines.push_back(HistoryItem(line));
+ list->lines.push_back(HistoryItem(user, details.text));
if (list->lines.size() > list->maxlen)
list->lines.pop_front();
}
@@ -146,7 +152,8 @@ class ModuleChanHistory : public Module
void OnPostJoin(Membership* memb) CXX11_OVERRIDE
{
- if (IS_REMOTE(memb->user))
+ LocalUser* localuser = IS_LOCAL(memb->user);
+ if (!localuser)
return;
if (memb->user->IsModeSet(botmode) && !dobots)
@@ -169,8 +176,12 @@ class ModuleChanHistory : public Module
for(std::deque<HistoryItem>::iterator i = list->lines.begin(); i != list->lines.end(); ++i)
{
- if (i->ts >= mintime)
- memb->user->Write(i->line);
+ const HistoryItem& item = *i;
+ if (item.ts >= mintime)
+ {
+ ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, item.sourcemask, memb->chan, item.text);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
+ }
}
}
diff --git a/src/modules/m_chanlog.cpp b/src/modules/m_chanlog.cpp
index f618a539c..85e7ca2eb 100644
--- a/src/modules/m_chanlog.cpp
+++ b/src/modules/m_chanlog.cpp
@@ -70,7 +70,8 @@ class ModuleChanLog : public Module
Channel *c = ServerInstance->FindChan(it->second);
if (c)
{
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "PRIVMSG %s :%s", c->name.c_str(), snotice.c_str());
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->Config->ServerName, c, snotice);
+ c->Write(ServerInstance->GetRFCEvents().privmsg, privmsg);
ServerInstance->PI->SendMessage(c, 0, snotice);
}
}
diff --git a/src/modules/m_cloaking.cpp b/src/modules/m_cloaking.cpp
index c277759d1..b9ff085c3 100644
--- a/src/modules/m_cloaking.cpp
+++ b/src/modules/m_cloaking.cpp
@@ -313,7 +313,13 @@ class ModuleCloaking : public Module
if (u->IsModeSet(cu) && !cu.active)
{
u->SetMode(cu, false);
- u->WriteCommand("MODE", "-" + ConvToStr(cu.GetModeChar()));
+
+ if (!IS_LOCAL(u))
+ return;
+ Modes::ChangeList modechangelist;
+ modechangelist.push_remove(&cu);
+ ClientProtocol::Events::Mode modeevent(ServerInstance->FakeClient, NULL, u, modechangelist);
+ static_cast<LocalUser*>(u)->Send(modeevent);
}
cu.active = false;
}
diff --git a/src/modules/m_conn_waitpong.cpp b/src/modules/m_conn_waitpong.cpp
index b4441c88c..f2e9590c8 100644
--- a/src/modules/m_conn_waitpong.cpp
+++ b/src/modules/m_conn_waitpong.cpp
@@ -46,8 +46,10 @@ class ModuleWaitPong : public Module
ModResult OnUserRegister(LocalUser* user) CXX11_OVERRIDE
{
std::string pingrpl = ServerInstance->GenRandomStr(10);
-
- user->Write("PING :%s", pingrpl.c_str());
+ {
+ ClientProtocol::Messages::Ping pingmsg(pingrpl);
+ user->Send(ServerInstance->GetRFCEvents().ping, pingmsg);
+ }
if(sendsnotice)
user->WriteNotice("*** If you are having problems connecting due to ping timeouts, please type /quote PONG " + pingrpl + " or /raw PONG " + pingrpl + " now.");
diff --git a/src/modules/m_delayjoin.cpp b/src/modules/m_delayjoin.cpp
index f9cd837d7..7c557eb35 100644
--- a/src/modules/m_delayjoin.cpp
+++ b/src/modules/m_delayjoin.cpp
@@ -33,14 +33,50 @@ class DelayJoinMode : public ModeHandler
ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string& parameter, bool adding) CXX11_OVERRIDE;
};
+
+namespace
+{
+
+/** Hook handler for join client protocol events.
+ * This allows us to block join protocol events completely, including all associated messages (e.g. MODE, away-notify AWAY).
+ * This is not the same as OnUserJoin() because that runs only when a real join happens but this runs also when a module
+ * such as hostcycle generates a join.
+ */
+class JoinHook : public ClientProtocol::EventHook
+{
+ const LocalIntExt& unjoined;
+
+ public:
+ JoinHook(Module* mod, const LocalIntExt& unjoinedref)
+ : ClientProtocol::EventHook(mod, "JOIN", 10)
+ , unjoined(unjoinedref)
+ {
+ }
+
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
+ {
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+ const User* const u = join.GetMember()->user;
+ if ((unjoined.get(u)) && (u != user))
+ return MOD_RES_DENY;
+ return MOD_RES_PASSTHRU;
+ }
+};
+
+}
+
class ModuleDelayJoin : public Module
{
DelayJoinMode djm;
+ void RevealUser(User* user, Channel* chan);
public:
LocalIntExt unjoined;
+ JoinHook joinhook;
+
ModuleDelayJoin()
: djm(this)
, unjoined("delayjoin", ExtensionItem::EXT_MEMBERSHIP, this)
+ , joinhook(this, unjoined)
{
}
@@ -68,7 +104,7 @@ ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channe
* they remain permanently invisible on this channel!
*/
MessageTarget msgtarget(channel, 0);
- MessageDetails msgdetails(MSG_PRIVMSG, "");
+ MessageDetails msgdetails(MSG_PRIVMSG, "", ClientProtocol::TagMap());
const Channel::MemberMap& users = channel->GetUsers();
for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
{
@@ -111,10 +147,7 @@ static void populate(CUList& except, Membership* memb)
void ModuleDelayJoin::OnUserJoin(Membership* memb, bool sync, bool created, CUList& except)
{
if (memb->chan->IsModeSet(djm))
- {
unjoined.set(memb, 1);
- populate(except, memb);
- }
}
void ModuleDelayJoin::OnUserPart(Membership* memb, std::string &partmessage, CUList& except)
@@ -147,20 +180,20 @@ void ModuleDelayJoin::OnUserMessage(User* user, const MessageTarget& target, con
return;
Channel* channel = target.Get<Channel>();
+ RevealUser(user, channel);
+}
- Membership* memb = channel->GetUser(user);
+void ModuleDelayJoin::RevealUser(User* user, Channel* chan)
+{
+ Membership* memb = chan->GetUser(user);
if (!memb || !unjoined.set(memb, 0))
return;
/* Display the join to everyone else (the user who joined got it earlier) */
- channel->WriteAllExceptSender(user, false, 0, "JOIN %s", channel->name.c_str());
-
- std::string ms = memb->modes;
- for(unsigned int i=0; i < memb->modes.length(); i++)
- ms.append(" ").append(user->nick);
-
- if (ms.length() > 0)
- channel->WriteAllExceptSender(user, false, 0, "MODE %s +%s", channel->name.c_str(), ms.c_str());
+ CUList except_list;
+ except_list.insert(user);
+ ClientProtocol::Events::Join joinevent(memb);
+ chan->Write(joinevent, 0, except_list);
}
/* make the user visible if he receives any mode change */
@@ -182,9 +215,7 @@ ModResult ModuleDelayJoin::OnRawMode(User* user, Channel* channel, ModeHandler*
if (!dest)
return MOD_RES_PASSTHRU;
- Membership* memb = channel->GetUser(dest);
- if (memb && unjoined.set(memb, 0))
- channel->WriteAllExceptSender(dest, false, 0, "JOIN %s", channel->name.c_str());
+ RevealUser(dest, channel);
return MOD_RES_PASSTHRU;
}
diff --git a/src/modules/m_hostcycle.cpp b/src/modules/m_hostcycle.cpp
index 0f7405dcc..a3c81df6e 100644
--- a/src/modules/m_hostcycle.cpp
+++ b/src/modules/m_hostcycle.cpp
@@ -24,13 +24,16 @@
class ModuleHostCycle : public Module
{
Cap::Reference chghostcap;
+ const std::string quitmsghost;
+ const std::string quitmsgident;
/** Send fake quit/join/mode messages for host or ident cycle.
*/
- void DoHostCycle(User* user, const std::string& newident, const std::string& newhost, const char* quitmsg)
+ void DoHostCycle(User* user, const std::string& newident, const std::string& newhost, const std::string& reason)
{
- // GetFullHost() returns the original data at the time this function is called
- const std::string quitline = ":" + user->GetFullHost() + " QUIT :" + quitmsg;
+ // The user has the original ident/host at the time this function is called
+ ClientProtocol::Messages::Quit quitmsg(user, reason);
+ ClientProtocol::Event quitevent(ServerInstance->GetRFCEvents().quit, quitmsg);
already_sent_t silent_id = ServerInstance->Users.NextAlreadySentId();
already_sent_t seen_id = ServerInstance->Users.NextAlreadySentId();
@@ -50,7 +53,7 @@ class ModuleHostCycle : public Module
if (i->second)
{
u->already_sent = seen_id;
- u->Write(quitline);
+ u->Send(quitevent);
}
else
{
@@ -65,17 +68,8 @@ class ModuleHostCycle : public Module
{
Membership* memb = *i;
Channel* c = memb->chan;
- const std::string joinline = ":" + newfullhost + " JOIN " + c->name;
- std::string modeline;
- if (!memb->modes.empty())
- {
- modeline = ":" + (ServerInstance->Config->CycleHostsFromUser ? newfullhost : ServerInstance->Config->ServerName)
- + " MODE " + c->name + " +" + memb->modes;
-
- for (size_t j = 0; j < memb->modes.length(); j++)
- modeline.append(" ").append(user->nick);
- }
+ ClientProtocol::Events::Join joinevent(memb, newfullhost);
const Channel::MemberMap& ulist = c->GetUsers();
for (Channel::MemberMap::const_iterator j = ulist.begin(); j != ulist.end(); ++j)
@@ -90,13 +84,11 @@ class ModuleHostCycle : public Module
if (u->already_sent != seen_id)
{
- u->Write(quitline);
+ u->Send(quitevent);
u->already_sent = seen_id;
}
- u->Write(joinline);
- if (!memb->modes.empty())
- u->Write(modeline);
+ u->Send(joinevent);
}
}
}
@@ -104,17 +96,19 @@ class ModuleHostCycle : public Module
public:
ModuleHostCycle()
: chghostcap(this, "chghost")
+ , quitmsghost("Changing host")
+ , quitmsgident("Changing ident")
{
}
void OnChangeIdent(User* user, const std::string& newident) CXX11_OVERRIDE
{
- DoHostCycle(user, newident, user->GetDisplayedHost(), "Changing ident");
+ DoHostCycle(user, newident, user->GetDisplayedHost(), quitmsgident);
}
void OnChangeHost(User* user, const std::string& newhost) CXX11_OVERRIDE
{
- DoHostCycle(user, user->ident, newhost, "Changing host");
+ DoHostCycle(user, user->ident, newhost, quitmsghost);
}
Version GetVersion() CXX11_OVERRIDE
diff --git a/src/modules/m_ircv3.cpp b/src/modules/m_ircv3.cpp
index 92e8a0881..14b1cf8a1 100644
--- a/src/modules/m_ircv3.cpp
+++ b/src/modules/m_ircv3.cpp
@@ -22,24 +22,110 @@
#include "modules/cap.h"
#include "modules/ircv3.h"
+class AwayMessage : public ClientProtocol::Message
+{
+ public:
+ AwayMessage(User* user)
+ : ClientProtocol::Message("AWAY", user)
+ {
+ SetParams(user, user->awaymsg);
+ }
+
+ AwayMessage()
+ : ClientProtocol::Message("AWAY")
+ {
+ }
+
+ void SetParams(User* user, const std::string& awaymsg)
+ {
+ // Going away: 1 parameter which is the away reason
+ // Back from away: no parameter
+ if (!awaymsg.empty())
+ PushParam(awaymsg);
+ }
+};
+
+class JoinHook : public ClientProtocol::EventHook
+{
+ ClientProtocol::Events::Join extendedjoinmsg;
+
+ public:
+ const std::string asterisk;
+ ClientProtocol::EventProvider awayprotoev;
+ AwayMessage awaymsg;
+ Cap::Capability extendedjoincap;
+ Cap::Capability awaycap;
+
+ JoinHook(Module* mod)
+ : ClientProtocol::EventHook(mod, "JOIN")
+ , asterisk(1, '*')
+ , awayprotoev(mod, "AWAY")
+ , extendedjoincap(mod, "extended-join")
+ , awaycap(mod, "away-notify")
+ {
+ }
+
+ void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
+ {
+ const ClientProtocol::Events::Join& join = static_cast<const ClientProtocol::Events::Join&>(ev);
+
+ // An extended join has two extra parameters:
+ // First the account name of the joining user or an asterisk if the user is not logged in.
+ // The second parameter is the realname of the joining user.
+
+ Membership* const memb = join.GetMember();
+ const std::string* account = &asterisk;
+ const AccountExtItem* const accountext = GetAccountExtItem();
+ if (accountext)
+ {
+ const std::string* accountname = accountext->get(memb->user);
+ if (accountname)
+ account = accountname;
+ }
+
+ extendedjoinmsg.ClearParams();
+ extendedjoinmsg.SetSource(join);
+ extendedjoinmsg.PushParamRef(memb->chan->name);
+ extendedjoinmsg.PushParamRef(*account);
+ extendedjoinmsg.PushParamRef(memb->user->GetRealName());
+
+ awaymsg.ClearParams();
+ if ((memb->user->IsAway()) && (awaycap.IsActive()))
+ {
+ awaymsg.SetSource(join);
+ awaymsg.SetParams(memb->user, memb->user->awaymsg);
+ }
+ }
+
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
+ {
+ if (extendedjoincap.get(user))
+ messagelist.front() = &extendedjoinmsg;
+
+ if ((!awaymsg.GetParams().empty()) && (awaycap.get(user)))
+ messagelist.push_back(&awaymsg);
+
+ return MOD_RES_PASSTHRU;
+ }
+};
+
class ModuleIRCv3
: public Module
, public AccountEventListener
, public Away::EventListener
{
Cap::Capability cap_accountnotify;
- Cap::Capability cap_awaynotify;
- Cap::Capability cap_extendedjoin;
+ JoinHook joinhook;
- CUList last_excepts;
+ ClientProtocol::EventProvider accountprotoev;
public:
ModuleIRCv3()
: AccountEventListener(this)
, Away::EventListener(this)
, cap_accountnotify(this, "account-notify")
- , cap_awaynotify(this, "away-notify")
- , cap_extendedjoin(this, "extended-join")
+ , joinhook(this)
+ , accountprotoev(this, "ACCOUNT")
{
}
@@ -47,141 +133,41 @@ class ModuleIRCv3
{
ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
cap_accountnotify.SetActive(conf->getBool("accountnotify", true));
- cap_awaynotify.SetActive(conf->getBool("awaynotify", true));
- cap_extendedjoin.SetActive(conf->getBool("extendedjoin", true));
+ joinhook.awaycap.SetActive(conf->getBool("awaynotify", true));
+ joinhook.extendedjoincap.SetActive(conf->getBool("extendedjoin", true));
}
void OnAccountChange(User* user, const std::string& newaccount) CXX11_OVERRIDE
{
- // :nick!user@host ACCOUNT account
- // or
- // :nick!user@host ACCOUNT *
- std::string line = ":" + user->GetFullHost() + " ACCOUNT ";
- if (newaccount.empty())
- line += "*";
- else
- line += newaccount;
-
- IRCv3::WriteNeighborsWithCap(user, line, cap_accountnotify);
- }
-
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
- {
- // Remember who is not going to see the JOIN because of other modules
- if ((cap_awaynotify.IsActive()) && (memb->user->IsAway()))
- last_excepts = excepts;
-
- if (!cap_extendedjoin.IsActive())
- return;
-
- /*
- * Send extended joins to clients who have the extended-join capability.
- * An extended join looks like this:
- *
- * :nick!user@host JOIN #chan account :realname
- *
- * account is the joining user's account if he's logged in, otherwise it's an asterisk (*).
- */
-
- std::string line;
- std::string mode;
-
- const Channel::MemberMap& userlist = memb->chan->GetUsers();
- for (Channel::MemberMap::const_iterator it = userlist.begin(); it != userlist.end(); ++it)
- {
- // Send the extended join line if the current member is local, has the extended-join cap and isn't excepted
- User* member = IS_LOCAL(it->first);
- if ((member) && (cap_extendedjoin.get(member)) && (excepts.find(member) == excepts.end()))
- {
- // Construct the lines we're going to send if we haven't constructed them already
- if (line.empty())
- {
- bool has_account = false;
- line = ":" + memb->user->GetFullHost() + " JOIN " + memb->chan->name + " ";
- const AccountExtItem* accountext = GetAccountExtItem();
- if (accountext)
- {
- std::string* accountname;
- accountname = accountext->get(memb->user);
- if (accountname)
- {
- line += *accountname;
- has_account = true;
- }
- }
-
- if (!has_account)
- line += "*";
-
- line += " :" + memb->user->GetRealName();
-
- // If the joining user received privileges from another module then we must send them as well,
- // since silencing the normal join means the MODE will be silenced as well
- if (!memb->modes.empty())
- {
- const std::string& modefrom = ServerInstance->Config->CycleHostsFromUser ? memb->user->GetFullHost() : ServerInstance->Config->ServerName;
- mode = ":" + modefrom + " MODE " + memb->chan->name + " +" + memb->modes;
-
- for (unsigned int i = 0; i < memb->modes.length(); i++)
- mode += " " + memb->user->nick;
- }
- }
-
- // Write the JOIN and the MODE, if any
- member->Write(line);
- if ((!mode.empty()) && (member != memb->user))
- member->Write(mode);
-
- // Prevent the core from sending the JOIN and MODE to this user
- excepts.insert(it->first);
- }
- }
+ // Logged in: 1 parameter which is the account name
+ // Logged out: 1 parameter which is a "*"
+ ClientProtocol::Message msg("ACCOUNT", user);
+ const std::string& param = (newaccount.empty() ? joinhook.asterisk : newaccount);
+ msg.PushParamRef(param);
+ ClientProtocol::Event accountevent(accountprotoev, msg);
+ IRCv3::WriteNeighborsWithCap(user, accountevent, cap_accountnotify);
}
void OnUserAway(User* user) CXX11_OVERRIDE
{
- if (!cap_awaynotify.IsActive())
+ if (!joinhook.awaycap.IsActive())
return;
// Going away: n!u@h AWAY :reason
- const std::string line = ":" + user->GetFullHost() + " AWAY :" + user->awaymsg;
- IRCv3::WriteNeighborsWithCap(user, line, cap_awaynotify);
+ AwayMessage msg(user);
+ ClientProtocol::Event awayevent(joinhook.awayprotoev, msg);
+ IRCv3::WriteNeighborsWithCap(user, awayevent, joinhook.awaycap);
}
void OnUserBack(User* user) CXX11_OVERRIDE
{
- if (!cap_awaynotify.IsActive())
+ if (!joinhook.awaycap.IsActive())
return;
// Back from away: n!u@h AWAY
- const std::string line = ":" + user->GetFullHost() + " AWAY";
- IRCv3::WriteNeighborsWithCap(user, line, cap_awaynotify);
- }
-
- void OnPostJoin(Membership *memb) CXX11_OVERRIDE
- {
- if ((!cap_awaynotify.IsActive()) || (!memb->user->IsAway()))
- return;
-
- std::string line = ":" + memb->user->GetFullHost() + " AWAY :" + memb->user->awaymsg;
-
- const Channel::MemberMap& userlist = memb->chan->GetUsers();
- for (Channel::MemberMap::const_iterator it = userlist.begin(); it != userlist.end(); ++it)
- {
- // Send the away notify line if the current member is local, has the away-notify cap and isn't excepted
- User* member = IS_LOCAL(it->first);
- if ((member) && (cap_awaynotify.get(member)) && (last_excepts.find(member) == last_excepts.end()) && (it->second != memb))
- {
- member->Write(line);
- }
- }
-
- last_excepts.clear();
- }
-
- void Prioritize() CXX11_OVERRIDE
- {
- ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST);
+ AwayMessage msg(user);
+ ClientProtocol::Event awayevent(joinhook.awayprotoev, msg);
+ IRCv3::WriteNeighborsWithCap(user, awayevent, joinhook.awaycap);
}
Version GetVersion() CXX11_OVERRIDE
diff --git a/src/modules/m_ircv3_capnotify.cpp b/src/modules/m_ircv3_capnotify.cpp
index 93c30df12..757b0858f 100644
--- a/src/modules/m_ircv3_capnotify.cpp
+++ b/src/modules/m_ircv3_capnotify.cpp
@@ -46,19 +46,53 @@ class CapNotify : public Cap::Capability
}
};
+class CapNotifyMessage : public Cap::MessageBase
+{
+ public:
+ CapNotifyMessage(bool add, const std::string& capname)
+ : Cap::MessageBase((add ? "NEW" : "DEL"))
+ {
+ PushParamRef(capname);
+ }
+};
+
+class CapNotifyValueMessage : public Cap::MessageBase
+{
+ std::string s;
+ const std::string::size_type pos;
+
+ public:
+ CapNotifyValueMessage(const std::string& capname)
+ : Cap::MessageBase("NEW")
+ , s(capname)
+ , pos(s.size()+1)
+ {
+ s.push_back('=');
+ PushParamRef(s);
+ }
+
+ void SetCapValue(const std::string& capvalue)
+ {
+ s.erase(pos);
+ s.append(capvalue);
+ InvalidateCache();
+ }
+};
+
class ModuleIRCv3CapNotify : public Module, public Cap::EventListener, public ReloadModule::EventListener
{
CapNotify capnotify;
std::string reloadedmod;
std::vector<std::string> reloadedcaps;
+ ClientProtocol::EventProvider protoev;
void Send(const std::string& capname, Cap::Capability* cap, bool add)
{
- std::string msg = (add ? "NEW :" : "DEL :");
- msg.append(capname);
- std::string msgwithval = msg;
- msgwithval.push_back('=');
- std::string::size_type msgpos = msgwithval.size();
+ CapNotifyMessage msg(add, capname);
+ CapNotifyValueMessage msgwithval(capname);
+
+ ClientProtocol::Event event(protoev, msg);
+ ClientProtocol::Event eventwithval(protoev, msgwithval);
const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
@@ -73,13 +107,14 @@ class ModuleIRCv3CapNotify : public Module, public Cap::EventListener, public Re
const std::string* capvalue = cap->GetValue(user);
if ((capvalue) && (!capvalue->empty()))
{
- msgwithval.append(*capvalue);
- user->WriteCommand("CAP", msgwithval);
- msgwithval.erase(msgpos);
+ msgwithval.SetUser(user);
+ msgwithval.SetCapValue(*capvalue);
+ user->Send(eventwithval);
continue;
}
}
- user->WriteCommand("CAP", msg);
+ msg.SetUser(user);
+ user->Send(event);
}
}
@@ -88,6 +123,7 @@ class ModuleIRCv3CapNotify : public Module, public Cap::EventListener, public Re
: Cap::EventListener(this)
, ReloadModule::EventListener(this)
, capnotify(this)
+ , protoev(this, "CAP_NOTIFY")
{
}
diff --git a/src/modules/m_ircv3_chghost.cpp b/src/modules/m_ircv3_chghost.cpp
index 0a9e055b4..aa53612cb 100644
--- a/src/modules/m_ircv3_chghost.cpp
+++ b/src/modules/m_ircv3_chghost.cpp
@@ -24,17 +24,21 @@
class ModuleIRCv3ChgHost : public Module
{
Cap::Capability cap;
+ ClientProtocol::EventProvider protoevprov;
void DoChgHost(User* user, const std::string& ident, const std::string& host)
{
- std::string line(1, ':');
- line.append(user->GetFullHost()).append(" CHGHOST ").append(ident).append(1, ' ').append(host);
- IRCv3::WriteNeighborsWithCap(user, line, cap);
+ ClientProtocol::Message msg("CHGHOST", user);
+ msg.PushParamRef(ident);
+ msg.PushParamRef(host);
+ ClientProtocol::Event protoev(protoevprov, msg);
+ IRCv3::WriteNeighborsWithCap(user, protoev, cap);
}
public:
ModuleIRCv3ChgHost()
: cap(this, "chghost")
+ , protoevprov(this, "CHGHOST")
{
}
diff --git a/src/modules/m_ircv3_echomessage.cpp b/src/modules/m_ircv3_echomessage.cpp
index 056b02194..702552ea7 100644
--- a/src/modules/m_ircv3_echomessage.cpp
+++ b/src/modules/m_ircv3_echomessage.cpp
@@ -21,8 +21,6 @@
#include "inspircd.h"
#include "modules/cap.h"
-static const char* MessageTypeStringSp[] = { "PRIVMSG ", "NOTICE " };
-
class ModuleIRCv3EchoMessage : public Module
{
Cap::Capability cap;
@@ -38,27 +36,31 @@ class ModuleIRCv3EchoMessage : public Module
if (!cap.get(user))
return;
- std::string msg = MessageTypeStringSp[details.type];
+ // Caps are only set on local users
+ LocalUser* const localuser = static_cast<LocalUser*>(user);
+
+ const std::string& text = details.echooriginal ? details.originaltext : details.text;
if (target.type == MessageTarget::TYPE_USER)
{
User* destuser = target.Get<User>();
- msg.append(destuser->nick);
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, destuser, text, details.type);
+ privmsg.AddTags(details.tags_in);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
}
else if (target.type == MessageTarget::TYPE_CHANNEL)
{
- if (target.status)
- msg.push_back(target.status);
-
Channel* chan = target.Get<Channel>();
- msg.append(chan->name);
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, chan, text, details.type, target.status);
+ privmsg.AddTags(details.tags_in);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
}
else
{
const std::string* servername = target.Get<std::string>();
- msg.append(*servername);
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, user, *servername, text, details.type);
+ privmsg.AddTags(details.tags_in);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
}
- msg.append(" :").append(details.echooriginal ? details.originaltext : details.text);
- user->WriteFrom(user, msg);
}
void OnUserMessageBlocked(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
diff --git a/src/modules/m_ircv3_invitenotify.cpp b/src/modules/m_ircv3_invitenotify.cpp
index 3783ff33c..bcb6f51d5 100644
--- a/src/modules/m_ircv3_invitenotify.cpp
+++ b/src/modules/m_ircv3_invitenotify.cpp
@@ -32,8 +32,8 @@ class ModuleIRCv3InviteNotify : public Module
void OnUserInvite(User* source, User* dest, Channel* chan, time_t expiry, unsigned int notifyrank, CUList& notifyexcepts) CXX11_OVERRIDE
{
- std::string msg = "INVITE ";
- msg.append(dest->nick).append(1, ' ').append(chan->name);
+ ClientProtocol::Messages::Invite invitemsg(source, dest, chan);
+ ClientProtocol::Event inviteevent(ServerInstance->GetRFCEvents().invite, invitemsg);
const Channel::MemberMap& users = chan->GetUsers();
for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
{
@@ -47,8 +47,10 @@ class ModuleIRCv3InviteNotify : public Module
if (memb->getRank() < notifyrank)
continue;
+ // Caps are only set on local users
+ LocalUser* const localuser = static_cast<LocalUser*>(user);
// Send and add the user to the exceptions so they won't get the NOTICE invite announcement message
- user->WriteFrom(source, msg);
+ localuser->Send(inviteevent);
notifyexcepts.insert(user);
}
}
diff --git a/src/modules/m_knock.cpp b/src/modules/m_knock.cpp
index a0a8455a8..1b66e01a4 100644
--- a/src/modules/m_knock.cpp
+++ b/src/modules/m_knock.cpp
@@ -81,7 +81,13 @@ class CommandKnock : public Command
c->WriteNotice(InspIRCd::Format("User %s is KNOCKing on %s (%s)", user->nick.c_str(), c->name.c_str(), parameters[1].c_str()));
if (sendnumeric)
- c->WriteChannelWithServ(ServerInstance->Config->ServerName, "710 %s %s %s :is KNOCKing: %s", c->name.c_str(), c->name.c_str(), user->GetFullHost().c_str(), parameters[1].c_str());
+ {
+ Numeric::Numeric numeric(710);
+ numeric.push(c->name).push(user->GetFullHost()).push("is KNOCKing: " + parameters[1]);
+
+ ClientProtocol::Messages::Numeric numericmsg(numeric, c->name);
+ c->Write(ServerInstance->GetRFCEvents().numeric, numericmsg);
+ }
user->WriteNotice("KNOCKing on " + c->name);
return CMD_SUCCESS;
diff --git a/src/modules/m_ldapoper.cpp b/src/modules/m_ldapoper.cpp
index 094b37744..cde5b00d7 100644
--- a/src/modules/m_ldapoper.cpp
+++ b/src/modules/m_ldapoper.cpp
@@ -48,7 +48,8 @@ class LDAPOperBase : public LDAPInterface
CommandBase::Params params;
params.push_back(opername);
params.push_back(password);
- oper_command->Handle(user, params);
+ ClientProtocol::TagMap tags;
+ oper_command->Handle(user, CommandBase::Params(params, tags));
}
void Fallback()
diff --git a/src/modules/m_passforward.cpp b/src/modules/m_passforward.cpp
index e3f8624fc..08d3533cd 100644
--- a/src/modules/m_passforward.cpp
+++ b/src/modules/m_passforward.cpp
@@ -91,8 +91,8 @@ class ModulePassForward : public Module
}
std::string tmp;
- FormatStr(tmp,forwardmsg, user);
- user->WriteServ(tmp);
+ FormatStr(tmp, forwardmsg, user);
+ ServerInstance->Parser.ProcessBuffer(user, tmp);
tmp.clear();
FormatStr(tmp,forwardcmd, user);
diff --git a/src/modules/m_samode.cpp b/src/modules/m_samode.cpp
index 195b767ab..8f28a7e9c 100644
--- a/src/modules/m_samode.cpp
+++ b/src/modules/m_samode.cpp
@@ -26,6 +26,8 @@
*/
class CommandSamode : public Command
{
+ bool logged;
+
public:
bool active;
CommandSamode(Module* Creator) : Command(Creator,"SAMODE", 2)
@@ -55,24 +57,29 @@ class CommandSamode : public Command
Modes::ChangeList emptychangelist;
ServerInstance->Modes->ProcessSingle(ServerInstance->FakeClient, NULL, ServerInstance->FakeClient, emptychangelist);
+ logged = false;
this->active = true;
- CmdResult result = ServerInstance->Parser.CallHandler("MODE", parameters, user);
+ ServerInstance->Parser.CallHandler("MODE", parameters, user);
this->active = false;
- if (result == CMD_SUCCESS)
+ if (!logged)
{
- // If lastparse is empty and the MODE command handler returned CMD_SUCCESS then
- // the client queried the list of a listmode (e.g. /SAMODE #chan b), which was
- // handled internally by the MODE command handler.
+ // If we haven't logged anything yet then the client queried the list of a listmode
+ // (e.g. /SAMODE #chan b), which was handled internally by the MODE command handler.
//
- // Viewing the modes of a user or a channel can also result in CMD_SUCCESS, but
+ // Viewing the modes of a user or a channel could also result in this, but
// that is not possible with /SAMODE because we require at least 2 parameters.
- const std::string& lastparse = ServerInstance->Modes.GetLastParse();
- ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " + (lastparse.empty() ? stdalgo::string::join(parameters) : lastparse));
+ LogUsage(user, stdalgo::string::join(parameters));
}
return CMD_SUCCESS;
}
+
+ void LogUsage(const User* user, const std::string& text)
+ {
+ logged = true;
+ ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " + text);
+ }
};
class ModuleSaMode : public Module
@@ -96,6 +103,25 @@ class ModuleSaMode : public Module
return MOD_RES_PASSTHRU;
}
+ void OnMode(User* user, User* destuser, Channel* destchan, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags) CXX11_OVERRIDE
+ {
+ if (!cmd.active)
+ return;
+
+ std::string logtext = (destuser ? destuser->nick : destchan->name);
+ logtext.push_back(' ');
+ logtext += ClientProtocol::Messages::Mode::ToModeLetters(modes);
+
+ for (Modes::ChangeList::List::const_iterator i = modes.getlist().begin(); i != modes.getlist().end(); ++i)
+ {
+ const Modes::Change& item = *i;
+ if (!item.param.empty())
+ logtext.append(1, ' ').append(item.param);
+ }
+
+ cmd.LogUsage(user, logtext);
+ }
+
void Prioritize() CXX11_OVERRIDE
{
Module *override = ServerInstance->Modules->Find("m_override.so");
diff --git a/src/modules/m_sasl.cpp b/src/modules/m_sasl.cpp
index d37e1c90f..480f8f6db 100644
--- a/src/modules/m_sasl.cpp
+++ b/src/modules/m_sasl.cpp
@@ -145,7 +145,7 @@ static Events::ModuleEventProvider* saslevprov;
static void SendSASL(LocalUser* user, const std::string& agent, char mode, const std::vector<std::string>& parameters)
{
- CommandBase::Params params(parameters.size() + 3);
+ CommandBase::Params params;
params.push_back(user->uuid);
params.push_back(agent);
params.push_back(ConvToStr(mode));
@@ -157,6 +157,8 @@ static void SendSASL(LocalUser* user, const std::string& agent, char mode, const
}
}
+static ClientProtocol::EventProvider* g_protoev;
+
/**
* Tracks SASL authentication state like charybdis does. --nenolod
*/
@@ -223,7 +225,15 @@ class SaslAuthenticator
return this->state;
if (msg[2] == "C")
- this->user->Write("AUTHENTICATE %s", msg[3].c_str());
+ {
+ ClientProtocol::Message authmsg("AUTHENTICATE");
+ authmsg.PushParamRef(msg[3]);
+
+ ClientProtocol::Event authevent(*g_protoev, authmsg);
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (localuser)
+ localuser->Send(authevent);
+ }
else if (msg[2] == "D")
{
this->state = SASL_DONE;
@@ -377,6 +387,7 @@ class ModuleSASL : public Module
CommandAuthenticate auth;
CommandSASL sasl;
Events::ModuleEventProvider sasleventprov;
+ ClientProtocol::EventProvider protoev;
public:
ModuleSASL()
@@ -386,8 +397,10 @@ class ModuleSASL : public Module
, auth(this, authExt, cap)
, sasl(this, authExt)
, sasleventprov(this, "event/sasl")
+ , protoev(this, auth.name)
{
saslevprov = &sasleventprov;
+ g_protoev = &protoev;
}
void init() CXX11_OVERRIDE
diff --git a/src/modules/m_showfile.cpp b/src/modules/m_showfile.cpp
index 565aaf78b..99c545140 100644
--- a/src/modules/m_showfile.cpp
+++ b/src/modules/m_showfile.cpp
@@ -63,13 +63,14 @@ class CommandShowFile : public Command
user->WriteRemoteNumeric(endnumeric, endtext.c_str());
}
- else
+ else if (IS_LOCAL(user))
{
- const char* msgcmd = (method == SF_MSG ? "PRIVMSG" : "NOTICE");
+ LocalUser* const localuser = IS_LOCAL(user);
for (file_cache::const_iterator i = contents.begin(); i != contents.end(); ++i)
{
const std::string& line = *i;
- user->WriteCommand(msgcmd, ":" + line);
+ ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, line, ((method == SF_MSG) ? MSG_PRIVMSG : MSG_NOTICE));
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
}
}
return CMD_SUCCESS;
diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp
index 3c9089115..0ff180a83 100644
--- a/src/modules/m_spanningtree/main.cpp
+++ b/src/modules/m_spanningtree/main.cpp
@@ -731,7 +731,7 @@ void ModuleSpanningTree::OnUserBack(User* user)
CommandAway::Builder(user).Broadcast();
}
-void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags, const std::string& output_mode)
+void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags)
{
if (processflags & ModeParser::MODE_LOCALONLY)
return;
@@ -743,7 +743,7 @@ void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::
CmdBuilder params(source, "MODE");
params.push(u->uuid);
- params.push(output_mode);
+ params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes));
params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
params.Broadcast();
}
@@ -752,7 +752,7 @@ void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::
CmdBuilder params(source, "FMODE");
params.push(c->name);
params.push_int(c->age);
- params.push(output_mode);
+ params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes));
params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
params.Broadcast();
}
diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h
index 4a65f1c37..60f819e9c 100644
--- a/src/modules/m_spanningtree/main.h
+++ b/src/modules/m_spanningtree/main.h
@@ -172,7 +172,7 @@ class ModuleSpanningTree
void OnLoadModule(Module* mod) CXX11_OVERRIDE;
void OnUnloadModule(Module* mod) CXX11_OVERRIDE;
ModResult OnAcceptConnection(int newsock, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server) CXX11_OVERRIDE;
- void OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags, const std::string& output_mode) CXX11_OVERRIDE;
+ void OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags) CXX11_OVERRIDE;
CullResult cull() CXX11_OVERRIDE;
~ModuleSpanningTree();
Version GetVersion() CXX11_OVERRIDE;
diff --git a/src/modules/m_spanningtree/treesocket2.cpp b/src/modules/m_spanningtree/treesocket2.cpp
index 3bc1fc71a..513fa6dbf 100644
--- a/src/modules/m_spanningtree/treesocket2.cpp
+++ b/src/modules/m_spanningtree/treesocket2.cpp
@@ -343,7 +343,8 @@ void TreeSocket::ProcessConnectedLine(std::string& prefix, std::string& command,
res = scmd->Handle(who, params);
else
{
- res = cmd->Handle(who, params);
+ ClientProtocol::TagMap tags;
+ res = cmd->Handle(who, CommandBase::Params(params, tags));
if (res == CMD_INVALID)
throw ProtocolException("Error in command handler");
}
diff --git a/src/modules/m_sqloper.cpp b/src/modules/m_sqloper.cpp
index 2b298f662..da538caef 100644
--- a/src/modules/m_sqloper.cpp
+++ b/src/modules/m_sqloper.cpp
@@ -143,7 +143,8 @@ class OperQuery : public SQL::Query
return;
// Now handle /OPER.
- oper_command->Handle(user, params);
+ ClientProtocol::TagMap tags;
+ oper_command->Handle(user, CommandBase::Params(params, tags));
}
else
{
diff --git a/src/modules/m_timedbans.cpp b/src/modules/m_timedbans.cpp
index ffb84a44f..058028f61 100644
--- a/src/modules/m_timedbans.cpp
+++ b/src/modules/m_timedbans.cpp
@@ -113,7 +113,6 @@ class CommandTban : public Command
return CMD_FAILURE;
}
- CUList tmp;
T.mask = mask;
T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
T.chan = channel;
@@ -124,7 +123,8 @@ class CommandTban : public Command
PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
- channel->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, tmp, "NOTICE %s :%s", channel->name.c_str(), addban.c_str());
+ ClientProtocol::Messages::Privmsg notice(ServerInstance->FakeClient, channel, addban, MSG_NOTICE);
+ channel->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar);
ServerInstance->PI->SendChannelNotice(channel, pfxchar, addban);
return CMD_SUCCESS;
}
@@ -210,13 +210,13 @@ class ModuleTimedBans : public Module
std::string mask = i->mask;
Channel* cr = i->chan;
{
- CUList empty;
const std::string expiry = "*** Timed ban on " + cr->name + " expired.";
// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
- cr->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, empty, "NOTICE %s :%s", cr->name.c_str(), expiry.c_str());
+ ClientProtocol::Messages::Privmsg notice(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, cr, expiry, MSG_NOTICE);
+ cr->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar);
ServerInstance->PI->SendChannelNotice(cr, pfxchar, expiry);
Modes::ChangeList setban;
diff --git a/src/usermanager.cpp b/src/usermanager.cpp
index 02c030a42..7466f385b 100644
--- a/src/usermanager.cpp
+++ b/src/usermanager.cpp
@@ -28,21 +28,23 @@ namespace
{
class WriteCommonQuit : public User::ForEachNeighborHandler
{
- std::string line;
- std::string operline;
+ ClientProtocol::Messages::Quit quitmsg;
+ ClientProtocol::Event quitevent;
+ ClientProtocol::Messages::Quit operquitmsg;
+ ClientProtocol::Event operquitevent;
void Execute(LocalUser* user) CXX11_OVERRIDE
{
- user->Write(user->IsOper() ? operline : line);
+ user->Send(user->IsOper() ? operquitevent : quitevent);
}
public:
WriteCommonQuit(User* user, const std::string& msg, const std::string& opermsg)
- : line(":" + user->GetFullHost() + " QUIT :")
- , operline(line)
+ : quitmsg(user, msg)
+ , quitevent(ServerInstance->GetRFCEvents().quit, quitmsg)
+ , operquitmsg(user, opermsg)
+ , operquitevent(ServerInstance->GetRFCEvents().quit, operquitmsg)
{
- line += msg;
- operline += opermsg;
user->ForEachNeighbor(*this, false);
}
};
@@ -177,7 +179,12 @@ void UserManager::QuitUser(User* user, const std::string& quitreason, const std:
user->quitting = true;
ServerInstance->Logs->Log("USERS", LOG_DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
- user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->GetRealHost().c_str(), operreason ? operreason->c_str() : quitreason.c_str());
+ LocalUser* const localuser = IS_LOCAL(user);
+ if (localuser)
+ {
+ ClientProtocol::Messages::Error errormsg(InspIRCd::Format("Closing link: (%s@%s) [%s]", user->ident.c_str(), user->GetRealHost().c_str(), operreason ? operreason->c_str() : quitreason.c_str()));
+ localuser->Send(ServerInstance->GetRFCEvents().error, errormsg);
+ }
std::string reason;
reason.assign(quitreason, 0, ServerInstance->Config->Limits.MaxQuit);
@@ -264,12 +271,13 @@ void UserManager::ServerNoticeAll(const char* text, ...)
{
std::string message;
VAFORMAT(message, text, text);
- message = "NOTICE $" + ServerInstance->Config->ServerName + " :" + message;
+ ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, ServerInstance->Config->ServerName, message, MSG_NOTICE);
+ ClientProtocol::Event msgevent(ServerInstance->GetRFCEvents().privmsg, msg);
for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
{
- User* t = *i;
- t->WriteServ(message);
+ LocalUser* user = *i;
+ user->Send(msgevent);
}
}
@@ -320,8 +328,8 @@ void UserManager::DoBackgroundUserStuff()
this->QuitUser(curr, message);
continue;
}
-
- curr->Write("PING :" + ServerInstance->Config->ServerName);
+ ClientProtocol::Messages::Ping ping;
+ curr->Send(ServerInstance->GetRFCEvents().ping, ping);
curr->lastping = 0;
curr->nping = ServerInstance->Time() + curr->MyClass->GetPingTime();
}
diff --git a/src/users.cpp b/src/users.cpp
index 737a3fa5c..e05ef1853 100644
--- a/src/users.cpp
+++ b/src/users.cpp
@@ -26,6 +26,8 @@
#include "inspircd.h"
#include "xline.h"
+ClientProtocol::MessageList LocalUser::sendmsglist;
+
bool User::IsNoticeMaskSet(unsigned char sm)
{
if (!isalpha(sm))
@@ -87,6 +89,7 @@ User::User(const std::string& uid, Server* srv, UserType type)
LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* servaddr)
: User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL)
, eh(this)
+ , serializer(NULL)
, bytes_in(0)
, bytes_out(0)
, cmds_in(0)
@@ -359,7 +362,16 @@ void User::Oper(OperInfo* info)
this->SetMode(opermh, true);
this->oper = info;
- this->WriteCommand("MODE", "+o");
+
+ LocalUser* localuser = IS_LOCAL(this);
+ if (localuser)
+ {
+ Modes::ChangeList changelist;
+ changelist.push_add(opermh);
+ ClientProtocol::Events::Mode modemsg(ServerInstance->FakeClient, NULL, localuser, changelist);
+ localuser->Send(modemsg);
+ }
+
FOREACH_MOD(OnOper, (this, info->name));
std::string opername;
@@ -384,7 +396,7 @@ void User::Oper(OperInfo* info)
ServerInstance->Users->all_opers.push_back(this);
// Expand permissions from config for faster lookup
- if (IS_LOCAL(this))
+ if (localuser)
oper->init();
FOREACH_MOD(OnPostOper, (this, oper->name, opername));
@@ -573,7 +585,10 @@ void LocalUser::FullConnect()
ServerInstance->Parser.CallHandler(command, parameters, this);
if (ServerInstance->Config->RawLog)
- WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str());
+ {
+ ClientProtocol::Messages::Privmsg rawlogmsg(ServerInstance->FakeClient, this, "*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
+ this->Send(ServerInstance->GetRFCEvents().privmsg, rawlogmsg);
+ }
/*
* We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
@@ -651,7 +666,11 @@ bool User::ChangeNick(const std::string& newnick, time_t newts)
}
if (this->registered == REG_ALL)
- this->WriteCommon("NICK %s", newnick.c_str());
+ {
+ ClientProtocol::Messages::Nick nickmsg(this, newnick);
+ ClientProtocol::Event nickevent(ServerInstance->GetRFCEvents().nick, nickmsg);
+ this->WriteCommonRaw(nickevent, true);
+ }
const std::string oldnick = nick;
nick = newnick;
@@ -667,7 +686,10 @@ bool User::ChangeNick(const std::string& newnick, time_t newts)
void LocalUser::OverruleNick()
{
- this->WriteFrom(this, "NICK %s", this->uuid.c_str());
+ {
+ ClientProtocol::Messages::Nick nickmsg(this, this->uuid);
+ this->Send(ServerInstance->GetRFCEvents().nick, nickmsg);
+ }
this->WriteNumeric(ERR_NICKNAMEINUSE, this->nick, "Nickname overruled.");
// Clear the bit before calling ChangeNick() to make it NOT run the OnUserPostNick() hook
@@ -763,35 +785,24 @@ void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_elin
}
}
-static std::string wide_newline("\r\n");
-
-void User::Write(const std::string& text)
-{
-}
-
-void User::Write(const char *text, ...)
-{
-}
-
-void LocalUser::Write(const std::string& text)
+void LocalUser::Write(const ClientProtocol::SerializedMessage& text)
{
if (!SocketEngine::BoundsCheckFd(&eh))
return;
- // The maximum size of an IRC message minus the terminating CR+LF.
- const size_t maxmessage = ServerInstance->Config->Limits.MaxLine - 2;
- if (text.length() > maxmessage)
+ if (ServerInstance->Config->RawLog)
{
- // This should happen rarely or never. Crop the string at MaxLine and try again.
- std::string try_again(text, 0, maxmessage);
- Write(try_again);
- return;
- }
+ if (text.empty())
+ return;
+
+ std::string::size_type nlpos = text.find_first_of("\r\n", 0, 2);
+ if (nlpos == std::string::npos)
+ nlpos = text.length(); // TODO is this ok, test it
- ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
+ ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %.*s", uuid.c_str(), (int) nlpos, text.c_str());
+ }
eh.AddWriteBuf(text);
- eh.AddWriteBuf(wide_newline);
const size_t bytessent = text.length() + 2;
ServerInstance->stats.Sent += bytessent;
@@ -799,53 +810,47 @@ void LocalUser::Write(const std::string& text)
this->cmds_out++;
}
-/** Write()
- */
-void LocalUser::Write(const char *text, ...)
-{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- this->Write(textbuffer);
-}
-
-void User::WriteServ(const std::string& text)
-{
- this->Write(":%s %s",ServerInstance->Config->ServerName.c_str(),text.c_str());
-}
-
-/** WriteServ()
- * Same as Write(), except `text' is prefixed with `:server.name '.
- */
-void User::WriteServ(const char* text, ...)
+void LocalUser::Send(ClientProtocol::Event& protoev)
{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- this->WriteServ(textbuffer);
-}
+ if (!serializer)
+ return;
-void User::WriteCommand(const char* command, const std::string& text)
-{
- this->WriteServ(command + (this->registered & REG_NICK ? " " + this->nick : " *") + " " + text);
+ // In the most common case a static LocalUser field, sendmsglist, is passed to the event to be
+ // populated. The list is cleared before returning.
+ // To handle re-enters, if sendmsglist is non-empty upon entering the method then a temporary
+ // list is used instead of the static one.
+ if (sendmsglist.empty())
+ {
+ Send(protoev, sendmsglist);
+ sendmsglist.clear();
+ }
+ else
+ {
+ ClientProtocol::MessageList msglist;
+ Send(protoev, msglist);
+ }
}
-namespace
+void LocalUser::Send(ClientProtocol::Event& protoev, ClientProtocol::MessageList& msglist)
{
- std::string BuildNumeric(const std::string& source, User* targetuser, unsigned int num, const Command::Params& params)
+ // Modules can personalize the messages sent per user for the event
+ protoev.GetMessagesForUser(this, msglist);
+ for (ClientProtocol::MessageList::const_iterator i = msglist.begin(); i != msglist.end(); ++i)
{
- const char* const target = (targetuser->registered & REG_NICK ? targetuser->nick.c_str() : "*");
- std::string raw = InspIRCd::Format(":%s %03u %s", source.c_str(), num, target);
- if (!params.empty())
- {
- for (std::vector<std::string>::const_iterator i = params.begin(); i != params.end()-1; ++i)
- raw.append(1, ' ').append(*i);
- raw.append(" :").append(params.back());
- }
- return raw;
+ ClientProtocol::Message& curr = **i;
+ ModResult res;
+ FIRST_MOD_RESULT(OnUserWrite, res, (this, curr));
+ if (res != MOD_RES_DENY)
+ Write(serializer->SerializeForUser(this, curr));
}
}
void User::WriteNumeric(const Numeric::Numeric& numeric)
{
+ LocalUser* const localuser = IS_LOCAL(this);
+ if (!localuser)
+ return;
+
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric));
@@ -853,24 +858,8 @@ void User::WriteNumeric(const Numeric::Numeric& numeric)
if (MOD_RESULT == MOD_RES_DENY)
return;
- const std::string& servername = (numeric.GetServer() ? numeric.GetServer()->GetName() : ServerInstance->Config->ServerName);
- this->Write(BuildNumeric(servername, this, numeric.GetNumeric(), numeric.GetParams()));
-}
-
-void User::WriteFrom(User *user, const std::string &text)
-{
- const std::string message = ":" + user->GetFullHost() + " " + text;
- this->Write(message);
-}
-
-
-/* write text from an originating user to originating user */
-
-void User::WriteFrom(User *user, const char* text, ...)
-{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- this->WriteFrom(user, textbuffer);
+ ClientProtocol::Messages::Numeric numericmsg(numeric, localuser);
+ localuser->Send(ServerInstance->GetRFCEvents().numeric, numericmsg);
}
void User::WriteRemoteNotice(const std::string& text)
@@ -887,32 +876,24 @@ namespace
{
class WriteCommonRawHandler : public User::ForEachNeighborHandler
{
- const std::string& msg;
+ ClientProtocol::Event& ev;
void Execute(LocalUser* user) CXX11_OVERRIDE
{
- user->Write(msg);
+ user->Send(ev);
}
public:
- WriteCommonRawHandler(const std::string& message)
- : msg(message)
+ WriteCommonRawHandler(ClientProtocol::Event& protoev)
+ : ev(protoev)
{
}
};
}
-void User::WriteCommon(const char* text, ...)
+void User::WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self)
{
- std::string textbuffer;
- VAFORMAT(textbuffer, text, text);
- textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
- this->WriteCommonRaw(textbuffer, true);
-}
-
-void User::WriteCommonRaw(const std::string &line, bool include_self)
-{
- WriteCommonRawHandler handler(line);
+ WriteCommonRawHandler handler(protoev);
ForEachNeighbor(handler, include_self);
}
@@ -1203,6 +1184,16 @@ void User::PurgeEmptyChannels()
this->UnOper();
}
+void User::WriteNotice(const std::string& text)
+{
+ LocalUser* const localuser = IS_LOCAL(this);
+ if (!localuser)
+ return;
+
+ ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, text, MSG_NOTICE);
+ localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
+}
+
const std::string& FakeUser::GetFullHost()
{
if (!ServerInstance->Config->HideServer.empty())