diff options
Diffstat (limited to 'src/modules')
25 files changed, 421 insertions, 237 deletions
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; |