From 0f9b6f82cdf861796e12951dbe4ea0aa3f6f04d0 Mon Sep 17 00:00:00 2001 From: attilamolnar Date: Thu, 7 Jun 2012 20:38:44 +0200 Subject: [PATCH] New module to support IRCv3 extras, namely account-notify, away-notify and extended-join CAPs --- src/modules/m_ircv3.cpp | 210 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 src/modules/m_ircv3.cpp diff --git a/src/modules/m_ircv3.cpp b/src/modules/m_ircv3.cpp new file mode 100644 index 0000000000..54cc45327d --- /dev/null +++ b/src/modules/m_ircv3.cpp @@ -0,0 +1,210 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2012 Attila Molnar + * + * 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 . + */ + +/* $ModDesc: Provides support for extended-join, away-notify and account-notify CAP capabilities */ + +#include "inspircd.h" +#include "account.h" +#include "m_cap.h" + +static dynamic_reference accountprovider("account"); + +class ModuleIRCv3 : public Module +{ + GenericCap cap_accountnotify; + GenericCap cap_awaynotify; + GenericCap cap_extendedjoin; + bool accountnotify; + bool awaynotify; + bool extendedjoin; + + void WriteNeighboursWithExt(User* user, const std::string& line, const LocalIntExt& ext) + { + std::vector chans; + for (UCListIter it = user->chans.begin(); it != user->chans.end(); ++it) + chans.push_back(it->chan); + + std::map exceptions; + FOREACH_MOD(I_OnBuildNeighborList, 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::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 already_sent; + for (std::vector::const_iterator 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); + } + } + } + + public: + ModuleIRCv3() : cap_accountnotify(this, "account-notify"), + cap_awaynotify(this, "away-notify"), + cap_extendedjoin(this, "extended-join") + { + } + + void init() + { + Implementation eventlist[] = { I_OnUserJoin, I_OnSetAway, I_OnEvent }; + ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); + } + + void ReadConfig(ConfigReadStatus&) + { + ConfigTag* conf = ServerInstance->Config->GetTag("ircv3"); + accountnotify = conf->getBool("accoutnotify", true); + awaynotify = conf->getBool("awaynotify", true); + extendedjoin = conf->getBool("extendedjoin", true); + } + + void OnEvent(Event& ev) + { + if (awaynotify) + cap_awaynotify.HandleEvent(ev); + if (extendedjoin) + cap_extendedjoin.HandleEvent(ev); + + if (accountnotify) + { + cap_accountnotify.HandleEvent(ev); + + if (ev.id == "account_login") + { + AccountEvent* ae = static_cast(&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); + } + } + } + + void OnUserJoin(Membership* memb, bool sync, bool created, CUList& excepts) + { + if (!extendedjoin) + 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 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()) + { + std::string accountname; + if (accountprovider) + accountname = accountprovider->GetAccountName(memb->user); + + if (accountname.empty()) + accountname = "*"; + + line = ":" + memb->user->GetFullHost() + " JOIN " + memb->chan->name + " " + accountname + " :" + memb->user->fullname; + + // If the joining user received privileges from another module then we must send them as well, + // since silencing the 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); + } + } + } + + ModResult OnSetAway(User* user, const std::string &awaymsg) + { + 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; + + WriteNeighboursWithExt(user, line, cap_awaynotify.ext); + } + return MOD_RES_PASSTHRU; + } + + void Prioritize() + { + ServerInstance->Modules->SetPriority(this, I_OnUserJoin, PRIORITY_LAST); + } + + Version GetVersion() + { + return Version("Provides support for extended-join, away-notify and account-notify CAP capabilities", VF_VENDOR); + } +}; + +MODULE_INIT(ModuleIRCv3)