anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

cs_ban.cpp (7038B)

      1 /* ChanServ core functions
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "module.h"
     13 
     14 static Module *me;
     15 
     16 class TempBan : public Timer
     17 {
     18  private:
     19 	Anope::string channel;
     20 	Anope::string mask;
     21 	Anope::string mode;
     22 
     23  public:
     24 	TempBan(time_t seconds, Channel *c, const Anope::string &banmask, const Anope::string &mod) : Timer(me, seconds), channel(c->name), mask(banmask), mode(mod) { }
     25 
     26 	void Tick(time_t ctime) anope_override
     27 	{
     28 		Channel *c = Channel::Find(this->channel);
     29 		if (c)
     30 			c->RemoveMode(NULL, mode, this->mask);
     31 	}
     32 };
     33 
     34 class CommandCSBan : public Command
     35 {
     36  public:
     37 	CommandCSBan(Module *creator) : Command(creator, "chanserv/ban", 2, 4)
     38 	{
     39 		this->SetDesc(_("Bans a given nick or mask on a channel"));
     40 		this->SetSyntax(_("\037channel\037 [+\037expiry\037] {\037nick\037 | \037mask\037} [\037reason\037]"));
     41 	}
     42 
     43 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
     44 	{
     45 		Configuration::Block *block = Config->GetCommand(source);
     46 		const Anope::string &mode = block->Get<Anope::string>("mode", "BAN");
     47 		ChannelMode *cm = ModeManager::FindChannelModeByName(mode);
     48 		if (cm == NULL)
     49 			return;
     50 
     51 		const Anope::string &chan = params[0];
     52 		ChannelInfo *ci = ChannelInfo::Find(chan);
     53 		if (ci == NULL)
     54 		{
     55 			source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
     56 			return;
     57 		}
     58 
     59 		Channel *c = ci->c;
     60 		if (c == NULL)
     61 		{
     62 			source.Reply(CHAN_X_NOT_IN_USE, chan.c_str());
     63 			return;
     64 		}
     65 		else if (IRCD->GetMaxListFor(c, cm) && c->HasMode(mode) >= IRCD->GetMaxListFor(c, cm))
     66 		{
     67 			source.Reply(_("The %s list for %s is full."), mode.lower().c_str(), c->name.c_str());
     68 			return;
     69 		}
     70 
     71 		Anope::string expiry, target, reason;
     72 		time_t ban_time;
     73 		if (params[1][0] == '+')
     74 		{
     75 			ban_time = Anope::DoTime(params[1]);
     76 			if (ban_time < 0)
     77 			{
     78 				source.Reply(BAD_EXPIRY_TIME);
     79 				return;
     80 			}
     81 			if (params.size() < 3)
     82 			{
     83 				this->SendSyntax(source);
     84 				return;
     85 			}
     86 			target = params[2];
     87 			reason = "Requested";
     88 			if (params.size() > 3)
     89 				reason = params[3];
     90 		}
     91 		else
     92 		{
     93 			ban_time = 0;
     94 			target = params[1];
     95 			reason = "Requested";
     96 			if (params.size() > 2)
     97 				reason = params[2];
     98 			if (params.size() > 3)
     99 				reason += " " + params[3];
    100 		}
    101 
    102 		unsigned reasonmax = Config->GetModule("chanserv")->Get<unsigned>("reasonmax", "200");
    103 		if (reason.length() > reasonmax)
    104 			reason = reason.substr(0, reasonmax);
    105 
    106 		Anope::string signkickformat = Config->GetModule("chanserv")->Get<Anope::string>("signkickformat", "%m (%n)");
    107 		signkickformat = signkickformat.replace_all_cs("%n", source.GetNick());
    108 
    109 		User *u = source.GetUser();
    110 		User *u2 = User::Find(target, true);
    111 
    112 		AccessGroup u_access = source.AccessFor(ci);
    113 
    114 		if (!u_access.HasPriv("BAN") && !source.HasPriv("chanserv/kick"))
    115 			source.Reply(ACCESS_DENIED);
    116 		else if (u2)
    117 		{
    118 			AccessGroup u2_access = ci->AccessFor(u2);
    119 
    120 			if (u != u2 && ci->HasExt("PEACE") && u2_access >= u_access && !source.HasPriv("chanserv/kick"))
    121 				source.Reply(ACCESS_DENIED);
    122 			/*
    123 			 * Don't ban/kick the user on channels where he is excepted
    124 			 * to prevent services <-> server wars.
    125 			 */
    126 			else if (c->MatchesList(u2, "EXCEPT"))
    127 				source.Reply(CHAN_EXCEPTED, u2->nick.c_str(), ci->name.c_str());
    128 			else if (u2->IsProtected())
    129 				source.Reply(ACCESS_DENIED);
    130 			else
    131 			{
    132 				Anope::string mask = ci->GetIdealBan(u2);
    133 
    134 				bool override = !u_access.HasPriv("BAN") || (u != u2 && ci->HasExt("PEACE") && u2_access >= u_access);
    135 				Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << mask;
    136 
    137 				if (!c->HasMode(mode, mask))
    138 				{
    139 					c->SetMode(NULL, mode, mask);
    140 					if (ban_time)
    141 					{
    142 						new TempBan(ban_time, c, mask, mode);
    143 						source.Reply(_("Ban on \002%s\002 expires in %s."), mask.c_str(), Anope::Duration(ban_time, source.GetAccount()).c_str());
    144 					}
    145 				}
    146 
    147 				/* We still allow host banning while not allowing to kick */
    148 				if (!c->FindUser(u2))
    149 					return;
    150 
    151 				if (block->Get<bool>("kick", "yes"))
    152 				{
    153 					if (ci->HasExt("SIGNKICK") || (ci->HasExt("SIGNKICK_LEVEL") && !source.AccessFor(ci).HasPriv("SIGNKICK")))
    154 					{
    155 						signkickformat = signkickformat.replace_all_cs("%m", reason);
    156 						c->Kick(ci->WhoSends(), u2, "%s", signkickformat.c_str());
    157 					}
    158 					else
    159 						c->Kick(ci->WhoSends(), u2, "%s", reason.c_str());
    160 				}
    161 			}
    162 		}
    163 		else
    164 		{
    165 			bool founder = u_access.HasPriv("FOUNDER");
    166 			bool override = !founder && !u_access.HasPriv("BAN");
    167 
    168 			Anope::string mask = IRCD->NormalizeMask(target);
    169 
    170 			Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << mask;
    171 
    172 			if (!c->HasMode(mode, mask))
    173 			{
    174 				c->SetMode(NULL, mode, mask);
    175 				if (ban_time)
    176 				{
    177 					new TempBan(ban_time, c, mask, mode);
    178 					source.Reply(_("Ban on \002%s\002 expires in %s."), mask.c_str(), Anope::Duration(ban_time, source.GetAccount()).c_str());
    179 				}
    180 			}
    181 
    182 			int matched = 0, kicked = 0;
    183 			for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end;)
    184 			{
    185 				ChanUserContainer *uc = it->second;
    186 				++it;
    187 
    188 				Entry e(mode, mask);
    189 				if (e.Matches(uc->user))
    190 				{
    191 					++matched;
    192 
    193 					AccessGroup u2_access = ci->AccessFor(uc->user);
    194 
    195 					if (matched > 1 && !founder)
    196 						continue;
    197 					if (u != uc->user && ci->HasExt("PEACE") && u2_access >= u_access)
    198 						continue;
    199 					else if (ci->c->MatchesList(uc->user, "EXCEPT"))
    200 						continue;
    201 					else if (uc->user->IsProtected())
    202 						continue;
    203 
    204 					if (block->Get<bool>("kick", "yes"))
    205 					{
    206 						++kicked;
    207 						if (ci->HasExt("SIGNKICK") || (ci->HasExt("SIGNKICK_LEVEL") && !u_access.HasPriv("SIGNKICK")))
    208 						{
    209 							reason += " (Matches " + mask + ")";
    210 							signkickformat = signkickformat.replace_all_cs("%m", reason);
    211 							c->Kick(ci->WhoSends(), uc->user, "%s", signkickformat.c_str());
    212 						}
    213 						else
    214 							c->Kick(ci->WhoSends(), uc->user, "%s (Matches %s)", reason.c_str(), mask.c_str());
    215 					}
    216 				}
    217 			}
    218 
    219 			if (matched)
    220 				source.Reply(_("Kicked %d/%d users matching %s from %s."), kicked, matched, mask.c_str(), c->name.c_str());
    221 			else
    222 				source.Reply(_("No users on %s match %s."), c->name.c_str(), mask.c_str());
    223 		}
    224 	}
    225 
    226 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    227 	{
    228 		this->SendSyntax(source);
    229 		source.Reply(" ");
    230 		source.Reply(_("Bans a given nick or mask on a channel. An optional expiry may\n"
    231 				"be given to cause services to remove the ban after a set amount\n"
    232 				"of time.\n"
    233 				" \n"
    234 				"By default, limited to AOPs or those with level 5 access\n"
    235 				"and above on the channel. Channel founders may ban masks."));
    236 		return true;
    237 	}
    238 };
    239 
    240 class CSBan : public Module
    241 {
    242 	CommandCSBan commandcsban;
    243 
    244  public:
    245 	CSBan(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcsban(this)
    246 	{
    247 		me = this;
    248 	}
    249 };
    250 
    251 MODULE_INIT(CSBan)