#include "inspircd.h"
#include "modules/account.h"
+#include "modules/away.h"
#include "modules/cap.h"
+#include "modules/ircv3.h"
-class ModuleIRCv3 : public Module
+class AwayMessage : public ClientProtocol::Message
{
- GenericCap cap_accountnotify;
- GenericCap cap_awaynotify;
- GenericCap cap_extendedjoin;
- bool accountnotify;
- bool awaynotify;
- bool extendedjoin;
-
- CUList last_excepts;
-
- void WriteNeighboursWithExt(User* user, const std::string& line, const LocalIntExt& ext)
+ public:
+ AwayMessage(User* user)
+ : ClientProtocol::Message("AWAY", user)
{
- UserChanList chans(user->chans);
-
- std::map<User*, bool> exceptions;
- FOREACH_MOD(OnBuildNeighborList, (user, chans, exceptions));
-
- // Send it to all local users who were explicitly marked as neighbours by modules and have the required ext
- for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
- {
- LocalUser* u = IS_LOCAL(i->first);
- if ((u) && (i->second) && (ext.get(u)))
- u->Write(line);
- }
-
- // Now consider sending it to all other users who has at least a common channel with the user
- std::set<User*> already_sent;
- for (UCListIter i = chans.begin(); i != chans.end(); ++i)
- {
- const UserMembList* userlist = (*i)->GetUsers();
- for (UserMembList::const_iterator m = userlist->begin(); m != userlist->end(); ++m)
- {
- /*
- * Send the line if the channel member in question meets all of the following criteria:
- * - local
- * - not the user who is doing the action (i.e. whose channels we're iterating)
- * - has the given extension
- * - not on the except list built by modules
- * - we haven't sent the line to the member yet
- *
- */
- LocalUser* member = IS_LOCAL(m->first);
- if ((member) && (member != user) && (ext.get(member)) && (exceptions.find(member) == exceptions.end()) && (already_sent.insert(member).second))
- member->Write(line);
- }
- }
+ SetParams(user, user->awaymsg);
}
- public:
- ModuleIRCv3() : cap_accountnotify(this, "account-notify"),
- cap_awaynotify(this, "away-notify"),
- cap_extendedjoin(this, "extended-join")
+ AwayMessage()
+ : ClientProtocol::Message("AWAY")
{
}
- void init() CXX11_OVERRIDE
+ void SetParams(User* user, const std::string& awaymsg)
{
- OnRehash(NULL);
+ // 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;
- void OnRehash(User* user) CXX11_OVERRIDE
+ 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")
{
- ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
- accountnotify = conf->getBool("accoutnotify", true);
- awaynotify = conf->getBool("awaynotify", true);
- extendedjoin = conf->getBool("extendedjoin", true);
}
- void OnEvent(Event& ev) CXX11_OVERRIDE
+ void OnEventInit(const ClientProtocol::Event& ev) CXX11_OVERRIDE
{
- if (awaynotify)
- cap_awaynotify.HandleEvent(ev);
- if (extendedjoin)
- cap_extendedjoin.HandleEvent(ev);
+ 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.
- if (accountnotify)
+ Membership* const memb = join.GetMember();
+ const std::string* account = &asterisk;
+ const AccountExtItem* const accountext = GetAccountExtItem();
+ if (accountext)
{
- cap_accountnotify.HandleEvent(ev);
-
- if (ev.id == "account_login")
- {
- AccountEvent* ae = static_cast<AccountEvent*>(&ev);
-
- // :nick!user@host ACCOUNT account
- // or
- // :nick!user@host ACCOUNT *
- std::string line = ":" + ae->user->GetFullHost() + " ACCOUNT ";
- if (ae->account.empty())
- line += "*";
- else
- line += std::string(ae->account);
-
- WriteNeighboursWithExt(ae->user, line, cap_accountnotify.ext);
- }
+ 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);
}
}
- void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) CXX11_OVERRIDE
+ ModResult OnPreEventSend(LocalUser* user, const ClientProtocol::Event& ev, ClientProtocol::MessageList& messagelist) CXX11_OVERRIDE
{
- // Remember who is not going to see the JOIN because of other modules
- if ((awaynotify) && (memb->user->IsAway()))
- last_excepts = excepts;
+ if (extendedjoincap.get(user))
+ messagelist.front() = &extendedjoinmsg;
- if (!extendedjoin)
- return;
+ if ((!awaymsg.GetParams().empty()) && (awaycap.get(user)))
+ messagelist.push_back(&awaymsg);
- /*
- * 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 (*).
- */
+ return MOD_RES_PASSTHRU;
+ }
+};
- std::string line;
- std::string mode;
+class ModuleIRCv3
+ : public Module
+ , public AccountEventListener
+ , public Away::EventListener
+{
+ Cap::Capability cap_accountnotify;
+ JoinHook joinhook;
- const UserMembList* userlist = memb->chan->GetUsers();
- for (UserMembCIter 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.ext.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->fullname;
-
- // 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);
- }
- }
+ ClientProtocol::EventProvider accountprotoev;
+
+ public:
+ ModuleIRCv3()
+ : AccountEventListener(this)
+ , Away::EventListener(this)
+ , cap_accountnotify(this, "account-notify")
+ , joinhook(this)
+ , accountprotoev(this, "ACCOUNT")
+ {
}
- ModResult OnSetAway(User* user, const std::string &awaymsg) CXX11_OVERRIDE
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
- if (awaynotify)
- {
- // Going away: n!u@h AWAY :reason
- // Back from away: n!u@h AWAY
- std::string line = ":" + user->GetFullHost() + " AWAY";
- if (!awaymsg.empty())
- line += " :" + awaymsg;
+ ConfigTag* conf = ServerInstance->Config->ConfValue("ircv3");
+ cap_accountnotify.SetActive(conf->getBool("accountnotify", true));
+ joinhook.awaycap.SetActive(conf->getBool("awaynotify", true));
+ joinhook.extendedjoincap.SetActive(conf->getBool("extendedjoin", true));
+ }
- WriteNeighboursWithExt(user, line, cap_awaynotify.ext);
- }
- return MOD_RES_PASSTHRU;
+ void OnAccountChange(User* user, const std::string& newaccount) CXX11_OVERRIDE
+ {
+ // 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 OnPostJoin(Membership *memb) CXX11_OVERRIDE
+ void OnUserAway(User* user) CXX11_OVERRIDE
{
- if ((!awaynotify) || (!memb->user->IsAway()))
+ if (!joinhook.awaycap.IsActive())
return;
- std::string line = ":" + memb->user->GetFullHost() + " AWAY :" + memb->user->awaymsg;
-
- const UserMembList* userlist = memb->chan->GetUsers();
- for (UserMembCIter 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.ext.get(member)) && (last_excepts.find(member) == last_excepts.end()))
- {
- member->Write(line);
- }
- }
-
- last_excepts.clear();
+ // Going away: n!u@h AWAY :reason
+ AwayMessage msg(user);
+ ClientProtocol::Event awayevent(joinhook.awayprotoev, msg);
+ IRCv3::WriteNeighborsWithCap(user, awayevent, joinhook.awaycap);
}
- void Prioritize()
+ void OnUserBack(User* user) CXX11_OVERRIDE
{
- ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST);
+ if (!joinhook.awaycap.IsActive())
+ return;
+
+ // Back from away: n!u@h AWAY
+ AwayMessage msg(user);
+ ClientProtocol::Event awayevent(joinhook.awayprotoev, msg);
+ IRCv3::WriteNeighborsWithCap(user, awayevent, joinhook.awaycap);
}
Version GetVersion() CXX11_OVERRIDE