1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2020 iwalkalone <iwalkalone69@gmail.com>
5  *   Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
6  *   Copyright (C) 2018 linuxdaemon <linuxdaemon.irc@gmail.com>
7  *   Copyright (C) 2017 B00mX0r <b00mx0r@aureus.pw>
8  *   Copyright (C) 2013, 2017-2018, 2020-2021 Sadie Powell <sadie@witchery.services>
9  *   Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
10  *   Copyright (C) 2012-2016 Attila Molnar <attilamolnar@hush.com>
11  *   Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
12  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
13  *   Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
14  *   Copyright (C) 2009 John Brooks <special@inspircd.org>
15  *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
16  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
17  *   Copyright (C) 2005-2008, 2010 Craig Edwards <brain@inspircd.org>
18  *
19  * This file is part of InspIRCd.  InspIRCd is free software: you can
20  * redistribute it and/or modify it under the terms of the GNU General Public
21  * License as published by the Free Software Foundation, version 2.
22  *
23  * This program is distributed in the hope that it will be useful, but WITHOUT
24  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
25  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
26  * details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
30  */
31 
32 
33 #include "inspircd.h"
34 #include "listmode.h"
35 
36 // Holds a timed ban
37 class TimedBan
38 {
39  public:
40 	std::string mask;
41 	std::string setter;
42 	time_t expire;
43 	Channel* chan;
44 };
45 
46 typedef std::vector<TimedBan> timedbans;
47 timedbans TimedBanList;
48 
49 // Handle /TBAN
50 class CommandTban : public Command
51 {
52 	ChanModeReference banmode;
53 
IsBanSet(Channel * chan,const std::string & mask)54 	bool IsBanSet(Channel* chan, const std::string& mask)
55 	{
56 		ListModeBase* banlm = static_cast<ListModeBase*>(*banmode);
57 		if (!banlm)
58 			return false;
59 
60 		const ListModeBase::ModeList* bans = banlm->GetList(chan);
61 		if (bans)
62 		{
63 			for (ListModeBase::ModeList::const_iterator i = bans->begin(); i != bans->end(); ++i)
64 			{
65 				const ListModeBase::ListItem& ban = *i;
66 				if (irc::equals(ban.mask, mask))
67 					return true;
68 			}
69 		}
70 
71 		return false;
72 	}
73 
74  public:
75 	bool sendnotice;
76 
CommandTban(Module * Creator)77 	CommandTban(Module* Creator)
78 		: Command(Creator,"TBAN", 3)
79 		, banmode(Creator, "ban")
80 	{
81 		syntax = "<channel> <duration> <banmask>";
82 	}
83 
Handle(User * user,const Params & parameters)84 	CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
85 	{
86 		Channel* channel = ServerInstance->FindChan(parameters[0]);
87 		if (!channel)
88 		{
89 			user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
90 			return CMD_FAILURE;
91 		}
92 
93 		unsigned int cm = channel->GetPrefixValue(user);
94 		if (cm < HALFOP_VALUE)
95 		{
96 			user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You do not have permission to set bans on this channel");
97 			return CMD_FAILURE;
98 		}
99 
100 		TimedBan T;
101 		unsigned long duration;
102 		if (!InspIRCd::Duration(parameters[1], duration))
103 		{
104 			user->WriteNotice("Invalid ban time");
105 			return CMD_FAILURE;
106 		}
107 		unsigned long expire = duration + ServerInstance->Time();
108 
109 		std::string mask = parameters[2];
110 		bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
111 		if (!isextban && !InspIRCd::IsValidMask(mask))
112 			mask.append("!*@*");
113 
114 		if (IsBanSet(channel, mask))
115 		{
116 			user->WriteNotice("Ban already set");
117 			return CMD_FAILURE;
118 		}
119 
120 		Modes::ChangeList setban;
121 		setban.push_add(*banmode, mask);
122 		// Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to
123 		// make it so that the user sets the mode themselves
124 		ServerInstance->Modes->Process(user, channel, NULL, setban);
125 		if (ServerInstance->Modes->GetLastChangeList().empty())
126 		{
127 			user->WriteNotice("Invalid ban mask");
128 			return CMD_FAILURE;
129 		}
130 
131 		// Attempt to find the actual set ban mask.
132 		const Modes::ChangeList::List& list = ServerInstance->Modes->GetLastChangeList().getlist();
133 		for (Modes::ChangeList::List::const_iterator iter = list.begin(); iter != list.end(); ++iter)
134 		{
135 			const Modes::Change& mc = *iter;
136 			if (mc.mh == *banmode)
137 			{
138 				// We found the actual mask.
139 				mask = mc.param;
140 				break;
141 			}
142 		}
143 
144 		T.mask = mask;
145 		T.setter = user->nick;
146 		T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
147 		T.chan = channel;
148 		TimedBanList.push_back(T);
149 
150 		if (sendnotice)
151 		{
152 			const std::string message = InspIRCd::Format("Timed ban %s added by %s on %s lasting for %s.",
153 			mask.c_str(), user->nick.c_str(), channel->name.c_str(), InspIRCd::DurationString(duration).c_str());
154 			// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
155 			PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
156 			char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
157 
158 			channel->WriteRemoteNotice(message, pfxchar);
159 		}
160 
161 		return CMD_SUCCESS;
162 	}
163 
GetRouting(User * user,const Params & parameters)164 	RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
165 	{
166 		return ROUTE_BROADCAST;
167 	}
168 };
169 
170 class BanWatcher : public ModeWatcher
171 {
172  public:
BanWatcher(Module * parent)173 	BanWatcher(Module* parent)
174 		: ModeWatcher(parent, "ban", MODETYPE_CHANNEL)
175 	{
176 	}
177 
AfterMode(User * source,User * dest,Channel * chan,const std::string & banmask,bool adding)178 	void AfterMode(User* source, User* dest, Channel* chan, const std::string& banmask, bool adding) CXX11_OVERRIDE
179 	{
180 		if (adding)
181 			return;
182 
183 		for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end(); ++i)
184 		{
185 			if (i->chan != chan)
186 				continue;
187 
188 			const std::string& target = i->mask;
189 			if (irc::equals(banmask, target))
190 			{
191 				TimedBanList.erase(i);
192 				break;
193 			}
194 		}
195 	}
196 };
197 
198 class ChannelMatcher
199 {
200 	Channel* const chan;
201 
202  public:
ChannelMatcher(Channel * ch)203 	ChannelMatcher(Channel* ch)
204 		: chan(ch)
205 	{
206 	}
207 
operator ()(const TimedBan & tb) const208 	bool operator()(const TimedBan& tb) const
209 	{
210 		return (tb.chan == chan);
211 	}
212 };
213 
214 class ModuleTimedBans : public Module
215 {
216 	CommandTban cmd;
217 	BanWatcher banwatcher;
218 
219  public:
ModuleTimedBans()220 	ModuleTimedBans()
221 		: cmd(this)
222 		, banwatcher(this)
223 	{
224 	}
225 
ReadConfig(ConfigStatus & status)226 	void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
227 	{
228 		ConfigTag* tag = ServerInstance->Config->ConfValue("timedbans");
229 		cmd.sendnotice = tag->getBool("sendnotice", true);
230 	}
231 
OnBackgroundTimer(time_t curtime)232 	void OnBackgroundTimer(time_t curtime) CXX11_OVERRIDE
233 	{
234 		timedbans expired;
235 		for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();)
236 		{
237 			if (curtime > i->expire)
238 			{
239 				expired.push_back(*i);
240 				i = TimedBanList.erase(i);
241 			}
242 			else
243 				++i;
244 		}
245 
246 		for (timedbans::iterator i = expired.begin(); i != expired.end(); i++)
247 		{
248 			const std::string mask = i->mask;
249 			Channel* cr = i->chan;
250 
251 			if (cmd.sendnotice)
252 			{
253 				const std::string message = InspIRCd::Format("Timed ban %s set by %s on %s has expired.",
254 				mask.c_str(), i->setter.c_str(), cr->name.c_str());
255 				// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
256 				PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h');
257 				char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';
258 
259 				cr->WriteRemoteNotice(message, pfxchar);
260 			}
261 
262 			Modes::ChangeList setban;
263 			setban.push_remove(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
264 			ServerInstance->Modes->Process(ServerInstance->FakeClient, cr, NULL, setban);
265 		}
266 	}
267 
OnChannelDelete(Channel * chan)268 	void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
269 	{
270 		// Remove all timed bans affecting the channel from internal bookkeeping
271 		TimedBanList.erase(std::remove_if(TimedBanList.begin(), TimedBanList.end(), ChannelMatcher(chan)), TimedBanList.end());
272 	}
273 
GetVersion()274 	Version GetVersion() CXX11_OVERRIDE
275 	{
276 		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);
277 	}
278 };
279 
280 MODULE_INIT(ModuleTimedBans)
281