/* +------------------------------------+ * | Inspire Internet Relay Chat Daemon | * +------------------------------------+ * * InspIRCd: (C) 2002-2011 InspIRCd Development Team * See: http://wiki.inspircd.org/Credits * * This program is free but copyrighted software; see * the file COPYING for details. * * --------------------------------------------------- */ #include "inspircd.h" #include "protocol.h" #include "account.h" #include "opflags.h" #include "u_listmode.h" /* $ModDesc: Provides support for the +w channel mode, autoop list */ static dynamic_reference db("accountdb"); /** Handles +n user mode */ class NoAutoopMode : public SimpleUserModeHandler { public: NoAutoopMode(Module* Creator) : SimpleUserModeHandler(Creator, "noautoop", 'n') { } }; /** Handles +w channel mode */ class AutoOpList : public ListModeBase { public: AutoOpList(Module* Creator) : ListModeBase(Creator, "autoop", 'w', "End of Channel Access List", 910, 911, true) { levelrequired = OP_VALUE; fixed_letter = false; tidy = false; } void AccessCheck(ModePermissionData& perm) { std::string::size_type pos = perm.mc.value.find(':'); if (pos == 0 || pos == std::string::npos) { if (perm.mc.adding) perm.result = MOD_RES_DENY; return; } unsigned int mylevel = perm.chan->GetAccessRank(perm.source); irc::commasepstream modes(perm.mc.value.substr(0, pos)); std::string mid; while (modes.GetToken(mid)) { ModeHandler* mh = mid.length() == 1 ? ServerInstance->Modes->FindMode(mid[0], MODETYPE_CHANNEL) : ServerInstance->Modes->FindMode(mid); if (perm.mc.adding && mh && mh->GetLevelRequired() > mylevel) { perm.ErrorNumeric(482, "%s :You must be able to set mode '%s' to include it in an autoop", perm.chan->name.c_str(), mid.c_str()); perm.result = MOD_RES_DENY; } } } }; class ModuleAutoOp : public Module { AutoOpList mh; NoAutoopMode mh2; dynamic_reference opflags; bool checkonlogin, checkonhostchange, allow_selfup; public: ModuleAutoOp() : mh(this), mh2(this), opflags("opflags") { } void init() { mh.init(); ServerInstance->Modules->AddService(mh); ServerInstance->Modules->AddService(mh2); Implementation list[] = { I_OnPostJoin, I_OnEvent, I_OnChangeHost, I_OnPermissionCheck, I_OnGarbageCollect }; ServerInstance->Modules->Attach(list, this, sizeof(list)/sizeof(Implementation)); } void PrepareAutoop(Membership* memb, irc::modestacker& ms, std::set& flags) { modelist* list = mh.extItem.get(memb->chan); if (!list) return; for (modelist::const_iterator it = list->begin(); it != list->end(); ++it) { std::string::size_type colon = it->mask.find(':'); if (colon == std::string::npos) continue; if (!memb->chan->CheckBan(memb->user, it->mask.substr(colon+1))) continue; irc::commasepstream modes(it->mask.substr(0, colon)); std::string mflag; while (modes.GetToken(mflag)) { ModeHandler* given = mflag.length() == 1 ? ServerInstance->Modes->FindMode(mflag[0], MODETYPE_CHANNEL) : ServerInstance->Modes->FindMode(mflag); if (given && given->GetPrefixRank()) ms.push(irc::modechange(given->id, memb->user->nick, true)); else if (!given) { // it's a flag flags.insert(mflag); } } } } void DoAutoop(Membership* memb) { irc::modestacker ms; std::set flags; PrepareAutoop(memb, ms, flags); if (!ms.empty()) { User* src = ServerInstance->Config->CycleHostsFromUser ? memb->user : ServerInstance->FakeClient; ServerInstance->Modes->Process(src, memb->chan, ms, false, true); ServerInstance->Modes->Send(src, memb->chan, ms); ServerInstance->PI->SendMode(src, memb->chan, ms); } if (opflags && !flags.empty()) { std::string message = ServerInstance->Config->ServerName + " set " + memb->user->nick + " opflags to " + opflags->SetFlags(memb, flags, true); memb->chan->WriteChannelWithServ(ServerInstance->Config->ServerName.c_str(), "NOTICE %s :%s", memb->chan->name.c_str(), message.c_str()); ServerInstance->PI->SendChannelNotice(memb->chan, 0, message); } } void OnPostJoin(Membership* memb) { if (!IS_LOCAL(memb->user) || memb->user->IsModeSet('n')) return; DoAutoop(memb); } void OnEvent(Event& event) { if(checkonlogin && event.id == "account_login"){ AccountEvent& acct_event = static_cast(event); if(!IS_LOCAL(acct_event.user) || acct_event.user->IsModeSet('n')) return; for (UCListIter v = acct_event.user->chans.begin(); v != acct_event.user->chans.end(); ++v) DoAutoop(&*v); } } void OnChangeHost(User* u, const std::string&) { if(checkonhostchange && !u->IsModeSet('n')) for (UCListIter v = u->chans.begin(); v != u->chans.end(); ++v) DoAutoop(&*v); } void OnPermissionCheck(PermissionData& perm) { if (!allow_selfup || !perm.chan || perm.user != perm.source || perm.result != MOD_RES_PASSTHRU) return; Membership* memb = perm.chan->GetUser(perm.user); if (!memb) return; irc::modestacker ms; std::set flags; PrepareAutoop(memb, ms, flags); if (perm.name.substr(0,5) == "mode/") { ModePermissionData& mpd = static_cast(perm); if (!mpd.mc.adding) return; for(std::vector::const_iterator i = ms.sequence.begin(); i != ms.sequence.end(); ++i) { if (i->mode == mpd.mc.mode) { mpd.result = MOD_RES_ALLOW; return; } } } else if (perm.name == "opflags") { OpFlagPermissionData& opd = static_cast(perm); irc::commasepstream deltaflags(opd.delta); std::string flag; while (deltaflags.GetToken(flag)) { if (flag[0] == '-' || flag[0] == '=') return; if (flag[0] == '+') flag = flag.substr(1); if (flags.find(flag) == flags.end()) return; } // all flags mentioned in this change are in the autoop list opd.result = MOD_RES_ALLOW; } } void ReadConfig(ConfigReadStatus&) { mh.DoRehash(); ConfigTag* tag = ServerInstance->Config->GetTag("autoop"); allow_selfup = tag->getBool("selfop", true); checkonlogin = tag->getBool("checkonlogin", true); checkonhostchange = tag->getBool("checkonhostchange", true); } void Prioritize() { ServerInstance->Modules->SetPriority(this, I_OnGarbageCollect, PRIORITY_AFTER, ServerInstance->Modules->Find("m_account_register.so")); } void OnGarbageCollect () { if(!db) return; irc::modestacker ms; for (chan_hash::const_iterator iter = ServerInstance->chanlist->begin(); iter != ServerInstance->chanlist->end(); ++iter) { modelist* list = mh.extItem.get(iter->second); if (!list) continue; ms.sequence.clear(); for (modelist::const_iterator it = list->begin(); it != list->end(); ++it) { std::string::size_type colon = it->mask.find_first_of(':'); if (colon == std::string::npos) continue; std::string mask = it->mask.substr(colon + 1); if(mask.length() < 2 || mask[0] != 'r' || mask[1] != ':') continue; if(!db->GetAccount(mask.substr(2), false)) ms.sequence.push_back(irc::modechange(mh.id, it->mask, false)); } if(!ms.sequence.empty()) ServerInstance->SendMode(ServerInstance->FakeClient, iter->second, ms, true); } } Version GetVersion() { return Version("Provides support for the +w channel mode", VF_VENDOR); } }; MODULE_INIT(ModuleAutoOp)