anope

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

os_forbid.cpp (15667B)

      1 /* OperServ 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 #include "modules/os_forbid.h"
     14 
     15 static ServiceReference<NickServService> nickserv("NickServService", "NickServ");
     16 
     17 struct ForbidDataImpl : ForbidData, Serializable
     18 {
     19 	ForbidDataImpl() : Serializable("ForbidData") { }
     20 	void Serialize(Serialize::Data &data) const anope_override;
     21 	static Serializable* Unserialize(Serializable *obj, Serialize::Data &data);
     22 };
     23 
     24 void ForbidDataImpl::Serialize(Serialize::Data &data) const
     25 {
     26 	data["mask"] << this->mask;
     27 	data["creator"] << this->creator;
     28 	data["reason"] << this->reason;
     29 	data["created"] << this->created;
     30 	data["expires"] << this->expires;
     31 	data["type"] << this->type;
     32 }
     33 
     34 Serializable* ForbidDataImpl::Unserialize(Serializable *obj, Serialize::Data &data)
     35 {
     36 	if (!forbid_service)
     37 		return NULL;
     38 
     39 	ForbidDataImpl *fb;
     40 	if (obj)
     41 		fb = anope_dynamic_static_cast<ForbidDataImpl *>(obj);
     42 	else
     43 		fb = new ForbidDataImpl();
     44 
     45 	data["mask"] >> fb->mask;
     46 	data["creator"] >> fb->creator;
     47 	data["reason"] >> fb->reason;
     48 	data["created"] >> fb->created;
     49 	data["expires"] >> fb->expires;
     50 	unsigned int t;
     51 	data["type"] >> t;
     52 	fb->type = static_cast<ForbidType>(t);
     53 
     54 	if (t > FT_SIZE - 1)
     55 		return NULL;
     56 
     57 	if (!obj)
     58 		forbid_service->AddForbid(fb);
     59 	return fb;
     60 }
     61 
     62 class MyForbidService : public ForbidService
     63 {
     64 	Serialize::Checker<std::vector<ForbidData *>[FT_SIZE - 1]> forbid_data;
     65 
     66 	inline std::vector<ForbidData *>& forbids(unsigned t) { return (*this->forbid_data)[t - 1]; }
     67 
     68  public:
     69 	MyForbidService(Module *m) : ForbidService(m), forbid_data("ForbidData") { }
     70 
     71 	~MyForbidService()
     72 	{
     73 		std::vector<ForbidData *> f = GetForbids();
     74 		for (unsigned i = 0; i < f.size(); ++i)
     75 			delete f[i];
     76 	}
     77 
     78 	void AddForbid(ForbidData *d) anope_override
     79 	{
     80 		this->forbids(d->type).push_back(d);
     81 	}
     82 
     83 	void RemoveForbid(ForbidData *d) anope_override
     84 	{
     85 		std::vector<ForbidData *>::iterator it = std::find(this->forbids(d->type).begin(), this->forbids(d->type).end(), d);
     86 		if (it != this->forbids(d->type).end())
     87 			this->forbids(d->type).erase(it);
     88 		delete d;
     89 	}
     90 
     91 	ForbidData *CreateForbid() anope_override
     92 	{
     93 		return new ForbidDataImpl();
     94 	}
     95 
     96 	ForbidData *FindForbid(const Anope::string &mask, ForbidType ftype) anope_override
     97 	{
     98 		for (unsigned i = this->forbids(ftype).size(); i > 0; --i)
     99 		{
    100 			ForbidData *d = this->forbids(ftype)[i - 1];
    101 
    102 			if (Anope::Match(mask, d->mask, false, true))
    103 				return d;
    104 		}
    105 		return NULL;
    106 	}
    107 
    108 	ForbidData *FindForbidExact(const Anope::string &mask, ForbidType ftype) anope_override
    109 	{
    110 		for (unsigned i = this->forbids(ftype).size(); i > 0; --i)
    111 		{
    112 			ForbidData *d = this->forbids(ftype)[i - 1];
    113 
    114 			if (d->mask.equals_ci(mask))
    115 				return d;
    116 		}
    117 		return NULL;
    118 	}
    119 
    120 	std::vector<ForbidData *> GetForbids() anope_override
    121 	{
    122 		std::vector<ForbidData *> f;
    123 		for (unsigned j = FT_NICK; j < FT_SIZE; ++j)
    124 			for (unsigned i = this->forbids(j).size(); i > 0; --i)
    125 			{
    126 				ForbidData *d = this->forbids(j).at(i - 1);
    127 
    128 				if (d->expires && !Anope::NoExpire && Anope::CurTime >= d->expires)
    129 				{
    130 					Anope::string ftype = "none";
    131 					if (d->type == FT_NICK)
    132 						ftype = "nick";
    133 					else if (d->type == FT_CHAN)
    134 						ftype = "chan";
    135 					else if (d->type == FT_EMAIL)
    136 						ftype = "email";
    137 
    138 					Log(LOG_NORMAL, "expire/forbid", Config->GetClient("OperServ")) << "Expiring forbid for " << d->mask << " type " << ftype;
    139 					this->forbids(j).erase(this->forbids(j).begin() + i - 1);
    140 					delete d;
    141 				}
    142 				else
    143 					f.push_back(d);
    144 			}
    145 
    146 		return f;
    147 	}
    148 };
    149 
    150 class CommandOSForbid : public Command
    151 {
    152 	ServiceReference<ForbidService> fs;
    153  public:
    154 	CommandOSForbid(Module *creator) : Command(creator, "operserv/forbid", 1, 5), fs("ForbidService", "forbid")
    155 	{
    156 		this->SetDesc(_("Forbid usage of nicknames, channels, and emails"));
    157 		this->SetSyntax(_("ADD {NICK|CHAN|EMAIL|REGISTER} [+\037expiry\037] \037entry\037 \037reason\037"));
    158 		this->SetSyntax(_("DEL {NICK|CHAN|EMAIL|REGISTER} \037entry\037"));
    159 		this->SetSyntax("LIST [NICK|CHAN|EMAIL|REGISTER]");
    160 	}
    161 
    162 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    163 	{
    164 		if (!this->fs)
    165 			return;
    166 
    167 		const Anope::string &command = params[0];
    168 		const Anope::string &subcommand = params.size() > 1 ? params[1] : "";
    169 
    170 		ForbidType ftype = FT_SIZE;
    171 		if (subcommand.equals_ci("NICK"))
    172 			ftype = FT_NICK;
    173 		else if (subcommand.equals_ci("CHAN"))
    174 			ftype = FT_CHAN;
    175 		else if (subcommand.equals_ci("EMAIL"))
    176 			ftype = FT_EMAIL;
    177 		else if (subcommand.equals_ci("REGISTER"))
    178 			ftype = FT_REGISTER;
    179 
    180 		if (command.equals_ci("ADD") && params.size() > 3 && ftype != FT_SIZE)
    181 		{
    182 			const Anope::string &expiry = params[2][0] == '+' ? params[2] : "";
    183 			const Anope::string &entry = !expiry.empty() ? params[3] : params[2];
    184 			Anope::string reason;
    185 			if (expiry.empty())
    186 				reason = params[3] + " ";
    187 			if (params.size() > 4)
    188 				reason += params[4];
    189 			reason.trim();
    190 
    191 			if (entry.replace_all_cs("?*", "").empty())
    192 			{
    193 				source.Reply(_("The mask must contain at least one non wildcard character."));
    194 				return;
    195 			}
    196 
    197 			time_t expiryt = 0;
    198 
    199 			if (!expiry.empty())
    200 			{
    201 				expiryt = Anope::DoTime(expiry);
    202 				if (expiryt < 0)
    203 				{
    204 					source.Reply(BAD_EXPIRY_TIME);
    205 					return;
    206 				}
    207 				else if (expiryt)
    208 					expiryt += Anope::CurTime;
    209 			}
    210 
    211 			NickAlias *target = NickAlias::Find(entry);
    212 			if (target != NULL && Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && target->nc->IsServicesOper())
    213 			{
    214 				source.Reply(ACCESS_DENIED);
    215 				return;
    216 			}
    217 
    218 			ForbidData *d = this->fs->FindForbidExact(entry, ftype);
    219 			bool created = false;
    220 			if (d == NULL)
    221 			{
    222 				d = new ForbidDataImpl();
    223 				created = true;
    224 			}
    225 
    226 			d->mask = entry;
    227 			d->creator = source.GetNick();
    228 			d->reason = reason;
    229 			d->created = Anope::CurTime;
    230 			d->expires = expiryt;
    231 			d->type = ftype;
    232 			if (created)
    233 				this->fs->AddForbid(d);
    234 
    235 			if (Anope::ReadOnly)
    236 				source.Reply(READ_ONLY_MODE);
    237 
    238 			Log(LOG_ADMIN, source, this) << "to add a forbid on " << entry << " of type " << subcommand;
    239 			source.Reply(_("Added a forbid on %s of type %s to expire on %s."), entry.c_str(), subcommand.lower().c_str(), d->expires ? Anope::strftime(d->expires, source.GetAccount()).c_str() : "never");
    240 
    241 			/* apply forbid */
    242 			switch (ftype)
    243 			{
    244 				case FT_NICK:
    245 				{
    246 					int na_matches = 0;
    247 
    248 					for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
    249 						module->OnUserNickChange(it->second, "");
    250 
    251 					for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end;)
    252 					{
    253 						NickAlias *na = it->second;
    254 						++it;
    255 
    256 						d = this->fs->FindForbid(na->nick, FT_NICK);
    257 						if (d == NULL)
    258 							continue;
    259 
    260 						++na_matches;
    261 
    262 						delete na;
    263 					}
    264 
    265 					source.Reply(_("\002%d\002 nickname(s) dropped."), na_matches);
    266 					break;
    267 				}
    268 				case FT_CHAN:
    269 				{
    270 					int chan_matches = 0, ci_matches = 0;
    271 
    272 					for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end;)
    273 					{
    274 						Channel *c = it->second;
    275 						++it;
    276 
    277 						d = this->fs->FindForbid(c->name, FT_CHAN);
    278 						if (d == NULL)
    279 							continue;
    280 
    281 						ServiceReference<ChanServService> chanserv("ChanServService", "ChanServ");
    282 						BotInfo *OperServ = Config->GetClient("OperServ");
    283 						if (IRCD->CanSQLineChannel && OperServ)
    284 						{
    285 							time_t inhabit = Config->GetModule("chanserv")->Get<time_t>("inhabit", "15s");
    286 							XLine x(c->name, OperServ->nick, Anope::CurTime + inhabit, d->reason);
    287 							IRCD->SendSQLine(NULL, &x);
    288 						}
    289 						else if (chanserv)
    290 						{
    291 							chanserv->Hold(c);
    292 						}
    293 
    294 						++chan_matches;
    295 
    296 						for (Channel::ChanUserList::const_iterator cit = c->users.begin(), cit_end = c->users.end(); cit != cit_end;)
    297 						{
    298 							User *u = cit->first;
    299 							++cit;
    300 
    301 							if (u->server == Me || u->HasMode("OPER"))
    302 								continue;
    303 
    304 							reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: %s")), d->reason.c_str());
    305 
    306 							c->Kick(source.service, u, "%s", reason.c_str());
    307 						}
    308 					}
    309 
    310 					for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(); it != RegisteredChannelList->end();)
    311 					{
    312 						ChannelInfo *ci = it->second;
    313 						++it;
    314 
    315 						d = this->fs->FindForbid(ci->name, FT_CHAN);
    316 						if (d == NULL)
    317 							continue;
    318 
    319 						++ci_matches;
    320 
    321 						delete ci;
    322 					}
    323 
    324 					source.Reply(_("\002%d\002 channel(s) cleared, and \002%d\002 channel(s) dropped."), chan_matches, ci_matches);
    325 
    326 					break;
    327 				}
    328 				default:
    329 					break;
    330 			}
    331 
    332 		}
    333 		else if (command.equals_ci("DEL") && params.size() > 2 && ftype != FT_SIZE)
    334 		{
    335 			const Anope::string &entry = params[2];
    336 
    337 			ForbidData *d = this->fs->FindForbidExact(entry, ftype);
    338 			if (d != NULL)
    339 			{
    340 				if (Anope::ReadOnly)
    341 					source.Reply(READ_ONLY_MODE);
    342 
    343 				Log(LOG_ADMIN, source, this) << "to remove forbid on " << d->mask << " of type " << subcommand;
    344 				source.Reply(_("%s deleted from the %s forbid list."), d->mask.c_str(), subcommand.c_str());
    345 				this->fs->RemoveForbid(d);
    346 			}
    347 			else
    348 				source.Reply(_("Forbid on %s was not found."), entry.c_str());
    349 		}
    350 		else if (command.equals_ci("LIST"))
    351 		{
    352 			const std::vector<ForbidData *> &forbids = this->fs->GetForbids();
    353 			if (forbids.empty())
    354 				source.Reply(_("Forbid list is empty."));
    355 			else
    356 			{
    357 				ListFormatter list(source.GetAccount());
    358 				list.AddColumn(_("Mask")).AddColumn(_("Type")).AddColumn(_("Creator")).AddColumn(_("Expires")).AddColumn(_("Reason"));
    359 
    360 				unsigned shown = 0;
    361 				for (unsigned i = 0; i < forbids.size(); ++i)
    362 				{
    363 					ForbidData *d = forbids[i];
    364 
    365 					if (ftype != FT_SIZE && ftype != d->type)
    366 						continue;
    367 
    368 					Anope::string stype;
    369 					if (d->type == FT_NICK)
    370 						stype = "NICK";
    371 					else if (d->type == FT_CHAN)
    372 						stype = "CHAN";
    373 					else if (d->type == FT_EMAIL)
    374 						stype = "EMAIL";
    375 					else if (d->type == FT_REGISTER)
    376 						stype = "REGISTER";
    377 					else
    378 						continue;
    379 
    380 					ListFormatter::ListEntry entry;
    381 					entry["Mask"] = d->mask;
    382 					entry["Type"] = stype;
    383 					entry["Creator"] = d->creator;
    384 					entry["Expires"] = d->expires ? Anope::strftime(d->expires, NULL, true).c_str() : Language::Translate(source.GetAccount(), _("Never"));
    385 					entry["Reason"] = d->reason;
    386 					list.AddEntry(entry);
    387 					++shown;
    388 				}
    389 
    390 				if (!shown)
    391 				{
    392 					source.Reply(_("There are no forbids of type %s."), subcommand.upper().c_str());
    393 				}
    394 				else
    395 				{
    396 					source.Reply(_("Forbid list:"));
    397 
    398 					std::vector<Anope::string> replies;
    399 					list.Process(replies);
    400 
    401 					for (unsigned i = 0; i < replies.size(); ++i)
    402 						source.Reply(replies[i]);
    403 
    404 					if (shown >= forbids.size())
    405 						source.Reply(_("End of forbid list."));
    406 					else
    407 						source.Reply(_("End of forbid list - %d/%d entries shown."), shown, forbids.size());
    408 				}
    409 			}
    410 		}
    411 		else
    412 			this->OnSyntaxError(source, command);
    413 
    414 		return;
    415 	}
    416 
    417 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    418 	{
    419 		this->SendSyntax(source);
    420 		source.Reply(" ");
    421 		source.Reply(_("Forbid allows you to forbid usage of certain nicknames, channels,\n"
    422 				"and email addresses. Wildcards are accepted for all entries."));
    423 
    424 		const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
    425 		if (!regexengine.empty())
    426 		{
    427 			source.Reply(" ");
    428 			source.Reply(_("Regex matches are also supported using the %s engine.\n"
    429 					"Enclose your pattern in // if this is desired."), regexengine.c_str());
    430 		}
    431 
    432 		return true;
    433 	}
    434 };
    435 
    436 class OSForbid : public Module
    437 {
    438 	MyForbidService forbidService;
    439 	Serialize::Type forbiddata_type;
    440 	CommandOSForbid commandosforbid;
    441 
    442  public:
    443 	OSForbid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
    444 		forbidService(this), forbiddata_type("ForbidData", ForbidDataImpl::Unserialize), commandosforbid(this)
    445 	{
    446 
    447 	}
    448 
    449 	void OnUserConnect(User *u, bool &exempt) anope_override
    450 	{
    451 		if (u->Quitting() || exempt)
    452 			return;
    453 
    454 		this->OnUserNickChange(u, "");
    455 	}
    456 
    457 	void OnUserNickChange(User *u, const Anope::string &) anope_override
    458 	{
    459 		if (u->HasMode("OPER"))
    460 			return;
    461 
    462 		ForbidData *d = this->forbidService.FindForbid(u->nick, FT_NICK);
    463 		if (d != NULL)
    464 		{
    465 			BotInfo *bi = Config->GetClient("NickServ");
    466 			if (!bi)
    467 				bi = Config->GetClient("OperServ");
    468 			if (bi)
    469 				u->SendMessage(bi, _("This nickname has been forbidden: %s"), d->reason.c_str());
    470 			if (nickserv)
    471 				nickserv->Collide(u, NULL);
    472 		}
    473 	}
    474 
    475 	EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override
    476 	{
    477 		BotInfo *OperServ = Config->GetClient("OperServ");
    478 		if (u->HasMode("OPER") || !OperServ)
    479 			return EVENT_CONTINUE;
    480 
    481 		ForbidData *d = this->forbidService.FindForbid(c->name, FT_CHAN);
    482 		if (d != NULL)
    483 		{
    484 			ServiceReference<ChanServService> chanserv("ChanServService", "ChanServ");
    485 			if (IRCD->CanSQLineChannel)
    486 			{
    487 				time_t inhabit = Config->GetModule("chanserv")->Get<time_t>("inhabit", "15s");
    488 				XLine x(c->name, OperServ->nick, Anope::CurTime + inhabit, d->reason);
    489 				IRCD->SendSQLine(NULL, &x);
    490 			}
    491 			else if (chanserv)
    492 			{
    493 				chanserv->Hold(c);
    494 			}
    495 
    496 			reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: %s")), d->reason.c_str());
    497 
    498 			return EVENT_STOP;
    499 		}
    500 
    501 		return EVENT_CONTINUE;
    502 	}
    503 
    504 	EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
    505 	{
    506 		if (command->name == "nickserv/info" && params.size() > 0)
    507 		{
    508 			ForbidData *d = this->forbidService.FindForbid(params[0], FT_NICK);
    509 			if (d != NULL)
    510 			{
    511 				if (source.IsOper())
    512 					source.Reply(_("Nick \002%s\002 is forbidden by %s: %s"), params[0].c_str(), d->creator.c_str(), d->reason.c_str());
    513 				else
    514 					source.Reply(_("Nick \002%s\002 is forbidden."), params[0].c_str());
    515 				return EVENT_STOP;
    516 			}
    517 		}
    518 		else if (command->name == "chanserv/info" && params.size() > 0)
    519 		{
    520 			ForbidData *d = this->forbidService.FindForbid(params[0], FT_CHAN);
    521 			if (d != NULL)
    522 			{
    523 				if (source.IsOper())
    524 					source.Reply(_("Channel \002%s\002 is forbidden by %s: %s"), params[0].c_str(), d->creator.c_str(), d->reason.c_str());
    525 				else
    526 					source.Reply(_("Channel \002%s\002 is forbidden."), params[0].c_str());
    527 				return EVENT_STOP;
    528 			}
    529 		}
    530 		else if (source.IsOper())
    531 			return EVENT_CONTINUE;
    532 		else if (command->name == "nickserv/register" && params.size() > 1)
    533 		{
    534 			ForbidData *d = this->forbidService.FindForbid(source.GetNick(), FT_REGISTER);
    535 			if (d != NULL)
    536 			{
    537 				source.Reply(NICK_CANNOT_BE_REGISTERED, source.GetNick().c_str());
    538 				return EVENT_STOP;
    539 			}
    540 
    541 			d = this->forbidService.FindForbid(params[1], FT_EMAIL);
    542 			if (d != NULL)
    543 			{
    544 				source.Reply(_("Your email address is not allowed, choose a different one."));
    545 				return EVENT_STOP;
    546 			}
    547 		}
    548 		else if (command->name == "nickserv/set/email" && params.size() > 0)
    549 		{
    550 			ForbidData *d = this->forbidService.FindForbid(params[0], FT_EMAIL);
    551 			if (d != NULL)
    552 			{
    553 				source.Reply(_("Your email address is not allowed, choose a different one."));
    554 				return EVENT_STOP;
    555 			}
    556 		}
    557 		else if (command->name == "chanserv/register" && !params.empty())
    558 		{
    559 			ForbidData *d = this->forbidService.FindForbid(params[0], FT_REGISTER);
    560 			if (d != NULL)
    561 			{
    562 				source.Reply(CHAN_X_INVALID, params[0].c_str());
    563 				return EVENT_STOP;
    564 			}
    565 		}
    566 
    567 		return EVENT_CONTINUE;
    568 	}
    569 };
    570 
    571 MODULE_INIT(OSForbid)