anope

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

messages.cpp (14582B)

      1 /* Common message handlers
      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 "services.h"
     13 #include "modules.h"
     14 #include "users.h"
     15 #include "protocol.h"
     16 #include "config.h"
     17 #include "uplink.h"
     18 #include "opertype.h"
     19 #include "messages.h"
     20 #include "servers.h"
     21 #include "channels.h"
     22 
     23 using namespace Message;
     24 
     25 void Away::Run(MessageSource &source, const std::vector<Anope::string> &params)
     26 {
     27 	const Anope::string &msg = !params.empty() ? params[0] : "";
     28 
     29 	FOREACH_MOD(OnUserAway, (source.GetUser(), msg));
     30 	if (!msg.empty())
     31 		Log(source.GetUser(), "away") << "is now away: " << msg;
     32 	else
     33 		Log(source.GetUser(), "away") << "is no longer away";
     34 }
     35 
     36 void Capab::Run(MessageSource &source, const std::vector<Anope::string> &params)
     37 {
     38 	if (params.size() == 1)
     39 	{
     40 		spacesepstream sep(params[0]);
     41 		Anope::string token;
     42 		while (sep.GetToken(token))
     43 			Servers::Capab.insert(token);
     44 	}
     45 	else
     46 		for (unsigned i = 0; i < params.size(); ++i)
     47 			Servers::Capab.insert(params[i]);
     48 }
     49 
     50 void Error::Run(MessageSource &source, const std::vector<Anope::string> &params)
     51 {
     52 	Log(LOG_TERMINAL) << "ERROR: " << params[0];
     53 	Anope::QuitReason = "Received ERROR from uplink: " + params[0];
     54 	Anope::Quitting = true;
     55 }
     56 
     57 void Invite::Run(MessageSource &source, const std::vector<Anope::string> &params)
     58 {
     59 	User *targ = User::Find(params[0]);
     60 	Channel *c = Channel::Find(params[1]);
     61 
     62 	if (!targ || targ->server != Me || !c || c->FindUser(targ))
     63 		return;
     64 
     65 	FOREACH_MOD(OnInvite, (source.GetUser(), c, targ));
     66 }
     67 
     68 void Join::Run(MessageSource &source, const std::vector<Anope::string> &params)
     69 {
     70 	User *user = source.GetUser();
     71 	const Anope::string &channels = params[0];
     72 
     73 	Anope::string channel;
     74 	commasepstream sep(channels);
     75 
     76 	while (sep.GetToken(channel))
     77 	{
     78 		/* Special case for /join 0 */
     79 		if (channel == "0")
     80 		{
     81 			for (User::ChanUserList::iterator it = user->chans.begin(), it_end = user->chans.end(); it != it_end; )
     82 			{
     83 				ChanUserContainer *cc = it->second;
     84 				Channel *c = cc->chan;
     85 				++it;
     86 
     87 				FOREACH_MOD(OnPrePartChannel, (user, c));
     88 				cc->chan->DeleteUser(user);
     89 				FOREACH_MOD(OnPartChannel, (user, c, c->name, ""));
     90 			}
     91 			continue;
     92 		}
     93 
     94 		std::list<SJoinUser> users;
     95 		users.push_back(std::make_pair(ChannelStatus(), user));
     96 
     97 		Channel *chan = Channel::Find(channel);
     98 		SJoin(source, channel, chan ? chan->creation_time : Anope::CurTime, "", users);
     99 	}
    100 }
    101 
    102 void Join::SJoin(MessageSource &source, const Anope::string &chan, time_t ts, const Anope::string &modes, const std::list<SJoinUser> &users)
    103 {
    104 	bool created;
    105 	Channel *c = Channel::FindOrCreate(chan, created, ts ? ts : Anope::CurTime);
    106 	bool keep_their_modes = true;
    107 
    108 	if (created)
    109 		c->syncing = true;
    110 	/* Some IRCds do not include a TS */
    111 	else if (!ts)
    112 		;
    113 	/* Our creation time is newer than what the server gave us, so reset the channel to the older time */
    114 	else if (c->creation_time > ts)
    115 	{
    116 		c->creation_time = ts;
    117 		c->Reset();
    118 	}
    119 	/* Their TS is newer, don't accept any modes from them */
    120 	else if (ts > c->creation_time)
    121 		keep_their_modes = false;
    122 
    123 	/* Update the modes for the channel */
    124 	if (keep_their_modes && !modes.empty())
    125 		/* If we are syncing, mlock is checked later in Channel::Sync. It is important to not check it here
    126 		 * so that Channel::SetCorrectModes can correctly detect the presence of channel mode +r.
    127 		 */
    128 		c->SetModesInternal(source, modes, ts, !c->syncing);
    129 
    130 	for (std::list<SJoinUser>::const_iterator it = users.begin(), it_end = users.end(); it != it_end; ++it)
    131 	{
    132 		const ChannelStatus &status = it->first;
    133 		User *u = it->second;
    134 		keep_their_modes = ts <= c->creation_time; // OnJoinChannel can call modules which can modify this channel's ts
    135 
    136 		if (c->FindUser(u))
    137 			continue;
    138 
    139 		/* Add the user to the channel */
    140 		c->JoinUser(u, keep_their_modes ? &status : NULL);
    141 
    142 		/* Check if the user is allowed to join */
    143 		if (c->CheckKick(u))
    144 			continue;
    145 
    146 		/* Set whatever modes the user should have, and remove any that
    147 		 * they aren't allowed to have (secureops etc).
    148 		 */
    149 		c->SetCorrectModes(u, true);
    150 
    151 		FOREACH_MOD(OnJoinChannel, (u, c));
    152 	}
    153 
    154 	/* Channel is done syncing */
    155 	if (c->syncing)
    156 	{
    157 		/* Sync the channel (mode lock, topic, etc) */
    158 		/* the channel is synced when the netmerge is complete */
    159 		Server *src = source.GetServer() ? source.GetServer() : Me;
    160 		if (src && src->IsSynced())
    161 		{
    162 			c->Sync();
    163 
    164 			if (c->CheckDelete())
    165 				c->QueueForDeletion();
    166 		}
    167 	}
    168 }
    169 
    170 void Kick::Run(MessageSource &source, const std::vector<Anope::string> &params)
    171 {
    172 	const Anope::string &channel = params[0];
    173 	const Anope::string &users = params[1];
    174 	const Anope::string &reason = params.size() > 2 ? params[2] : "";
    175 
    176 	Channel *c = Channel::Find(channel);
    177 	if (!c)
    178 		return;
    179 
    180 	Anope::string user;
    181 	commasepstream sep(users);
    182 
    183 	while (sep.GetToken(user))
    184 		c->KickInternal(source, user, reason);
    185 }
    186 
    187 void Kill::Run(MessageSource &source, const std::vector<Anope::string> &params)
    188 {
    189 	User *u = User::Find(params[0]);
    190 	BotInfo *bi;
    191 
    192 	if (!u)
    193 		return;
    194 
    195 	/* Recover if someone kills us. */
    196 	if (u->server == Me && (bi = dynamic_cast<BotInfo *>(u)))
    197 	{
    198 		static time_t last_time = 0;
    199 
    200 		if (last_time == Anope::CurTime)
    201 		{
    202 			Anope::QuitReason = "Kill loop detected. Are Services U:Lined?";
    203 			Anope::Quitting = true;
    204 			return;
    205 		}
    206 		last_time = Anope::CurTime;
    207 
    208 		bi->OnKill();
    209 	}
    210 	else
    211 		u->KillInternal(source, params[1]);
    212 }
    213 
    214 void Message::Mode::Run(MessageSource &source, const std::vector<Anope::string> &params)
    215 {
    216 	Anope::string buf;
    217 	for (unsigned i = 1; i < params.size(); ++i)
    218 		buf += " " + params[i];
    219 
    220 	if (IRCD->IsChannelValid(params[0]))
    221 	{
    222 		Channel *c = Channel::Find(params[0]);
    223 
    224 		if (c)
    225 			c->SetModesInternal(source, buf.substr(1), 0);
    226 	}
    227 	else
    228 	{
    229 		User *u = User::Find(params[0]);
    230 
    231 		if (u)
    232 			u->SetModesInternal(source, "%s", buf.substr(1).c_str());
    233 	}
    234 }
    235 
    236 /* XXX We should cache the file somewhere not open/read/close it on every request */
    237 void MOTD::Run(MessageSource &source, const std::vector<Anope::string> &params)
    238 {
    239 	Server *s = Server::Find(params[0]);
    240 	if (s != Me)
    241 		return;
    242 
    243 	FILE *f = fopen(Config->GetBlock("serverinfo")->Get<const Anope::string>("motd").c_str(), "r");
    244 	if (f)
    245 	{
    246 		IRCD->SendNumeric(375, source.GetSource(), ":- %s Message of the Day", s->GetName().c_str());
    247 		char buf[BUFSIZE];
    248 		while (fgets(buf, sizeof(buf), f))
    249 		{
    250 			buf[strlen(buf) - 1] = 0;
    251 			IRCD->SendNumeric(372, source.GetSource(), ":- %s", buf);
    252 		}
    253 		fclose(f);
    254 		IRCD->SendNumeric(376, source.GetSource(), ":End of /MOTD command.");
    255 	}
    256 	else
    257 		IRCD->SendNumeric(422, source.GetSource(), ":- MOTD file not found!  Please contact your IRC administrator.");
    258 }
    259 
    260 void Notice::Run(MessageSource &source, const std::vector<Anope::string> &params)
    261 {
    262 	Anope::string message = params[1];
    263 
    264 	User *u = source.GetUser();
    265 
    266 	/* ignore channel notices */
    267 	if (!IRCD->IsChannelValid(params[0]))
    268 	{
    269 		BotInfo *bi = BotInfo::Find(params[0]);
    270 		if (!bi)
    271 			return;
    272 		FOREACH_MOD(OnBotNotice, (u, bi, message));
    273 	}
    274 }
    275 
    276 void Part::Run(MessageSource &source, const std::vector<Anope::string> &params)
    277 {
    278 	User *u = source.GetUser();
    279 	const Anope::string &reason = params.size() > 1 ? params[1] : "";
    280 
    281 	Anope::string channel;
    282 	commasepstream sep(params[0]);
    283 
    284 	while (sep.GetToken(channel))
    285 	{
    286 		Channel *c = Channel::Find(channel);
    287 
    288 		if (!c || !u->FindChannel(c))
    289 			continue;
    290 
    291 		Log(u, c, "part") << "Reason: " << (!reason.empty() ? reason : "No reason");
    292 		FOREACH_MOD(OnPrePartChannel, (u, c));
    293 		c->DeleteUser(u);
    294 		FOREACH_MOD(OnPartChannel, (u, c, c->name, !reason.empty() ? reason : ""));
    295 	}
    296 }
    297 
    298 void Ping::Run(MessageSource &source, const std::vector<Anope::string> &params)
    299 {
    300 	IRCD->SendPong(params.size() > 1 ? params[1] : Me->GetSID(), params[0]);
    301 }
    302 
    303 void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> &params)
    304 {
    305 	const Anope::string &receiver = params[0];
    306 	Anope::string message = params[1];
    307 
    308 	User *u = source.GetUser();
    309 
    310 	if (IRCD->IsChannelValid(receiver))
    311 	{
    312 		Channel *c = Channel::Find(receiver);
    313 		if (c)
    314 		{
    315 			FOREACH_MOD(OnPrivmsg, (u, c, message));
    316 		}
    317 	}
    318 	else
    319 	{
    320 		/* If a server is specified (nick@server format), make sure it matches
    321 		 * us, and strip it off. */
    322 		Anope::string botname = receiver;
    323 		size_t s = receiver.find('@');
    324 		bool nick_only = false;
    325 		if (s != Anope::string::npos)
    326 		{
    327 			Anope::string servername(receiver.begin() + s + 1, receiver.end());
    328 			botname = botname.substr(0, s);
    329 			nick_only = true;
    330 			if (!servername.equals_ci(Me->GetName()))
    331 				return;
    332 		}
    333 		else if (!IRCD->RequiresID && Config->UseStrictPrivmsg)
    334 		{
    335 			BotInfo *bi = BotInfo::Find(receiver);
    336 			if (!bi)
    337 				return;
    338 			Log(LOG_DEBUG) << "Ignored PRIVMSG without @ from " << u->nick;
    339 			u->SendMessage(bi, _("\"/msg %s\" is no longer supported.  Use \"/msg %s@%s\" or \"/%s\" instead."), bi->nick.c_str(), bi->nick.c_str(), Me->GetName().c_str(), bi->nick.c_str());
    340 			return;
    341 		}
    342 
    343 		BotInfo *bi = BotInfo::Find(botname, nick_only);
    344 
    345 		if (bi)
    346 		{
    347 			if (message[0] == '\1' && message[message.length() - 1] == '\1')
    348 			{
    349 				if (message.substr(0, 6).equals_ci("\1PING "))
    350 				{
    351 					Anope::string buf = message;
    352 					buf.erase(buf.begin());
    353 					buf.erase(buf.end() - 1);
    354 					IRCD->SendCTCP(bi, u->nick, "%s", buf.c_str());
    355 				}
    356 				else if (message.substr(0, 9).equals_ci("\1VERSION\1"))
    357 				{
    358 					Module *enc = ModuleManager::FindFirstOf(ENCRYPTION);
    359 					IRCD->SendCTCP(bi, u->nick, "VERSION Anope-%s %s :%s - (%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "(none)", Anope::VersionBuildString().c_str());
    360 				}
    361 				return;
    362 			}
    363 
    364 			EventReturn MOD_RESULT;
    365 			FOREACH_RESULT(OnBotPrivmsg, MOD_RESULT, (u, bi, message));
    366 			if (MOD_RESULT == EVENT_STOP)
    367 				return;
    368 
    369 			bi->OnMessage(u, message);
    370 		}
    371 	}
    372 
    373 	return;
    374 }
    375 
    376 void Quit::Run(MessageSource &source, const std::vector<Anope::string> &params)
    377 {
    378 	const Anope::string &reason = params[0];
    379 	User *user = source.GetUser();
    380 
    381 	Log(user, "quit") << "quit (Reason: " << (!reason.empty() ? reason : "no reason") << ")";
    382 
    383 	user->Quit(reason);
    384 }
    385 
    386 void SQuit::Run(MessageSource &source, const std::vector<Anope::string> &params)
    387 {
    388 	Server *s = Server::Find(params[0]);
    389 
    390 	if (!s)
    391 	{
    392 		Log(LOG_DEBUG) << "SQUIT for nonexistent server " << params[0];
    393 		return;
    394 	}
    395 
    396 	if (s == Me)
    397 	{
    398 		if (Me->GetLinks().empty())
    399 			return;
    400 
    401 		s = Me->GetLinks().front();
    402 	}
    403 
    404 	s->Delete(s->GetName() + " " + s->GetUplink()->GetName());
    405 }
    406 
    407 void Stats::Run(MessageSource &source, const std::vector<Anope::string> &params)
    408 {
    409 	User *u = source.GetUser();
    410 
    411 	switch (params[0][0])
    412 	{
    413 		case 'l':
    414 			if (u->HasMode("OPER"))
    415 			{
    416 				IRCD->SendNumeric(211, source.GetSource(), "Server SendBuf SentBytes SentMsgs RecvBuf RecvBytes RecvMsgs ConnTime");
    417 				IRCD->SendNumeric(211, source.GetSource(), "%s %d %d %d %d %d %d %ld", Config->Uplinks[Anope::CurrentUplink].host.c_str(), UplinkSock->WriteBufferLen(), TotalWritten, -1, UplinkSock->ReadBufferLen(), TotalRead, -1, static_cast<long>(Anope::CurTime - Anope::StartTime));
    418 			}
    419 
    420 			IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]);
    421 			break;
    422 		case 'o':
    423 		case 'O':
    424 			/* Check whether the user is an operator */
    425 			if (!u->HasMode("OPER") && Config->GetBlock("options")->Get<bool>("hidestatso"))
    426 				IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]);
    427 			else
    428 			{
    429 				for (unsigned i = 0; i < Oper::opers.size(); ++i)
    430 				{
    431 					Oper *o = Oper::opers[i];
    432 
    433 					const NickAlias *na = NickAlias::Find(o->name);
    434 					if (na)
    435 						IRCD->SendNumeric(243, source.GetSource(), "O * * %s %s 0", o->name.c_str(), o->ot->GetName().replace_all_cs(" ", "_").c_str());
    436 				}
    437 
    438 				IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]);
    439 			}
    440 
    441 			break;
    442 		case 'u':
    443 		{
    444 			long uptime = static_cast<long>(Anope::CurTime - Anope::StartTime);
    445 			IRCD->SendNumeric(242, source.GetSource(), ":Services up %d day%s, %02d:%02d:%02d", uptime / 86400, uptime / 86400 == 1 ? "" : "s", (uptime / 3600) % 24, (uptime / 60) % 60, uptime % 60);
    446 			IRCD->SendNumeric(250, source.GetSource(), ":Current users: %d (%d ops); maximum %d", UserListByNick.size(), OperCount, MaxUserCount);
    447 			IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]);
    448 			break;
    449 		} /* case 'u' */
    450 
    451 		default:
    452 			IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]);
    453 	}
    454 
    455 	return;
    456 }
    457 
    458 void Time::Run(MessageSource &source, const std::vector<Anope::string> &params)
    459 {
    460 	time_t t;
    461 	time(&t);
    462 	struct tm *tm = localtime(&t);
    463 	char buf[64];
    464 	strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y %Z", tm);
    465 	IRCD->SendNumeric(391, source.GetSource(), "%s :%s", Me->GetName().c_str(), buf);
    466 	return;
    467 }
    468 
    469 void Topic::Run(MessageSource &source, const std::vector<Anope::string> &params)
    470 {
    471 	Channel *c = Channel::Find(params[0]);
    472 	if (c)
    473 		c->ChangeTopicInternal(source.GetUser(), source.GetSource(), params[1], Anope::CurTime);
    474 
    475 	return;
    476 }
    477 
    478 void Version::Run(MessageSource &source, const std::vector<Anope::string> &params)
    479 {
    480 	Module *enc = ModuleManager::FindFirstOf(ENCRYPTION);
    481 	IRCD->SendNumeric(351, source.GetSource(), "Anope-%s %s :%s -(%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "(none)", Anope::VersionBuildString().c_str());
    482 }
    483 
    484 void Whois::Run(MessageSource &source, const std::vector<Anope::string> &params)
    485 {
    486 	User *u = User::Find(params[0]);
    487 
    488 	if (u && u->server == Me)
    489 	{
    490 		const BotInfo *bi = BotInfo::Find(u->GetUID());
    491 		IRCD->SendNumeric(311, source.GetSource(), "%s %s %s * :%s", u->nick.c_str(), u->GetIdent().c_str(), u->host.c_str(), u->realname.c_str());
    492 		if (bi)
    493 			IRCD->SendNumeric(307, source.GetSource(), "%s :is a registered nick", bi->nick.c_str());
    494 		IRCD->SendNumeric(312, source.GetSource(), "%s %s :%s", u->nick.c_str(), Me->GetName().c_str(), Config->GetBlock("serverinfo")->Get<const Anope::string>("description").c_str());
    495 		if (bi)
    496 			IRCD->SendNumeric(317, source.GetSource(), "%s %ld %ld :seconds idle, signon time", bi->nick.c_str(), static_cast<long>(Anope::CurTime - bi->lastmsg), static_cast<long>(bi->signon));
    497 		IRCD->SendNumeric(313, source.GetSource(), "%s :is a Network Service",  u->nick.c_str());
    498 		IRCD->SendNumeric(318, source.GetSource(), "%s :End of /WHOIS list.", u->nick.c_str());
    499 	}
    500 	else
    501 		IRCD->SendNumeric(401, source.GetSource(), "%s :No such user.", params[0].c_str());
    502 }