X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_chanprotect.cpp;h=affd0c8d674324649a674af542da9834a85ccf2f;hb=e6601069038c35c546fd3f3dce95024b0d13f1b4;hp=6314cca945e44297df7d745ae1235501e682c77b;hpb=ab01aaeeee9aed655df2eec2522072233fe3aa57;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_chanprotect.cpp b/src/modules/m_chanprotect.cpp index 6314cca94..affd0c8d6 100644 --- a/src/modules/m_chanprotect.cpp +++ b/src/modules/m_chanprotect.cpp @@ -1,380 +1,308 @@ -/* +------------------------------------+ - * | Inspire Internet Relay Chat Daemon | - * +------------------------------------+ +/* + * InspIRCd -- Internet Relay Chat Daemon * - * Inspire is copyright (C) 2002-2004 ChatSpike-Dev. - * E-mail: - * - * - * - * Written by Craig Edwards, Craig McLure, and others. - * This program is free but copyrighted software; see - * the file COPYING for details. + * Copyright (C) 2009 Daniel De Graaf + * Copyright (C) 2006-2009 Robin Burchell + * Copyright (C) 2008 Thomas Stagner + * Copyright (C) 2008 Pippijn van Steenhoven + * Copyright (C) 2004-2008 Craig Edwards + * Copyright (C) 2007 John Brooks + * Copyright (C) 2007 Dennis Friis * - * --------------------------------------------------- + * 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 . */ -using namespace std; -#include -#include "users.h" -#include "channels.h" -#include "modules.h" -#include "helperfuncs.h" +#include "inspircd.h" /* $ModDesc: Provides channel modes +a and +q */ -char dummyvalue[] = "on"; +#define PROTECT_VALUE 40000 +#define FOUNDER_VALUE 50000 -class ModuleChanProtect : public Module +struct ChanProtectSettings { - Server *Srv; + bool DeprivSelf; + bool DeprivOthers; bool FirstInGetsFounder; - ConfigReader *Conf; - + bool booting; + ChanProtectSettings() : booting(true) {} +}; + +static ChanProtectSettings settings; + +/** Handles basic operation of +qa channel modes + */ +class FounderProtectBase +{ + private: + const std::string type; + const char mode; + const int list; + const int end; public: - - ModuleChanProtect() + FounderProtectBase(char Mode, const std::string &mtype, int l, int e) : + type(mtype), mode(Mode), list(l), end(e) { - - // here we initialise our module. Use new to create new instances of the required - // classes. - - Srv = new Server; - Conf = new ConfigReader; - - // set up our modes. We're using listmodes and not normal extmodes here. - // listmodes only need one parameter as everything else is assumed by the - // nature of the mode thats being created. - Srv->AddExtendedListMode('a'); - Srv->AddExtendedListMode('q'); - - // read our config options (main config file) - FirstInGetsFounder = Conf->ReadFlag("options","noservices",0); - } - - virtual void On005Numeric(std::string &output) - { - std::stringstream line(output); - std::string temp1, temp2; - while (!line.eof()) - { - line >> temp1; - if (temp1.substr(0,10) == "CHANMODES=") - { - // append the chanmode to the end - temp1 = temp1.substr(10,temp1.length()); - temp1 = "CHANMODES=qa" + temp1; - } - temp2 = temp2 + temp1 + " "; - } - if (temp2.length()) - output = temp2.substr(0,temp2.length()-1); - } - - virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, std::string reason) - { - // FIX: when someone gets kicked from a channel we must remove their Extensibles! - user->Shrink("cm_founder_"+std::string(chan->name)); - user->Shrink("cm_protect_"+std::string(chan->name)); } - virtual void OnUserPart(userrec* user, chanrec* channel) + void RemoveMode(Channel* channel, irc::modestacker* stack) { - // FIX: when someone parts a channel we must remove their Extensibles! - user->Shrink("cm_founder_"+std::string(channel->name)); - user->Shrink("cm_protect_"+std::string(channel->name)); - } + const UserMembList* cl = channel->GetUsers(); + std::vector mode_junk; + mode_junk.push_back(channel->name); + irc::modestacker modestack(false); + std::deque stackresult; - virtual void OnRehash() - { - // on a rehash we delete our classes for good measure and create them again. - delete Conf; - Conf = new ConfigReader; - // re-read our config options on a rehash - FirstInGetsFounder = Conf->ReadFlag("options","noservices",0); - } - - virtual void OnUserJoin(userrec* user, chanrec* channel) - { - // if the user is the first user into the channel, mark them as the founder, but only if - // the config option for it is set - if (FirstInGetsFounder) + for (UserMembCIter i = cl->begin(); i != cl->end(); i++) { - if (Srv->CountUsers(channel) == 1) + if (i->second->hasMode(mode)) { - // we're using Extensible::Extend to add data into user objects. - // this way is best as it adds data thats accessible to other modules - // (so long as you document your code properly) without breaking anything - // because its encapsulated neatly in a map. - if (user->Extend("cm_founder_"+std::string(channel->name),dummyvalue)) - { - Srv->Log(DEBUG,"Marked user "+std::string(user->nick)+" as founder for "+std::string(channel->name)); - } + if (stack) + stack->Push(mode, i->first->nick); + else + modestack.Push(mode, i->first->nick); } } - } - - virtual int OnAccessCheck(userrec* source,userrec* dest,chanrec* channel,int access_type) - { - // here we perform access checks, this is the important bit that actually stops kicking/deopping - // etc of protected users. There are many types of access check, we're going to handle - // a relatively small number of them relevent to our module using a switch statement. - - // don't allow action if: - // (A) Theyre founder (no matter what) - // (B) Theyre protected, and you're not - // always allow the action if: - // (A) The source is ulined - - - // firstly, if a ulined nick, or a server, is setting the mode, then allow them to set the mode - // without any access checks, we're not worthy :p - if ((Srv->IsUlined(source->nick)) || (Srv->IsUlined(source->server)) || (!strcmp(source->server,""))) + + if (stack) + return; + + while (modestack.GetStackedLine(stackresult)) { - return ACR_ALLOW; + mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end()); + ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient); + mode_junk.erase(mode_junk.begin() + 1, mode_junk.end()); } + } - switch (access_type) + void DisplayList(User* user, Channel* channel) + { + const UserMembList* cl = channel->GetUsers(); + for (UserMembCIter i = cl->begin(); i != cl->end(); ++i) { - // a user has been deopped. Do we let them? hmmm... - case AC_DEOP: - if (dest->GetExt("cm_founder_"+std::string(channel->name))) - { - Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as the're a channel founder"); - return ACR_DENY; - } - if ((dest->GetExt("cm_protect_"+std::string(channel->name))) && (!source->GetExt("cm_protect_"+std::string(channel->name)))) - { - Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't deop "+std::string(dest->nick)+" as the're protected (+a)"); - return ACR_DENY; - } - break; - - // a user is being kicked. do we chop off the end of the army boot? - case AC_KICK: - if (dest->GetExt("cm_founder_"+std::string(channel->name))) - { - Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as the're a channel founder"); - return ACR_DENY; - } - if ((dest->GetExt("cm_protect_"+std::string(channel->name))) && (!source->GetExt("cm_protect_"+std::string(channel->name)))) - { - Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't kick "+std::string(dest->nick)+" as the're protected (+a)"); - return ACR_DENY; - } - break; - - // a user is being dehalfopped. Yes, we do disallow -h of a +ha user - case AC_DEHALFOP: - if (dest->GetExt("cm_founder_"+std::string(channel->name))) - { - Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as the're a channel founder"); - return ACR_DENY; - } - if ((dest->GetExt("cm_protect_"+std::string(channel->name))) && (!source->GetExt("cm_protect_"+std::string(channel->name)))) - { - Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't de-halfop "+std::string(dest->nick)+" as the're protected (+a)"); - return ACR_DENY; - } - break; - - // same with devoice. - case AC_DEVOICE: - if (dest->GetExt("cm_founder_"+std::string(channel->name))) - { - Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as the're a channel founder"); - return ACR_DENY; - } - if ((dest->GetExt("cm_protect_"+std::string(channel->name))) && (!source->GetExt("cm_protect_"+std::string(channel->name)))) - { - Srv->SendServ(source->fd,"484 "+std::string(source->nick)+" "+std::string(channel->name)+" :Can't devoice "+std::string(dest->nick)+" as the're protected (+a)"); - return ACR_DENY; - } - break; + if (i->second->hasMode(mode)) + { + user->WriteServ("%d %s %s %s", list, user->nick.c_str(), channel->name.c_str(), i->first->nick.c_str()); + } } - - // we dont know what this access check is, or dont care. just carry on, nothing to see here. - return ACR_DEFAULT; + user->WriteServ("%d %s %s :End of channel %s list", end, user->nick.c_str(), channel->name.c_str(), type.c_str()); + } + + bool CanRemoveOthers(User* u1, Channel* c) + { + Membership* m1 = c->GetUser(u1); + return (settings.DeprivOthers && m1 && m1->hasMode(mode)); + } +}; + +/** Abstraction of FounderProtectBase for channel mode +q + */ +class ChanFounder : public ModeHandler, public FounderProtectBase +{ + public: + ChanFounder(Module* Creator) + : ModeHandler(Creator, "founder", 'q', PARAM_ALWAYS, MODETYPE_CHANNEL), + FounderProtectBase('q', "founder", 386, 387) + { + ModeHandler::list = true; + levelrequired = FOUNDER_VALUE; + m_paramtype = TR_NICK; + } + + void setPrefix(int pfx) + { + prefix = pfx; + } + + unsigned int GetPrefixRank() + { + return FOUNDER_VALUE; + } + + void RemoveMode(Channel* channel, irc::modestacker* stack) + { + FounderProtectBase::RemoveMode(channel, stack); + } + + void RemoveMode(User* user, irc::modestacker* stack) + { } - virtual int OnExtendedMode(userrec* user, void* target, char modechar, int type, bool mode_on, string_list ¶ms) + ModResult AccessCheck(User* source, Channel* channel, std::string ¶meter, bool adding) { - // not out mode, bail - if ((modechar == 'q') && (type == MT_CHANNEL)) + User* theuser = ServerInstance->FindNick(parameter); + // remove own privs? + if (source == theuser && !adding && settings.DeprivSelf) + return MOD_RES_ALLOW; + + if (!adding && FounderProtectBase::CanRemoveOthers(source, channel)) { - // set up parameters - chanrec* chan = (chanrec*)target; - userrec* theuser = Srv->FindNick(params[0]); - - // cant find the user given as the parameter, eat the mode change. - if (!theuser) - return -1; - - // given user isnt even on the channel, eat the mode change - if (!Srv->IsOnChannel(theuser,chan)) - return -1; - - // source is a server, or ulined, we'll let them +-q the user. - if ((Srv->IsUlined(user->nick)) || (Srv->IsUlined(user->server)) || (!strcmp(user->server,""))) - { - if (mode_on) - { - if (!theuser->GetExt("cm_founder_"+std::string(chan->name))) - { - theuser->Extend("cm_founder_"+std::string(chan->name),dummyvalue); - return 1; - } - } - else - { - if (theuser->GetExt("cm_founder_"+std::string(chan->name))) - { - theuser->Shrink("cm_founder_"+std::string(chan->name)); - return 1; - } - } - - return -1; - } - else - { - // whoops, someones being naughty! - WriteServ(user->fd,"468 %s %s :Only servers may set channel mode +q",user->nick, chan->name); - return -1; - } + return MOD_RES_PASSTHRU; } - if ((modechar == 'a') && (type == MT_CHANNEL)) + else { - // set up parameters - chanrec* chan = (chanrec*)target; - userrec* theuser = Srv->FindNick(params[0]); - - // cant find the user given as the parameter, eat the mode change. - if (!theuser) - return -1; - - // given user isnt even on the channel, eat the mode change - if (!Srv->IsOnChannel(theuser,chan)) - return -1; - - // source has +q, is a server, or ulined, we'll let them +-a the user. - if ((Srv->IsUlined(user->nick)) || (Srv->IsUlined(user->server)) || (!strcmp(user->server,"")) || (user->GetExt("cm_founder_"+std::string(chan->name)))) - { - if (mode_on) - { - if (!theuser->GetExt("cm_protect_"+std::string(chan->name))) - { - theuser->Extend("cm_protect_"+std::string(chan->name),dummyvalue); - return 1; - } - } - else - { - if (theuser->GetExt("cm_protect_"+std::string(chan->name))) - { - theuser->Shrink("cm_protect_"+std::string(chan->name)); - return 1; - } - } - - return -1; - } - else - { - // bzzzt, wrong answer! - WriteServ(user->fd,"482 %s %s :You are not a channel founder",user->nick, chan->name); - return -1; - } + source->WriteNumeric(468, "%s %s :Only servers may set channel mode +q", source->nick.c_str(), channel->name.c_str()); + return MOD_RES_DENY; } - return 0; } - virtual void OnSendList(userrec* user, chanrec* channel, char mode) + ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) { - if (mode == 'q') - { - chanuserlist cl = Srv->GetUsers(channel); - for (int i = 0; i < cl.size(); i++) - { - if (cl[i]->GetExt("cm_founder_"+std::string(channel->name))) - { - WriteServ(user->fd,"386 %s %s %s",user->nick, channel->name,cl[i]->nick); - } - } - WriteServ(user->fd,"387 %s %s :End of channel founder list",user->nick, channel->name); - } - if (mode == 'a') - { - chanuserlist cl = Srv->GetUsers(channel); - for (int i = 0; i < cl.size(); i++) - { - if (cl[i]->GetExt("cm_protect_"+std::string(channel->name))) - { - WriteServ(user->fd,"388 %s %s %s",user->nick, channel->name,cl[i]->nick); - } - } - WriteServ(user->fd,"389 %s %s :End of channel protected user list",user->nick, channel->name); - } + return MODEACTION_ALLOW; + } + void DisplayList(User* user, Channel* channel) + { + FounderProtectBase::DisplayList(user,channel); } - - virtual ~ModuleChanProtect() +}; + +/** Abstraction of FounderProtectBase for channel mode +a + */ +class ChanProtect : public ModeHandler, public FounderProtectBase +{ + public: + ChanProtect(Module* Creator) + : ModeHandler(Creator, "admin", 'a', PARAM_ALWAYS, MODETYPE_CHANNEL), + FounderProtectBase('a',"protected user", 388, 389) { - delete Conf; - delete Srv; + ModeHandler::list = true; + levelrequired = PROTECT_VALUE; + m_paramtype = TR_NICK; } - - virtual Version GetVersion() + + void setPrefix(int pfx) { - return Version(1,0,0,0,VF_STATIC|VF_VENDOR); + prefix = pfx; } - - virtual string_list OnChannelSync(chanrec* chan) + + + unsigned int GetPrefixRank() + { + return PROTECT_VALUE; + } + + void RemoveMode(Channel* channel, irc::modestacker* stack) { - // this is called when the server is linking into a net and wants to sync channel data. - // we should send our mode changes for the channel here to ensure that other servers - // know whos +q/+a on the channel. - chanuserlist cl = Srv->GetUsers(chan); - string_list commands; - for (int i = 0; i < cl.size(); i++) + FounderProtectBase::RemoveMode(channel, stack); + } + + void RemoveMode(User* user, irc::modestacker* stack) + { + } + + ModResult AccessCheck(User* source, Channel* channel, std::string ¶meter, bool adding) + { + User* theuser = ServerInstance->FindNick(parameter); + // source has +q + if (channel->GetPrefixValue(source) > PROTECT_VALUE) + return MOD_RES_ALLOW; + + // removing own privs? + if (source == theuser && !adding && settings.DeprivSelf) + return MOD_RES_ALLOW; + + if (!adding && FounderProtectBase::CanRemoveOthers(source, channel)) { - if (cl[i]->GetExt("cm_founder_"+std::string(chan->name))) - { - commands.push_back("M "+std::string(chan->name)+" +q "+std::string(cl[i]->nick)); - } - if (cl[i]->GetExt("cm_protect_"+std::string(chan->name))) - { - commands.push_back("M "+std::string(chan->name)+" +a "+std::string(cl[i]->nick)); - } + return MOD_RES_PASSTHRU; + } + else + { + source->WriteNumeric(482, "%s %s :You are not a channel founder", source->nick.c_str(), channel->name.c_str()); + return MOD_RES_DENY; } - return commands; } -}; + ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding) + { + return MODEACTION_ALLOW; + } + void DisplayList(User* user, Channel* channel) + { + FounderProtectBase::DisplayList(user, channel); + } + +}; -class ModuleChanProtectFactory : public ModuleFactory +class ModuleChanProtect : public Module { + ChanProtect cp; + ChanFounder cf; public: - ModuleChanProtectFactory() + ModuleChanProtect() : cp(this), cf(this) { } - - ~ModuleChanProtectFactory() + + void init() { + /* Load config stuff */ + LoadSettings(); + settings.booting = false; + + ServerInstance->Modules->AddService(cf); + ServerInstance->Modules->AddService(cp); + + Implementation eventlist[] = { I_OnUserPreJoin }; + ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); } - - virtual Module * CreateModule() + + void LoadSettings() { - return new ModuleChanProtect; + ConfigTag* tag = ServerInstance->Config->ConfValue("chanprotect"); + + settings.FirstInGetsFounder = tag->getBool("noservices"); + + std::string qpre = tag->getString("qprefix"); + char QPrefix = qpre.empty() ? 0 : qpre[0]; + + std::string apre = tag->getString("aprefix"); + char APrefix = apre.empty() ? 0 : apre[0]; + + if ((APrefix && QPrefix) && APrefix == QPrefix) + throw ModuleException("What the smeg, why are both your +q and +a prefixes the same character?"); + + if (settings.booting) + { + if (APrefix && ServerInstance->Modes->FindPrefix(APrefix) && ServerInstance->Modes->FindPrefix(APrefix) != &cp) + throw ModuleException("Looks like the +a prefix you picked for m_chanprotect is already in use. Pick another."); + + if (QPrefix && ServerInstance->Modes->FindPrefix(QPrefix) && ServerInstance->Modes->FindPrefix(QPrefix) != &cf) + throw ModuleException("Looks like the +q prefix you picked for m_chanprotect is already in use. Pick another."); + + cp.setPrefix(APrefix); + cf.setPrefix(QPrefix); + } + settings.DeprivSelf = tag->getBool("deprotectself", true); + settings.DeprivOthers = tag->getBool("deprotectothers", true); } - -}; + ModResult OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven) + { + // if the user is the first user into the channel, mark them as the founder, but only if + // the config option for it is set + + if (settings.FirstInGetsFounder && !chan) + privs += 'q'; -extern "C" void * init_module( void ) -{ - return new ModuleChanProtectFactory; -} + return MOD_RES_PASSTHRU; + } + + Version GetVersion() + { + return Version("Founder and Protect modes (+qa)", VF_VENDOR); + } +}; +MODULE_INIT(ModuleChanProtect)