anope

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

unreal4.cpp (51888B)

      1 /* Unreal IRCD 4 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 #include "modules/sasl.h"
     15 
     16 typedef Anope::map<Anope::string> ModData;
     17 static Anope::string UplinkSID;
     18 
     19 class UnrealIRCdProto : public IRCDProto
     20 {
     21  public:
     22 	PrimitiveExtensibleItem<ModData> ClientModData;
     23 	PrimitiveExtensibleItem<ModData> ChannelModData;
     24 
     25 	UnrealIRCdProto(Module *creator) : IRCDProto(creator, "UnrealIRCd 4+"), ClientModData(creator, "ClientModData"), ChannelModData(creator, "ChannelModData")
     26 	{
     27 		DefaultPseudoclientModes = "+BioqS";
     28 		CanSVSNick = true;
     29 		CanSVSJoin = true;
     30 		CanSetVHost = true;
     31 		CanSetVIdent = true;
     32 		CanSNLine = true;
     33 		CanSQLine = true;
     34 		CanSZLine = true;
     35 		CanSVSHold = true;
     36 		CanCertFP = true;
     37 		RequiresID = true;
     38 		MaxModes = 12;
     39 	}
     40 
     41  private:
     42 	/* SVSNOOP */
     43 	void SendSVSNOOP(const Server *server, bool set) anope_override
     44 	{
     45 		UplinkSocket::Message() << "SVSNOOP " << server->GetSID() << " " << (set ? "+" : "-");
     46 	}
     47 
     48 	void SendAkillDel(const XLine *x) anope_override
     49 	{
     50 		if (x->IsRegex() || x->HasNickOrReal())
     51 			return;
     52 
     53 		/* ZLine if we can instead */
     54 		if (x->GetUser() == "*")
     55 		{
     56 			cidr a(x->GetHost());
     57 			if (a.valid())
     58 			{
     59 				IRCD->SendSZLineDel(x);
     60 				return;
     61 			}
     62 		}
     63 
     64 		UplinkSocket::Message() << "TKL - G " << x->GetUser() << " " << x->GetHost() << " " << x->by;
     65 	}
     66 
     67 	void SendTopic(const MessageSource &source, Channel *c) anope_override
     68 	{
     69 		UplinkSocket::Message(source) << "TOPIC " << c->name << " " << c->topic_setter << " " << c->topic_ts << " :" << c->topic;
     70 	}
     71 
     72 	void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
     73 	{
     74 		UplinkSocket::Message(bi) << "NOTICE $" << dest->GetName() << " :" << msg;
     75 	}
     76 
     77 	void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override
     78 	{
     79 		UplinkSocket::Message(bi) << "PRIVMSG $" << dest->GetName() << " :" << msg;
     80 	}
     81 
     82 	void SendVhostDel(User *u) anope_override
     83 	{
     84 		BotInfo *HostServ = Config->GetClient("HostServ");
     85 		u->RemoveMode(HostServ, "VHOST");
     86 	}
     87 
     88 	void SendAkill(User *u, XLine *x) anope_override
     89 	{
     90 		if (x->IsRegex() || x->HasNickOrReal())
     91 		{
     92 			if (!u)
     93 			{
     94 				/* No user (this akill was just added), and contains nick and/or realname. Find users that match and ban them */
     95 				for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
     96 					if (x->manager->Check(it->second, x))
     97 						this->SendAkill(it->second, x);
     98 				return;
     99 			}
    100 
    101 			const XLine *old = x;
    102 
    103 			if (old->manager->HasEntry("*@" + u->host))
    104 				return;
    105 
    106 			/* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
    107 			XLine *xline = new XLine("*@" + u->host, old->by, old->expires, old->reason, old->id);
    108 			old->manager->AddXLine(xline);
    109 			x = xline;
    110 
    111 			Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->mask << " because " << u->GetMask() << "#" << u->realname << " matches " << old->mask;
    112 		}
    113 
    114 		/* ZLine if we can instead */
    115 		if (x->GetUser() == "*")
    116 		{
    117 			cidr a(x->GetHost());
    118 			if (a.valid())
    119 			{
    120 				IRCD->SendSZLine(u, x);
    121 				return;
    122 			}
    123 		}
    124 
    125 		// Calculate the time left before this would expire, capping it at 2 days
    126 		time_t timeleft = x->expires - Anope::CurTime;
    127 		if (timeleft > 172800 || !x->expires)
    128 			timeleft = 172800;
    129 		UplinkSocket::Message() << "TKL + G " << x->GetUser() << " " << x->GetHost() << " " << x->by << " " << Anope::CurTime + timeleft << " " << x->created << " :" << x->GetReason();
    130 	}
    131 
    132 	void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) anope_override
    133 	{
    134 		UplinkSocket::Message(source) << "SVSKILL " << user->GetUID() << " :" << buf;
    135 		user->KillInternal(source, buf);
    136 	}
    137 
    138 	void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) anope_override
    139 	{
    140 		UplinkSocket::Message(source) << "SVS2MODE " << u->GetUID() <<" " << buf;
    141 	}
    142 
    143 	void SendClientIntroduction(User *u) anope_override
    144 	{
    145 		Anope::string modes = "+" + u->GetModes();
    146 		UplinkSocket::Message(u->server) << "UID " << u->nick << " 1 " << u->timestamp << " " << u->GetIdent() << " " << u->host << " "
    147 					  << u->GetUID() << " * " << modes << " " << (!u->vhost.empty() ? u->vhost : "*") << " "
    148 					  << (!u->chost.empty() ? u->chost : "*") << " " << "*" << " :" << u->realname;
    149 	}
    150 
    151 	void SendServer(const Server *server) anope_override
    152 	{
    153 		if (server == Me)
    154 			UplinkSocket::Message() << "SERVER " << server->GetName() << " " << server->GetHops() + 1 << " :" << server->GetDescription();
    155 		else
    156 			UplinkSocket::Message(Me) << "SID " << server->GetName() << " " << server->GetHops() + 1 << " " << server->GetSID() << " :" << server->GetDescription();
    157 	}
    158 
    159 	/* JOIN */
    160 	void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override
    161 	{
    162 		UplinkSocket::Message(Me) << "SJOIN " << c->creation_time << " " << c->name
    163 			<< " +" << c->GetModes(true, true) << " :" << user->GetUID();
    164 		if (status)
    165 		{
    166 			/* First save the channel status incase uc->Status == status */
    167 			ChannelStatus cs = *status;
    168 			/* If the user is internally on the channel with flags, kill them so that
    169 			 * the stacker will allow this.
    170 			 */
    171 			ChanUserContainer *uc = c->FindUser(user);
    172 			if (uc != NULL)
    173 				uc->status.Clear();
    174 
    175 			BotInfo *setter = BotInfo::Find(user->GetUID());
    176 			for (size_t i = 0; i < cs.Modes().length(); ++i)
    177 				c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false);
    178 
    179 			if (uc != NULL)
    180 				uc->status = cs;
    181 		}
    182 	}
    183 
    184 	/* unsqline
    185 	*/
    186 	void SendSQLineDel(const XLine *x) anope_override
    187 	{
    188 		UplinkSocket::Message() << "UNSQLINE " << x->mask;
    189 	}
    190 
    191 	/* SQLINE */
    192 	/*
    193 	** - Unreal will translate this to TKL for us
    194 	**
    195 	*/
    196 	void SendSQLine(User *, const XLine *x) anope_override
    197 	{
    198 		UplinkSocket::Message() << "SQLINE " << x->mask << " :" << x->GetReason();
    199 	}
    200 
    201 	/* Functions that use serval cmd functions */
    202 
    203 	void SendVhost(User *u, const Anope::string &vIdent, const Anope::string &vhost) anope_override
    204 	{
    205 		if (!vIdent.empty())
    206 			UplinkSocket::Message(Me) << "CHGIDENT " << u->GetUID() << " " << vIdent;
    207 		if (!vhost.empty())
    208 			UplinkSocket::Message(Me) << "CHGHOST " << u->GetUID() << " " << vhost;
    209 		// Internally unreal sets +xt on chghost
    210 		BotInfo *bi = Config->GetClient("HostServ");
    211 		u->SetMode(bi, "CLOAK");
    212 		u->SetMode(bi, "VHOST");
    213 	}
    214 
    215 	void SendConnect() anope_override
    216 	{
    217 		/*
    218 		   NICKv2 = Nick Version 2
    219 		   VHP    = Sends hidden host
    220 		   UMODE2 = sends UMODE2 on user modes
    221 		   NICKIP = Sends IP on NICK
    222 		   SJ3    = Supports SJOIN
    223 		   NOQUIT = No Quit
    224 		   TKLEXT = Extended TKL we don't use it but best to have it
    225 		   MLOCK  = Supports the MLOCK server command
    226 		   VL     = Version Info
    227 		   SID    = SID/UID mode
    228 		*/
    229 		UplinkSocket::Message() << "PASS :" << Config->Uplinks[Anope::CurrentUplink].password;
    230 		UplinkSocket::Message() << "PROTOCTL " << "NICKv2 VHP UMODE2 NICKIP SJOIN SJOIN2 SJ3 NOQUIT TKLEXT MLOCK SID MTAGS";
    231 		UplinkSocket::Message() << "PROTOCTL " << "EAUTH=" << Me->GetName() << ",,,Anope-" << Anope::VersionShort();
    232 		UplinkSocket::Message() << "PROTOCTL " << "SID=" << Me->GetSID();
    233 		SendServer(Me);
    234 	}
    235 
    236 	void SendSASLMechanisms(std::vector<Anope::string> &mechanisms) anope_override
    237 	{
    238 		Anope::string mechlist;
    239 		for (unsigned i = 0; i < mechanisms.size(); ++i)
    240 			mechlist += "," + mechanisms[i];
    241 
    242 		UplinkSocket::Message() << "MD client " << Me->GetName() << " saslmechlist :" << (mechanisms.empty() ? "" : mechlist.substr(1));
    243 	}
    244 
    245 	/* SVSHOLD - set */
    246 	void SendSVSHold(const Anope::string &nick, time_t t) anope_override
    247 	{
    248 		UplinkSocket::Message() << "TKL + Q H " << nick << " " << Me->GetName() << " " << Anope::CurTime + t << " " << Anope::CurTime << " :Being held for registered user";
    249 	}
    250 
    251 	/* SVSHOLD - release */
    252 	void SendSVSHoldDel(const Anope::string &nick) anope_override
    253 	{
    254 		UplinkSocket::Message() << "TKL - Q * " << nick << " " << Me->GetName();
    255 	}
    256 
    257 	/* UNSGLINE */
    258 	/*
    259 	 * SVSNLINE - :realname mask
    260 	*/
    261 	void SendSGLineDel(const XLine *x) anope_override
    262 	{
    263 		UplinkSocket::Message() << "SVSNLINE - :" << x->mask;
    264 	}
    265 
    266 	/* UNSZLINE */
    267 	void SendSZLineDel(const XLine *x) anope_override
    268 	{
    269 		UplinkSocket::Message() << "TKL - Z * " << x->GetHost() << " " << x->by;
    270 	}
    271 
    272 	/* SZLINE */
    273 	void SendSZLine(User *, const XLine *x) anope_override
    274 	{
    275 		// Calculate the time left before this would expire, capping it at 2 days
    276 		time_t timeleft = x->expires - Anope::CurTime;
    277 		if (timeleft > 172800 || !x->expires)
    278 			timeleft = 172800;
    279 		UplinkSocket::Message() << "TKL + Z * " << x->GetHost() << " " << x->by << " " << Anope::CurTime + timeleft << " " << x->created << " :" << x->GetReason();
    280 	}
    281 
    282 	/* SGLINE */
    283 	/*
    284 	 * SVSNLINE + reason_where_is_space :realname mask with spaces
    285 	*/
    286 	void SendSGLine(User *, const XLine *x) anope_override
    287 	{
    288 		Anope::string edited_reason = x->GetReason();
    289 		edited_reason = edited_reason.replace_all_cs(" ", "_");
    290 		UplinkSocket::Message() << "SVSNLINE + " << edited_reason << " :" << x->mask;
    291 	}
    292 
    293 	/* svsjoin
    294 		parv[0] - sender
    295 		parv[1] - nick to make join
    296 		parv[2] - channel to join
    297 		parv[3] - (optional) channel key(s)
    298 	*/
    299 	/* In older Unreal SVSJOIN and SVSNLINE tokens were mixed so SVSJOIN and SVSNLINE are broken
    300 	   when coming from a none TOKEN'd server
    301 	*/
    302 	void SendSVSJoin(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) anope_override
    303 	{
    304 		if (!param.empty())
    305 			UplinkSocket::Message() << "SVSJOIN " << user->GetUID() << " " << chan << " :" << param;
    306 		else
    307 			UplinkSocket::Message() << "SVSJOIN " << user->GetUID() << " " << chan;
    308 	}
    309 
    310 	void SendSVSPart(const MessageSource &source, User *user, const Anope::string &chan, const Anope::string &param) anope_override
    311 	{
    312 		if (!param.empty())
    313 			UplinkSocket::Message() << "SVSPART " << user->GetUID() << " " << chan << " :" << param;
    314 		else
    315 			UplinkSocket::Message() << "SVSPART " << user->GetUID() << " " << chan;
    316 	}
    317 
    318 	void SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) anope_override
    319 	{
    320 		UplinkSocket::Message(Me) << "SENDUMODE o :from " << source.GetName() << ": " << buf;
    321 	}
    322 
    323 	void SendSWhois(const MessageSource &source, const Anope::string &who, const Anope::string &mask) anope_override
    324 	{
    325 		UplinkSocket::Message() << "SWHOIS " << who << " :" << mask;
    326 	}
    327 
    328 	void SendEOB() anope_override
    329 	{
    330 		UplinkSocket::Message(Me) << "EOS";
    331 	}
    332 
    333 	bool IsNickValid(const Anope::string &nick) anope_override
    334 	{
    335 		if (nick.equals_ci("ircd") || nick.equals_ci("irc"))
    336 			return false;
    337 
    338 		return IRCDProto::IsNickValid(nick);
    339 	}
    340 
    341 	bool IsChannelValid(const Anope::string &chan) anope_override
    342 	{
    343 		if (chan.find(':') != Anope::string::npos)
    344 			return false;
    345 
    346 		return IRCDProto::IsChannelValid(chan);
    347 	}
    348 
    349 	bool IsExtbanValid(const Anope::string &mask) anope_override
    350 	{
    351 		return mask.length() >= 4 && mask[0] == '~' && mask[2] == ':';
    352 	}
    353 
    354 	void SendLogin(User *u, NickAlias *na) anope_override
    355 	{
    356 		/* 3.2.10.4+ treats users logged in with accounts as fully registered, even if -r, so we can not set this here. Just use the timestamp. */
    357 		if (Servers::Capab.count("ESVID") > 0 && !na->nc->HasExt("UNCONFIRMED"))
    358 			IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %s", na->nc->display.c_str());
    359 		else
    360 			IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %d", u->signon);
    361 	}
    362 
    363 	void SendLogout(User *u) anope_override
    364 	{
    365 		IRCD->SendMode(Config->GetClient("NickServ"), u, "+d 0");
    366 	}
    367 
    368 	void SendChannel(Channel *c) anope_override
    369 	{
    370 		UplinkSocket::Message(Me) << "SJOIN " << c->creation_time << " " << c->name
    371 			<< " +" << c->GetModes(true, true) << " :";
    372 	}
    373 
    374 	void SendSASLMessage(const SASL::Message &message) anope_override
    375 	{
    376 		size_t p = message.target.find('!');
    377 		Anope::string distmask;
    378 
    379 		if (p == Anope::string::npos)
    380 		{
    381 			Server *s = Server::Find(message.target.substr(0, 3));
    382 			if (!s)
    383 				return;
    384 			distmask = s->GetName();
    385 		}
    386 		else
    387 		{
    388 			distmask = message.target.substr(0, p);
    389 		}
    390 
    391 		UplinkSocket::Message(BotInfo::Find(message.source)) << "SASL " << distmask << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : " " + message.ext);
    392 	}
    393 
    394 	void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
    395 	{
    396 		size_t p = uid.find('!');
    397 		Anope::string distmask;
    398 
    399 		if (p == Anope::string::npos)
    400 		{
    401 			Server *s = Server::Find(uid.substr(0, 3));
    402 			if (!s)
    403 				return;
    404 			distmask = s->GetName();
    405 		}
    406 		else
    407 		{
    408 			distmask = uid.substr(0, p);
    409 		}
    410 		UplinkSocket::Message(Me) << "SVSLOGIN " << distmask << " " << uid << " " << acc;
    411 	}
    412 
    413 	bool IsIdentValid(const Anope::string &ident) anope_override
    414 	{
    415 		if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
    416 			return false;
    417 
    418 		for (unsigned i = 0; i < ident.length(); ++i)
    419 		{
    420 			const char &c = ident[i];
    421 
    422 			if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-')
    423 				continue;
    424 
    425 			if (c == '-' || c == '.' || c == '_')
    426 				continue;
    427 
    428 			return false;
    429 		}
    430 
    431 		return true;
    432 	}
    433 };
    434 
    435 class UnrealExtBan : public ChannelModeVirtual<ChannelModeList>
    436 {
    437 	char ext;
    438 
    439  public:
    440 	UnrealExtBan(const Anope::string &mname, const Anope::string &basename, char extban) : ChannelModeVirtual<ChannelModeList>(mname, basename)
    441 		, ext(extban)
    442 	{
    443 	}
    444 
    445 	ChannelMode *Wrap(Anope::string &param) anope_override
    446 	{
    447 		param = "~" + Anope::string(ext) + ":" + param;
    448 		return ChannelModeVirtual<ChannelModeList>::Wrap(param);
    449 	}
    450 
    451 	ChannelMode *Unwrap(ChannelMode *cm, Anope::string &param) anope_override
    452 	{
    453 		if (cm->type != MODE_LIST || param.length() < 4 || param[0] != '~' || param[1] != ext || param[2] != ':')
    454 			return cm;
    455 
    456 		param = param.substr(3);
    457 		return this;
    458 	}
    459 };
    460 
    461 namespace UnrealExtban
    462 {
    463 	class ChannelMatcher : public UnrealExtBan
    464 	{
    465 	 public:
    466 		ChannelMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    467 		{
    468 		}
    469 
    470 		bool Matches(User *u, const Entry *e) anope_override
    471 		{
    472 			const Anope::string &mask = e->GetMask();
    473 			Anope::string channel = mask.substr(3);
    474 
    475 			ChannelMode *cm = NULL;
    476 			if (channel[0] != '#')
    477 			{
    478 				char modeChar = ModeManager::GetStatusChar(channel[0]);
    479 				channel.erase(channel.begin());
    480 				cm = ModeManager::FindChannelModeByChar(modeChar);
    481 				if (cm != NULL && cm->type != MODE_STATUS)
    482 					cm = NULL;
    483 			}
    484 
    485 			Channel *c = Channel::Find(channel);
    486 			if (c != NULL)
    487 			{
    488 				ChanUserContainer *uc = c->FindUser(u);
    489 				if (uc != NULL)
    490 					if (cm == NULL || uc->status.HasMode(cm->mchar))
    491 						return true;
    492 			}
    493 
    494 			return false;
    495 		}
    496 	};
    497 
    498 	class EntryMatcher : public UnrealExtBan
    499 	{
    500 	 public:
    501 		EntryMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    502 		{
    503 		}
    504 
    505 		bool Matches(User *u, const Entry *e) anope_override
    506 		{
    507 			const Anope::string &mask = e->GetMask();
    508 			Anope::string real_mask = mask.substr(3);
    509 
    510 			return Entry(this->name, real_mask).Matches(u);
    511 		}
    512 	};
    513 
    514 	class RealnameMatcher : public UnrealExtBan
    515 	{
    516 	 public:
    517 		RealnameMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    518 		{
    519 		}
    520 
    521 		bool Matches(User *u, const Entry *e) anope_override
    522 		{
    523 			const Anope::string &mask = e->GetMask();
    524 			Anope::string real_mask = mask.substr(3);
    525 
    526 			return Anope::Match(u->realname, real_mask);
    527 		}
    528 	};
    529 
    530 	class RegisteredMatcher : public UnrealExtBan
    531 	{
    532 	 public:
    533 		RegisteredMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    534 		{
    535 		}
    536 
    537 		bool Matches(User *u, const Entry *e) anope_override
    538 		{
    539 			const Anope::string &mask = e->GetMask();
    540 			return u->HasMode("REGISTERED") && mask.equals_ci(u->nick);
    541 		}
    542 	};
    543 
    544 	class AccountMatcher : public UnrealExtBan
    545 	{
    546 	 public:
    547 		AccountMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    548 		{
    549 		}
    550 
    551 		bool Matches(User *u, const Entry *e) anope_override
    552 		{
    553 			const Anope::string &mask = e->GetMask();
    554 			Anope::string real_mask = mask.substr(3);
    555 
    556 			if (real_mask == "0" && !u->Account()) /* ~a:0 is special and matches all unauthenticated users */
    557 				return true;
    558 
    559 			return u->Account() && Anope::Match(u->Account()->display, real_mask);
    560 		}
    561 	};
    562 
    563 	class FingerprintMatcher : public UnrealExtBan
    564 	{
    565 	 public:
    566 		FingerprintMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    567 		{
    568 		}
    569 
    570 		bool Matches(User *u, const Entry *e) anope_override
    571 		{
    572 			const Anope::string &mask = e->GetMask();
    573 			Anope::string real_mask = mask.substr(3);
    574 			return !u->fingerprint.empty() && Anope::Match(u->fingerprint, real_mask);
    575 		}
    576 	};
    577 	
    578 	class OperclassMatcher : public UnrealExtBan
    579 	{
    580 	 public:
    581 	 	OperclassMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    582 		{
    583 		}
    584 		
    585 		bool Matches(User *u, const Entry *e) anope_override
    586 		{
    587 			const Anope::string &mask = e->GetMask();
    588 			Anope::string real_mask = mask.substr(3);
    589 			ModData *moddata = u->GetExt<ModData>("ClientModData");
    590 			return moddata != NULL && moddata->find("operclass") != moddata->end() && Anope::Match((*moddata)["operclass"], real_mask);
    591 		}
    592 	};
    593 	
    594 	class TimedBanMatcher : public UnrealExtBan
    595 	{
    596 	 public:
    597 	 	TimedBanMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    598 		{
    599 		}
    600 		
    601 		bool Matches(User *u, const Entry *e) anope_override
    602 		{
    603 			/* strip down the time (~t:1234:) and call other matchers */
    604 			const Anope::string &mask = e->GetMask();
    605 			Anope::string real_mask = mask.substr(3);
    606 			real_mask = real_mask.substr(real_mask.find(":") + 1);
    607 			return Entry("BAN", real_mask).Matches(u);
    608 		}
    609 	};
    610 
    611 	class CountryMatcher : public UnrealExtBan
    612 	{
    613 	 public:
    614 	 	CountryMatcher(const Anope::string &mname, const Anope::string &mbase, char c) : UnrealExtBan(mname, mbase, c)
    615 		{
    616 		}
    617 		
    618 		bool Matches(User *u, const Entry *e) anope_override
    619 		{
    620 			const Anope::string &mask = e->GetMask();
    621 			Anope::string real_mask = mask.substr(3);
    622 			ModData *moddata = u->GetExt<ModData>("ClientModData");
    623 			if (moddata == NULL || moddata->find("geoip") == moddata->end())
    624 				return false;
    625 
    626 			sepstream sep((*moddata)["geoip"], '|');/* "cc=PL|cd=Poland" */
    627 			Anope::string tokenbuf;
    628 			while (sep.GetToken(tokenbuf))
    629 			{
    630 				if (tokenbuf.rfind("cc=", 0) == 0)
    631 					return (tokenbuf.substr(3, 2) == real_mask);
    632 			}
    633 			return false;
    634 		}
    635 	};
    636 	
    637 }
    638 
    639 class ChannelModeFlood : public ChannelModeParam
    640 {
    641  public:
    642 	ChannelModeFlood(char modeChar, bool minusNoArg) : ChannelModeParam("FLOOD", modeChar, minusNoArg) { }
    643 
    644 	/* Borrowed part of this check from UnrealIRCd */
    645 	bool IsValid(Anope::string &value) const anope_override
    646 	{
    647 		if (value.empty())
    648 			return false;
    649 		try
    650 		{
    651 			Anope::string rest;
    652 			if (value[0] != ':' && convertTo<unsigned>(value[0] == '*' ? value.substr(1) : value, rest, false) > 0 && rest[0] == ':' && rest.length() > 1 && convertTo<unsigned>(rest.substr(1), rest, false) > 0 && rest.empty())
    653 				return true;
    654 		}
    655 		catch (const ConvertException &) { }
    656 
    657 		/* '['<number><1 letter>[optional: '#'+1 letter],[next..]']'':'<number> */
    658 		size_t end_bracket = value.find(']', 1);
    659 		if (end_bracket == Anope::string::npos)
    660 			return false;
    661 		Anope::string xbuf = value.substr(0, end_bracket);
    662 		if (value[end_bracket + 1] != ':')
    663 			return false;
    664 		commasepstream args(xbuf.substr(1));
    665 		Anope::string arg;
    666 		while (args.GetToken(arg))
    667 		{
    668 			/* <number><1 letter>[optional: '#'+1 letter] */
    669 			size_t p = 0;
    670 			while (p < arg.length() && isdigit(arg[p]))
    671 				++p;
    672 			if (p == arg.length() || !(arg[p] == 'c' || arg[p] == 'j' || arg[p] == 'k' || arg[p] == 'm' || arg[p] == 'n' || arg[p] == 't'))
    673 				continue; /* continue instead of break for forward compatibility. */
    674 			try
    675 			{
    676 				int v = arg.substr(0, p).is_number_only() ? convertTo<int>(arg.substr(0, p)) : 0;
    677 				if (v < 1 || v > 999)
    678 					return false;
    679 			}
    680 			catch (const ConvertException &)
    681 			{
    682 				return false;
    683 			}
    684 		}
    685 
    686 		return true;
    687 	}
    688 };
    689 
    690 class ChannelModeHistory : public ChannelModeParam /* stolen from inspircd3's ColonDelimitedParamMode */
    691 {
    692  public:
    693 	ChannelModeHistory(char modeChar) : ChannelModeParam("HISTORY", modeChar, true) { }
    694 
    695 	bool IsValid(Anope::string &value) const anope_override
    696 	{
    697 		if (value.empty())
    698 			return false; // empty param is never valid
    699 
    700 		Anope::string::size_type pos = value.find(':');
    701 		if ((pos == Anope::string::npos) || (pos == 0))
    702 			return false; // no ':' or it's the first char, both are invalid
    703 
    704 		Anope::string rest;
    705 		try
    706 		{
    707 			if (convertTo<int>(value, rest, false) <= 0)
    708 				return false; // negative numbers and zero are invalid
    709 
    710 			rest = rest.substr(1);
    711 			int n;
    712 			// The part after the ':' is a duration and it
    713 			// can be in the user friendly "1d3h20m" format, make sure we accept that
    714 			n = Anope::DoTime(rest);
    715 
    716 			if (n <= 0)
    717 				return false;
    718 		}
    719 		catch (const ConvertException &e)
    720 		{
    721 			// conversion error, invalid
    722 			return false;
    723 		}
    724 
    725 		return true;
    726 	}
    727 };
    728 
    729 class ChannelModeUnrealSSL : public ChannelMode
    730 {
    731  public:
    732 	ChannelModeUnrealSSL(const Anope::string &n, char c) : ChannelMode(n, c)
    733 	{
    734 	}
    735 
    736 	bool CanSet(User *u) const anope_override
    737 	{
    738 		return false;
    739 	}
    740 };
    741 
    742 struct IRCDMessageCapab : Message::Capab
    743 {
    744 	IRCDMessageCapab(Module *creator) : Message::Capab(creator, "PROTOCTL") { }
    745 
    746 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
    747 	{
    748 		for (unsigned i = 0; i < params.size(); ++i)
    749 		{
    750 			Anope::string capab = params[i];
    751 
    752 			if (capab.find("USERMODES=") != Anope::string::npos)
    753 			{
    754 				Anope::string modebuf(capab.begin() + 10, capab.end());
    755 				for (size_t t = 0, end = modebuf.length(); t < end; ++t)
    756 				{
    757 					switch (modebuf[t])
    758 					{
    759 						case 'B':
    760 							ModeManager::AddUserMode(new UserMode("BOT", 'B'));
    761 							continue;
    762 						case 'G':
    763 							ModeManager::AddUserMode(new UserMode("CENSOR", 'G'));
    764 							continue;
    765 						case 'H':
    766 							ModeManager::AddUserMode(new UserModeOperOnly("HIDEOPER", 'H'));
    767 							continue;
    768 						case 'I':
    769 							ModeManager::AddUserMode(new UserModeOperOnly("HIDEIDLE", 'I'));
    770 							continue;
    771 						case 'R':
    772 							ModeManager::AddUserMode(new UserMode("REGPRIV", 'R'));
    773 							continue;
    774 						case 'S':
    775 							ModeManager::AddUserMode(new UserModeOperOnly("PROTECTED", 'S'));
    776 							continue;
    777 						case 'T':
    778 							ModeManager::AddUserMode(new UserMode("NOCTCP", 'T'));
    779 							continue;
    780 						case 'W':
    781 							ModeManager::AddUserMode(new UserModeOperOnly("WHOIS", 'W'));
    782 							continue;
    783 						case 'd':
    784 							ModeManager::AddUserMode(new UserMode("DEAF", 'd'));
    785 							continue;
    786 						case 'D':
    787 							ModeManager::AddUserMode(new UserMode("PRIVDEAF", 'D'));
    788 							continue;
    789 						case 'i':
    790 							ModeManager::AddUserMode(new UserMode("INVIS", 'i'));
    791 							continue;
    792 						case 'o':
    793 							ModeManager::AddUserMode(new UserModeOperOnly("OPER", 'o'));
    794 							continue;
    795 						case 'p':
    796 							ModeManager::AddUserMode(new UserMode("PRIV", 'p'));
    797 							continue;
    798 						case 'q':
    799 							ModeManager::AddUserMode(new UserModeOperOnly("GOD", 'q'));
    800 							continue;
    801 						case 'r':
    802 							ModeManager::AddUserMode(new UserModeNoone("REGISTERED", 'r'));
    803 							continue;
    804 						case 's':
    805 							ModeManager::AddUserMode(new UserModeOperOnly("SNOMASK", 's'));
    806 							continue;
    807 						case 't':
    808 							ModeManager::AddUserMode(new UserModeNoone("VHOST", 't'));
    809 							continue;
    810 						case 'w':
    811 							ModeManager::AddUserMode(new UserMode("WALLOPS", 'w'));
    812 							continue;
    813 						case 'x':
    814 							ModeManager::AddUserMode(new UserMode("CLOAK", 'x'));
    815 							continue;
    816 						case 'z':
    817 							ModeManager::AddUserMode(new UserModeNoone("SSL", 'z'));
    818 							continue;
    819 						case 'Z':
    820 							ModeManager::AddUserMode(new UserMode("SSLPRIV", 'Z'));
    821 							continue;
    822 						default:
    823 							ModeManager::AddUserMode(new UserMode("", modebuf[t]));
    824 					}
    825 				}
    826 			}
    827 			else if (capab.find("CHANMODES=") != Anope::string::npos)
    828 			{
    829 				Anope::string modes(capab.begin() + 10, capab.end());
    830 				commasepstream sep(modes);
    831 				Anope::string modebuf;
    832 
    833 				sep.GetToken(modebuf);
    834 				for (size_t t = 0, end = modebuf.length(); t < end; ++t)
    835 				{
    836 					switch (modebuf[t])
    837 					{
    838 						case 'b':
    839 							ModeManager::AddChannelMode(new ChannelModeList("BAN", 'b'));
    840 
    841 							ModeManager::AddChannelMode(new UnrealExtban::ChannelMatcher("CHANNELBAN", "BAN", 'c'));
    842 							ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("JOINBAN", "BAN", 'j'));
    843 							ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("NONICKBAN", "BAN", 'n'));
    844 							ModeManager::AddChannelMode(new UnrealExtban::EntryMatcher("QUIET", "BAN", 'q'));
    845 							ModeManager::AddChannelMode(new UnrealExtban::RealnameMatcher("REALNAMEBAN", "BAN", 'r'));
    846 							ModeManager::AddChannelMode(new UnrealExtban::RegisteredMatcher("REGISTEREDBAN", "BAN", 'R'));
    847 							ModeManager::AddChannelMode(new UnrealExtban::AccountMatcher("ACCOUNTBAN", "BAN", 'a'));
    848 							ModeManager::AddChannelMode(new UnrealExtban::FingerprintMatcher("SSLBAN", "BAN", 'S'));
    849 							ModeManager::AddChannelMode(new UnrealExtban::TimedBanMatcher("TIMEDBAN", "BAN", 't'));
    850 							ModeManager::AddChannelMode(new UnrealExtban::OperclassMatcher("OPERCLASSBAN", "BAN", 'O'));
    851 							ModeManager::AddChannelMode(new UnrealExtban::CountryMatcher("COUNTRYBAN", "BAN", 'C'));
    852 							continue;
    853 						case 'e':
    854 							ModeManager::AddChannelMode(new ChannelModeList("EXCEPT", 'e'));
    855 							continue;
    856 						case 'I':
    857 							ModeManager::AddChannelMode(new ChannelModeList("INVITEOVERRIDE", 'I'));
    858 							continue;
    859 						default:
    860 							ModeManager::AddChannelMode(new ChannelModeList("", modebuf[t]));
    861 					}
    862 				}
    863 
    864 				sep.GetToken(modebuf);
    865 				for (size_t t = 0, end = modebuf.length(); t < end; ++t)
    866 				{
    867 					switch (modebuf[t])
    868 					{
    869 						case 'k':
    870 							ModeManager::AddChannelMode(new ChannelModeKey('k'));
    871 							continue;
    872 						case 'f':
    873 							ModeManager::AddChannelMode(new ChannelModeFlood('f', false));
    874 							continue;
    875 						case 'L':
    876 							ModeManager::AddChannelMode(new ChannelModeParam("REDIRECT", 'L'));
    877 							continue;
    878 						default:
    879 							ModeManager::AddChannelMode(new ChannelModeParam("", modebuf[t]));
    880 					}
    881 				}
    882 
    883 				sep.GetToken(modebuf);
    884 				for (size_t t = 0, end = modebuf.length(); t < end; ++t)
    885 				{
    886 					switch (modebuf[t])
    887 					{
    888 						case 'l':
    889 							ModeManager::AddChannelMode(new ChannelModeParam("LIMIT", 'l', true));
    890 							continue;
    891 						case 'H':
    892 							ModeManager::AddChannelMode(new ChannelModeHistory('H'));
    893 							continue;
    894 						default:
    895 							ModeManager::AddChannelMode(new ChannelModeParam("", modebuf[t], true));
    896 					}
    897 				}
    898 
    899 				sep.GetToken(modebuf);
    900 				for (size_t t = 0, end = modebuf.length(); t < end; ++t)
    901 				{
    902 					switch (modebuf[t])
    903 					{
    904 						case 'p':
    905 							ModeManager::AddChannelMode(new ChannelMode("PRIVATE", 'p'));
    906 							continue;
    907 						case 's':
    908 							ModeManager::AddChannelMode(new ChannelMode("SECRET", 's'));
    909 							continue;
    910 						case 'm':
    911 							ModeManager::AddChannelMode(new ChannelMode("MODERATED", 'm'));
    912 							continue;
    913 						case 'n':
    914 							ModeManager::AddChannelMode(new ChannelMode("NOEXTERNAL", 'n'));
    915 							continue;
    916 						case 't':
    917 							ModeManager::AddChannelMode(new ChannelMode("TOPIC", 't'));
    918 							continue;
    919 						case 'i':
    920 							ModeManager::AddChannelMode(new ChannelMode("INVITE", 'i'));
    921 							continue;
    922 						case 'r':
    923 							ModeManager::AddChannelMode(new ChannelModeNoone("REGISTERED", 'r'));
    924 							continue;
    925 						case 'R':
    926 							ModeManager::AddChannelMode(new ChannelMode("REGISTEREDONLY", 'R'));
    927 							continue;
    928 						case 'c':
    929 							ModeManager::AddChannelMode(new ChannelMode("BLOCKCOLOR", 'c'));
    930 							continue;
    931 						case 'O':
    932 							ModeManager::AddChannelMode(new ChannelModeOperOnly("OPERONLY", 'O'));
    933 							continue;
    934 						case 'Q':
    935 							ModeManager::AddChannelMode(new ChannelMode("NOKICK", 'Q'));
    936 							continue;
    937 						case 'K':
    938 							ModeManager::AddChannelMode(new ChannelMode("NOKNOCK", 'K'));
    939 							continue;
    940 						case 'V':
    941 							ModeManager::AddChannelMode(new ChannelMode("NOINVITE", 'V'));
    942 							continue;
    943 						case 'C':
    944 							ModeManager::AddChannelMode(new ChannelMode("NOCTCP", 'C'));
    945 							continue;
    946 						case 'z':
    947 							ModeManager::AddChannelMode(new ChannelMode("SSL", 'z'));
    948 							continue;
    949 						case 'N':
    950 							ModeManager::AddChannelMode(new ChannelMode("NONICK", 'N'));
    951 							continue;
    952 						case 'S':
    953 							ModeManager::AddChannelMode(new ChannelMode("STRIPCOLOR", 'S'));
    954 							continue;
    955 						case 'M':
    956 							ModeManager::AddChannelMode(new ChannelMode("REGMODERATED", 'M'));
    957 							continue;
    958 						case 'T':
    959 							ModeManager::AddChannelMode(new ChannelMode("NONOTICE", 'T'));
    960 							continue;
    961 						case 'G':
    962 							ModeManager::AddChannelMode(new ChannelMode("CENSOR", 'G'));
    963 							continue;
    964 						case 'Z':
    965 							ModeManager::AddChannelMode(new ChannelModeUnrealSSL("ALLSSL", 'Z'));
    966 							continue;
    967 						case 'd':
    968 							// post delayed. means that channel is -D but invisible users still exist.
    969 							continue;
    970 						case 'D':
    971 							ModeManager::AddChannelMode(new ChannelMode("DELAYEDJOIN", 'D'));
    972 							continue;
    973 						case 'P':
    974 							ModeManager::AddChannelMode(new ChannelModeOperOnly("PERM", 'P'));
    975 							continue;
    976 						default:
    977 							ModeManager::AddChannelMode(new ChannelMode("", modebuf[t]));
    978 					}
    979 				}
    980 			}
    981 			else if (!capab.find("SID="))
    982 			{
    983 				UplinkSID = capab.substr(4);
    984 			}
    985 			else if (!capab.find("PREFIX=")) /* PREFIX=(qaohv)~&@%+ */
    986 			{
    987 				Anope::string modes(capab.begin() + 7, capab.end());
    988 				reverse(modes.begin(), modes.end()); /* +%@&!)vhoaq( */
    989 				std::size_t mode_count = modes.find(')');
    990 				Anope::string mode_prefixes = modes.substr(0, mode_count);
    991 				Anope::string mode_chars = modes.substr(mode_count+1, mode_count);
    992 				
    993 				for (size_t t = 0, end = mode_chars.length(); t < end; ++t)
    994 				{
    995 					Anope::string mode_name;
    996 					switch (mode_chars[t])
    997 					{
    998 
    999 						case 'v':
   1000 							mode_name = "VOICE";
   1001 							break;
   1002 						case 'h':
   1003 							mode_name = "HALFOP";
   1004 							break;
   1005 						case 'o':
   1006 							mode_name = "OP";
   1007 							break;
   1008 						case 'a':
   1009 							mode_name = "PROTECT";
   1010 							break;
   1011 						case 'q':
   1012 							mode_name = "OWNER";
   1013 							break;
   1014 						default:
   1015 							mode_name = "";
   1016 							break;
   1017 					}
   1018 					ModeManager::AddChannelMode(new ChannelModeStatus(mode_name, mode_chars[t], mode_prefixes[t], t));
   1019 				}
   1020 			}
   1021 		}
   1022 
   1023 		Message::Capab::Run(source, params);
   1024 	}
   1025 };
   1026 
   1027 struct IRCDMessageChgHost : IRCDMessage
   1028 {
   1029 	IRCDMessageChgHost(Module *creator) : IRCDMessage(creator, "CHGHOST", 2) { }
   1030 
   1031 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1032 	{
   1033 		User *u = User::Find(params[0]);
   1034 		if (u)
   1035 			u->SetDisplayedHost(params[1]);
   1036 	}
   1037 };
   1038 
   1039 struct IRCDMessageChgIdent : IRCDMessage
   1040 {
   1041 	IRCDMessageChgIdent(Module *creator) : IRCDMessage(creator, "CHGIDENT", 2) { }
   1042 
   1043 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1044 	{
   1045 		User *u = User::Find(params[0]);
   1046 		if (u)
   1047 			u->SetVIdent(params[1]);
   1048 	}
   1049 };
   1050 
   1051 struct IRCDMessageChgName : IRCDMessage
   1052 {
   1053 	IRCDMessageChgName(Module *creator) : IRCDMessage(creator, "CHGNAME", 2) { }
   1054 
   1055 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1056 	{
   1057 		User *u = User::Find(params[0]);
   1058 		if (u)
   1059 			u->SetRealname(params[1]);
   1060 	}
   1061 };
   1062 
   1063 struct IRCDMessageMD : IRCDMessage
   1064 {
   1065 	PrimitiveExtensibleItem<ModData> &ClientModData;
   1066 	PrimitiveExtensibleItem<ModData> &ChannelModData;
   1067 
   1068 	IRCDMessageMD(Module *creator, PrimitiveExtensibleItem<ModData> &clmoddata, PrimitiveExtensibleItem<ModData> &chmoddata) : IRCDMessage(creator, "MD", 3), ClientModData(clmoddata), ChannelModData(chmoddata)
   1069 	{
   1070 		SetFlag(IRCDMESSAGE_SOFT_LIMIT);
   1071 	}
   1072 
   1073 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1074 	{
   1075 		const Anope::string &mdtype = params[0],
   1076 				    &obj = params[1],
   1077 				    &var = params[2],
   1078 				    &value = params.size() > 3 ? params[3] : "";
   1079 
   1080 		if (mdtype == "client") /* can be a server too! */
   1081 		{
   1082 			User *u = User::Find(obj);
   1083 
   1084 			if (u == NULL)
   1085 				return;
   1086 			
   1087 			ModData &clientmd = *ClientModData.Require(u);
   1088 			
   1089 			if (value.empty())
   1090 			{
   1091 				clientmd.erase(var);
   1092 				Log(LOG_DEBUG) << "Erased client moddata " << var << " from " << u->nick;
   1093 			}
   1094 			else
   1095 			{
   1096 				clientmd[var] = value;
   1097 				Log(LOG_DEBUG) << "Set client moddata " << var << "=\"" << value << "\" to " << u->nick;
   1098 			}
   1099 			if (var == "certfp" && !value.empty())
   1100 			{
   1101 				u->Extend<bool>("ssl");
   1102 				u->fingerprint = value;
   1103 				FOREACH_MOD(OnFingerprint, (u));
   1104 			}
   1105 		}
   1106 		else if (mdtype == "channel")
   1107 		{
   1108 			Channel *c = Channel::Find(obj);
   1109 
   1110 			if (c == NULL)
   1111 				return;
   1112 			
   1113 			ModData &channelmd = *ChannelModData.Require(c);
   1114 			
   1115 			if (value.empty())
   1116 			{
   1117 				channelmd.erase(var);
   1118 				Log(LOG_DEBUG) << "Erased channel moddata " << var << " from " << c->name;
   1119 			}
   1120 			else
   1121 			{
   1122 				channelmd[var] = value;
   1123 				Log(LOG_DEBUG) << "Set channel moddata " << var << "=\"" << value << "\" to " << c->name;
   1124 			}
   1125 		}
   1126 	}
   1127 };
   1128 
   1129 struct IRCDMessageMode : IRCDMessage
   1130 {
   1131 	IRCDMessageMode(Module *creator, const Anope::string &mname) : IRCDMessage(creator, mname, 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
   1132 
   1133 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1134 	{
   1135 		bool server_source = source.GetServer() != NULL;
   1136 		Anope::string modes = params[1];
   1137 		for (unsigned i = 2; i < params.size() - (server_source ? 1 : 0); ++i)
   1138 			modes += " " + params[i];
   1139 
   1140 		if (IRCD->IsChannelValid(params[0]))
   1141 		{
   1142 			Channel *c = Channel::Find(params[0]);
   1143 			time_t ts = 0;
   1144 
   1145 			try
   1146 			{
   1147 				if (server_source)
   1148 					ts = convertTo<time_t>(params[params.size() - 1]);
   1149 			}
   1150 			catch (const ConvertException &) { }
   1151 
   1152 			if (c)
   1153 				c->SetModesInternal(source, modes, ts);
   1154 		}
   1155 		else
   1156 		{
   1157 			User *u = User::Find(params[0]);
   1158 			if (u)
   1159 				u->SetModesInternal(source, "%s", params[1].c_str());
   1160 		}
   1161 	}
   1162 };
   1163 
   1164 /* netinfo
   1165  *  argv[0] = max global count
   1166  *  argv[1] = time of end sync
   1167  *  argv[2] = unreal protocol using (numeric)
   1168  *  argv[3] = cloak-crc (> u2302)
   1169  *  argv[4] = free(**)
   1170  *  argv[5] = free(**)
   1171  *  argv[6] = free(**)
   1172  *  argv[7] = ircnet
   1173  */
   1174 struct IRCDMessageNetInfo : IRCDMessage
   1175 {
   1176 	IRCDMessageNetInfo(Module *creator) : IRCDMessage(creator, "NETINFO", 8) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
   1177 
   1178 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1179 	{
   1180 		UplinkSocket::Message() << "NETINFO " << MaxUserCount << " " << Anope::CurTime << " " << convertTo<int>(params[2]) << " " << params[3] << " 0 0 0 :" << params[7];
   1181 	}
   1182 };
   1183 
   1184 struct IRCDMessageNick : IRCDMessage
   1185 {
   1186 	IRCDMessageNick(Module *creator) : IRCDMessage(creator, "NICK", 2) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
   1187 
   1188 	/*
   1189 	** NICK - new
   1190 	**	  source  = NULL
   1191 	**	  parv[0] = nickname
   1192 	**	  parv[1] = hopcount
   1193 	**	  parv[2] = timestamp
   1194 	**	  parv[3] = username
   1195 	**	  parv[4] = hostname
   1196 	**	  parv[5] = servername
   1197 	**	  parv[6] = servicestamp
   1198 	**	  parv[7] = umodes
   1199 	**	  parv[8] = virthost, * if none
   1200 	**	  parv[9] = ip
   1201 	**	  parv[10] = info
   1202 	**
   1203 	** NICK - change
   1204 	**	  source  = oldnick
   1205 	**	  parv[0] = new nickname
   1206 	**	  parv[1] = hopcount
   1207 	*/
   1208 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1209 	{
   1210 		if (params.size() == 11)
   1211 		{
   1212 			Anope::string ip;
   1213 			if (params[9] != "*")
   1214 			{
   1215 				Anope::string decoded_ip;
   1216 				Anope::B64Decode(params[9], decoded_ip);
   1217 
   1218 				sockaddrs ip_addr;
   1219 				ip_addr.ntop(params[9].length() == 8 ? AF_INET : AF_INET6, decoded_ip.c_str());
   1220 				ip = ip_addr.addr();
   1221 			}
   1222 
   1223 			Anope::string vhost = params[8];
   1224 			if (vhost.equals_cs("*"))
   1225 				vhost.clear();
   1226 
   1227 			time_t user_ts = params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime;
   1228 
   1229 			Server *s = Server::Find(params[5]);
   1230 			if (s == NULL)
   1231 			{
   1232 				Log(LOG_DEBUG) << "User " << params[0] << " introduced from nonexistent server " << params[5] << "?";
   1233 				return;
   1234 			}
   1235 
   1236 			NickAlias *na = NULL;
   1237 
   1238 			if (params[6] == "0")
   1239 				;
   1240 			else if (params[6].is_pos_number_only())
   1241 			{
   1242 				if (convertTo<time_t>(params[6]) == user_ts)
   1243 					na = NickAlias::Find(params[0]);
   1244 			}
   1245 			else
   1246 			{
   1247 				na = NickAlias::Find(params[6]);
   1248 			}
   1249 
   1250 			User::OnIntroduce(params[0], params[3], params[4], vhost, ip, s, params[10], user_ts, params[7], "", na ? *na->nc : NULL);
   1251 		}
   1252 		else
   1253 		{
   1254 			User *u = source.GetUser();
   1255 			if (u)
   1256 				u->ChangeNick(params[0]);
   1257 		}
   1258 	}
   1259 };
   1260 
   1261 /** This is here because:
   1262  *
   1263  * If we had three servers, A, B & C linked like so: A<->B<->C
   1264  * If Anope is linked to A and B splits from A and then reconnects
   1265  * B introduces itself, introduces C, sends EOS for C, introduces Bs clients
   1266  * introduces Cs clients, sends EOS for B. This causes all of Cs clients to be introduced
   1267  * with their server "not syncing". We now send a PING immediately when receiving a new server
   1268  * and then finish sync once we get a pong back from that server.
   1269  */
   1270 struct IRCDMessagePong : IRCDMessage
   1271 {
   1272 	IRCDMessagePong(Module *creator) : IRCDMessage(creator, "PONG", 0) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
   1273 
   1274 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1275 	{
   1276 		if (!source.GetServer()->IsSynced())
   1277 			source.GetServer()->Sync(false);
   1278 	}
   1279 };
   1280 
   1281 struct IRCDMessageSASL : IRCDMessage
   1282 {
   1283 	IRCDMessageSASL(Module *creator) : IRCDMessage(creator, "SASL", 4) { SetFlag(IRCDMESSAGE_SOFT_LIMIT); SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
   1284 
   1285 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1286 	{
   1287 		if (!SASL::sasl)
   1288 			return;
   1289 
   1290 		SASL::Message m;
   1291 		m.source = params[1];
   1292 		m.target = params[0];
   1293 		m.type = params[2];
   1294 		m.data = params[3];
   1295 		m.ext = params.size() > 4 ? params[4] : "";
   1296 
   1297 		SASL::sasl->ProcessMessage(m);
   1298 	}
   1299 };
   1300 
   1301 struct IRCDMessageSDesc : IRCDMessage
   1302 {
   1303 	IRCDMessageSDesc(Module *creator) : IRCDMessage(creator, "SDESC", 1) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
   1304 
   1305 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1306 	{
   1307 		source.GetServer()->SetDescription(params[0]);
   1308 	}
   1309 };
   1310 
   1311 struct IRCDMessageSetHost : IRCDMessage
   1312 {
   1313 	IRCDMessageSetHost(Module *creator) : IRCDMessage(creator, "SETHOST", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
   1314 
   1315 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1316 	{
   1317 		User *u = source.GetUser();
   1318 
   1319 		/* When a user sets +x we receive the new host and then the mode change */
   1320 		if (u->HasMode("CLOAK"))
   1321 			u->SetDisplayedHost(params[0]);
   1322 		else
   1323 			u->SetCloakedHost(params[0]);
   1324 	}
   1325 };
   1326 
   1327 struct IRCDMessageSetIdent : IRCDMessage
   1328 {
   1329 	IRCDMessageSetIdent(Module *creator) : IRCDMessage(creator, "SETIDENT", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
   1330 
   1331 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1332 	{
   1333 		User *u = source.GetUser();
   1334 		u->SetVIdent(params[0]);
   1335 	}
   1336 };
   1337 
   1338 struct IRCDMessageSetName : IRCDMessage
   1339 {
   1340 	IRCDMessageSetName(Module *creator) : IRCDMessage(creator, "SETNAME", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
   1341 
   1342 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1343 	{
   1344 		User *u = source.GetUser();
   1345 		u->SetRealname(params[0]);
   1346 	}
   1347 };
   1348 
   1349 struct IRCDMessageServer : IRCDMessage
   1350 {
   1351 	IRCDMessageServer(Module *creator) : IRCDMessage(creator, "SERVER", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
   1352 
   1353 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1354 	{
   1355 		unsigned int hops = Anope::string(params[1]).is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
   1356 
   1357 		if (params[1].equals_cs("1"))
   1358 		{
   1359 			Anope::string desc;
   1360 			spacesepstream(params[2]).GetTokenRemainder(desc, 1);
   1361 
   1362 			new Server(source.GetServer() == NULL ? Me : source.GetServer(), params[0], hops, desc, UplinkSID);
   1363 		}
   1364 		else
   1365 			new Server(source.GetServer(), params[0], hops, params[2]);
   1366 
   1367 		IRCD->SendPing(Me->GetName(), params[0]);
   1368 	}
   1369 };
   1370 
   1371 struct IRCDMessageSID : IRCDMessage
   1372 {
   1373 	IRCDMessageSID(Module *creator) : IRCDMessage(creator, "SID", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
   1374 
   1375 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1376 	{
   1377 		unsigned int hops = Anope::string(params[1]).is_pos_number_only() ? convertTo<unsigned>(params[1]) : 0;
   1378 
   1379 		new Server(source.GetServer(), params[0], hops, params[3], params[2]);
   1380 
   1381 		IRCD->SendPing(Me->GetName(), params[0]);
   1382 	}
   1383 };
   1384 
   1385 static char UnrealSjoinPrefixToModeChar(char sjoin_prefix)
   1386 {
   1387 	switch(sjoin_prefix)
   1388 	{
   1389 		case '*':
   1390 			return ModeManager::GetStatusChar('~');
   1391 		case '~':
   1392 			return ModeManager::GetStatusChar('&');
   1393 		default:
   1394 			return ModeManager::GetStatusChar(sjoin_prefix); /* remaining are regular */
   1395 	}
   1396 }
   1397 
   1398 struct IRCDMessageSJoin : IRCDMessage
   1399 {
   1400 	IRCDMessageSJoin(Module *creator) : IRCDMessage(creator, "SJOIN", 3) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); SetFlag(IRCDMESSAGE_SOFT_LIMIT); }
   1401 
   1402 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1403 	{
   1404 		Anope::string modes;
   1405 		if (params.size() >= 4)
   1406 			for (unsigned i = 2; i < params.size() - 1; ++i)
   1407 				modes += " " + params[i];
   1408 		if (!modes.empty())
   1409 			modes.erase(modes.begin());
   1410 
   1411 		std::list<Anope::string> bans, excepts, invites;
   1412 		std::list<Message::Join::SJoinUser> users;
   1413 
   1414 		spacesepstream sep(params[params.size() - 1]);
   1415 		Anope::string buf;
   1416 		while (sep.GetToken(buf))
   1417 		{
   1418 			/* Ban */
   1419 			if (buf[0] == '&')
   1420 			{
   1421 				buf.erase(buf.begin());
   1422 				bans.push_back(buf);
   1423 			}
   1424 			/* Except */
   1425 			else if (buf[0] == '"')
   1426 			{
   1427 				buf.erase(buf.begin());
   1428 				excepts.push_back(buf);
   1429 			}
   1430 			/* Invex */
   1431 			else if (buf[0] == '\'')
   1432 			{
   1433 				buf.erase(buf.begin());
   1434 				invites.push_back(buf);
   1435 			}
   1436 			else
   1437 			{
   1438 				Message::Join::SJoinUser sju;
   1439 
   1440 				/* Get prefixes from the nick */
   1441 				for (char ch; (ch = UnrealSjoinPrefixToModeChar(buf[0]));)
   1442 				{
   1443 					sju.first.AddMode(ch);
   1444 					buf.erase(buf.begin());
   1445 				}
   1446 
   1447 				sju.second = User::Find(buf);
   1448 				if (!sju.second)
   1449 				{
   1450 					Log(LOG_DEBUG) << "SJOIN for nonexistent user " << buf << " on " << params[1];
   1451 					continue;
   1452 				}
   1453 
   1454 				users.push_back(sju);
   1455 			}
   1456 		}
   1457 
   1458 		time_t ts = Anope::string(params[0]).is_pos_number_only() ? convertTo<time_t>(params[0]) : Anope::CurTime;
   1459 		Message::Join::SJoin(source, params[1], ts, modes, users);
   1460 
   1461 		if (!bans.empty() || !excepts.empty() || !invites.empty())
   1462 		{
   1463 			Channel *c = Channel::Find(params[1]);
   1464 
   1465 			if (!c || c->creation_time != ts)
   1466 				return;
   1467 
   1468 			ChannelMode *ban = ModeManager::FindChannelModeByName("BAN"),
   1469 				*except = ModeManager::FindChannelModeByName("EXCEPT"),
   1470 				*invex = ModeManager::FindChannelModeByName("INVITEOVERRIDE");
   1471 
   1472 			if (ban)
   1473 				for (std::list<Anope::string>::iterator it = bans.begin(), it_end = bans.end(); it != it_end; ++it)
   1474 					c->SetModeInternal(source, ban, *it);
   1475 			if (except)
   1476 				for (std::list<Anope::string>::iterator it = excepts.begin(), it_end = excepts.end(); it != it_end; ++it)
   1477 					c->SetModeInternal(source, except, *it);
   1478 			if (invex)
   1479 				for (std::list<Anope::string>::iterator it = invites.begin(), it_end = invites.end(); it != it_end; ++it)
   1480 					c->SetModeInternal(source, invex, *it);
   1481 		}
   1482 	}
   1483 };
   1484 
   1485 struct IRCDMessageTopic : IRCDMessage
   1486 {
   1487 	IRCDMessageTopic(Module *creator) : IRCDMessage(creator, "TOPIC", 4) { }
   1488 
   1489 	/*
   1490 	**	source = sender prefix
   1491 	**	parv[0] = channel name
   1492 	**	parv[1] = topic nickname
   1493 	**	parv[2] = topic time
   1494 	**	parv[3] = topic text
   1495 	*/
   1496 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1497 	{
   1498 		Channel *c = Channel::Find(params[0]);
   1499 		if (c)
   1500 			c->ChangeTopicInternal(source.GetUser(), params[1], params[3], Anope::string(params[2]).is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime);
   1501 	}
   1502 };
   1503 
   1504 /*
   1505  *      parv[0] = nickname
   1506  *      parv[1] = hopcount
   1507  *      parv[2] = timestamp
   1508  *      parv[3] = username
   1509  *      parv[4] = hostname
   1510  *      parv[5] = UID
   1511  *      parv[6] = servicestamp
   1512  *      parv[7] = umodes
   1513  *      parv[8] = virthost, * if none
   1514  *      parv[9] = cloaked host, * if none
   1515  *      parv[10] = ip
   1516  *      parv[11] = info
   1517  */
   1518 struct IRCDMessageUID : IRCDMessage
   1519 {
   1520 	IRCDMessageUID(Module *creator) : IRCDMessage(creator, "UID", 12) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
   1521 
   1522 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1523 	{
   1524 		Anope::string
   1525 			nickname  = params[0],
   1526 			hopcount  = params[1],
   1527 			timestamp = params[2],
   1528 			username  = params[3],
   1529 			hostname  = params[4],
   1530 			uid       = params[5],
   1531 			account   = params[6],
   1532 			umodes    = params[7],
   1533 			vhost     = params[8],
   1534 			chost     = params[9],
   1535 			ip        = params[10],
   1536 			info      = params[11];
   1537 
   1538 		if (ip != "*")
   1539 		{
   1540 			Anope::string decoded_ip;
   1541 			Anope::B64Decode(ip, decoded_ip);
   1542 
   1543 			sockaddrs ip_addr;
   1544 			ip_addr.ntop(ip.length() == 8 ? AF_INET : AF_INET6, decoded_ip.c_str());
   1545 			ip = ip_addr.addr();
   1546 		}
   1547 
   1548 		if (vhost == "*")
   1549 			vhost.clear();
   1550 
   1551 		if (chost == "*")
   1552 			chost.clear();
   1553 
   1554 		time_t user_ts;
   1555 		try
   1556 		{
   1557 			user_ts = convertTo<time_t>(timestamp);
   1558 		}
   1559 		catch (const ConvertException &)
   1560 		{
   1561 			user_ts = Anope::CurTime;
   1562 		}
   1563 
   1564 		NickAlias *na = NULL;
   1565 
   1566 		if (account == "0")
   1567 		{
   1568 			;
   1569 		}
   1570 		else if (account.is_pos_number_only())
   1571 		{
   1572 			if (convertTo<time_t>(account) == user_ts)
   1573 				na = NickAlias::Find(nickname);
   1574 		}
   1575 		else
   1576 		{
   1577 			na = NickAlias::Find(account);
   1578 		}
   1579 
   1580 		User *u = User::OnIntroduce(nickname, username, hostname, vhost, ip, source.GetServer(), info, user_ts, umodes, uid, na ? *na->nc : NULL);
   1581 
   1582 		if (u && !chost.empty() && chost != u->GetCloakedHost())
   1583 			u->SetCloakedHost(chost);
   1584 	}
   1585 };
   1586 
   1587 struct IRCDMessageUmode2 : IRCDMessage
   1588 {
   1589 	IRCDMessageUmode2(Module *creator) : IRCDMessage(creator, "UMODE2", 1) { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
   1590 
   1591 	void Run(MessageSource &source, const std::vector<Anope::string> &params) anope_override
   1592 	{
   1593 		source.GetUser()->SetModesInternal(source, "%s", params[0].c_str());
   1594 	}
   1595 };
   1596 
   1597 class ProtoUnreal : public Module
   1598 {
   1599 	UnrealIRCdProto ircd_proto;
   1600 
   1601 	/* Core message handlers */
   1602 	Message::Away message_away;
   1603 	Message::Error message_error;
   1604 	Message::Invite message_invite;
   1605 	Message::Join message_join;
   1606 	Message::Kick message_kick;
   1607 	Message::Kill message_kill, message_svskill;
   1608 	Message::MOTD message_motd;
   1609 	Message::Notice message_notice;
   1610 	Message::Part message_part;
   1611 	Message::Ping message_ping;
   1612 	Message::Privmsg message_privmsg;
   1613 	Message::Quit message_quit;
   1614 	Message::SQuit message_squit;
   1615 	Message::Stats message_stats;
   1616 	Message::Time message_time;
   1617 	Message::Version message_version;
   1618 	Message::Whois message_whois;
   1619 
   1620 	/* Our message handlers */
   1621 	IRCDMessageCapab message_capab;
   1622 	IRCDMessageChgHost message_chghost;
   1623 	IRCDMessageChgIdent message_chgident;
   1624 	IRCDMessageChgName message_chgname;
   1625 	IRCDMessageMD message_md;
   1626 	IRCDMessageMode message_mode, message_svsmode, message_svs2mode;
   1627 	IRCDMessageNetInfo message_netinfo;
   1628 	IRCDMessageNick message_nick;
   1629 	IRCDMessagePong message_pong;
   1630 	IRCDMessageSASL message_sasl;
   1631 	IRCDMessageSDesc message_sdesc;
   1632 	IRCDMessageSetHost message_sethost;
   1633 	IRCDMessageSetIdent message_setident;
   1634 	IRCDMessageSetName message_setname;
   1635 	IRCDMessageServer message_server;
   1636 	IRCDMessageSID message_sid;
   1637 	IRCDMessageSJoin message_sjoin;
   1638 	IRCDMessageTopic message_topic;
   1639 	IRCDMessageUID message_uid;
   1640 	IRCDMessageUmode2 message_umode2;
   1641 
   1642 	bool use_server_side_mlock;
   1643 
   1644  public:
   1645 	ProtoUnreal(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR),
   1646 		ircd_proto(this),
   1647 		message_away(this), message_error(this), message_invite(this), message_join(this), message_kick(this),
   1648 		message_kill(this), message_svskill(this, "SVSKILL"), message_motd(this), message_notice(this), message_part(this), message_ping(this),
   1649 		message_privmsg(this), message_quit(this), message_squit(this), message_stats(this), message_time(this),
   1650 		message_version(this), message_whois(this),
   1651 
   1652 		message_capab(this), message_chghost(this), message_chgident(this), message_chgname(this),
   1653 		message_md(this, ircd_proto.ClientModData, ircd_proto.ChannelModData),message_mode(this, "MODE"),
   1654 		message_svsmode(this, "SVSMODE"), message_svs2mode(this, "SVS2MODE"), message_netinfo(this), message_nick(this), message_pong(this),
   1655 		message_sasl(this), message_sdesc(this), message_sethost(this), message_setident(this), message_setname(this), message_server(this),
   1656 		message_sid(this), message_sjoin(this), message_topic(this), message_uid(this), message_umode2(this)
   1657 	{
   1658 
   1659 	}
   1660 
   1661 	void Prioritize() anope_override
   1662 	{
   1663 		ModuleManager::SetPriority(this, PRIORITY_FIRST);
   1664 	}
   1665 
   1666 	void OnReload(Configuration::Conf *conf) anope_override
   1667 	{
   1668 		use_server_side_mlock = conf->GetModule(this)->Get<bool>("use_server_side_mlock");
   1669 	}
   1670 
   1671 	void OnUserNickChange(User *u, const Anope::string &) anope_override
   1672 	{
   1673 		u->RemoveModeInternal(Me, ModeManager::FindUserModeByName("REGISTERED"));
   1674 		if (Servers::Capab.count("ESVID") == 0)
   1675 			IRCD->SendLogout(u);
   1676 	}
   1677 
   1678 	void OnChannelSync(Channel *c) anope_override
   1679 	{
   1680 		if (!c->ci)
   1681 			return;
   1682 
   1683 		ModeLocks *modelocks = c->ci->GetExt<ModeLocks>("modelocks");
   1684 		if (use_server_side_mlock && Servers::Capab.count("MLOCK") > 0 && modelocks)
   1685 		{
   1686 			Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
   1687 			UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(c->creation_time) << " " << c->ci->name << " " << modes;
   1688 		}
   1689 	}
   1690 
   1691 	void OnChanRegistered(ChannelInfo *ci) anope_override
   1692 	{
   1693 		ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
   1694 		if (!ci->c || !use_server_side_mlock || !modelocks || !Servers::Capab.count("MLOCK"))
   1695 			return;
   1696 		Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "");
   1697 		UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
   1698 	}
   1699 
   1700 	void OnDelChan(ChannelInfo *ci) anope_override
   1701 	{
   1702 		if (!ci->c || !use_server_side_mlock || !Servers::Capab.count("MLOCK"))
   1703 			return;
   1704 		UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " :";
   1705 	}
   1706 
   1707 	EventReturn OnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
   1708 	{
   1709 		ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
   1710 		ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
   1711 		if (use_server_side_mlock && cm && modelocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
   1712 		{
   1713 			Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "") + cm->mchar;
   1714 			UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
   1715 		}
   1716 
   1717 		return EVENT_CONTINUE;
   1718 	}
   1719 
   1720 	EventReturn OnUnMLock(ChannelInfo *ci, ModeLock *lock) anope_override
   1721 	{
   1722 		ModeLocks *modelocks = ci->GetExt<ModeLocks>("modelocks");
   1723 		ChannelMode *cm = ModeManager::FindChannelModeByName(lock->name);
   1724 		if (use_server_side_mlock && cm && modelocks && ci->c && (cm->type == MODE_REGULAR || cm->type == MODE_PARAM) && Servers::Capab.count("MLOCK") > 0)
   1725 		{
   1726 			Anope::string modes = modelocks->GetMLockAsString(false).replace_all_cs("+", "").replace_all_cs("-", "").replace_all_cs(cm->mchar, "");
   1727 			UplinkSocket::Message(Me) << "MLOCK " << static_cast<long>(ci->c->creation_time) << " " << ci->name << " " << modes;
   1728 		}
   1729 
   1730 		return EVENT_CONTINUE;
   1731 	}
   1732 
   1733 	void OnChannelUnban(User *u, ChannelInfo *ci) anope_override
   1734 	{
   1735 		UplinkSocket::Message(ci->WhoSends()) << "SVS2MODE " << ci->c->name << " -b " << u->GetUID();
   1736 		/* Unreal will remove all matching bans for us regardless of our internal matching.
   1737 		   Don't use Message(Me) here as certain Unreal versions will always respond with TS-less MODE message. */
   1738 	}
   1739 };
   1740 
   1741 MODULE_INIT(ProtoUnreal)