X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_denychans.cpp;h=4bddcf1cda30c3f13801c4da25e19d38683f8dfc;hb=56375392ba94f2552bbeeeab4fd39e1e50295525;hp=3a5ea7462796321fa3b7252f5f05be4399f00042;hpb=177f5c59c4dfb86447212da516222c020264bc29;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_denychans.cpp b/src/modules/m_denychans.cpp index 3a5ea7462..4bddcf1cd 100644 --- a/src/modules/m_denychans.cpp +++ b/src/modules/m_denychans.cpp @@ -1,133 +1,193 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * InspIRCd: (C) 2002-2008 InspIRCd Development Team - * See: http://www.inspircd.org/wiki/index.php/Credits + *. Copyright (C) 2018 Sadie Powell + * Copyright (C) 2009 Daniel De Graaf + * Copyright (C) 2007-2008 Robin Burchell + * Copyright (C) 2007 Dennis Friis + * Copyright (C) 2005 Craig Edwards * - * This program is free but copyrighted software; see - * the file COPYING for details. + * 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 . */ + #include "inspircd.h" -#include "wildcard.h" -/* $ModDesc: Implements config tags which allow blocking of joins to channels */ +enum +{ + // InspIRCd-specific. + ERR_BADCHANNEL = 926 +}; + +struct BadChannel +{ + bool allowopers; + std::string name; + std::string reason; + std::string redirect; + + BadChannel(const std::string& Name, const std::string& Redirect, const std::string& Reason, bool AllowOpers) + : allowopers(AllowOpers) + , name(Name) + , reason(Reason) + , redirect(Redirect) + { + } +}; + +typedef std::vector BadChannels; +typedef std::vector GoodChannels; class ModuleDenyChannels : public Module { private: - - - ConfigReader *Conf; + BadChannels badchannels; + GoodChannels goodchannels; + UserModeReference antiredirectmode; + ChanModeReference redirectmode; public: - ModuleDenyChannels(InspIRCd* Me) : Module(Me) + ModuleDenyChannels() + : antiredirectmode(this, "antiredirect") + , redirectmode(this, "redirect") { - - Conf = new ConfigReader(ServerInstance); - Implementation eventlist[] = { I_OnUserPreJoin, I_OnRehash }; - ServerInstance->Modules->Attach(eventlist, this, 2); } - - virtual void OnRehash(User* user, const std::string ¶m) + + void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE { - delete Conf; - Conf = new ConfigReader(ServerInstance); - /* check for redirect validity and loops/chains */ - for (int i =0; i < Conf->Enumerate("badchan"); i++) + GoodChannels goodchans; + ConfigTagList tags = ServerInstance->Config->ConfTags("goodchan"); + for (ConfigIter iter = tags.first; iter != tags.second; ++iter) + { + ConfigTag* tag = iter->second; + + // Ensure that we have the parameter. + const std::string name = tag->getString("name"); + if (name.empty()) + throw ModuleException(" is a mandatory field, at " + tag->getTagLocation()); + + goodchans.push_back(name); + } + + BadChannels badchans; + tags = ServerInstance->Config->ConfTags("badchan"); + for (ConfigIter i = tags.first; i != tags.second; ++i) { - std::string name = Conf->ReadValue("badchan","name",i); - std::string redirect = Conf->ReadValue("badchan","redirect",i); - + ConfigTag* tag = i->second; + + // Ensure that we have the parameter. + const std::string name = tag->getString("name"); + if (name.empty()) + throw ModuleException(" is a mandatory field, at " + tag->getTagLocation()); + + // Ensure that we have the parameter. + const std::string reason = tag->getString("reason"); + if (reason.empty()) + throw ModuleException(" is a mandatory field, at " + tag->getTagLocation()); + + const std::string redirect = tag->getString("redirect"); if (!redirect.empty()) { - - if (!ServerInstance->IsChannel(redirect.c_str())) - { - if (user) - user->WriteServ("Notice %s :Invalid badchan redirect '%s'", user->nick, redirect.c_str()); - throw ModuleException("Invalid badchan redirect, not a channel"); - } - - for (int j =0; j < Conf->Enumerate("badchan"); j++) + // Ensure that contains a channel name. + if (!ServerInstance->IsChannel(redirect)) + throw ModuleException(" is not a valid channel name, at " + tag->getTagLocation()); + + // We defer the rest of the validation of the redirect channel until we have + // finished parsing all of the badchans. + } + + badchans.push_back(BadChannel(name, redirect, reason, tag->getBool("allowopers"))); + } + + // Now we have all of the badchan information recorded we can check that all redirect + // channels can actually be redirected to. + for (BadChannels::const_iterator i = badchans.begin(); i != badchans.end(); ++i) + { + const BadChannel& badchan = *i; + + // If there is no redirect channel we have nothing to do. + if (badchan.redirect.empty()) + continue; + + // If the redirect channel is whitelisted then it is okay. + bool whitelisted = false; + for (GoodChannels::const_iterator j = goodchans.begin(); j != goodchans.end(); ++j) + { + if (InspIRCd::Match(badchan.redirect, *j)) { - if (match(redirect.c_str(), Conf->ReadValue("badchan","name",j).c_str())) - { - bool goodchan = false; - for (int k =0; k < Conf->Enumerate("goodchan"); k++) - { - if (match(redirect.c_str(), Conf->ReadValue("goodchan","name",k).c_str())) - goodchan = true; - } - - if (!goodchan) - { - /* is a badchan */ - if (user) - user->WriteServ("NOTICE %s :Badchan %s redirects to badchan %s", user->nick, name.c_str(), redirect.c_str()); - throw ModuleException("Badchan redirect loop"); - } - } + whitelisted = true; + break; } } + + if (whitelisted) + continue; + + // If the redirect channel is not blacklisted then it is okay. + for (BadChannels::const_iterator j = badchans.begin(); j != badchans.end(); ++j) + if (InspIRCd::Match(badchan.redirect, j->name)) + throw ModuleException(" cannot be a blacklisted channel name"); } - } - virtual ~ModuleDenyChannels() - { - delete Conf; + // The config file contained no errors so we can apply the new configuration. + badchannels.swap(badchans); + goodchannels.swap(goodchans); } - - virtual Version GetVersion() + + Version GetVersion() CXX11_OVERRIDE { - return Version(1,1,0,1,VF_VENDOR,API_VERSION); + return Version("Implements config tags which allow blocking of joins to channels", VF_VENDOR); } - virtual int OnUserPreJoin(User* user, Channel* chan, const char* cname, std::string &privs) + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven) CXX11_OVERRIDE { - for (int j =0; j < Conf->Enumerate("badchan"); j++) + for (BadChannels::const_iterator j = badchannels.begin(); j != badchannels.end(); ++j) { - if (match(cname, Conf->ReadValue("badchan","name",j).c_str())) + const BadChannel& badchan = *j; + + // If the channel does not match the current entry we have nothing else to do. + if (!InspIRCd::Match(cname, badchan.name)) + continue; + + // If the user is an oper and opers are allowed to enter this blacklisted channel + // then allow the join. + if (user->IsOper() && badchan.allowopers) + return MOD_RES_PASSTHRU; + + // If the channel matches a whitelist then allow the join. + for (GoodChannels::const_iterator i = goodchannels.begin(); i != goodchannels.end(); ++i) + if (InspIRCd::Match(cname, *i)) + return MOD_RES_PASSTHRU; + + // If there is no redirect chan, the user has enabled the antiredirect mode, or + // the target channel redirects elsewhere we just tell the user and deny the join. + Channel* target = NULL; + if (badchan.redirect.empty() || user->IsModeSet(antiredirectmode) + || ((target = ServerInstance->FindChan(badchan.redirect)) && target->IsModeSet(redirectmode))) { - if (IS_OPER(user) && Conf->ReadFlag("badchan","allowopers",j)) - { - return 0; - } - else - { - std::string reason = Conf->ReadValue("badchan","reason",j); - std::string redirect = Conf->ReadValue("badchan","redirect",j); - - for (int i = 0; i < Conf->Enumerate("goodchan"); i++) - { - if (match(cname, Conf->ReadValue("goodchan", "name", i).c_str())) - { - return 0; - } - } - - if (ServerInstance->IsChannel(redirect.c_str())) - { - /* simple way to avoid potential loops: don't redirect to +L channels */ - Channel *newchan = ServerInstance->FindChan(redirect); - if ((!newchan) || (!(newchan->IsModeSet('L')))) - { - user->WriteServ("926 %s %s :Channel %s is forbidden, redirecting to %s: %s",user->nick,cname,cname,redirect.c_str(), reason.c_str()); - Channel::JoinUser(ServerInstance,user,redirect.c_str(),false,"",false,ServerInstance->Time(true)); - return 1; - } - } - - user->WriteServ("926 %s %s :Channel %s is forbidden: %s",user->nick,cname,cname,reason.c_str()); - return 1; - } + user->WriteNumeric(ERR_BADCHANNEL, cname, InspIRCd::Format("Channel %s is forbidden: %s", + cname.c_str(), badchan.reason.c_str())); + return MOD_RES_DENY; } + + // Redirect the user to the target channel. + user->WriteNumeric(ERR_BADCHANNEL, cname, InspIRCd::Format("Channel %s is forbidden, redirecting to %s: %s", + cname.c_str(), badchan.redirect.c_str(), badchan.reason.c_str())); + Channel::JoinUser(user, badchan.redirect); + return MOD_RES_DENY; } - return 0; + return MOD_RES_PASSTHRU; } };