anope

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

hybrid.cpp (28614B)

      1 /* ircd-hybrid protocol module. Minimum supported version of ircd-hybrid is 8.2.23.
      2  *
      3  * (C) 2003-2022 Anope Team <team@anope.org>
      4  * (C) 2012-2022 ircd-hybrid development team
      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 static Anope::string UplinkSID;
     16 static bool UseSVSAccount = false;  // Temporary backwards compatibility hack until old proto is deprecated
     17 
     18 class HybridProto : public IRCDProto
     19 {
     20 	void SendSVSKillInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
     21 	{
     22 		IRCDProto::SendSVSKillInternal(source, u, buf);
     23 		u->KillInternal(source, buf);
     24 	}
     25 
     26  public:
     27 	HybridProto(Module *creator) : IRCDProto(creator, "ircd-hybrid 8.2.23+")
     28 	{
     29 		DefaultPseudoclientModes = "+oi";
     30 		CanSVSNick = true;
     31 		CanSVSHold = true;
     32 		CanSVSJoin = true;
     33 		CanSNLine = true;
     34 		CanSQLine = true;
     35 		CanSQLineChannel = true;
     36 		CanSZLine = true;
     37 		CanCertFP = true;
     38 		CanSetVHost = true;
     39 		RequiresID = true;
     40 		MaxModes = 6;
     41 	}
     42 
     43 	void SendInvite(const MessageSource &source, const Channel *c, User *u) anope_override
     44 	{
     45 		UplinkSocket::Message(source) << "INVITE " << u->GetUID() << " " << c->name << " " << c->creation_time;
     46 	}
     47 
     48 	void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
     49 	{
     50 		UplinkSocket::Message(bi) << "NOTICE $$" << dest->GetName() << " :" << msg;
     51 	}
     52 
     53 	void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
     54 	{
     55 		UplinkSocket::Message(bi) << "PRIVMSG $$" << dest->GetName() << " :" << msg;
     56 	}
     57 
     58 	void SendSQLine(User *, const XLine *x) anope_override
     59 	{
     60 		UplinkSocket::Message(Me) << "RESV * " << (x->expires ? x->expires - Anope::CurTime : 0) << " " << x->mask << " :" << x->reason;
     61 	}
     62 
     63 	void SendSGLineDel(const XLine *x) anope_override
     64 	{
     65 		UplinkSocket::Message(Me) << "UNXLINE * " << x->mask;
     66 	}
     67 
     68 	void SendSGLine(User *, const XLine *x) anope_override
     69 	{
     70 		UplinkSocket::Message(Me) << "XLINE * " << x->mask << " " << (x->expires ? x->expires - Anope::CurTime : 0) << " :" << x->GetReason();
     71 	}
     72 
     73 	void SendSZLineDel(const XLine *x) anope_override
     74 	{
     75 		UplinkSocket::Message(Me) << "UNDLINE * " << x->GetHost();
     76 	}
     77 
     78 	void SendSZLine(User *, const XLine *x) anope_override
     79 	{
     80 		/* Calculate the time left before this would expire, capping it at 2 days */
     81 		time_t timeleft = x->expires - Anope::CurTime;
     82 
     83 		if (timeleft > 172800 || !x->expires)
     84 			timeleft = 172800;
     85 
     86 		UplinkSocket::Message(Me) << "DLINE * " << timeleft << " " << x->GetHost() << " :" << x->GetReason();
     87 	}
     88 
     89 	void SendAkillDel(const XLine *x) anope_override
     90 	{
     91 		if (x->IsRegex() || x->HasNickOrReal())
     92 			return;
     93 
     94 		UplinkSocket::Message(Me) << "UNKLINE * " << x->GetUser() << " " << x->GetHost();
     95 	}
     96 
     97 	void SendSQLineDel(const XLine *x) anope_override
     98 	{
     99 		UplinkSocket::Message(Me) << "UNRESV * " << x->mask;
    100 	}
    101 
    102 	void SendJoin(User *u, Channel *c, const ChannelStatus *status) anope_override
    103 	{
    104 		UplinkSocket::Message(Me) << "SJOIN " << c->creation_time << " " << c->name << " +" << c->GetModes(true, true) << " :" << u->GetUID();
    105 
    106 		/*
    107 		 * Note that we can send this with the SJOIN but choose not to
    108 		 * because the mode stacker will handle this and probably will
    109 		 * merge these modes with +nrt and other mlocked modes.
    110 		 */
    111 		if (status)
    112 		{
    113 			/* First save the channel status in case uc->status == status */
    114 			ChannelStatus cs = *status;
    115 
    116 			/*
    117 			 * If the user is internally on the channel with flags, kill them so that
    118 			 * the stacker will allow this.
    119 			 */
    120 			ChanUserContainer *uc = c->FindUser(u);
    121 			if (uc)
    122 				uc->status.Clear();
    123 
    124 			BotInfo *setter = BotInfo::Find(u->GetUID());
    125 			for (size_t i = 0; i < cs.Modes().length(); ++i)
    126 				c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), u->GetUID(), false);
    127 
    128 			if (uc)
    129 				uc->status = cs;
    130 		}
    131 	}
    132 
    133 	void SendAkill(User *u, XLine *x) anope_override
    134 	{
    135 		if (x->IsRegex() || x->HasNickOrReal())
    136 		{
    137 			if (!u)
    138 			{
    139 				/*
    140 				 * No user (this akill was just added), and contains nick and/or realname.
    141 				 * Find users that match and ban them.
    142 				 */
    143 				for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
    144 					if (x->manager->Check(it->second, x))
    145 						this->SendAkill(it->second, x);
    146 
    147 				return;
    148 			}
    149 
    150 			const XLine *old = x;
    151 
    152 			if (old->manager->HasEntry("*@" + u->host))
    153 				return;
    154 
    155 			/* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
    156 			XLine *xline = new XLine("*@" + u->host, old->by, old->expires, old->reason, old->id);
    157 
    158 			old->manager->AddXLine(xline);
    159 			x = xline;
    160 
    161 			Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->mask << " because " << u->GetMask() << "#"
    162 					<< u->realname << " matches " << old->mask;
    163 		}
    164 
    165 		/* Calculate the time left before this would expire, capping it at 2 days */
    166 		time_t timeleft = x->expires - Anope::CurTime;
    167 
    168 		if (timeleft > 172800 || !x->expires)
    169 			timeleft = 172800;
    170 
    171 		UplinkSocket::Message(Me) << "KLINE * " << timeleft << " " << x->GetUser() << " " << x->GetHost() << " :" << x->GetReason();
    172 	}
    173 
    174 	void SendServer(const Server *server) anope_override
    175 	{
    176 		if (server == Me)
    177 			UplinkSocket::Message() << "SERVER " << server->GetName() << " " << server->GetHops() + 1 << " " << server->GetSID() << " +" << " :" << server->GetDescription();
    178 		else
    179 			UplinkSocket::Message(Me) << "SID " << server->GetName() << " " << server->GetHops() + 1 << " " << server->GetSID() << " +" << " :" << server->GetDescription();
    180 	}
    181 
    182 	void SendConnect() anope_override
    183 	{
    184 		UplinkSocket::Message() << "PASS " << Config->Uplinks[Anope::CurrentUplink].password;
    185 
    186 		/*
    187 		 * TBURST - Supports topic burst
    188 		 * ENCAP  - Supports ENCAP
    189 		 * EOB    - Supports End Of Burst message
    190 		 * RHOST  - Supports UID message with realhost information
    191 		 * MLOCK  - Supports MLOCK
    192 		 */
    193 		UplinkSocket::Message() << "CAPAB :ENCAP TBURST EOB RHOST MLOCK";
    194 
    195 		SendServer(Me);
    196 
    197 		UplinkSocket::Message(Me) << "SVINFO 6 6 0 :" << Anope::CurTime;
    198 	}
    199 
    200 	void SendClientIntroduction(User *u) anope_override
    201 	{
    202 		Anope::string modes = "+" + u->GetModes();
    203 
    204 		UplinkSocket::Message(Me) << "UID " << u->nick << " 1 " << u->timestamp << " " << modes << " " << u->GetIdent() << " "
    205 						<< u->host << " " << u->host << " 0.0.0.0 " << u->GetUID() << " * :" << u->realname;
    206 	}
    207 
    208 	void SendEOB() anope_override
    209 	{
    210 		UplinkSocket::Message(Me) << "EOB";
    211 	}
    212 
    213 	void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
    214 	{
    215 		UplinkSocket::Message(source) << "SVSMODE " << u->GetUID() << " " << u->timestamp << " " << buf;
    216 	}
    217 
    218 	void SendLogin(User *u, NickAlias *na) anope_override
    219 	{
    220 		if (UseSVSAccount == false)
    221 			IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %s", na->nc->display.c_str());
    222 		else
    223 			UplinkSocket::Message(Me) << "SVSACCOUNT " << u->GetUID() << " " << u->timestamp << " " << na->nc->display;
    224 	}
    225 
    226 	void SendLogout(User *u) anope_override
    227 	{
    228 		if (UseSVSAccount == false)
    229 			IRCD->SendMode(Config->GetClient("NickServ"), u, "+d *");
    230 		else
    231 			UplinkSocket::Message(Me) << "SVSACCOUNT " << u->GetUID() << " " << u->timestamp << " *";
    232 	}
    233 
    234 	void SendChannel(Channel *c) anope_override
    235 	{
    236 		Anope::string modes = "+" + c->GetModes(true, true);
    237 		UplinkSocket::Message(Me) << "SJOIN " << c->creation_time << " " << c->name << " " << modes << " :";
    238 	}
    239 
    240 	void SendTopic(const MessageSource &source, Channel *c) anope_override
    241 	{
    242 		UplinkSocket::Message(source) << "TBURST " << c->creation_time << " " << c->name << " " << c->topic_ts << " " << c->topic_setter << " :" << c->topic;
    243 	}
    244 
    245 	void SendForceNickChange(User *u, const Anope::string &newnick, time_t when) anope_override
    246 	{
    247 		UplinkSocket::Message(Me) << "SVSNICK " << u->GetUID() << " " << u->timestamp << " " << newnick << " " << when;
    248 	}
    249 
    250 	void SendSVSJoin(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &) anope_override
    251 	{
    252 		UplinkSocket::Message(source) << "SVSJOIN " << u->GetUID() << " " << chan;
    253 	}
    254 
    255 	void SendSVSPart(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &param) anope_override
    256 	{
    257 		if (!param.empty())
    258 			UplinkSocket::Message(source) << "SVSPART " << u->GetUID() << " " << chan << " :" << param;
    259 		else
    260 			UplinkSocket::Message(source) << "SVSPART " << u->GetUID() << " " << chan;
    261 	}
    262 
    263 	void SendSVSHold(const Anope::string &nick, time_t t) anope_override
    264 	{
    265 		XLine x(nick, Me->GetName(), Anope::CurTime + t, "Being held for registered user");
    266 		this->SendSQLine(NULL, &x);
    267 	}
    268 
    269 	void SendSVSHoldDel(const Anope::string &nick) anope_override
    270 	{
    271 		XLine x(nick);
    272 		this->SendSQLineDel(&x);
    273 	}
    274 
    275 	void SendVhost(User *u, const Anope::string &ident, const Anope::string &host) anope_override
    276 	{
    277 		UplinkSocket::Message(Me) << "SVSHOST " << u->GetUID() << " " << u->timestamp << " " << host;
    278 	}
    279 
    280 	void SendVhostDel(User *u) anope_override
    281 	{
    282 		UplinkSocket::Message(Me) << "SVSHOST " << u->GetUID() << " " << u->timestamp << " " << u->host;
    283 	}
    284 
    285 	bool IsExtbanValid(const Anope::string &mask) anope_override
    286 	{
    287 		return mask.length() >= 4 && mask[0] == '$' && mask[2] == ':';
    288 	}
    289 
    290 	bool IsIdentValid(const Anope::string &ident) anope_override
    291 	{
    292 		if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
    293 			return false;
    294 
    295 		/*
    296 		 * If the user name begins with a tilde (~), make sure there is at least
    297 		 * one succeeding character.
    298 		 */
    299 		unsigned i = ident[0] == '~';
    300 		if (i >= ident.length())
    301 			return false;
    302 
    303 		/* User names may not start with a '-', '_', or '.'. */
    304 		const char &a = ident[i];
    305 		if (a == '-' || a == '_' || a == '.')
    306 			return false;
    307 
    308 		for (i = 0; i < ident.length(); ++i)
    309 		{
    310 			const char &c = ident[i];
    311 
    312 			/* A tilde can only be used as the first character of a user name. */
    313 			if (c == '~' && i == 0)
    314 				continue;
    315 
    316 			if ((c >= 'A' && c <= 'Z') ||
    317 				(c >= 'a' && c <= 'z') ||
    318 				(c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.')
    319 				continue;
    320 
    321 			return false;
    322 		}
    323 
    324 		return true;
    325 	}
    326 };
    327 
    328 struct IRCDMessageBMask : IRCDMessage
    329 {
    330 	IRCDMessageBMask(Module *creator) : IRCDMessage(creator, "BMASK", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
    331 
    332 	/*            0          1        2 3               */
    333 	/* :0MC BMASK 1350157102 #channel b :*!*@*.test.com */
    334 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    335 	{
    336 		Channel *c = Channel::Find(params[1]);
    337 		ChannelMode *mode = ModeManager::FindChannelModeByChar(params[2][0]);
    338 
    339 		if (c && mode)
    340 		{
    341 			spacesepstream bans(params[3]);
    342 			Anope::string token;
    343 
    344 			while (bans.GetToken(token))
    345 				c->SetModeInternal(source, mode, token);
    346 		}
    347 	}
    348 };
    349 
    350 struct IRCDMessageCapab : Message::Capab
    351 {
    352 	IRCDMessageCapab(Module *creator) : Message::Capab(creator, "CAPAB") { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    353 
    354 	/*       0                 */
    355 	/* CAPAB :TBURST EOB MLOCK */
    356 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    357 	{
    358 		spacesepstream sep(params[0]);
    359 		Anope::string capab;
    360 
    361 		while (sep.GetToken(capab))
    362 		{
    363 			if (capab.find("HOP") != Anope::string::npos || capab.find("RHOST") != Anope::string::npos)
    364 				ModeManager::AddChannelMode(new ChannelModeStatus("HALFOP", 'h', '%', 1));
    365 			if (capab.find("AOP") != Anope::string::npos)
    366 				ModeManager::AddChannelMode(new ChannelModeStatus("PROTECT", 'a', '&', 3));
    367 			if (capab.find("QOP") != Anope::string::npos)
    368 				ModeManager::AddChannelMode(new ChannelModeStatus("OWNER", 'q', '~', 4));
    369 		}
    370 
    371 		Message::Capab::Run(source, params);
    372 	}
    373 };
    374 
    375 struct IRCDMessageCertFP: IRCDMessage
    376 {
    377 	IRCDMessageCertFP(Module *creator) : IRCDMessage(creator, "CERTFP", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
    378 
    379 	/*                   0                                                                */
    380 	/* :0MCAAAAAB CERTFP 4C62287BA6776A89CD4F8FF10A62FFB35E79319F51AF6C62C674984974FCCB1D */
    381 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    382 	{
    383 		User *u = source.GetUser();
    384 
    385 		u->fingerprint = params[0];
    386 		FOREACH_MOD(OnFingerprint, (u));
    387 	}
    388 };
    389 
    390 struct IRCDMessageEOB : IRCDMessage
    391 {
    392 	IRCDMessageEOB(Module *creator) : IRCDMessage(creator, "EOB", 0) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
    393 
    394 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    395 	{
    396 		source.GetServer()->Sync(true);
    397 	}
    398 };
    399 
    400 struct IRCDMessageJoin : Message::Join
    401 {
    402 	IRCDMessageJoin(Module *creator) : Message::Join(creator, "JOIN") { }
    403 
    404 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    405 	{
    406 		if (params.size() < 2)
    407 			return;
    408 
    409 		std::vector<Anope::string> p = params;
    410 		p.erase(p.begin());
    411 
    412 		Message::Join::Run(source, p);
    413 	}
    414 };
    415 
    416 struct IRCDMessageMetadata : IRCDMessage
    417 {
    418 	IRCDMessageMetadata(Module *creator) : IRCDMessage(creator, "METADATA", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
    419 
    420 	/*               0      1         2      3                                                                 */
    421 	/* :0MC METADATA client 0MCAAAAAB certfp :4C62287BA6776A89CD4F8FF10A62FFB35E79319F51AF6C62C674984974FCCB1D */
    422 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    423 	{
    424 		if (params[0].equals_cs("client"))
    425 		{
    426 			User *u = User::Find(params[1]);
    427 			if (!u)
    428 			{
    429 				Log(LOG_DEBUG) << "METADATA for nonexistent user " << params[1];
    430 				return;
    431 			}
    432 
    433 			if (params[2].equals_cs("certfp"))
    434 			{
    435 				u->fingerprint = params[3];
    436 				FOREACH_MOD(OnFingerprint, (u));
    437 			}
    438 		}
    439 	}
    440 };
    441 
    442 struct IRCDMessageMLock : IRCDMessage
    443 {
    444 	IRCDMessageMLock(Module *creator) : IRCDMessage(creator, "MLOCK", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
    445 
    446 	/*            0          1        2          3   */
    447 	/* :0MC MLOCK 1350157102 #channel 1350158923 :nt */
    448 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    449 	{
    450 		Channel *c = Channel::Find(params[1]);
    451 
    452 		if (c && c->ci)
    453 		{
    454 			ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks");
    455 			Anope::string modes;
    456 
    457 			if (modelocks)
    458 				modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
    459 
    460 			// Mode lock string is not what we say it is?
    461 			if (modes != params[3])
    462 				UplinkSocket::Message(Me) << "MLOCK " << c->creation_time << " " << c->name << " " << Anope::CurTime << " :" << modes;
    463 		}
    464 	}
    465 };
    466 
    467 struct IRCDMessageNick : IRCDMessage
    468 {
    469 	IRCDMessageNick(Module *creator) : IRCDMessage(creator, "NICK", 2) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
    470 
    471 	/*                 0       1          */
    472 	/* :0MCAAAAAB NICK newnick 1350157102 */
    473 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    474 	{
    475 		source.GetUser()->ChangeNick(params[0], convertTo<time_t>(params[1]));
    476 	}
    477 };
    478 
    479 struct IRCDMessagePass : IRCDMessage
    480 {
    481 	IRCDMessagePass(Module *creator) : IRCDMessage(creator, "PASS", 1) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    482 
    483 	/*      0        */
    484 	/* PASS password */
    485 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    486 	{
    487 		if (params.size() == 4)
    488 			UplinkSID = params[3];
    489 	}
    490 };
    491 
    492 struct IRCDMessagePong : IRCDMessage
    493 {
    494 	IRCDMessagePong(Module *creator) : IRCDMessage(creator, "PONG", 0) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    495 
    496 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    497 	{
    498 		source.GetServer()->Sync(false);
    499 	}
    500 };
    501 
    502 struct IRCDMessageServer : IRCDMessage
    503 {
    504 	IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    505 
    506 	/*        0          1 2   3 4                        */
    507 	/* SERVER hades.arpa 1 4XY + :ircd-hybrid test server */
    508 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    509 	{
    510 		/* Servers other than our immediate uplink are introduced via SID */
    511 		if (params[1] != "1")
    512 			return;
    513 
    514 		if (params.size() == 5)
    515 		{
    516 			UplinkSID = params[2];
    517 			UseSVSAccount = true;
    518 		}
    519 
    520 		new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], 1, params.back(), UplinkSID);
    521 
    522 		IRCD->SendPing(Me->GetName(), params[0]);
    523 	}
    524 };
    525 
    526 struct IRCDMessageSID : IRCDMessage
    527 {
    528 	IRCDMessageSID(Module *creator) : IRCDMessage(creator, "SID", 5) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    529 
    530 	/*          0          1 2   3 4                        */
    531 	/* :0MC SID hades.arpa 2 4XY + :ircd-hybrid test server */
    532 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    533 	{
    534 		unsigned int hops = params[1].is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
    535 		new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, params.back(), params[2]);
    536 
    537 		IRCD->SendPing(Me->GetName(), params[0]);
    538 	}
    539 };
    540 
    541 struct IRCDMessageSJoin : IRCDMessage
    542 {
    543 	IRCDMessageSJoin(Module *creator) : IRCDMessage(creator, "SJOIN", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    544 
    545 	/*            0          1       2   3                      */
    546 	/* :0MC SJOIN 1654877335 #nether +nt :@0MCAAAAAB +0MCAAAAAC */
    547 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    548 	{
    549 		Anope::string modes;
    550 
    551 		for (unsigned i = 2; i < params.size() - 1; ++i)
    552 			modes += " " + params[i];
    553 
    554 		if (!modes.empty())
    555 			modes.erase(modes.begin());
    556 
    557 		std::list<Message::Join::SJoinUser> users;
    558 
    559 		spacesepstream sep(params[params.size() - 1]);
    560 		Anope::string buf;
    561 
    562 		while (sep.GetToken(buf))
    563 		{
    564 			Message::Join::SJoinUser sju;
    565 
    566 			/* Get prefixes from the nick */
    567 			for (char ch; (ch = ModeManager::GetStatusChar(buf[0])); )
    568 			{
    569 				buf.erase(buf.begin());
    570 				sju.first.AddMode(ch);
    571 			}
    572 
    573 			sju.second = User::Find(buf);
    574 			if (!sju.second)
    575 			{
    576 				Log(LOG_DEBUG) << "SJOIN for nonexistent user " << buf << " on " << params[1];
    577 				continue;
    578 			}
    579 
    580 			users.push_back(sju);
    581 		}
    582 
    583 		time_t ts = Anope::string(params[0]).is_pos_number_only() ? convertTo<time_t>(params[0]) : Anope::CurTime;
    584 		Message::Join::SJoin(source, params[1], ts, modes, users);
    585 	}
    586 };
    587 
    588 struct IRCDMessageSVSMode : IRCDMessage
    589 {
    590 	IRCDMessageSVSMode(Module *creator) : IRCDMessage(creator, "SVSMODE", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    591 
    592 	/*              0         1          2  */
    593 	/* :0MC SVSMODE 0MCAAAAAB 1350157102 +r */
    594 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    595 	{
    596 		User *u = User::Find(params[0]);
    597 
    598 		if (!u)
    599 			return;
    600 
    601 		if (!params[1].is_pos_number_only() || convertTo<time_t>(params[1]) != u->timestamp)
    602 			return;
    603 
    604 		u->SetModesInternal(source, "%s", params[2].c_str());
    605 	}
    606 };
    607 
    608 struct IRCDMessageTBurst : IRCDMessage
    609 {
    610 	IRCDMessageTBurst(Module *creator) : IRCDMessage(creator, "TBURST", 5) { }
    611 
    612 	/*             0          1       2          3                      4                      */
    613 	/* :0MC TBURST 1654867975 #nether 1654877335 Steve!~steve@the.mines :Join the ghast nation */
    614 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    615 	{
    616 		Anope::string setter;
    617 		sepstream(params[3], '!').GetToken(setter, 0);
    618 		time_t topic_time = Anope::string(params[2]).is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime;
    619 		Channel *c = Channel::Find(params[1]);
    620 
    621 		if (c)
    622 			c->ChangeTopicInternal(NULL, setter, params[4], topic_time);
    623 	}
    624 };
    625 
    626 struct IRCDMessageTMode : IRCDMessage
    627 {
    628 	IRCDMessageTMode(Module *creator) : IRCDMessage(creator, "TMODE", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    629 
    630 	/*            0          1       2    */
    631 	/* :0MC TMODE 1654867975 #nether +ntR */
    632 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    633 	{
    634 		time_t ts = 0;
    635 
    636 		try
    637 		{
    638 			ts = convertTo<time_t>(params[0]);
    639 		}
    640 		catch (const ConvertException &) { }
    641 
    642 		Channel *c = Channel::Find(params[1]);
    643 		Anope::string modes = params[2];
    644 
    645 		for (unsigned i = 3; i < params.size(); ++i)
    646 			modes += " " + params[i];
    647 
    648 		if (c)
    649 			c->SetModesInternal(source, modes, ts);
    650 	}
    651 };
    652 
    653 struct IRCDMessageUID : IRCDMessage
    654 {
    655 	IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 11) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
    656 
    657 	/*          0     1 2          3   4      5            6         7        8         9     10                   */
    658 	/* :0MC UID Steve 1 1350157102 +oi ~steve virtual.host real.host 10.0.0.1 0MCAAAAAB Steve :Mining all the time */
    659 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    660 	{
    661 		NickAlias *na = NULL;
    662 
    663 		if (params[9] != "*")
    664 			na = NickAlias::Find(params[9]);
    665 
    666 		/* Source is always the server */
    667 		User::OnIntroduce(params[0], params[4], params[6], params[5], params[7], source.GetServer(), params[10],
    668 				params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : 0,
    669 				params[3], params[8], na ? *na->nc : NULL);
    670 	}
    671 };
    672 
    673 class ProtoHybrid : public Module
    674 {
    675 	HybridProto ircd_proto;
    676 
    677 	/* Core message handlers */
    678 	Message::Away message_away;
    679 	Message::Error message_error;
    680 	Message::Invite message_invite;
    681 	Message::Kick message_kick;
    682 	Message::Kill message_kill;
    683 	Message::Mode message_mode;
    684 	Message::MOTD message_motd;
    685 	Message::Notice message_notice;
    686 	Message::Part message_part;
    687 	Message::Ping message_ping;
    688 	Message::Privmsg message_privmsg;
    689 	Message::Quit message_quit;
    690 	Message::SQuit message_squit;
    691 	Message::Stats message_stats;
    692 	Message::Time message_time;
    693 	Message::Topic message_topic;
    694 	Message::Version message_version;
    695 	Message::Whois message_whois;
    696 
    697 	/* Our message handlers */
    698 	IRCDMessageBMask message_bmask;
    699 	IRCDMessageCapab message_capab;
    700 	IRCDMessageCertFP message_certfp;
    701 	IRCDMessageEOB message_eob;
    702 	IRCDMessageJoin message_join;
    703 	IRCDMessageMetadata message_metadata;
    704 	IRCDMessageMLock message_mlock;
    705 	IRCDMessageNick message_nick;
    706 	IRCDMessagePass message_pass;
    707 	IRCDMessagePong message_pong;
    708 	IRCDMessageServer message_server;
    709 	IRCDMessageSID message_sid;
    710 	IRCDMessageSJoin message_sjoin;
    711 	IRCDMessageSVSMode message_svsmode;
    712 	IRCDMessageTBurst message_tburst;
    713 	IRCDMessageTMode message_tmode;
    714 	IRCDMessageUID message_uid;
    715 
    716 	bool use_server_side_mlock;
    717 
    718 	void AddModes()
    719 	{
    720 		/* Add user modes */
    721 		ModeManager::AddUserMode(new UserModeOperOnly("ADMIN", 'a'));
    722 		ModeManager::AddUserMode(new UserMode("CALLERID", 'g'));
    723 		ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
    724 		ModeManager::AddUserMode(new UserModeOperOnly("LOCOPS", 'l'));
    725 		ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
    726 		ModeManager::AddUserMode(new UserMode("HIDECHANS", 'p'));
    727 		ModeManager::AddUserMode(new UserMode("HIDEIDLE", 'q'));
    728 		ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'r'));
    729 		ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
    730 		ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
    731 		ModeManager::AddUserMode(new UserMode("BOT", 'B'));
    732 		ModeManager::AddUserMode(new UserMode("DEAF", 'D'));
    733 		ModeManager::AddUserMode(new UserMode("SOFTCALLERID", 'G'));
    734 		ModeManager::AddUserMode(new UserModeOperOnly("HIDEOPER", 'H'));
    735 		ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
    736 		ModeManager::AddUserMode(new UserModeNoone("SSL", 'S'));
    737 		ModeManager::AddUserMode(new UserModeNoone("WEBIRC", 'W'));
    738 		ModeManager::AddUserMode(new UserMode("SECUREONLY", 'Z'));
    739 
    740 		/* b/e/I */
    741 		ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
    742 		ModeManager::AddChannelMode(new ChannelModeList("EXCEPT", 'e'));
    743 		ModeManager::AddChannelMode(new ChannelModeList("INVITEOVERRIDE", 'I'));
    744 
    745 		/* v/o */
    746 		ModeManager::AddChannelMode(new ChannelModeStatus("VOICE", 'v', '+', 0));
    747 		ModeManager::AddChannelMode(new ChannelModeStatus("OP", 'o', '@', 2));
    748 
    749 		/* l/k */
    750 		ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
    751 		ModeManager::AddChannelMode(new ChannelModeKey('k'));
    752 
    753 		/* Add channel modes */
    754 		ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
    755 		ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
    756 		ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
    757 		ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
    758 		ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
    759 		ModeManager::AddChannelMode(new ChannelModeNoone("REGISTERED", 'r'));
    760 		ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
    761 		ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
    762 		ModeManager::AddChannelMode(new ChannelMode("NOCTCP", 'C'));
    763 		ModeManager::AddChannelMode(new ChannelMode("NOKNOCK", 'K'));
    764 		ModeManager::AddChannelMode(new ChannelModeOperOnly("LBAN", 'L'));
    765 		ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
    766 		ModeManager::AddChannelMode(new ChannelMode("NONICK", 'N'));
    767 		ModeManager::AddChannelMode(new ChannelModeOperOnly("OPERONLY", 'O'));
    768 		ModeManager::AddChannelMode(new ChannelMode("NOKICK", 'Q'));
    769 		ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
    770 		ModeManager::AddChannelMode(new ChannelMode("SSL", 'S'));
    771 		ModeManager::AddChannelMode(new ChannelMode("NONOTICE", 'T'));
    772 		ModeManager::AddChannelMode(new ChannelMode("NOINVITE", 'V'));
    773 		ModeManager::AddChannelMode(new ChannelModeNoone("ISSECURE", 'Z'));
    774 	}
    775 
    776  public:
    777 	ProtoHybrid(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
    778 		ircd_proto(this),
    779 		message_away(this),
    780 		message_error(this),
    781 		message_invite(this),
    782 		message_kick(this),
    783 		message_kill(this),
    784 		message_mode(this),
    785 		message_motd(this),
    786 		message_notice(this),
    787 		message_part(this),
    788 		message_ping(this),
    789 		message_privmsg(this),
    790 		message_quit(this),
    791 		message_squit(this),
    792 		message_stats(this),
    793 		message_time(this),
    794 		message_topic(this),
    795 		message_version(this),
    796 		message_whois(this),
    797 		message_bmask(this),
    798 		message_capab(this),
    799 		message_certfp(this),
    800 		message_eob(this),
    801 		message_join(this),
    802 		message_metadata(this),
    803 		message_mlock(this),
    804 		message_nick(this),
    805 		message_pass(this),
    806 		message_pong(this),
    807 		message_server(this),
    808 		message_sid(this),
    809 		message_sjoin(this),
    810 		message_svsmode(this),
    811 		message_tburst(this),
    812 		message_tmode(this),
    813 		message_uid(this)
    814 	{
    815 		if (Config->GetModule(this))
    816 			this->AddModes();
    817 	}
    818 
    819 	void OnUserNickChange(User *u, const Anope::string &) anope_override
    820 	{
    821 		u->RemoveModeInternal(Me, ModeManager::FindUserModeByName("REGISTERED"));
    822 	}
    823 
    824 	void OnReload(Configuration::Conf *conf) anope_override
    825 	{
    826 		use_server_side_mlock = conf->GetModule(this)->Get<bool>("use_server_side_mlock");
    827 	}
    828 
    829 	void OnChannelSync(Channel *c) anope_override
    830 	{
    831 		if (!c->ci)
    832 			return;
    833 
    834 		ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks");
    835 		if (use_server_side_mlock && modelocks && Servers::Capab.count("MLOCK"))
    836 		{
    837 			Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
    838 			UplinkSocket::Message(Me) << "MLOCK " << c->creation_time << " " << c->ci->name << " " << Anope::CurTime << " :" << modes;
    839 		}
    840 	}
    841 
    842 	void OnDelChan(ChannelInfo *ci) anope_override
    843 	{
    844 		if (use_server_side_mlock && ci->c && Servers::Capab.count("MLOCK"))
    845 			UplinkSocket::Message(Me) << "MLOCK " << ci->c->creation_time << " " << ci->name << " " << Anope::CurTime << " :";
    846 	}
    847 
    848 	EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
    849 	{
    850 		ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
    851 		ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
    852 		if (use_server_side_mlock && cm && ci->c && modelocks && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK"))
    853 		{
    854 			Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
    855 			UplinkSocket::Message(Me) << "MLOCK " << ci->c->creation_time << " " << ci->name << " " << Anope::CurTime << " :" << modes;
    856 		}
    857 
    858 		return EVENT_CONTINUE;
    859 	}
    860 
    861 	EventReturn OnUnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
    862 	{
    863 		ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
    864 		ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
    865 		if (use_server_side_mlock && cm && modelocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK"))
    866 		{
    867 			Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
    868 			UplinkSocket::Message(Me) << "MLOCK " << ci->c->creation_time << " " << ci->name << " " << Anope::CurTime << " :" << modes;
    869 		}
    870 
    871 		return EVENT_CONTINUE;
    872 	}
    873 };
    874 
    875 MODULE_INIT(ProtoHybrid)