-/* +------------------------------------+
- * | Inspire Internet Relay Chat Daemon |
- * +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
*
- * InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
- * E-mail:
- * <brain@chatspike.net>
- * <Craig@chatspike.net>
- *
- * Written by Craig Edwards, Craig McLure, and others.
- * This program is free but copyrighted software; see
- * the file COPYING for details.
+ * Copyright (C) 2020 iwalkalone <iwalkalone69@gmail.com>
+ * Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
+ * Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
+ * Copyright (C) 2017 B00mX0r <b00mx0r@aureus.pw>
+ * Copyright (C) 2013, 2017-2018, 2020 Sadie Powell <sadie@witchery.services>
+ * Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
+ * Copyright (C) 2012-2016 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
+ * Copyright (C) 2009 John Brooks <special@inspircd.org>
+ * Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2005-2008, 2010 Craig Edwards <brain@inspircd.org>
*
- * ---------------------------------------------------
+ * 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 <http://www.gnu.org/licenses/>.
*/
-using namespace std;
-
-/* $ModDesc: Adds timed bans */
-#include <stdio.h>
-#include <vector>
-#include "users.h"
-#include "channels.h"
-#include "modules.h"
-
-#include "hashcomp.h"
-#include "configreader.h"
#include "inspircd.h"
+#include "listmode.h"
-
-
-
-class TimedBan : public classbase
+// Holds a timed ban
+class TimedBan
{
public:
- std::string channel;
std::string mask;
+ std::string setter;
time_t expire;
+ Channel* chan;
};
typedef std::vector<TimedBan> timedbans;
timedbans TimedBanList;
-class cmd_tban : public command_t
+// Handle /TBAN
+class CommandTban : public Command
{
- public:
- cmd_tban (InspIRCd* Instance) : command_t(Instance,"TBAN", 0, 3)
- {
- this->source = "m_timedbans.so";
- syntax = "<channel> <duration> <banmask>";
- }
+ ChanModeReference banmode;
- void Handle (const char** parameters, int pcnt, userrec *user)
+ bool IsBanSet(Channel* chan, const std::string& mask)
{
- chanrec* channel = ServerInstance->FindChan(parameters[0]);
- if (channel)
+ ListModeBase* banlm = static_cast<ListModeBase*>(*banmode);
+ if (!banlm)
+ return false;
+
+ const ListModeBase::ModeList* bans = banlm->GetList(chan);
+ if (bans)
{
- int cm = channel->GetStatus(user);
- if ((cm == STATUS_HOP) || (cm == STATUS_OP))
+ for (ListModeBase::ModeList::const_iterator i = bans->begin(); i != bans->end(); ++i)
{
- if (!ServerInstance->IsValidMask(parameters[2]))
- {
- user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban mask");
- return;
- }
- for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
- {
- if (!strcasecmp(i->data,parameters[2]))
- {
- user->WriteServ("NOTICE "+std::string(user->nick)+" :The ban "+std::string(parameters[2])+" is already on the banlist of "+std::string(parameters[0]));
- return;
- }
- }
- TimedBan T;
- std::string channelname = parameters[0];
- unsigned long expire = ServerInstance->Duration(parameters[1]) + time(NULL);
- if (ServerInstance->Duration(parameters[1]) < 1)
- {
- user->WriteServ("NOTICE "+std::string(user->nick)+" :Invalid ban time");
- return;
- }
- char duration[MAXBUF];
- snprintf(duration,MAXBUF,"%lu",ServerInstance->Duration(parameters[1]));
- std::string mask = parameters[2];
- const char *setban[32];
- setban[0] = parameters[0];
- setban[1] = "+b";
- setban[2] = parameters[2];
- // use CallCommandHandler to make it so that the user sets the mode
- // themselves
- ServerInstance->CallCommandHandler("MODE",setban,3,user);
- /* Check if the ban was actually added (e.g. banlist was NOT full) */
- bool was_added = false;
- for (BanList::iterator i = channel->bans.begin(); i != channel->bans.end(); i++)
- if (!strcasecmp(i->data,mask.c_str()))
- was_added = true;
- if (was_added)
- {
- T.channel = channelname;
- T.mask = mask;
- T.expire = expire;
- TimedBanList.push_back(T);
- channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s added a timed ban on %s lasting for %s seconds.", channel->name, user->nick, mask.c_str(), duration);
- }
- return;
+ const ListModeBase::ListItem& ban = *i;
+ if (irc::equals(ban.mask, mask))
+ return true;
}
- else user->WriteServ("482 %s %s :You must be at least a half-operator to change modes on this channel",user->nick, channel->name);
- return;
}
- user->WriteServ("401 %s %s :No such channel",user->nick, parameters[0]);
+
+ return false;
}
-};
-class ModuleTimedBans : public Module
-{
- cmd_tban* mycommand;
public:
- ModuleTimedBans(InspIRCd* Me)
- : Module::Module(Me)
+ bool sendnotice;
+
+ CommandTban(Module* Creator)
+ : Command(Creator,"TBAN", 3)
+ , banmode(Creator, "ban")
+ {
+ syntax = "<channel> <duration> <banmask>";
+ }
+
+ CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
{
-
- mycommand = new cmd_tban(ServerInstance);
- ServerInstance->AddCommand(mycommand);
- TimedBanList.clear();
+ Channel* channel = ServerInstance->FindChan(parameters[0]);
+ if (!channel)
+ {
+ user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
+ return CMD_FAILURE;
+ }
+
+ unsigned int cm = channel->GetPrefixValue(user);
+ if (cm < HALFOP_VALUE)
+ {
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You do not have permission to set bans on this channel");
+ return CMD_FAILURE;
+ }
+
+ TimedBan T;
+ unsigned long duration;
+ if (!InspIRCd::Duration(parameters[1], duration))
+ {
+ user->WriteNotice("Invalid ban time");
+ return CMD_FAILURE;
+ }
+ unsigned long expire = duration + ServerInstance->Time();
+
+ std::string mask = parameters[2];
+ bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
+ if (!isextban && !InspIRCd::IsValidMask(mask))
+ mask.append("!*@*");
+
+ if (IsBanSet(channel, mask))
+ {
+ user->WriteNotice("Ban already set");
+ return CMD_FAILURE;
+ }
+
+ Modes::ChangeList setban;
+ setban.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
+ // Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to
+ // make it so that the user sets the mode themselves
+ ServerInstance->Modes->Process(user, channel, NULL, setban);
+ if (ServerInstance->Modes->GetLastChangeList().empty())
+ {
+ user->WriteNotice("Invalid ban mask");
+ return CMD_FAILURE;
+ }
+
+ T.mask = mask;
+ T.setter = user->nick;
+ T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
+ T.chan = channel;
+ TimedBanList.push_back(T);
+
+ if (sendnotice)
+ {
+ const std::string message = InspIRCd::Format("Timed ban %s added by %s on %s lasting for %s.",
+ mask.c_str(), user->nick.c_str(), channel->name.c_str(), InspIRCd::DurationString(duration).c_str());
+ // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
+ char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
+
+ channel->WriteRemoteNotice(message, pfxchar);
+ }
+
+ return CMD_SUCCESS;
}
-
- virtual ~ModuleTimedBans()
+
+ RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- TimedBanList.clear();
+ return ROUTE_BROADCAST;
}
+};
- void Implements(char* List)
+class BanWatcher : public ModeWatcher
+{
+ public:
+ BanWatcher(Module* parent)
+ : ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
{
- List[I_OnDelBan] = List[I_OnBackgroundTimer] = 1;
}
- virtual int OnDelBan(userrec* source, chanrec* chan, const std::string &banmask)
+ void AfterMode(User* source, User* dest, Channel* chan, const std::string& banmask, bool adding) CXX11_OVERRIDE
{
- irc::string listitem = banmask.c_str();
- irc::string thischan = chan->name;
- for (timedbans::iterator i = TimedBanList.begin(); i < TimedBanList.end(); i++)
+ if (adding)
+ return;
+
+ for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); ++i)
{
- irc::string target = i->mask.c_str();
- irc::string tchan = i->channel.c_str();
- if ((listitem == target) && (tchan == thischan))
+ if (i->chan != chan)
+ continue;
+
+ const std::string& target = i->mask;
+ if (irc::equals(banmask, target))
{
TimedBanList.erase(i);
break;
}
}
- return 0;
}
+};
+
+class ChannelMatcher
+{
+ Channel* const chan;
- virtual void OnBackgroundTimer(time_t curtime)
+ public:
+ ChannelMatcher(Channel* ch)
+ : chan(ch)
{
- bool again = true;
- while (again)
- {
- again = false;
- for (timedbans::iterator i = TimedBanList.begin(); i < TimedBanList.end(); i++)
- {
- if (curtime > i->expire)
- {
- chanrec* cr = ServerInstance->FindChan(i->channel);
- again = true;
- if (cr)
- {
- cr->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :Timed ban on %s expired.", cr->name, i->mask.c_str());
- const char *setban[3];
- setban[0] = i->channel.c_str();
- setban[1] = "-b";
- setban[2] = i->mask.c_str();
- // kludge alert!
- // ::SendMode expects a userrec* to send the numeric replies
- // back to, so we create it a fake user that isnt in the user
- // hash and set its descriptor to FD_MAGIC_NUMBER so the data
- // falls into the abyss :p
- userrec* temp = new userrec(ServerInstance);
- temp->SetFd(FD_MAGIC_NUMBER);
- /* FIX: Send mode remotely*/
- std::deque<std::string> n;
- n.push_back(setban[0]);
- n.push_back("-b");
- n.push_back(setban[2]);
- ServerInstance->SendMode(setban,3,temp);
- Event rmode((char *)&n, NULL, "send_mode");
- rmode.Send(ServerInstance);
- DELETE(temp);
- }
- else
- {
- /* Where the hell did our channel go?! */
- TimedBanList.erase(i);
- }
- // we used to delete the item here, but we dont need to as the servermode above does it for us,
- break;
- }
- }
- }
}
-
- virtual Version GetVersion()
+
+ bool operator()(const TimedBan& tb) const
{
- return Version(1,0,0,0,VF_VENDOR);
+ return (tb.chan == chan);
}
};
-
-class ModuleTimedBansFactory : public ModuleFactory
+class ModuleTimedBans : public Module
{
+ CommandTban cmd;
+ BanWatcher banwatcher;
+
public:
- ModuleTimedBansFactory()
+ ModuleTimedBans()
+ : cmd(this)
+ , banwatcher(this)
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
{
+ ConfigTag* tag = ServerInstance->Config->ConfValue("timedbans");
+ cmd.sendnotice = tag->getBool("sendnotice", true);
}
-
- ~ModuleTimedBansFactory()
+
+ void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
{
+ timedbans expired;
+ for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();)
+ {
+ if (curtime > i->expire)
+ {
+ expired.push_back(*i);
+ i = TimedBanList.erase(i);
+ }
+ else
+ ++i;
+ }
+
+ for (timedbans::iterator i = expired.begin(); i != expired.end(); i++)
+ {
+ const std::string mask = i->mask;
+ Channel* cr = i->chan;
+
+ if (cmd.sendnotice)
+ {
+ const std::string message = InspIRCd::Format("Timed ban %s set by %s on %s has expired.",
+ mask.c_str(), i->setter.c_str(), cr->name.c_str());
+ // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
+ PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
+ char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
+
+ cr->WriteRemoteNotice(message, pfxchar);
+ }
+
+ Modes::ChangeList setban;
+ setban.push_remove(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, cr, NULL, setban);
+ }
+ }
+
+ void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
+ {
+ // Remove all timed bans affecting the channel from internal bookkeeping
+ TimedBanList.erase(std::remove_if(TimedBanList.begin(), TimedBanList.end(), ChannelMatcher(chan)), TimedBanList.end());
}
-
- virtual Module * CreateModule(InspIRCd* Me)
+
+ Version GetVersion() CXX11_OVERRIDE
{
- return new ModuleTimedBans(Me);
+ return Version("Adds the /TBAN command which allows channel operators to add bans which will be expired after the specified period.", VF_COMMON | VF_VENDOR);
}
-
};
-
-extern "C" void * init_module( void )
-{
- return new ModuleTimedBansFactory;
-}
+MODULE_INIT(ModuleTimedBans)