anope

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

chanserv.cpp (14004B)

      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 inline static Anope::string BotModes()
     16 {
     17 	return Config->GetModule("botserv")->Get<Anope::string>("botmodes",
     18 		Config->GetModule("chanserv")->Get<Anope::string>("botmodes", "o")
     19 	);
     20 }
     21 
     22 class ChanServCore : public Module, public ChanServService
     23 {
     24 	Reference<BotInfo> ChanServ;
     25 	std::vector<Anope::string> defaults;
     26 	ExtensibleItem<bool> inhabit;
     27 	ExtensibleRef<bool> persist;
     28 	bool always_lower;
     29 
     30  public:
     31 	ChanServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
     32 		ChanServService(this), inhabit(this, "inhabit"), persist("PERSIST"), always_lower(false)
     33 	{
     34 	}
     35 
     36 	void Hold(Channel *c) anope_override
     37 	{
     38 		/** A timer used to keep the BotServ bot/ChanServ in the channel
     39 		 * after kicking the last user in a channel
     40 		 */
     41 		class ChanServTimer : public Timer
     42 		{
     43 			Reference<BotInfo> &ChanServ;
     44 			ExtensibleItem<bool> &inhabit;
     45 			Reference<Channel> c;
     46 
     47 		 public:
     48 			/** Constructor
     49 			 * @param chan The channel
     50 			 */
     51 			ChanServTimer(Reference<BotInfo> &cs, ExtensibleItem<bool> &i, Module *m, Channel *chan) : Timer(m, Config->GetModule(m)->Get<time_t>("inhabit", "15s")), ChanServ(cs), inhabit(i), c(chan)
     52 			{
     53 				if (!ChanServ || !c)
     54 					return;
     55 				inhabit.Set(c, true);
     56 				if (!c->ci || !c->ci->bi)
     57 					ChanServ->Join(c);
     58 				else if (!c->FindUser(c->ci->bi))
     59 					c->ci->bi->Join(c);
     60 
     61 				/* Set +ntsi to prevent rejoin */
     62 				c->SetMode(NULL, "NOEXTERNAL");
     63 				c->SetMode(NULL, "TOPIC");
     64 				c->SetMode(NULL, "SECRET");
     65 				c->SetMode(NULL, "INVITE");
     66 			}
     67 
     68 			/** Called when the delay is up
     69 			 * @param The current time
     70 			 */
     71 			void Tick(time_t) anope_override
     72 			{
     73 				if (!c)
     74 					return;
     75 
     76 				/* In the event we don't part */
     77 				c->RemoveMode(NULL, "SECRET");
     78 				c->RemoveMode(NULL, "INVITE");
     79 
     80 				inhabit.Unset(c); /* now we're done changing modes, unset inhabit */
     81 
     82 				if (!c->ci || !c->ci->bi)
     83 				{
     84 					if (ChanServ)
     85 						ChanServ->Part(c);
     86 				}
     87 				/* If someone has rejoined this channel in the meantime, don't part the bot */
     88 				else if (c->users.size() <= 1)
     89 					c->ci->bi->Part(c);
     90 			}
     91 		};
     92 
     93 		if (inhabit.HasExt(c))
     94 			return;
     95 
     96 		new ChanServTimer(ChanServ, inhabit, this->owner, c);
     97 	}
     98 
     99 	void OnReload(Configuration::Conf *conf) anope_override
    100 	{
    101 		const Anope::string &channick = conf->GetModule(this)->Get<const Anope::string>("client");
    102 
    103 		if (channick.empty())
    104 			throw ConfigException(Module::name + ": <client> must be defined");
    105 
    106 		BotInfo *bi = BotInfo::Find(channick, true);
    107 		if (!bi)
    108 			throw ConfigException(Module::name + ": no bot named " + channick);
    109 
    110 		ChanServ = bi;
    111 
    112 		spacesepstream(conf->GetModule(this)->Get<const Anope::string>("defaults", "greet fantasy")).GetTokens(defaults);
    113 		if (defaults.empty())
    114 		{
    115 			defaults.push_back("KEEPTOPIC");
    116 			defaults.push_back("CS_SECURE");
    117 			defaults.push_back("SECUREFOUNDER");
    118 			defaults.push_back("SIGNKICK");
    119 		}
    120 		else if (defaults[0].equals_ci("none"))
    121 			defaults.clear();
    122 
    123 		always_lower = conf->GetModule(this)->Get<bool>("always_lower_ts");
    124 	}
    125 
    126 	void OnBotDelete(BotInfo *bi) anope_override
    127 	{
    128 		if (bi == ChanServ)
    129 			ChanServ = NULL;
    130 	}
    131 
    132 	EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) anope_override
    133 	{
    134 		if (bi == ChanServ && Config->GetModule(this)->Get<bool>("opersonly") && !u->HasMode("OPER"))
    135 		{
    136 			u->SendMessage(bi, ACCESS_DENIED);
    137 			return EVENT_STOP;
    138 		}
    139 
    140 		return EVENT_CONTINUE;
    141 	}
    142 
    143 	void OnDelCore(NickCore *nc) anope_override
    144 	{
    145 		std::deque<ChannelInfo *> chans;
    146 		nc->GetChannelReferences(chans);
    147 		int max_reg = Config->GetModule(this)->Get<int>("maxregistered");
    148 
    149 		for (unsigned i = 0; i < chans.size(); ++i)
    150 		{
    151 			ChannelInfo *ci = chans[i];
    152 
    153 			if (ci->GetFounder() == nc)
    154 			{
    155 				NickCore *newowner = NULL;
    156 				if (ci->GetSuccessor() && ci->GetSuccessor() != nc && (ci->GetSuccessor()->IsServicesOper() || !max_reg || ci->GetSuccessor()->channelcount < max_reg))
    157 					newowner = ci->GetSuccessor();
    158 				else
    159 				{
    160 					const ChanAccess *highest = NULL;
    161 					for (unsigned j = 0; j < ci->GetAccessCount(); ++j)
    162 					{
    163 						const ChanAccess *ca = ci->GetAccess(j);
    164 						NickCore *anc = ca->GetAccount();
    165 
    166 						if (!anc || (!anc->IsServicesOper() && max_reg && anc->channelcount >= max_reg) || (anc == nc))
    167 							continue;
    168 						if (!highest || *ca > *highest)
    169 							highest = ca;
    170 					}
    171 					if (highest)
    172 						newowner = highest->GetAccount();
    173 				}
    174 
    175 				if (newowner)
    176 				{
    177 					Log(LOG_NORMAL, "chanserv/drop", ChanServ) << "Transferring foundership of " << ci->name << " from deleted nick " << nc->display << " to " << newowner->display;
    178 					ci->SetFounder(newowner);
    179 					ci->SetSuccessor(NULL);
    180 				}
    181 				else
    182 				{
    183 					Log(LOG_NORMAL, "chanserv/drop", ChanServ) << "Deleting channel " << ci->name << " owned by deleted nick " << nc->display;
    184 
    185 					delete ci;
    186 					continue;
    187 				}
    188 			}
    189 
    190 			if (ci->GetSuccessor() == nc)
    191 				ci->SetSuccessor(NULL);
    192 
    193 			for (unsigned j = 0; j < ci->GetAccessCount(); ++j)
    194 			{
    195 				const ChanAccess *ca = ci->GetAccess(j);
    196 
    197 				if (ca->GetAccount() == nc)
    198 				{
    199 					delete ci->EraseAccess(j);
    200 					break;
    201 				}
    202 			}
    203 
    204 			for (unsigned j = 0; j < ci->GetAkickCount(); ++j)
    205 			{
    206 				const AutoKick *akick = ci->GetAkick(j);
    207 				if (akick->nc == nc)
    208 				{
    209 					ci->EraseAkick(j);
    210 					break;
    211 				}
    212 			}
    213 		}
    214 	}
    215 
    216 	void OnDelChan(ChannelInfo *ci) anope_override
    217 	{
    218 		/* remove access entries that are this channel */
    219 
    220 		std::deque<Anope::string> chans;
    221 		ci->GetChannelReferences(chans);
    222 
    223 		for (unsigned i = 0; i < chans.size(); ++i)
    224 		{
    225 			ChannelInfo *c = ChannelInfo::Find(chans[i]);
    226 			if (!c)
    227 				continue;
    228 
    229 			for (unsigned j = 0; j < c->GetAccessCount(); ++j)
    230 			{
    231 				ChanAccess *a = c->GetAccess(j);
    232 
    233 				if (a->Mask().equals_ci(ci->name))
    234 				{
    235 					delete a;
    236 					break;
    237 				}
    238 			}
    239 		}
    240 
    241 		if (ci->c)
    242 		{
    243 			ci->c->RemoveMode(ci->WhoSends(), "REGISTERED", "", false);
    244 
    245 			const Anope::string &require = Config->GetModule(this)->Get<const Anope::string>("require");
    246 			if (!require.empty())
    247 				ci->c->SetModes(ci->WhoSends(), false, "-%s", require.c_str());
    248 		}
    249 	}
    250 
    251 	EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    252 	{
    253 		if (!params.empty() || source.c || source.service != *ChanServ)
    254 			return EVENT_CONTINUE;
    255 		source.Reply(_("\002%s\002 allows you to register and control various\n"
    256 			"aspects of channels. %s can often prevent\n"
    257 			"malicious users from \"taking over\" channels by limiting\n"
    258 			"who is allowed channel operator privileges. Available\n"
    259 			"commands are listed below; to use them, type\n"
    260 			"\002%s%s \037command\037\002. For more information on a\n"
    261 			"specific command, type \002%s%s HELP \037command\037\002.\n"),
    262 			ChanServ->nick.c_str(), ChanServ->nick.c_str(), Config->StrictPrivmsg.c_str(), ChanServ->nick.c_str(), Config->StrictPrivmsg.c_str(), ChanServ->nick.c_str(), ChanServ->nick.c_str(), source.command.c_str());
    263 		return EVENT_CONTINUE;
    264 	}
    265 
    266 	void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    267 	{
    268 		if (!params.empty() || source.c || source.service != *ChanServ)
    269 			return;
    270 		time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
    271 		if (chanserv_expire >= 86400)
    272 			source.Reply(_(" \n"
    273 				"Note that any channel which is not used for %d days\n"
    274 				"(i.e. which no user on the channel's access list enters\n"
    275 				"for that period of time) will be automatically dropped."), chanserv_expire / 86400);
    276 		if (source.IsServicesOper())
    277 			source.Reply(_(" \n"
    278 				"Services Operators can also, depending on their access drop\n"
    279 				"any channel, view (and modify) the access, levels and akick\n"
    280 				"lists and settings for any channel."));
    281 	}
    282 
    283 	void OnCheckModes(Reference<Channel> &c) anope_override
    284 	{
    285 		if (!c)
    286 			return;
    287 
    288 		if (c->ci)
    289 			c->SetMode(c->ci->WhoSends(), "REGISTERED", "", false);
    290 		else
    291 			c->RemoveMode(c->ci->WhoSends(), "REGISTERED", "", false);
    292 
    293 		const Anope::string &require = Config->GetModule(this)->Get<const Anope::string>("require");
    294 		if (!require.empty())
    295 		{
    296 			if (c->ci)
    297 				c->SetModes(c->ci->WhoSends(), false, "+%s", require.c_str());
    298 			else
    299 				c->SetModes(c->ci->WhoSends(), false, "-%s", require.c_str());
    300 		}
    301 	}
    302 
    303 	void OnCreateChan(ChannelInfo *ci) anope_override
    304 	{
    305 		/* Set default chan flags */
    306 		for (unsigned i = 0; i < defaults.size(); ++i)
    307 			ci->Extend<bool>(defaults[i].upper());
    308 	}
    309 
    310 	EventReturn OnCanSet(User *u, const ChannelMode *cm) anope_override
    311 	{
    312 		if (Config->GetModule(this)->Get<const Anope::string>("nomlock").find(cm->mchar) != Anope::string::npos
    313 			|| Config->GetModule(this)->Get<const Anope::string>("require").find(cm->mchar) != Anope::string::npos)
    314 			return EVENT_STOP;
    315 		return EVENT_CONTINUE;
    316 	}
    317 
    318 	void OnChannelSync(Channel *c) anope_override
    319 	{
    320 		bool perm = c->HasMode("PERM") || (c->ci && persist && persist->HasExt(c->ci));
    321 		if (!perm && !c->botchannel && (c->users.empty() || (c->users.size() == 1 && c->users.begin()->second->user->server == Me)))
    322 		{
    323 			this->Hold(c);
    324 		}
    325 	}
    326 
    327 	void OnLog(Log *l) anope_override
    328 	{
    329 		if (l->type == LOG_CHANNEL)
    330 			l->bi = ChanServ;
    331 	}
    332 
    333 	void OnExpireTick() anope_override
    334 	{
    335 		time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
    336 
    337 		if (!chanserv_expire || Anope::NoExpire || Anope::ReadOnly)
    338 			return;
    339 
    340 		for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; )
    341 		{
    342 			ChannelInfo *ci = it->second;
    343 			++it;
    344 
    345 			bool expire = false;
    346 
    347 			if (Anope::CurTime - ci->last_used >= chanserv_expire)
    348 			{
    349 				if (ci->c)
    350 				{
    351 					time_t last_used = ci->last_used;
    352 					for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end && last_used == ci->last_used; ++cit)
    353 						ci->AccessFor(cit->second->user);
    354 					expire = last_used == ci->last_used;
    355 				}
    356 				else
    357 					expire = true;
    358 			}
    359 
    360 			FOREACH_MOD(OnPreChanExpire, (ci, expire));
    361 
    362 			if (expire)
    363 			{
    364 				Log(LOG_NORMAL, "chanserv/expire", ChanServ) << "Expiring channel " << ci->name << " (founder: " << (ci->GetFounder() ? ci->GetFounder()->display : "(none)") << ")";
    365 				FOREACH_MOD(OnChanExpire, (ci));
    366 				delete ci;
    367 			}
    368 		}
    369 	}
    370 
    371 	EventReturn OnCheckDelete(Channel *c) anope_override
    372 	{
    373 		/* Do not delete this channel if ChanServ/a BotServ bot is inhabiting it */
    374 		if (inhabit.HasExt(c))
    375 			return EVENT_STOP;
    376 
    377 		return EVENT_CONTINUE;
    378 	}
    379 
    380 	void OnPostInit() anope_override
    381 	{
    382 		if (!persist)
    383 			return;
    384 
    385 		ChannelMode *perm = ModeManager::FindChannelModeByName("PERM");
    386 
    387 		/* Find all persistent channels and create them, as we are about to finish burst to our uplink */
    388 		for (registered_channel_map::iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it)
    389 		{
    390 			ChannelInfo *ci = it->second;
    391 			if (!persist->HasExt(ci))
    392 				continue;
    393 
    394 			bool c;
    395 			ci->c = Channel::FindOrCreate(ci->name, c, ci->time_registered);
    396 			ci->c->syncing |= created;
    397 
    398 			if (perm)
    399 			{
    400 				ci->c->SetMode(NULL, perm);
    401 			}
    402 			else
    403 			{
    404 				if (!ci->bi)
    405 					ci->WhoSends()->Assign(NULL, ci);
    406 				if (ci->c->FindUser(ci->bi) == NULL)
    407 				{
    408 					ChannelStatus status(BotModes());
    409 					ci->bi->Join(ci->c, &status);
    410 				}
    411 			}
    412 		}
    413 
    414 	}
    415 
    416 	void OnChanRegistered(ChannelInfo *ci) anope_override
    417 	{
    418 		if (!persist || !ci->c)
    419 			return;
    420 		/* Mark the channel as persistent */
    421 		if (ci->c->HasMode("PERM"))
    422 			persist->Set(ci);
    423 		/* Persist may be in def cflags, set it here */
    424 		else if (persist->HasExt(ci))
    425 			ci->c->SetMode(NULL, "PERM");
    426 	}
    427 
    428 	void OnJoinChannel(User *u, Channel *c) anope_override
    429 	{
    430 		if (always_lower && c->ci && c->creation_time > c->ci->time_registered)
    431 		{
    432 			Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->time_registered;
    433 			c->creation_time = c->ci->time_registered;
    434 			IRCD->SendChannel(c);
    435 			c->Reset();
    436 		}
    437 	}
    438 
    439 	EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string &param) anope_override
    440 	{
    441 		if (!always_lower && Anope::CurTime == c->creation_time && c->ci && setter.GetUser() && !setter.GetUser()->server->IsULined())
    442 		{
    443 			ChanUserContainer *cu = c->FindUser(setter.GetUser());
    444 			ChannelMode *cm = ModeManager::FindChannelModeByName("OP");
    445 			if (cu && cm && !cu->status.HasMode(cm->mchar))
    446 			{
    447 				/* Our -o and their mode change crossing, bounce their mode */
    448 				c->RemoveMode(c->ci->WhoSends(), mode, param);
    449 				/* We don't set mlocks until after the join has finished processing, it will stack with this change,
    450 				 * so there isn't much for the user to remove except -nt etc which is likely locked anyway.
    451 				 */
    452 			}
    453 		}
    454 
    455 		return EVENT_CONTINUE;
    456 	}
    457 
    458 	void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
    459 	{
    460 		if (!show_all)
    461 			return;
    462 
    463 		time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d");
    464 		if (!ci->HasExt("CS_NO_EXPIRE") && chanserv_expire && !Anope::NoExpire && ci->last_used != Anope::CurTime)
    465 			info[_("Expires")] = Anope::strftime(ci->last_used + chanserv_expire, source.GetAccount());
    466 	}
    467 
    468 	void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override
    469 	{
    470 		if (always_lower)
    471 			// Since we always lower the TS, the other side will remove the modes if the channel ts lowers, so we don't
    472 			// have to worry about it
    473 			take_modes = false;
    474 		else if (ModeManager::FindChannelModeByName("REGISTERED"))
    475 			// Otherwise if the registered channel mode exists, we should remove modes if the channel is not +r
    476 			take_modes = !chan->HasMode("REGISTERED");
    477 	}
    478 };
    479 
    480 MODULE_INIT(ChanServCore)