anope

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

cs_mode.cpp (28211B)

      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 #include "modules/cs_mode.h"
     14 
     15 struct ModeLockImpl : ModeLock, Serializable
     16 {
     17 	ModeLockImpl() : Serializable("ModeLock")
     18 	{
     19 	}
     20 
     21 	~ModeLockImpl()
     22 	{
     23 		ChannelInfo *chan = ChannelInfo::Find(ci);
     24 		if (chan)
     25 		{
     26 			ModeLocks *ml = chan->GetExt<ModeLocks>("modelocks");
     27 			if (ml)
     28 				ml->RemoveMLock(this);
     29 		}
     30 	}
     31 
     32 	void Serialize(Serialize::Data &data) const anope_override;
     33 	static Serializable* Unserialize(Serializable *obj, Serialize::Data &data);
     34 };
     35 
     36 struct ModeLocksImpl : ModeLocks
     37 {
     38 	Serialize::Reference<ChannelInfo> ci;
     39 	Serialize::Checker<ModeList> mlocks;
     40 
     41 	ModeLocksImpl(Extensible *obj) : ci(anope_dynamic_static_cast<ChannelInfo *>(obj)), mlocks("ModeLock")
     42 	{
     43 	}
     44 
     45 	~ModeLocksImpl()
     46 	{
     47 		ModeList modelist;
     48 		mlocks->swap(modelist);
     49 		for (ModeList::iterator it = modelist.begin(); it != modelist.end(); ++it)
     50 		{
     51 			ModeLock *ml = *it;
     52 			delete ml;
     53 		}
     54 	}
     55 
     56 	bool HasMLock(ChannelMode *mode, const Anope::string &param, bool status) const anope_override
     57 	{
     58 		if (!mode)
     59 			return false;
     60 
     61 		for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
     62 		{
     63 			const ModeLock *ml = *it;
     64 
     65 			if (ml->name == mode->name && ml->set == status && ml->param == param)
     66 				return true;
     67 		}
     68 
     69 		return false;
     70 	}
     71 
     72 	bool SetMLock(ChannelMode *mode, bool status, const Anope::string &param, Anope::string setter, time_t created = Anope::CurTime) anope_override
     73 	{
     74 		if (!mode)
     75 			return false;
     76 
     77 		RemoveMLock(mode, status, param);
     78 
     79 		if (setter.empty())
     80 			setter = ci->GetFounder() ? ci->GetFounder()->display : "Unknown";
     81 
     82 		ModeLock *ml = new ModeLockImpl();
     83 		ml->ci = ci->name;
     84 		ml->set = status;
     85 		ml->name = mode->name;
     86 		ml->param = param;
     87 		ml->setter = setter;
     88 		ml->created = created;
     89 
     90 		EventReturn MOD_RESULT;
     91 		FOREACH_RESULT(OnMLock, MOD_RESULT, (this->ci, ml));
     92 		if (MOD_RESULT == EVENT_STOP)
     93 		{
     94 			delete ml;
     95 			return false;
     96 		}
     97 
     98 		this->mlocks->push_back(ml);
     99 		return true;
    100 	}
    101 
    102 	bool RemoveMLock(ChannelMode *mode, bool status, const Anope::string &param = "") anope_override
    103 	{
    104 		if (!mode)
    105 			return false;
    106 
    107 		for (ModeList::iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
    108 		{
    109 			ModeLock *m = *it;
    110 
    111 			if (m->name == mode->name)
    112 			{
    113 				// For list or status modes, we must check the parameter
    114 				if (mode->type == MODE_LIST || mode->type == MODE_STATUS)
    115 					if (m->param != param)
    116 						continue;
    117 
    118 				EventReturn MOD_RESULT;
    119 				FOREACH_RESULT(OnUnMLock, MOD_RESULT, (this->ci, m));
    120 				if (MOD_RESULT == EVENT_STOP)
    121 					break;
    122 
    123 				delete m;
    124 				return true;
    125 			}
    126 		}
    127 
    128 		return false;
    129 	}
    130 
    131 	void RemoveMLock(ModeLock *mlock) anope_override
    132 	{
    133 		ModeList::iterator it = std::find(this->mlocks->begin(), this->mlocks->end(), mlock);
    134 		if (it != this->mlocks->end())
    135 			this->mlocks->erase(it);
    136 	}
    137 
    138 	void ClearMLock() anope_override
    139 	{
    140 		ModeList ml;
    141 		this->mlocks->swap(ml);
    142 		for (unsigned i = 0; i < ml.size(); ++i)
    143 			delete ml[i];
    144 	}
    145 
    146 	const ModeList &GetMLock() const anope_override
    147 	{
    148 		return this->mlocks;
    149 	}
    150 
    151 	std::list<ModeLock *> GetModeLockList(const Anope::string &name) anope_override
    152 	{
    153 		std::list<ModeLock *> mlist;
    154 		for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
    155 		{
    156 			ModeLock *m = *it;
    157 			if (m->name == name)
    158 				mlist.push_back(m);
    159 		}
    160 		return mlist;
    161 	}
    162 
    163 	const ModeLock *GetMLock(const Anope::string &mname, const Anope::string &param = "") anope_override
    164 	{
    165 		for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
    166 		{
    167 			ModeLock *m = *it;
    168 
    169 			if (m->name == mname && m->param == param)
    170 				return m;
    171 		}
    172 
    173 		return NULL;
    174 	}
    175 
    176 	Anope::string GetMLockAsString(bool complete) const anope_override
    177 	{
    178 		Anope::string pos = "+", neg = "-", params;
    179 
    180 		for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it)
    181 		{
    182 			const ModeLock *ml = *it;
    183 			ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
    184 
    185 			if (!cm || cm->type == MODE_LIST || cm->type == MODE_STATUS)
    186 				continue;
    187 
    188 			if (ml->set)
    189 				pos += cm->mchar;
    190 			else
    191 				neg += cm->mchar;
    192 
    193 			if (complete && ml->set && !ml->param.empty() && cm->type == MODE_PARAM)
    194 				params += " " + ml->param;
    195 		}
    196 
    197 		if (pos.length() == 1)
    198 			pos.clear();
    199 		if (neg.length() == 1)
    200 			neg.clear();
    201 
    202 		return pos + neg + params;
    203 	}
    204 
    205 	void Check() anope_override
    206 	{
    207 		if (this->mlocks->empty())
    208 			ci->Shrink<ModeLocks>("modelocks");
    209 	}
    210 };
    211 
    212 void ModeLockImpl::Serialize(Serialize::Data &data) const
    213 {
    214 	data["ci"] << this->ci;
    215 	data["set"] << this->set;
    216 	data["name"] << this->name;
    217 	data["param"] << this->param;
    218 	data["setter"] << this->setter;
    219 	data.SetType("created", Serialize::Data::DT_INT); data["created"] << this->created;
    220 }
    221 
    222 Serializable* ModeLockImpl::Unserialize(Serializable *obj, Serialize::Data &data)
    223 {
    224 	Anope::string sci;
    225 
    226 	data["ci"] >> sci;
    227 
    228 	ChannelInfo *ci = ChannelInfo::Find(sci);
    229 	if (!ci)
    230 		return NULL;
    231 
    232 	ModeLockImpl *ml;
    233 	if (obj)
    234 		ml = anope_dynamic_static_cast<ModeLockImpl *>(obj);
    235 	else
    236 	{
    237 		ml = new ModeLockImpl();
    238 		ml->ci = ci->name;
    239 	}
    240 
    241 	data["set"] >> ml->set;
    242 	data["created"] >> ml->created;
    243 	data["setter"] >> ml->setter;
    244 	data["name"] >> ml->name;
    245 	data["param"] >> ml->param;
    246 
    247 	if (!obj)
    248 		ci->Require<ModeLocksImpl>("modelocks")->mlocks->push_back(ml);
    249 
    250 	return ml;
    251 }
    252 
    253 class CommandCSMode : public Command
    254 {
    255 	bool CanSet(CommandSource &source, ChannelInfo *ci, ChannelMode *cm, bool self)
    256 	{
    257 		if (!ci || !cm || cm->type != MODE_STATUS)
    258 			return false;
    259 
    260 		return source.AccessFor(ci).HasPriv(cm->name + (self ? "ME" : ""));
    261 	}
    262 
    263 	void DoLock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
    264 	{
    265 		User *u = source.GetUser();
    266 		const Anope::string &subcommand = params[2];
    267 		const Anope::string &param = params.size() > 3 ? params[3] : "";
    268 
    269 		bool override = !source.AccessFor(ci).HasPriv("MODE");
    270 		ModeLocks *modelocks = ci->Require<ModeLocks>("modelocks");
    271 
    272 		if (Anope::ReadOnly && !subcommand.equals_ci("LIST"))
    273 		{
    274 			source.Reply(READ_ONLY_MODE);
    275 			return;
    276 		}
    277 
    278 		if ((subcommand.equals_ci("ADD") || subcommand.equals_ci("SET")) && !param.empty())
    279 		{
    280 			/* If setting, remove the existing locks */
    281 			if (subcommand.equals_ci("SET"))
    282 			{
    283 				const ModeLocks::ModeList mlocks = modelocks->GetMLock();
    284 				for (ModeLocks::ModeList::const_iterator it = mlocks.begin(); it != mlocks.end(); ++it)
    285 				{
    286 					const ModeLock *ml = *it;
    287 					ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
    288 					if (cm && cm->CanSet(source.GetUser()))
    289 						modelocks->RemoveMLock(cm, ml->set, ml->param);
    290 				}
    291 			}
    292 
    293 			spacesepstream sep(param);
    294 			Anope::string modes;
    295 
    296 			sep.GetToken(modes);
    297 
    298 			Anope::string pos = "+", neg = "-", pos_params, neg_params;
    299 
    300 			int adding = 1;
    301 			bool needreply = true;
    302 			for (size_t i = 0; i < modes.length(); ++i)
    303 			{
    304 				switch (modes[i])
    305 				{
    306 					case '+':
    307 						adding = 1;
    308 						break;
    309 					case '-':
    310 						adding = 0;
    311 						break;
    312 					default:
    313 						needreply = false;
    314 						ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
    315 						if (!cm)
    316 						{
    317 							source.Reply(_("Unknown mode character %c ignored."), modes[i]);
    318 							break;
    319 						}
    320 						else if (u && !cm->CanSet(u))
    321 						{
    322 							source.Reply(_("You may not (un)lock mode %c."), modes[i]);
    323 							break;
    324 						}
    325 
    326 						Anope::string mode_param;
    327 						if (((cm->type == MODE_STATUS || cm->type == MODE_LIST) && !sep.GetToken(mode_param)) || (cm->type == MODE_PARAM && adding && !sep.GetToken(mode_param)))
    328 						{
    329 							source.Reply(_("Missing parameter for mode %c."), cm->mchar);
    330 							continue;
    331 						}
    332 
    333 						if (cm->type == MODE_STATUS && !CanSet(source, ci, cm, false))
    334 						{
    335 							source.Reply(ACCESS_DENIED);
    336 							continue;
    337 						}
    338 
    339 						if (cm->type == MODE_LIST && ci->c && IRCD->GetMaxListFor(ci->c, cm) && ci->c->HasMode(cm->name) >= IRCD->GetMaxListFor(ci->c, cm))
    340 						{
    341 							source.Reply(_("List for mode %c is full."), cm->mchar);
    342 							continue;
    343 						}
    344 
    345 						if (modelocks->GetMLock().size() >= Config->GetModule(this->owner)->Get<unsigned>("max", "32"))
    346 						{
    347 							source.Reply(_("The mode lock list of \002%s\002 is full."), ci->name.c_str());
    348 							continue;
    349 						}
    350 
    351 						modelocks->SetMLock(cm, adding, mode_param, source.GetNick());
    352 
    353 						if (adding)
    354 						{
    355 							pos += cm->mchar;
    356 							if (!mode_param.empty())
    357 								pos_params += " " + mode_param;
    358 						}
    359 						else
    360 						{
    361 							neg += cm->mchar;
    362 							if (!mode_param.empty())
    363 								neg_params += " " + mode_param;
    364 						}
    365 				}
    366 			}
    367 
    368 			if (pos == "+")
    369 				pos.clear();
    370 			if (neg == "-")
    371 				neg.clear();
    372 			Anope::string reply = pos + neg + pos_params + neg_params;
    373 
    374 			if (!reply.empty())
    375 			{
    376 				source.Reply(_("%s locked on %s."), reply.c_str(), ci->name.c_str());
    377 				Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to lock " << reply;
    378 			}
    379 			else if (needreply)
    380 				source.Reply(_("Nothing to do."));
    381 
    382 			if (ci->c)
    383 				ci->c->CheckModes();
    384 		}
    385 		else if (subcommand.equals_ci("DEL") && !param.empty())
    386 		{
    387 			spacesepstream sep(param);
    388 			Anope::string modes;
    389 
    390 			sep.GetToken(modes);
    391 
    392 			int adding = 1;
    393 			bool needreply = true;
    394 			for (size_t i = 0; i < modes.length(); ++i)
    395 			{
    396 				switch (modes[i])
    397 				{
    398 					case '+':
    399 						adding = 1;
    400 						break;
    401 					case '-':
    402 						adding = 0;
    403 						break;
    404 					default:
    405 						needreply = false;
    406 						ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
    407 						if (!cm)
    408 						{
    409 							source.Reply(_("Unknown mode character %c ignored."), modes[i]);
    410 							break;
    411 						}
    412 						else if (u && !cm->CanSet(u))
    413 						{
    414 							source.Reply(_("You may not (un)lock mode %c."), modes[i]);
    415 							break;
    416 						}
    417 
    418 						Anope::string mode_param;
    419 						if (cm->type != MODE_REGULAR && !sep.GetToken(mode_param))
    420 							source.Reply(_("Missing parameter for mode %c."), cm->mchar);
    421 						else
    422 						{
    423 							if (modelocks->RemoveMLock(cm, adding, mode_param))
    424 							{
    425 								if (!mode_param.empty())
    426 									mode_param = " " + mode_param;
    427 								source.Reply(_("%c%c%s has been unlocked from %s."), adding == 1 ? '+' : '-', cm->mchar, mode_param.c_str(), ci->name.c_str());
    428 								Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to unlock " << (adding ? '+' : '-') << cm->mchar << mode_param;
    429 							}
    430 							else
    431 								source.Reply(_("%c%c is not locked on %s."), adding == 1 ? '+' : '-', cm->mchar, ci->name.c_str());
    432 						}
    433 				}
    434 			}
    435 
    436 			if (needreply)
    437 				source.Reply(_("Nothing to do."));
    438 		}
    439 		else if (subcommand.equals_ci("LIST"))
    440 		{
    441 			const ModeLocks::ModeList mlocks = modelocks->GetMLock();
    442 			if (mlocks.empty())
    443 			{
    444 				source.Reply(_("Channel %s has no mode locks."), ci->name.c_str());
    445 			}
    446 			else
    447 			{
    448 				ListFormatter list(source.GetAccount());
    449 				list.AddColumn(_("Mode")).AddColumn(_("Param")).AddColumn(_("Creator")).AddColumn(_("Created"));
    450 
    451 				for (ModeLocks::ModeList::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it)
    452 				{
    453 					const ModeLock *ml = *it;
    454 					ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
    455 					if (!cm)
    456 						continue;
    457 
    458 					ListFormatter::ListEntry entry;
    459 					entry["Mode"] = Anope::printf("%c%c", ml->set ? '+' : '-', cm->mchar);
    460 					entry["Param"] = ml->param;
    461 					entry["Creator"] = ml->setter;
    462 					entry["Created"] = Anope::strftime(ml->created, NULL, true);
    463 					list.AddEntry(entry);
    464 				}
    465 
    466 				source.Reply(_("Mode locks for %s:"), ci->name.c_str());
    467 
    468 				std::vector<Anope::string> replies;
    469 				list.Process(replies);
    470 
    471 				for (unsigned i = 0; i < replies.size(); ++i)
    472 					source.Reply(replies[i]);
    473 			}
    474 		}
    475 		else
    476 			this->OnSyntaxError(source, subcommand);
    477 	}
    478 
    479 	void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
    480 	{
    481 		User *u = source.GetUser();
    482 
    483 		bool has_access = source.AccessFor(ci).HasPriv("MODE") || source.HasPriv("chanserv/administration");
    484 		bool can_override = source.HasPriv("chanserv/administration");
    485 
    486 		spacesepstream sep(params.size() > 3 ? params[3] : "");
    487 		Anope::string modes = params[2], param;
    488 
    489 		bool override = !source.AccessFor(ci).HasPriv("MODE") && source.HasPriv("chanserv/administration");
    490 
    491 		int adding = -1;
    492 		for (size_t i = 0; i < modes.length(); ++i)
    493 		{
    494 			switch (modes[i])
    495 			{
    496 				case '+':
    497 					adding = 1;
    498 					break;
    499 				case '-':
    500 					adding = 0;
    501 					break;
    502 				case '*':
    503 					if (adding == -1 || !has_access)
    504 						break;
    505 					for (unsigned j = 0; j < ModeManager::GetChannelModes().size() && ci->c; ++j)
    506 					{
    507 						ChannelMode *cm = ModeManager::GetChannelModes()[j];
    508 
    509 						if (!u || cm->CanSet(u) || can_override)
    510 						{
    511 							if (cm->type == MODE_REGULAR || (!adding && cm->type == MODE_PARAM))
    512 							{
    513 								if (adding)
    514 									ci->c->SetMode(NULL, cm);
    515 								else
    516 									ci->c->RemoveMode(NULL, cm);
    517 							}
    518 						}
    519 					}
    520 					break;
    521 				default:
    522 					if (adding == -1)
    523 						break;
    524 					ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
    525 					if (!cm || (u && !cm->CanSet(u) && !can_override))
    526 						continue;
    527 					switch (cm->type)
    528 					{
    529 						case MODE_REGULAR:
    530 							if (!has_access)
    531 								break;
    532 							if (adding)
    533 								ci->c->SetMode(NULL, cm);
    534 							else
    535 								ci->c->RemoveMode(NULL, cm);
    536 							break;
    537 						case MODE_PARAM:
    538 							if (!has_access)
    539 								break;
    540 							if (adding && !sep.GetToken(param))
    541 								break;
    542 							if (adding)
    543 								ci->c->SetMode(NULL, cm, param);
    544 							else
    545 								ci->c->RemoveMode(NULL, cm);
    546 							break;
    547 						case MODE_STATUS:
    548 						{
    549 							if (!sep.GetToken(param))
    550 								param = source.GetNick();
    551 
    552 							AccessGroup u_access = source.AccessFor(ci);
    553 
    554 							if (param.find_first_of("*?") != Anope::string::npos)
    555 							{
    556 								if (!this->CanSet(source, ci, cm, false))
    557 								{
    558 									if (can_override)
    559 									{
    560 										override = true;
    561 									}
    562 									else
    563 									{
    564 										source.Reply(_("You do not have access to set mode %c."), cm->mchar);
    565 										break;
    566 									}
    567 								}
    568 
    569 								for (Channel::ChanUserList::const_iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end;)
    570 								{
    571 									ChanUserContainer *uc = it->second;
    572 									++it;
    573 
    574 									AccessGroup targ_access = ci->AccessFor(uc->user);
    575 
    576 									if (uc->user->IsProtected())
    577 									{
    578 										source.Reply(_("You do not have the access to change %s's modes."), uc->user->nick.c_str());
    579 										continue;
    580 									}
    581 
    582 									if (ci->HasExt("PEACE") && targ_access >= u_access)
    583 									{
    584 										if (can_override)
    585 										{
    586 											override = true;
    587 										}
    588 										else
    589 										{
    590 											source.Reply(_("You do not have the access to change %s's modes."), uc->user->nick.c_str());
    591 											continue;
    592 										}
    593 									}
    594 
    595 									if (Anope::Match(uc->user->GetMask(), param))
    596 									{
    597 										if (adding)
    598 											ci->c->SetMode(NULL, cm, uc->user->GetUID());
    599 										else
    600 											ci->c->RemoveMode(NULL, cm, uc->user->GetUID());
    601 									}
    602 								}
    603 							}
    604 							else
    605 							{
    606 								User *target = User::Find(param, true);
    607 								if (target == NULL)
    608 								{
    609 									source.Reply(NICK_X_NOT_IN_USE, param.c_str());
    610 									break;
    611 								}
    612 
    613 								if (!this->CanSet(source, ci, cm, source.GetUser() == target))
    614 								{
    615 									if (can_override)
    616 									{
    617 										override = true;
    618 									}
    619 									else
    620 									{
    621 										source.Reply(_("You do not have access to set mode %c."), cm->mchar);
    622 										break;
    623 									}
    624 								}
    625 
    626 								if (source.GetUser() != target)
    627 								{
    628 									AccessGroup targ_access = ci->AccessFor(target);
    629 									if (ci->HasExt("PEACE") && targ_access >= u_access)
    630 									{
    631 										source.Reply(_("You do not have the access to change %s's modes."), target->nick.c_str());
    632 										break;
    633 									}
    634 									else if (can_override)
    635 									{
    636 										override = true;
    637 									}
    638 									else if (target->IsProtected())
    639 									{
    640 										source.Reply(ACCESS_DENIED);
    641 										break;
    642 									}
    643 								}
    644 
    645 								if (adding)
    646 									ci->c->SetMode(NULL, cm, target->GetUID());
    647 								else
    648 									ci->c->RemoveMode(NULL, cm, target->GetUID());
    649 							}
    650 							break;
    651 						}
    652 						case MODE_LIST:
    653 							if (!has_access)
    654 								break;
    655 							if (!sep.GetToken(param))
    656 								break;
    657 
    658 							// Change to internal name, eg giving -b ~q:*
    659 							cm = cm->Unwrap(param);
    660 
    661 							if (adding)
    662 							{
    663 								if (IRCD->GetMaxListFor(ci->c, cm) && ci->c->HasMode(cm->name) < IRCD->GetMaxListFor(ci->c, cm))
    664 									ci->c->SetMode(NULL, cm, param);
    665 							}
    666 							else
    667 							{
    668 								std::vector<Anope::string> v = ci->c->GetModeList(cm->name);
    669 								for (unsigned j = 0; j < v.size(); ++j)
    670 									if (Anope::Match(v[j], param))
    671 										ci->c->RemoveMode(NULL, cm, v[j]);
    672 							}
    673 					}
    674 			} // switch
    675 		}
    676 
    677 		Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << modes << (params.size() > 3 ? " " + params[3] : "");
    678 	}
    679 
    680 	void DoClear(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
    681 	{
    682 		const Anope::string &param = params.size() > 2 ? params[2] : "";
    683 
    684 		if (param.empty())
    685 		{
    686 			std::vector<Anope::string> new_params;
    687 			new_params.push_back(params[0]);
    688 			new_params.push_back("SET");
    689 			new_params.push_back("-*");
    690 			this->DoSet(source, ci, new_params);
    691 			return;
    692 		}
    693 
    694 		ChannelMode *cm;
    695 		if (param.length() == 1)
    696 			cm = ModeManager::FindChannelModeByChar(param[0]);
    697 		else
    698 		{
    699 			cm = ModeManager::FindChannelModeByName(param.upper());
    700 			if (!cm)
    701 				cm = ModeManager::FindChannelModeByName(param.substr(0, param.length() - 1).upper());
    702 		}
    703 
    704 		if (!cm)
    705 		{
    706 			source.Reply(_("There is no such mode %s."), param.c_str());
    707 			return;
    708 		}
    709 
    710 		if (cm->type != MODE_STATUS && cm->type != MODE_LIST)
    711 		{
    712 			source.Reply(_("Mode %s is not a status or list mode."), param.c_str());
    713 			return;
    714 		}
    715 
    716 		if (!cm->mchar)
    717 		{
    718 			source.Reply(_("Mode %s is a virtual mode and can't be cleared."), cm->name.c_str());
    719 			return;
    720 		}
    721 
    722 		std::vector<Anope::string> new_params;
    723 		new_params.push_back(params[0]);
    724 		new_params.push_back("SET");
    725 		new_params.push_back("-" + stringify(cm->mchar));
    726 		new_params.push_back("*");
    727 		this->DoSet(source, ci, new_params);
    728 	}
    729 
    730  public:
    731 	CommandCSMode(Module *creator) : Command(creator, "chanserv/mode", 2, 4)
    732 	{
    733 		this->SetDesc(_("Control modes and mode locks on a channel"));
    734 		this->SetSyntax(_("\037channel\037 LOCK {ADD|DEL|SET|LIST} [\037what\037]"));
    735 		this->SetSyntax(_("\037channel\037 SET \037modes\037"));
    736 		this->SetSyntax(_("\037channel\037 CLEAR [\037what\037]"));
    737 	}
    738 
    739 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    740 	{
    741 		const Anope::string &subcommand = params[1];
    742 
    743 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    744 
    745 		if (!ci)
    746 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    747 		else if (subcommand.equals_ci("LOCK") && params.size() > 2)
    748 		{
    749 			if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/administration"))
    750 				source.Reply(ACCESS_DENIED);
    751 			else
    752 				this->DoLock(source, ci, params);
    753 		}
    754 		else if (!ci->c)
    755 			source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str());
    756 		else if (subcommand.equals_ci("SET") && params.size() > 2)
    757 			this->DoSet(source, ci, params);
    758 		else if (subcommand.equals_ci("CLEAR"))
    759 		{
    760 			if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/administration"))
    761 				source.Reply(ACCESS_DENIED);
    762 			else
    763 				this->DoClear(source, ci, params);
    764 		}
    765 		else
    766 			this->OnSyntaxError(source, "");
    767 	}
    768 
    769 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    770 	{
    771 		this->SendSyntax(source);
    772 		source.Reply(" ");
    773 		source.Reply(_("Mainly controls mode locks and mode access (which is different from channel access)\n"
    774 			"on a channel.\n"
    775 			" \n"
    776 			"The \002%s LOCK\002 command allows you to add, delete, and view mode locks on a channel.\n"
    777 			"If a mode is locked on or off, services will not allow that mode to be changed. The \002SET\002\n"
    778 			"command will clear all existing mode locks and set the new one given, while \002ADD\002 and \002DEL\002\n"
    779 			"modify the existing mode lock.\n"
    780 			"Example:\n"
    781 			"     \002MODE #channel LOCK ADD +bmnt *!*@*aol*\002\n"
    782 			" \n"
    783 			"The \002%s SET\002 command allows you to set modes through services. Wildcards * and ? may\n"
    784 			"be given as parameters for list and status modes.\n"
    785 			"Example:\n"
    786 			"     \002MODE #channel SET +v *\002\n"
    787 			"       Sets voice status to all users in the channel.\n"
    788 			" \n"
    789 			"     \002MODE #channel SET -b ~c:*\n"
    790 			"       Clears all extended bans that start with ~c:\n"
    791 			" \n"
    792 			"The \002%s CLEAR\002 command is an easy way to clear modes on a channel. \037what\037 may be\n"
    793 			"any mode name. Examples include bans, excepts, inviteoverrides, ops, halfops, and voices. If \037what\037\n"
    794 			"is not given then all basic modes are removed."),
    795 			source.command.upper().c_str(), source.command.upper().c_str(), source.command.upper().c_str());
    796 		return true;
    797 	}
    798 };
    799 
    800 static Anope::map<std::pair<bool, Anope::string> > modes;
    801 
    802 class CommandCSModes : public Command
    803 {
    804  public:
    805 	CommandCSModes(Module *creator) : Command(creator, "chanserv/modes", 1, 2)
    806 	{
    807 		this->SetSyntax(_("\037channel\037 [\037user\037]"));
    808 	}
    809 
    810 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    811 	{
    812 		User *u = source.GetUser(),
    813 			*targ = params.size() > 1 ? User::Find(params[1], true) : u;
    814 		ChannelInfo *ci = ChannelInfo::Find(params[0]);
    815 
    816 		if (!targ)
    817 		{
    818 			if (params.size() > 1)
    819 				source.Reply(NICK_X_NOT_IN_USE, params[1].c_str());
    820 			return;
    821 		}
    822 
    823 		if (!ci)
    824 		{
    825 			source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
    826 			return;
    827 		}
    828 		else if (!ci->c)
    829 		{
    830 			source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str());
    831 			return;
    832 		}
    833 
    834 		AccessGroup u_access = source.AccessFor(ci), targ_access = ci->AccessFor(targ);
    835 		const std::pair<bool, Anope::string> &m = modes[source.command];
    836 
    837 		bool can_override = source.HasPriv("chanserv/administration");
    838 		bool override = false;
    839 
    840 		if (m.second.empty())
    841 		{
    842 			source.Reply(ACCESS_DENIED);
    843 			return;
    844 		}
    845 
    846 		if (u == targ ? !u_access.HasPriv(m.second + "ME") : !u_access.HasPriv(m.second))
    847 		{
    848 			if (!can_override)
    849 			{
    850 				source.Reply(ACCESS_DENIED);
    851 				return;
    852 			}
    853 			else
    854 				override = true;
    855 		}
    856 
    857 		if (!override && !m.first && u != targ && (targ->IsProtected() || (ci->HasExt("PEACE") && targ_access >= u_access)))
    858 		{
    859 			if (!can_override)
    860 			{
    861 				source.Reply(ACCESS_DENIED);
    862 				return;
    863 			}
    864 			else
    865 				override = true;
    866 		}
    867 
    868 		if (!ci->c->FindUser(targ))
    869 		{
    870 			source.Reply(NICK_X_NOT_ON_CHAN, targ->nick.c_str(), ci->name.c_str());
    871 			return;
    872 		}
    873 
    874 		if (m.first)
    875 			ci->c->SetMode(NULL, m.second, targ->GetUID());
    876 		else
    877 			ci->c->RemoveMode(NULL, m.second, targ->GetUID());
    878 
    879 		Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "on " << targ->nick;
    880 	}
    881 
    882 	const Anope::string GetDesc(CommandSource &source) const anope_override
    883 	{
    884 		const std::pair<bool, Anope::string> &m = modes[source.command];
    885 		if (!m.second.empty())
    886 		{
    887 			if (m.first)
    888 				return Anope::printf(Language::Translate(source.GetAccount(), _("Gives you or the specified nick %s status on a channel")), m.second.c_str());
    889 			else
    890 				return Anope::printf(Language::Translate(source.GetAccount(), _("Removes %s status from you or the specified nick on a channel")), m.second.c_str());
    891 		}
    892 		else
    893 			return "";
    894 	}
    895 
    896 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    897 	{
    898 		const std::pair<bool, Anope::string> &m = modes[source.command];
    899 		if (m.second.empty())
    900 			return false;
    901 
    902 		this->SendSyntax(source);
    903 		source.Reply(" ");
    904 		if (m.first)
    905 			source.Reply(_("Gives %s status to the selected nick on a channel. If \037nick\037 is\n"
    906 					"not given, it will %s you."),
    907 					m.second.upper().c_str(), m.second.lower().c_str());
    908 		else
    909 			source.Reply(_("Removes %s status from the selected nick on a channel. If \037nick\037 is\n"
    910 					"not given, it will de%s you."),
    911 					 m.second.upper().c_str(), m.second.lower().c_str());
    912 		source.Reply(" ");
    913 		source.Reply(_("You must have the %s(ME) privilege on the channel to use this command."), m.second.upper().c_str());
    914 
    915 		return true;
    916 	}
    917 };
    918 
    919 class CSMode : public Module
    920 {
    921 	CommandCSMode commandcsmode;
    922 	CommandCSModes commandcsmodes;
    923 	ExtensibleItem<ModeLocksImpl> modelocks;
    924 	Serialize::Type modelocks_type;
    925 
    926  public:
    927 	CSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
    928 		commandcsmode(this), commandcsmodes(this),
    929 		modelocks(this, "modelocks"),
    930 		modelocks_type("ModeLock", ModeLockImpl::Unserialize)
    931 	{
    932 
    933 	}
    934 
    935 	void OnReload(Configuration::Conf *conf) anope_override
    936 	{
    937 		modes.clear();
    938 
    939 		for (int i = 0; i < conf->CountBlock("command"); ++i)
    940 		{
    941 			Configuration::Block *block = conf->GetBlock("command", i);
    942 
    943 			const Anope::string &cname = block->Get<const Anope::string>("name"),
    944 					&cmd = block->Get<const Anope::string>("command");
    945 
    946 			if (cname.empty() || cmd != "chanserv/modes")
    947 				continue;
    948 
    949 			const Anope::string &set = block->Get<const Anope::string>("set"),
    950 					&unset = block->Get<const Anope::string>("unset");
    951 
    952 			if (set.empty() && unset.empty())
    953 				continue;
    954 
    955 			modes[cname] = std::make_pair(!set.empty(), !set.empty() ? set : unset);
    956 		}
    957 	}
    958 
    959 	void OnCheckModes(Reference<Channel> &c) anope_override
    960 	{
    961 		if (!c || !c->ci)
    962 			return;
    963 
    964 		ModeLocks *locks = modelocks.Get(c->ci);
    965 		if (locks)
    966 			for (ModeLocks::ModeList::const_iterator it = locks->GetMLock().begin(), it_end = locks->GetMLock().end(); it != it_end; ++it)
    967 			{
    968 				const ModeLock *ml = *it;
    969 				ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
    970 				if (!cm)
    971 					continue;
    972 
    973 				if (cm->type == MODE_REGULAR)
    974 				{
    975 					if (!c->HasMode(cm->name) && ml->set)
    976 						c->SetMode(NULL, cm, "", false);
    977 					else if (c->HasMode(cm->name) && !ml->set)
    978 						c->RemoveMode(NULL, cm, "", false);
    979 				}
    980 				else if (cm->type == MODE_PARAM)
    981 				{
    982 					/* If the channel doesn't have the mode, or it does and it isn't set correctly */
    983 					if (ml->set)
    984 					{
    985 						Anope::string param;
    986 						c->GetParam(cm->name, param);
    987 
    988 						if (!c->HasMode(cm->name) || (!param.empty() && !ml->param.empty() && !param.equals_cs(ml->param)))
    989 							c->SetMode(NULL, cm, ml->param, false);
    990 					}
    991 					else
    992 					{
    993 						if (c->HasMode(cm->name))
    994 							c->RemoveMode(NULL, cm, "", false);
    995 					}
    996 
    997 				}
    998 				else if (cm->type == MODE_LIST || cm->type == MODE_STATUS)
    999 				{
   1000 					if (ml->set)
   1001 						c->SetMode(NULL, cm, ml->param, false);
   1002 					else
   1003 						c->RemoveMode(NULL, cm, ml->param, false);
   1004 				}
   1005 			}
   1006 	}
   1007 
   1008 	void OnChanRegistered(ChannelInfo *ci) anope_override
   1009 	{
   1010 		ModeLocks *ml = modelocks.Require(ci);
   1011 		Anope::string mlock;
   1012 		spacesepstream sep(Config->GetModule(this)->Get<const Anope::string>("mlock", "+nt"));
   1013 		if (sep.GetToken(mlock))
   1014 		{
   1015 			bool add = true;
   1016 			for (unsigned i = 0; i < mlock.length(); ++i)
   1017 			{
   1018 				if (mlock[i] == '+')
   1019 				{
   1020 					add = true;
   1021 					continue;
   1022 				}
   1023 
   1024 				if (mlock[i] == '-')
   1025 				{
   1026 					add = false;
   1027 					continue;
   1028 				}
   1029 
   1030 				ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock[i]);
   1031 				if (!cm)
   1032 					continue;
   1033 
   1034 				Anope::string param;
   1035 				if (cm->type == MODE_PARAM)
   1036 				{
   1037 					ChannelModeParam *cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm);
   1038 					if (add || !cmp->minus_no_arg)
   1039 					{
   1040 						sep.GetToken(param);
   1041 						if (param.empty() || !cmp->IsValid(param))
   1042 							continue;
   1043 					}
   1044 				}
   1045 				else if (cm->type != MODE_REGULAR)
   1046 				{
   1047 					sep.GetToken(param);
   1048 					if (param.empty())
   1049 						continue;
   1050 				}
   1051 
   1052 				ml->SetMLock(cm, add, param);
   1053 			}
   1054 		}
   1055 		ml->Check();
   1056 	}
   1057 
   1058 	void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_hidden) anope_override
   1059 	{
   1060 		if (!show_hidden)
   1061 			return;
   1062 
   1063 		ModeLocks *ml = modelocks.Get(ci);
   1064 		if (ml)
   1065 			info[_("Mode lock")] = ml->GetMLockAsString(true);
   1066 	}
   1067 };
   1068 
   1069 MODULE_INIT(CSMode)