anope

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

cs_flags.cpp (14714B)

      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 std::map<Anope::string, char> defaultFlags;
     15 
     16 class FlagsChanAccess : public ChanAccess
     17 {
     18  public:
     19 	std::set<char> flags;
     20 
     21 	FlagsChanAccess(AccessProvider *p) : ChanAccess(p)
     22 	{
     23 	}
     24 
     25 	bool HasPriv(const Anope::string &priv) const anope_override
     26 	{
     27 		std::map<Anope::string, char>::iterator it = defaultFlags.find(priv);
     28 		if (it != defaultFlags.end() && this->flags.count(it->second) > 0)
     29 			return true;
     30 		return false;
     31 	}
     32 
     33 	Anope::string AccessSerialize() const anope_override
     34 	{
     35 		return Anope::string(this->flags.begin(), this->flags.end());
     36 	}
     37 
     38 	void AccessUnserialize(const Anope::string &data) anope_override
     39 	{
     40 		for (unsigned i = data.length(); i > 0; --i)
     41 			this->flags.insert(data[i - 1]);
     42 	}
     43 
     44 	static Anope::string DetermineFlags(const ChanAccess *access)
     45 	{
     46 		if (access->provider->name == "access/flags")
     47 			return access->AccessSerialize();
     48 
     49 		std::set<char> buffer;
     50 
     51 		for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
     52 			if (access->HasPriv(it->first))
     53 				buffer.insert(it->second);
     54 
     55 		if (buffer.empty())
     56 			return "(none)";
     57 		else
     58 			return Anope::string(buffer.begin(), buffer.end());
     59 	}
     60 };
     61 
     62 class FlagsAccessProvider : public AccessProvider
     63 {
     64  public:
     65 	static FlagsAccessProvider *ap;
     66 
     67 	FlagsAccessProvider(Module *o) : AccessProvider(o, "access/flags")
     68 	{
     69 		ap = this;
     70 	}
     71 
     72 	ChanAccess *Create() anope_override
     73 	{
     74 		return new FlagsChanAccess(this);
     75 	}
     76 };
     77 FlagsAccessProvider* FlagsAccessProvider::ap;
     78 
     79 class CommandCSFlags : public Command
     80 {
     81 	void DoModify(CommandSource &source, ChannelInfo *ci, Anope::string mask, const Anope::string &flags)
     82 	{
     83 		if (flags.empty())
     84 		{
     85 			this->OnSyntaxError(source, "");
     86 			return;
     87 		}
     88 
     89 		AccessGroup u_access = source.AccessFor(ci);
     90 		const ChanAccess *highest = u_access.Highest();
     91 		const NickAlias *na = NULL;
     92 
     93 		if (IRCD->IsChannelValid(mask))
     94 		{
     95 			if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
     96 			{
     97 				source.Reply(_("Channels may not be on access lists."));
     98 				return;
     99 			}
    100 
    101 			ChannelInfo *targ_ci = ChannelInfo::Find(mask);
    102 			if (targ_ci == NULL)
    103 			{
    104 				source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str());
    105 				return;
    106 			}
    107 			else if (ci == targ_ci)
    108 			{
    109 				source.Reply(_("You can't add a channel to its own access list."));
    110 				return;
    111 			}
    112 
    113 			mask = targ_ci->name;
    114 		}
    115 		else
    116 		{
    117 			na = NickAlias::Find(mask);
    118 			if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
    119 			{
    120 				source.Reply(_("Masks and unregistered users may not be on access lists."));
    121 				return;
    122 			}
    123 			else if (mask.find_first_of("!*@") == Anope::string::npos && !na)
    124 			{
    125 				User *targ = User::Find(mask, true);
    126 				if (targ != NULL)
    127 					mask = "*!*@" + targ->GetDisplayedHost();
    128 				else
    129 				{
    130 					source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
    131 					return;
    132 				}
    133 			}
    134 
    135 			if (na)
    136 				mask = na->nick;
    137 		}
    138 
    139 		ChanAccess *current = NULL;
    140 		unsigned current_idx;
    141 		std::set<char> current_flags;
    142 		bool override = false;
    143 		for (current_idx = ci->GetAccessCount(); current_idx > 0; --current_idx)
    144 		{
    145 			ChanAccess *access = ci->GetAccess(current_idx - 1);
    146 			if ((na && na->nc == access->GetAccount()) || mask.equals_ci(access->Mask()))
    147 			{
    148 				// Flags allows removing others that have the same access as you,
    149 				// but no other access system does.
    150 				if (highest && highest->provider != FlagsAccessProvider::ap && !u_access.founder)
    151 					// operator<= on the non-me entry!
    152 					if (*highest <= *access)
    153 					{
    154 						if (source.HasPriv("chanserv/access/modify"))
    155 							override = true;
    156 						else
    157 						{
    158 							source.Reply(ACCESS_DENIED);
    159 							return;
    160 						}
    161 					}
    162 
    163 				current = access;
    164 				Anope::string cur_flags = FlagsChanAccess::DetermineFlags(access);
    165 				for (unsigned j = cur_flags.length(); j > 0; --j)
    166 					current_flags.insert(cur_flags[j - 1]);
    167 				break;
    168 			}
    169 		}
    170 
    171 		unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
    172 		if (access_max && ci->GetDeepAccessCount() >= access_max)
    173 		{
    174 			source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
    175 			return;
    176 		}
    177 
    178 		Privilege *p = NULL;
    179 		bool add = true;
    180 		for (size_t i = 0; i < flags.length(); ++i)
    181 		{
    182 			char f = flags[i];
    183 			switch (f)
    184 			{
    185 				case '+':
    186 					add = true;
    187 					break;
    188 				case '-':
    189 					add = false;
    190 					break;
    191 				case '*':
    192 					for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
    193 					{
    194 						bool has = current_flags.count(it->second);
    195 						// If we are adding a flag they already have or removing one they don't have, don't bother
    196 						if (add == has)
    197 							continue;
    198 
    199 						if (!u_access.HasPriv(it->first) && !u_access.founder)
    200 						{
    201 							if (source.HasPriv("chanserv/access/modify"))
    202 								override = true;
    203 							else
    204 								continue;
    205 						}
    206 
    207 						if (add)
    208 							current_flags.insert(it->second);
    209 						else
    210 							current_flags.erase(it->second);
    211 					}
    212 					break;
    213 				default:
    214 					p = PrivilegeManager::FindPrivilege(flags.substr(i));
    215 					if (p != NULL && defaultFlags[p->name])
    216 					{
    217 						f = defaultFlags[p->name];
    218 						i = flags.length();
    219 					}
    220 
    221 					for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
    222 					{
    223 						if (f != it->second)
    224 							continue;
    225 						else if (!u_access.HasPriv(it->first) && !u_access.founder)
    226 						{
    227 							if (source.HasPriv("chanserv/access/modify"))
    228 								override = true;
    229 							else
    230 							{
    231 								source.Reply(_("You cannot set the \002%c\002 flag."), f);
    232 								break;
    233 							}
    234 						}
    235 						if (add)
    236 							current_flags.insert(f);
    237 						else
    238 							current_flags.erase(f);
    239 						break;
    240 					}
    241 			}
    242 		}
    243 		if (current_flags.empty())
    244 		{
    245 			if (current != NULL)
    246 			{
    247 				ci->EraseAccess(current_idx - 1);
    248 				FOREACH_MOD(OnAccessDel, (ci, source, current));
    249 				delete current;
    250 				Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
    251 				source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str());
    252 			}
    253 			else
    254 			{
    255 				source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str());
    256 			}
    257 			return;
    258 		}
    259 
    260 		ServiceReference<AccessProvider> provider("AccessProvider", "access/flags");
    261 		if (!provider)
    262 			return;
    263 		FlagsChanAccess *access = anope_dynamic_static_cast<FlagsChanAccess *>(provider->Create());
    264 		access->SetMask(mask, ci);
    265 		access->creator = source.GetNick();
    266 		access->last_seen = current ? current->last_seen : 0;
    267 		access->created = Anope::CurTime;
    268 		access->flags = current_flags;
    269 
    270 		if (current != NULL)
    271 			delete current;
    272 
    273 		ci->AddAccess(access);
    274 
    275 		FOREACH_MOD(OnAccessAdd, (ci, source, access));
    276 
    277 		Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to modify " << mask << "'s flags to " << access->AccessSerialize();
    278 		if (p != NULL)
    279 		{
    280 			if (add)
    281 				source.Reply(_("Privilege \002%s\002 added to \002%s\002 on \002%s\002, new flags are +\002%s\002"), p->name.c_str(), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
    282 			else
    283 				source.Reply(_("Privilege \002%s\002 removed from \002%s\002 on \002%s\002, new flags are +\002%s\002"), p->name.c_str(), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
    284 		}
    285 		else
    286 			source.Reply(_("Flags for \002%s\002 on %s set to +\002%s\002"), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
    287 	}
    288 
    289 	void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
    290 	{
    291 		const Anope::string &arg = params.size() > 2 ? params[2] : "";
    292 
    293 		if (!ci->GetAccessCount())
    294 		{
    295 			source.Reply(_("%s access list is empty."), ci->name.c_str());
    296 			return;
    297 		}
    298 
    299 		ListFormatter list(source.GetAccount());
    300 
    301 		list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Flags")).AddColumn(_("Creator")).AddColumn(_("Created"));
    302 
    303 		unsigned count = 0;
    304 		for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
    305 		{
    306 			const ChanAccess *access = ci->GetAccess(i);
    307 			const Anope::string &flags = FlagsChanAccess::DetermineFlags(access);
    308 
    309 			if (!arg.empty())
    310 			{
    311 				if (arg[0] == '+')
    312 				{
    313 					bool pass = true;
    314 					for (size_t j = 1; j < arg.length(); ++j)
    315 						if (flags.find(arg[j]) == Anope::string::npos)
    316 							pass = false;
    317 					if (pass == false)
    318 						continue;
    319 				}
    320 				else if (!Anope::Match(access->Mask(), arg))
    321 					continue;
    322 			}
    323 
    324 			ListFormatter::ListEntry entry;
    325 			++count;
    326 			entry["Number"] = stringify(i + 1);
    327 			entry["Mask"] = access->Mask();
    328 			entry["Flags"] = flags;
    329 			entry["Creator"] = access->creator;
    330 			entry["Created"] = Anope::strftime(access->created, source.nc, true);
    331 			list.AddEntry(entry);
    332 		}
    333 
    334 		if (list.IsEmpty())
    335 			source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
    336 		else
    337 		{
    338 			std::vector<Anope::string> replies;
    339 			list.Process(replies);
    340 
    341 			source.Reply(_("Flags list for %s"), ci->name.c_str());
    342 			for (unsigned i = 0; i < replies.size(); ++i)
    343 				source.Reply(replies[i]);
    344 			if (count == ci->GetAccessCount())
    345 				source.Reply(_("End of access list."));
    346 			else
    347 				source.Reply(_("End of access list - %d/%d entries shown."), count, ci->GetAccessCount());
    348 		}
    349 	}
    350 
    351 	void DoClear(CommandSource &source, ChannelInfo *ci)
    352 	{
    353 		if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
    354 			source.Reply(ACCESS_DENIED);
    355 		else
    356 		{
    357 			ci->ClearAccess();
    358 
    359 			FOREACH_MOD(OnAccessClear, (ci, source));
    360 
    361 			source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str());
    362 
    363 			bool override = !source.IsFounder(ci);
    364 			Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
    365 		}
    366 
    367 		return;
    368 	}
    369 
    370  public:
    371 	CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 1, 4)
    372 	{
    373 		this->SetDesc(_("Modify the list of privileged users"));
    374 		this->SetSyntax(_("\037channel\037 [MODIFY] \037mask\037 \037changes\037"));
    375 		this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037]"));
    376 		this->SetSyntax(_("\037channel\037 CLEAR"));
    377 	}
    378 
    379 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    380 	{
    381 		const Anope::string &chan = params[0];
    382 		const Anope::string &cmd = params.size() > 1 ? params[1] : "";
    383 
    384 		ChannelInfo *ci = ChannelInfo::Find(chan);
    385 		if (ci == NULL)
    386 		{
    387 			source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
    388 			return;
    389 		}
    390 
    391 		bool is_list = cmd.empty() || cmd.equals_ci("LIST");
    392 		bool has_access = false;
    393 		if (source.HasPriv("chanserv/access/modify"))
    394 			has_access = true;
    395 		else if (is_list && source.HasPriv("chanserv/access/list"))
    396 			has_access = true;
    397 		else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST"))
    398 			has_access = true;
    399 		else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE"))
    400 			has_access = true;
    401 
    402 		if (!has_access)
    403 			source.Reply(ACCESS_DENIED);
    404 		else if (Anope::ReadOnly && !is_list)
    405 			source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
    406 		else if (is_list)
    407 			this->DoList(source, ci, params);
    408 		else if (cmd.equals_ci("CLEAR"))
    409 			this->DoClear(source, ci);
    410 		else
    411 		{
    412 			Anope::string mask, flags;
    413 			if (cmd.equals_ci("MODIFY"))
    414 			{
    415 				mask = params.size() > 2 ? params[2] : "";
    416 				flags = params.size() > 3 ? params[3] : "";
    417 			}
    418 			else
    419 			{
    420 				mask = cmd;
    421 				flags = params.size() > 2 ? params[2] : "";
    422 			}
    423 
    424 			this->DoModify(source, ci, mask, flags);
    425 		}
    426 	}
    427 
    428 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    429 	{
    430 		this->SendSyntax(source);
    431 		source.Reply(" ");
    432 		source.Reply(_("%s is another way to modify the channel access list, similar to\n"
    433 				"the XOP and ACCESS methods."), source.command.c_str());
    434 		source.Reply(" ");
    435 		source.Reply(_("The \002MODIFY\002 command allows you to modify the access list. If the mask is\n"
    436 				"not already on the access list it is added, then the changes are applied.\n"
    437 				"If the mask has no more flags, then the mask is removed from the access list.\n"
    438 				"Additionally, you may use +* or -* to add or remove all flags, respectively. You are\n"
    439 				"only able to modify the access list if you have the proper permission on the channel,\n"
    440 				"and even then you can only give other people access to the equivalent of what your access is."));
    441 		source.Reply(" ");
    442 		source.Reply(_("The \002LIST\002 command allows you to list existing entries on the channel access list.\n"
    443 				"If a mask is given, the mask is wildcard matched against all existing entries on the\n"
    444 				"access list, and only those entries are returned. If a set of flags is given, only those\n"
    445 				"on the access list with the specified flags are returned."));
    446 		source.Reply(" ");
    447 		source.Reply(_("The \002CLEAR\002 command clears the channel access list. This requires channel founder access."));
    448 		source.Reply(" ");
    449 		source.Reply(_("The available flags are:"));
    450 
    451 		typedef std::multimap<char, Anope::string, ci::less> reverse_map;
    452 		reverse_map reverse;
    453 		for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
    454 			reverse.insert(std::make_pair(it->second, it->first));
    455 
    456 		for (reverse_map::iterator it = reverse.begin(), it_end = reverse.end(); it != it_end; ++it)
    457 		{
    458 			Privilege *p = PrivilegeManager::FindPrivilege(it->second);
    459 			if (p == NULL)
    460 				continue;
    461 			source.Reply("  %c - %s", it->first, Language::Translate(source.nc, p->desc.c_str()));
    462 		}
    463 
    464 		return true;
    465 	}
    466 };
    467 
    468 class CSFlags : public Module
    469 {
    470 	FlagsAccessProvider accessprovider;
    471 	CommandCSFlags commandcsflags;
    472 
    473  public:
    474 	CSFlags(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
    475 		accessprovider(this), commandcsflags(this)
    476 	{
    477 		this->SetPermanent(true);
    478 
    479 	}
    480 
    481 	void OnReload(Configuration::Conf *conf) anope_override
    482 	{
    483 		defaultFlags.clear();
    484 
    485 		for (int i = 0; i < conf->CountBlock("privilege"); ++i)
    486 		{
    487 			Configuration::Block *priv = conf->GetBlock("privilege", i);
    488 
    489 			const Anope::string &pname = priv->Get<const Anope::string>("name");
    490 
    491 			Privilege *p = PrivilegeManager::FindPrivilege(pname);
    492 			if (p == NULL)
    493 				continue;
    494 
    495 			const Anope::string &value = priv->Get<const Anope::string>("flag");
    496 			if (value.empty())
    497 				continue;
    498 
    499 			defaultFlags[p->name] = value[0];
    500 		}
    501 	}
    502 };
    503 
    504 MODULE_INIT(CSFlags)