/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2014 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 . */ /// $ModAuthor: Attila Molnar updated by reverse for v4 /// $ModAuthorMail: attilamolnar@hush.com /// $ModConfig: /// $ModDepends: core 4 /// $ModDesc: Provide extended ban :: to redirect users to another channel #include "inspircd.h" #include "listmode.h" #include "modules/extban.h" enum { // From UnrealIRCd ERR_LINKCHANNEL = 470, // From m_banredirect ERR_REDIRECT = 690 }; class BanWatcher : public ModeWatcher { public: ExtBan::Base& extban; BanWatcher(Module* parent, ExtBan::Base& xb) : ModeWatcher(parent, "ban", MODETYPE_CHANNEL) , extban(xb) { } bool IsExtBanRedirect(const std::string& mask, std::string& value, bool& inverted) { std::string name; if (!ExtBan::Parse(mask, name, value, inverted)) return false; if (inverted) return false; if (name.size() == 1) return name[0] == extban.GetLetter(); return irc::equals(name, extban.GetName()); } bool BeforeMode(User* source, User*, Channel* channel, Modes::Change& change) override { if (!IS_LOCAL(source) || !channel || !change.adding) return true; std::string value; bool inverted; if (!IsExtBanRedirect(change.param, value, inverted)) return true; const std::string::size_type p = value.find(':'); if (p == std::string::npos) { source->WriteNumeric(ERR_REDIRECT, INSP_FORMAT("Extban redirect \"{}\" is invalid. Format: ::", change.param)); return false; } const std::string targetname(value, 0, p); if (!ServerInstance->Channels.IsChannel(targetname)) { source->WriteNumeric(ERR_NOSUCHCHANNEL, channel->name, INSP_FORMAT("Invalid channel name in redirection ({})", targetname)); return false; } Channel* const targetchan = ServerInstance->Channels.Find(targetname); if (!targetchan) { source->WriteNumeric(ERR_NOSUCHCHANNEL, channel->name, INSP_FORMAT("Target channel {} must exist to be set as a redirect.", targetname)); return false; } if (targetchan == channel) { source->WriteNumeric(ERR_NOSUCHCHANNEL, channel->name, "You cannot set a ban redirection to the channel the ban is on"); return false; } if (targetchan->GetPrefixValue(source) < OP_VALUE) { source->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, INSP_FORMAT("You must be opped on {} to set it as a redirect.", targetname)); return false; } return true; } }; class ExtBanRedirect final : public ExtBan::MatchingBase { static bool CheckSimpleBan(User* user, const std::string& mask) { const auto at = mask.find('@'); if (at == std::string::npos) return false; const std::string prefix(mask, 0, at); if (!InspIRCd::Match(user->nick + "!" + user->GetDisplayedUser(), prefix) && !InspIRCd::Match(user->nick + "!" + user->GetRealUser(), prefix)) { return false; } const std::string suffix(mask, at + 1); return InspIRCd::Match(user->GetRealHost(), suffix) || InspIRCd::Match(user->GetDisplayedHost(), suffix) || InspIRCd::MatchCIDR(user->GetAddress(), suffix); } public: ExtBanRedirect(Module* Creator) : MatchingBase(Creator, "redirect", 'd') { } void Canonicalize(std::string& text) override { const auto p = text.find(':'); if (p == std::string::npos) return; std::string chan(text, 0, p); std::string mask(text, p + 1); ModeParser::CleanMask(mask); text.assign(chan).append(":").append(mask); } bool IsMatch(User* user, Channel* channel, const std::string& text) override { const auto p = text.find(':'); if (p == std::string::npos) return false; const std::string mask(text, p + 1); return CheckSimpleBan(user, mask); } }; class ModuleExtBanRedirect : public Module { ChanModeReference limitmode; ChanModeReference limitredirect; ExtBanRedirect extban; BanWatcher banwatcher; bool active; public: ModuleExtBanRedirect() : Module(VF_VENDOR, "Provide extended ban :: to redirect users to another channel") , limitmode(this, "limit") , limitredirect(this, "redirect") , extban(this) , banwatcher(this, extban) , active(false) { } void ReadConfig(ConfigStatus&) override { // The extban letter is configured via . // This tag is kept for compatibility with older configs. const auto& tag = ServerInstance->Config->ConfValue("extbanredirect"); const auto confletter = tag->getString("char", "", 1, 1); if (!confletter.empty() && confletter[0] != extban.GetLetter()) ServerInstance->Logs.Debug(MODNAME, "Ignoring ; use instead.", confletter[0]); } ModResult OnCheckBan(User* user, Channel* chan, const std::string& mask) override { LocalUser* localuser = IS_LOCAL(user); if (active || !localuser) return MOD_RES_PASSTHRU; std::string value; bool inverted; if (!banwatcher.IsExtBanRedirect(mask, value, inverted)) return MOD_RES_PASSTHRU; if (!extban.IsMatch(localuser, chan, value)) return MOD_RES_PASSTHRU; std::string::size_type p = value.find(':'); if (p == std::string::npos) return MOD_RES_PASSTHRU; const std::string targetname = value.substr(0, p); Channel* const target = ServerInstance->Channels.Find(targetname); if (target && target->IsModeSet(limitmode)) { if (target->IsModeSet(limitredirect) && target->GetUsers().size() >= ConvToNum(target->GetModeParameter(limitmode))) { // The core will send "You're banned" return MOD_RES_DENY; } } // Ok to redirect // The core will send "You're banned" localuser->WriteNumeric(ERR_LINKCHANNEL, chan->name, targetname, "You are banned from this channel, so you are automatically being transferred to the redirected channel."); active = true; Channel::JoinUser(localuser, targetname); active = false; return MOD_RES_DENY; } }; MODULE_INIT(ModuleExtBanRedirect)