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