anope

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

os_defcon.cpp (18009B)

      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_session.h"
     14 
     15 enum DefconLevel
     16 {
     17 	DEFCON_NO_NEW_CHANNELS,
     18 	DEFCON_NO_NEW_NICKS,
     19 	DEFCON_NO_MLOCK_CHANGE,
     20 	DEFCON_FORCE_CHAN_MODES,
     21 	DEFCON_REDUCE_SESSION,
     22 	DEFCON_NO_NEW_CLIENTS,
     23 	DEFCON_OPER_ONLY,
     24 	DEFCON_SILENT_OPER_ONLY,
     25 	DEFCON_AKILL_NEW_CLIENTS,
     26 	DEFCON_NO_NEW_MEMOS
     27 };
     28 
     29 bool DefConModesSet = false;
     30 
     31 struct DefconConfig
     32 {
     33 	std::vector<std::bitset<32> > DefCon;
     34 	std::set<Anope::string> DefConModesOn, DefConModesOff;
     35 	std::map<Anope::string, Anope::string> DefConModesOnParams;
     36 
     37 	int defaultlevel, sessionlimit;
     38 	Anope::string chanmodes, message, offmessage, akillreason;
     39 	std::vector<Anope::string> defcons;
     40 	time_t akillexpire, timeout;
     41 	bool globalondefcon;
     42 
     43 	unsigned max_session_kill;
     44 	time_t session_autokill_expiry;
     45 	Anope::string sle_reason, sle_detailsloc;
     46 
     47 	DefconConfig()
     48 	{
     49 		this->DefCon.resize(6);
     50 		this->defcons.resize(5);
     51 	}
     52 
     53 	bool Check(DefconLevel level)
     54 	{
     55 		return this->Check(this->defaultlevel, level);
     56 	}
     57 
     58 	bool Check(int dlevel, DefconLevel level)
     59 	{
     60 		return this->DefCon[dlevel].test(level);
     61 	}
     62 
     63 	void Add(int dlevel, DefconLevel level)
     64 	{
     65 		this->DefCon[dlevel][level] = true;
     66 	}
     67 
     68 	void Del(int dlevel, DefconLevel level)
     69 	{
     70 		this->DefCon[dlevel][level] = false;
     71 	}
     72 
     73 	bool SetDefConParam(const Anope::string &name, const Anope::string &buf)
     74 	{
     75 	       return DefConModesOnParams.insert(std::make_pair(name, buf)).second;
     76 	}
     77 
     78 	void UnsetDefConParam(const Anope::string &name)
     79 	{
     80 		DefConModesOnParams.erase(name);
     81 	}
     82 
     83 	bool GetDefConParam(const Anope::string &name, Anope::string &buf)
     84 	{
     85 	       std::map<Anope::string, Anope::string>::iterator it = DefConModesOnParams.find(name);
     86 
     87 	       buf.clear();
     88 
     89 	       if (it != DefConModesOnParams.end())
     90 	       {
     91 	               buf = it->second;
     92 	               return true;
     93 	       }
     94 
     95 	       return false;
     96 	}
     97 };
     98 
     99 static DefconConfig DConfig;
    100 
    101 static void runDefCon();
    102 static Anope::string defconReverseModes(const Anope::string &modes);
    103 
    104 static ServiceReference<GlobalService> GlobalService("GlobalService", "Global");
    105 
    106 static Timer *timeout;
    107 
    108 class DefConTimeout : public Timer
    109 {
    110 	int level;
    111 
    112  public:
    113 	DefConTimeout(Module *mod, int newlevel) : Timer(mod, DConfig.timeout), level(newlevel)
    114 	{
    115 		timeout = this;
    116 	}
    117 
    118 	~DefConTimeout()
    119 	{
    120 		timeout = NULL;
    121 	}
    122 
    123 	void Tick(time_t) anope_override
    124 	{
    125 		if (DConfig.defaultlevel != level)
    126 		{
    127 			DConfig.defaultlevel = level;
    128 			FOREACH_MOD(OnDefconLevel, (level));
    129 			Log(Config->GetClient("OperServ"), "operserv/defcon") << "Defcon level timeout, returning to level " << level;
    130 
    131 			if (DConfig.globalondefcon)
    132 			{
    133 				if (!DConfig.offmessage.empty())
    134 					GlobalService->SendGlobal(NULL, "", DConfig.offmessage);
    135 				else
    136 					GlobalService->SendGlobal(NULL, "", Anope::printf(Language::Translate(_("The Defcon level is now at: \002%d\002")), DConfig.defaultlevel));
    137 
    138 				if (!DConfig.message.empty())
    139 					GlobalService->SendGlobal(NULL, "", DConfig.message);
    140 			}
    141 
    142 			runDefCon();
    143 		}
    144 	}
    145 };
    146 
    147 class CommandOSDefcon : public Command
    148 {
    149 	void SendLevels(CommandSource &source)
    150 	{
    151 		if (DConfig.Check(DEFCON_NO_NEW_CHANNELS))
    152 			source.Reply(_("* No new channel registrations"));
    153 		if (DConfig.Check(DEFCON_NO_NEW_NICKS))
    154 			source.Reply(_("* No new nick registrations"));
    155 		if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE))
    156 			source.Reply(_("* No mode lock changes"));
    157 		if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && !DConfig.chanmodes.empty())
    158 			source.Reply(_("* Force channel modes (%s) to be set on all channels"), DConfig.chanmodes.c_str());
    159 		if (DConfig.Check(DEFCON_REDUCE_SESSION))
    160 			source.Reply(_("* Use the reduced session limit of %d"), DConfig.sessionlimit);
    161 		if (DConfig.Check(DEFCON_NO_NEW_CLIENTS))
    162 			source.Reply(_("* Kill any new clients connecting"));
    163 		if (DConfig.Check(DEFCON_OPER_ONLY))
    164 			source.Reply(_("* Ignore non-opers with a message"));
    165 		if (DConfig.Check(DEFCON_SILENT_OPER_ONLY))
    166 			source.Reply(_("* Silently ignore non-opers"));
    167 		if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS))
    168 			source.Reply(_("* AKILL any new clients connecting"));
    169 		if (DConfig.Check(DEFCON_NO_NEW_MEMOS))
    170 			source.Reply(_("* No new memos sent"));
    171 	}
    172 
    173  public:
    174 	CommandOSDefcon(Module *creator) : Command(creator, "operserv/defcon", 1, 1)
    175 	{
    176 		this->SetDesc(_("Manipulate the DefCon system"));
    177 		this->SetSyntax(_("[\0021\002|\0022\002|\0023\002|\0024\002|\0025\002]"));
    178 	}
    179 
    180 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    181 	{
    182 		const Anope::string &lvl = params[0];
    183 
    184 		if (lvl.empty())
    185 		{
    186 			source.Reply(_("Services are now at DEFCON \002%d\002."), DConfig.defaultlevel);
    187 			this->SendLevels(source);
    188 			return;
    189 		}
    190 
    191 		int newLevel = 0;
    192 		try
    193 		{
    194 			newLevel = convertTo<int>(lvl);
    195 		}
    196 		catch (const ConvertException &) { }
    197 
    198 		if (newLevel < 1 || newLevel > 5)
    199 		{
    200 			this->OnSyntaxError(source, "");
    201 			return;
    202 		}
    203 
    204 		DConfig.defaultlevel = newLevel;
    205 
    206 		FOREACH_MOD(OnDefconLevel, (newLevel));
    207 
    208 		delete timeout;
    209 
    210 		if (DConfig.timeout)
    211 			timeout = new DefConTimeout(this->module, 5);
    212 
    213 		source.Reply(_("Services are now at DEFCON \002%d\002."), DConfig.defaultlevel);
    214 		this->SendLevels(source);
    215 		Log(LOG_ADMIN, source, this) << "to change defcon level to " << newLevel;
    216 
    217 		/* Global notice the user what is happening. Also any Message that
    218 		   the Admin would like to add. Set in config file. */
    219 		if (DConfig.globalondefcon)
    220 		{
    221 			if (DConfig.defaultlevel == 5 && !DConfig.offmessage.empty())
    222 				GlobalService->SendGlobal(NULL, "", DConfig.offmessage);
    223 			else if (DConfig.defaultlevel != 5)
    224 			{
    225 				GlobalService->SendGlobal(NULL, "", Anope::printf(_("The Defcon level is now at: \002%d\002"), DConfig.defaultlevel));
    226 				if (!DConfig.message.empty())
    227 					GlobalService->SendGlobal(NULL, "", DConfig.message);
    228 			}
    229 		}
    230 
    231 		/* Run any defcon functions, e.g. FORCE CHAN MODE */
    232 		runDefCon();
    233 		return;
    234 	}
    235 
    236 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    237 	{
    238 		this->SendSyntax(source);
    239 		source.Reply(" ");
    240 		source.Reply(_("The defcon system can be used to implement a pre-defined\n"
    241 				"set of restrictions to services useful during an attempted\n"
    242 				"attack on the network."));
    243 		return true;
    244 	}
    245 };
    246 
    247 class OSDefcon : public Module
    248 {
    249 	ServiceReference<SessionService> session_service;
    250 	ServiceReference<XLineManager> akills;
    251 	CommandOSDefcon commandosdefcon;
    252 
    253 	void ParseModeString()
    254 	{
    255 		int add = -1; /* 1 if adding, 0 if deleting, -1 if neither */
    256 		unsigned char mode;
    257 		ChannelMode *cm;
    258 		ChannelModeParam *cmp;
    259 		Anope::string modes, param;
    260 
    261 		spacesepstream ss(DConfig.chanmodes);
    262 
    263 		DConfig.DefConModesOn.clear();
    264 		DConfig.DefConModesOff.clear();
    265 		ss.GetToken(modes);
    266 
    267 		/* Loop while there are modes to set */
    268 		for (unsigned i = 0, end = modes.length(); i < end; ++i)
    269 		{
    270 			mode = modes[i];
    271 
    272 			switch (mode)
    273 			{
    274 				case '+':
    275 					add = 1;
    276 					continue;
    277 				case '-':
    278 					add = 0;
    279 					continue;
    280 				default:
    281 					if (add < 0)
    282 						continue;
    283 			}
    284 
    285 			if ((cm = ModeManager::FindChannelModeByChar(mode)))
    286 			{
    287 				if (cm->type == MODE_STATUS || cm->type == MODE_LIST)
    288 				{
    289 					Log(this) << "DefConChanModes mode character '" << mode << "' cannot be locked";
    290 					continue;
    291 				}
    292 				else if (add)
    293 				{
    294 					DConfig.DefConModesOn.insert(cm->name);
    295 					DConfig.DefConModesOff.erase(cm->name);
    296 
    297 					if (cm->type == MODE_PARAM)
    298 					{
    299 						cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm);
    300 
    301 						if (!ss.GetToken(param))
    302 						{
    303 							Log(this) << "DefConChanModes mode character '" << mode << "' has no parameter while one is expected";
    304 							continue;
    305 						}
    306 
    307 						if (!cmp->IsValid(param))
    308 							continue;
    309 
    310 						DConfig.SetDefConParam(cmp->name, param);
    311 					}
    312 				}
    313 				else if (DConfig.DefConModesOn.count(cm->name))
    314 				{
    315 					DConfig.DefConModesOn.erase(cm->name);
    316 
    317 					if (cm->type == MODE_PARAM)
    318 						DConfig.UnsetDefConParam(cm->name);
    319 				}
    320 			}
    321 		}
    322 
    323 		/* We can't mlock +L if +l is not mlocked as well. */
    324 		if ((cm = ModeManager::FindChannelModeByName("REDIRECT")) && DConfig.DefConModesOn.count(cm->name) && !DConfig.DefConModesOn.count("LIMIT"))
    325 		{
    326 			DConfig.DefConModesOn.erase("REDIRECT");
    327 
    328 			Log(this) << "DefConChanModes must lock mode +l as well to lock mode +L";
    329 		}
    330 	}
    331 
    332  public:
    333 	OSDefcon(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), session_service("SessionService", "session"), akills("XLineManager", "xlinemanager/sgline"), commandosdefcon(this)
    334 	{
    335 
    336 	}
    337 
    338 	void OnReload(Configuration::Conf *conf) anope_override
    339 	{
    340 		Configuration::Block *block = conf->GetModule(this);
    341 		DefconConfig dconfig;
    342 
    343 		dconfig.defaultlevel = block->Get<int>("defaultlevel");
    344 		dconfig.defcons[4] = block->Get<const Anope::string>("level4");
    345 		dconfig.defcons[3] = block->Get<const Anope::string>("level3");
    346 		dconfig.defcons[2] = block->Get<const Anope::string>("level2");
    347 		dconfig.defcons[1] = block->Get<const Anope::string>("level1");
    348 		dconfig.sessionlimit = block->Get<int>("sessionlimit");
    349 		dconfig.akillreason = block->Get<const Anope::string>("akillreason");
    350 		dconfig.akillexpire = block->Get<time_t>("akillexpire");
    351 		dconfig.chanmodes = block->Get<const Anope::string>("chanmodes");
    352 		dconfig.timeout = block->Get<time_t>("timeout");
    353 		dconfig.globalondefcon = block->Get<bool>("globalondefcon");
    354 		dconfig.message = block->Get<const Anope::string>("message");
    355 		dconfig.offmessage = block->Get<const Anope::string>("offmessage");
    356 
    357 		block = conf->GetModule("os_session");
    358 
    359 		dconfig.max_session_kill = block->Get<int>("maxsessionkill");
    360 		dconfig.session_autokill_expiry = block->Get<time_t>("sessionautokillexpiry");
    361 		dconfig.sle_reason = block->Get<const Anope::string>("sessionlimitexceeded");
    362 		dconfig.sle_detailsloc = block->Get<const Anope::string>("sessionlimitdetailsloc");
    363 
    364 		if (dconfig.defaultlevel < 1 || dconfig.defaultlevel > 5)
    365 			throw ConfigException("The value for <defcon:defaultlevel> must be between 1 and 5");
    366 		else if (dconfig.akillexpire <= 0)
    367 			throw ConfigException("The value for <defcon:akillexpire> must be greater than zero!");
    368 
    369 		for (unsigned level = 1; level < 5; ++level)
    370 		{
    371 			spacesepstream operations(dconfig.defcons[level]);
    372 			Anope::string operation;
    373 			while (operations.GetToken(operation))
    374 			{
    375 				if (operation.equals_ci("nonewchannels"))
    376 					dconfig.Add(level, DEFCON_NO_NEW_CHANNELS);
    377 				else if (operation.equals_ci("nonewnicks"))
    378 					dconfig.Add(level, DEFCON_NO_NEW_NICKS);
    379 				else if (operation.equals_ci("nomlockchanges"))
    380 					dconfig.Add(level, DEFCON_NO_MLOCK_CHANGE);
    381 				else if (operation.equals_ci("forcechanmodes"))
    382 					dconfig.Add(level, DEFCON_FORCE_CHAN_MODES);
    383 				else if (operation.equals_ci("reducedsessions"))
    384 					dconfig.Add(level, DEFCON_REDUCE_SESSION);
    385 				else if (operation.equals_ci("nonewclients"))
    386 					dconfig.Add(level, DEFCON_NO_NEW_CLIENTS);
    387 				else if (operation.equals_ci("operonly"))
    388 					dconfig.Add(level, DEFCON_OPER_ONLY);
    389 				else if (operation.equals_ci("silentoperonly"))
    390 					dconfig.Add(level, DEFCON_SILENT_OPER_ONLY);
    391 				else if (operation.equals_ci("akillnewclients"))
    392 					dconfig.Add(level, DEFCON_AKILL_NEW_CLIENTS);
    393 				else if (operation.equals_ci("nonewmemos"))
    394 					dconfig.Add(level, DEFCON_NO_NEW_MEMOS);
    395 			}
    396 
    397 			if (dconfig.Check(level, DEFCON_REDUCE_SESSION) && dconfig.sessionlimit <= 0)
    398 				throw ConfigException("The value for <defcon:sessionlimit> must be greater than zero!");
    399 			else if (dconfig.Check(level, DEFCON_AKILL_NEW_CLIENTS) && dconfig.akillreason.empty())
    400 				throw ConfigException("The value for <defcon:akillreason> must not be empty!");
    401 			else if (dconfig.Check(level, DEFCON_FORCE_CHAN_MODES) && dconfig.chanmodes.empty())
    402 				throw ConfigException("The value for <defcon:chanmodes> must not be empty!");
    403 		}
    404 
    405 		DConfig = dconfig;
    406 		this->ParseModeString();
    407 	}
    408 
    409 	EventReturn OnChannelModeSet(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string &param) anope_override
    410 	{
    411 		if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOff.count(mode->name) && source.GetUser() && !source.GetBot())
    412 		{
    413 			c->RemoveMode(Config->GetClient("OperServ"), mode, param);
    414 
    415 			return EVENT_STOP;
    416 		}
    417 
    418 		return EVENT_CONTINUE;
    419 	}
    420 
    421 	EventReturn OnChannelModeUnset(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string &) anope_override
    422 	{
    423 		if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOn.count(mode->name) && source.GetUser() && !source.GetBot())
    424 		{
    425 			Anope::string param;
    426 
    427 			if (DConfig.GetDefConParam(mode->name, param))
    428 				c->SetMode(Config->GetClient("OperServ"), mode, param);
    429 			else
    430 				c->SetMode(Config->GetClient("OperServ"), mode);
    431 
    432 			return EVENT_STOP;
    433 
    434 		}
    435 
    436 		return EVENT_CONTINUE;
    437 	}
    438 
    439 	EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> &params) anope_override
    440 	{
    441 		if (DConfig.Check(DEFCON_OPER_ONLY) && !source.IsOper())
    442 		{
    443 			source.Reply(_("Services are in DefCon mode, please try again later."));
    444 			return EVENT_STOP;
    445 		}
    446 		else if (DConfig.Check(DEFCON_SILENT_OPER_ONLY) && !source.IsOper())
    447 		{
    448 			return EVENT_STOP;
    449 		}
    450 		else if (command->name == "nickserv/register" || command->name == "nickserv/group")
    451 		{
    452 			if (DConfig.Check(DEFCON_NO_NEW_NICKS))
    453 			{
    454 				source.Reply(_("Services are in DefCon mode, please try again later."));
    455 				return EVENT_STOP;
    456 			}
    457 		}
    458 		else if (command->name == "chanserv/mode" && params.size() > 1 && params[1].equals_ci("LOCK"))
    459 		{
    460 			if (DConfig.Check(DEFCON_NO_MLOCK_CHANGE))
    461 			{
    462 				source.Reply(_("Services are in DefCon mode, please try again later."));
    463 				return EVENT_STOP;
    464 			}
    465 		}
    466 		else if (command->name == "chanserv/register")
    467 		{
    468 			if (DConfig.Check(DEFCON_NO_NEW_CHANNELS))
    469 			{
    470 				source.Reply(_("Services are in DefCon mode, please try again later."));
    471 				return EVENT_STOP;
    472 			}
    473 		}
    474 		else if (command->name == "memoserv/send")
    475 		{
    476 			if (DConfig.Check(DEFCON_NO_NEW_MEMOS))
    477 			{
    478 				source.Reply(_("Services are in DefCon mode, please try again later."));
    479 				return EVENT_STOP;
    480 			}
    481 		}
    482 
    483 		return EVENT_CONTINUE;
    484 	}
    485 
    486 	void OnUserConnect(User *u, bool &exempt) anope_override
    487 	{
    488 		if (exempt || u->Quitting() || !u->server->IsSynced() || u->server->IsULined())
    489 			return;
    490 
    491 		BotInfo *OperServ = Config->GetClient("OperServ");
    492 		if (DConfig.Check(DEFCON_AKILL_NEW_CLIENTS) && akills)
    493 		{
    494 			Log(OperServ, "operserv/defcon") << "DEFCON: adding akill for *@" << u->host;
    495 			XLine x("*@" + u->host, OperServ ? OperServ->nick : "defcon", Anope::CurTime + DConfig.akillexpire, DConfig.akillreason, XLineManager::GenerateUID());
    496 			akills->Send(NULL, &x);
    497 		}
    498 
    499 		if (DConfig.Check(DEFCON_NO_NEW_CLIENTS) || DConfig.Check(DEFCON_AKILL_NEW_CLIENTS))
    500 		{
    501 			u->Kill(OperServ, DConfig.akillreason);
    502 			return;
    503 		}
    504 
    505 		if (DConfig.sessionlimit <= 0 || !session_service)
    506 			return;
    507 
    508 		Session *session = session_service->FindSession(u->ip.addr());
    509 		Exception *exception = session_service->FindException(u);
    510 
    511 		if (DConfig.Check(DEFCON_REDUCE_SESSION) && !exception)
    512 		{
    513 			if (session && session->count > static_cast<unsigned>(DConfig.sessionlimit))
    514 			{
    515 				if (!DConfig.sle_reason.empty())
    516 				{
    517 					Anope::string message = DConfig.sle_reason.replace_all_cs("%IP%", u->ip.addr());
    518 					u->SendMessage(OperServ, message);
    519 				}
    520 				if (!DConfig.sle_detailsloc.empty())
    521 					u->SendMessage(OperServ, DConfig.sle_detailsloc);
    522 
    523 				++session->hits;
    524 				if (akills && DConfig.max_session_kill && session->hits >= DConfig.max_session_kill)
    525 				{
    526 					XLine x("*@" + session->addr.mask(), OperServ ? OperServ->nick : "", Anope::CurTime + DConfig.session_autokill_expiry, "Defcon session limit exceeded", XLineManager::GenerateUID());
    527 					akills->Send(NULL, &x);
    528 					Log(OperServ, "akill/defcon") << "[DEFCON] Added a temporary AKILL for \002*@" << session->addr.mask() << "\002 due to excessive connections";
    529 				}
    530 				else
    531 				{
    532 					u->Kill(OperServ, "Defcon session limit exceeded");
    533 				}
    534 			}
    535 		}
    536 	}
    537 
    538 	void OnChannelModeAdd(ChannelMode *cm) anope_override
    539 	{
    540 		if (DConfig.chanmodes.find(cm->mchar) != Anope::string::npos)
    541 			this->ParseModeString();
    542 	}
    543 
    544 	void OnChannelSync(Channel *c) anope_override
    545 	{
    546 		if (DConfig.Check(DEFCON_FORCE_CHAN_MODES))
    547 			c->SetModes(Config->GetClient("OperServ"), false, "%s", DConfig.chanmodes.c_str());
    548 	}
    549 };
    550 
    551 static void runDefCon()
    552 {
    553 	BotInfo *OperServ = Config->GetClient("OperServ");
    554 	if (DConfig.Check(DEFCON_FORCE_CHAN_MODES))
    555 	{
    556 		if (!DConfig.chanmodes.empty() && !DefConModesSet)
    557 		{
    558 			if (DConfig.chanmodes[0] == '+' || DConfig.chanmodes[0] == '-')
    559 			{
    560 				Log(OperServ, "operserv/defcon") << "DEFCON: setting " << DConfig.chanmodes << " on all channels";
    561 				DefConModesSet = true;
    562 				for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
    563 					it->second->SetModes(OperServ, false, "%s", DConfig.chanmodes.c_str());
    564 			}
    565 		}
    566 	}
    567 	else
    568 	{
    569 		if (!DConfig.chanmodes.empty() && DefConModesSet)
    570 		{
    571 			if (DConfig.chanmodes[0] == '+' || DConfig.chanmodes[0] == '-')
    572 			{
    573 				DefConModesSet = false;
    574 				Anope::string newmodes = defconReverseModes(DConfig.chanmodes);
    575 				if (!newmodes.empty())
    576 				{
    577 					Log(OperServ, "operserv/defcon") << "DEFCON: setting " << newmodes << " on all channels";
    578 					for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
    579 						it->second->SetModes(OperServ, true, "%s", newmodes.c_str());
    580 				}
    581 			}
    582 		}
    583 	}
    584 }
    585 
    586 static Anope::string defconReverseModes(const Anope::string &modes)
    587 {
    588 	if (modes.empty())
    589 		return "";
    590 	Anope::string newmodes;
    591 	for (unsigned i = 0, end = modes.length(); i < end; ++i)
    592 	{
    593 		if (modes[i] == '+')
    594 			newmodes += '-';
    595 		else if (modes[i] == '-')
    596 			newmodes += '+';
    597 		else
    598 			newmodes += modes[i];
    599 	}
    600 	return newmodes;
    601 }
    602 
    603 MODULE_INIT(OSDefcon)