anope

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

users.cpp (20057B)

      1 /* Routines to maintain a list of online users.
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "services.h"
     13 #include "modules.h"
     14 #include "users.h"
     15 #include "account.h"
     16 #include "protocol.h"
     17 #include "servers.h"
     18 #include "channels.h"
     19 #include "bots.h"
     20 #include "config.h"
     21 #include "opertype.h"
     22 #include "language.h"
     23 #include "sockets.h"
     24 #include "uplink.h"
     25 
     26 user_map UserListByNick, UserListByUID;
     27 
     28 int OperCount = 0;
     29 unsigned MaxUserCount = 0;
     30 time_t MaxUserTime = 0;
     31 
     32 std::list<User *> User::quitting_users;
     33 
     34 User::User(const Anope::string &snick, const Anope::string &sident, const Anope::string &shost, const Anope::string &svhost, const Anope::string &uip, Server *sserver, const Anope::string &srealname, time_t ts, const Anope::string &smodes, const Anope::string &suid, NickCore *account) : ip(uip)
     35 {
     36 	if (snick.empty() || sident.empty() || shost.empty())
     37 		throw CoreException("Bad args passed to User::User");
     38 
     39 	/* we used to do this by calloc, no more. */
     40 	quit = false;
     41 	server = NULL;
     42 	invalid_pw_count = invalid_pw_time = lastmemosend = lastnickreg = lastmail = 0;
     43 	on_access = false;
     44 
     45 	this->nick = snick;
     46 	this->ident = sident;
     47 	this->host = shost;
     48 	this->vhost = svhost;
     49 	this->chost = svhost;
     50 	this->server = sserver;
     51 	this->realname = srealname;
     52 	this->timestamp = this->signon = ts;
     53 	this->SetModesInternal(sserver, "%s", smodes.c_str());
     54 	this->uid = suid;
     55 	this->super_admin = false;
     56 	this->nc = NULL;
     57 
     58 	size_t old = UserListByNick.size();
     59 	UserListByNick[snick] = this;
     60 	if (!suid.empty())
     61 		UserListByUID[suid] = this;
     62 	if (old == UserListByNick.size())
     63 		Log(LOG_DEBUG) << "Duplicate user " << snick << " in user table?";
     64 
     65 	this->Login(account);
     66 	this->UpdateHost();
     67 
     68 	if (sserver) // Our bots are introduced on startup with no server
     69 	{
     70 		++sserver->users;
     71 		if (server->IsSynced())
     72 			Log(this, "connect") << (!vhost.empty() && vhost != host ? "(" + vhost + ") " : "") << "(" << srealname << ") " << (!uip.empty() && uip != host ? "[" + uip + "] " : "") << "connected to the network (" << sserver->GetName() << ")";
     73 	}
     74 
     75 	if (UserListByNick.size() > MaxUserCount)
     76 	{
     77 		MaxUserCount = UserListByNick.size();
     78 		MaxUserTime = Anope::CurTime;
     79 		if (sserver && sserver->IsSynced())
     80 			Log(this, "maxusers") << "connected - new maximum user count: " << UserListByNick.size();
     81 	}
     82 
     83 	bool exempt = false;
     84 	if (server && server->IsULined())
     85 		exempt = true;
     86 	FOREACH_MOD(OnUserConnect, (this, exempt));
     87 }
     88 
     89 static void CollideKill(User *target, const Anope::string &reason)
     90 {
     91 	if (target->server != Me)
     92 		target->Kill(Me, reason);
     93 	else
     94 	{
     95 		// Be sure my user is really dead
     96 		IRCD->SendQuit(target, "%s", reason.c_str());
     97 
     98 		// Reintroduce my client
     99 		if (BotInfo *bi = dynamic_cast<BotInfo *>(target))
    100 			bi->OnKill();
    101 		else
    102 			target->Quit(reason);
    103 	}
    104 }
    105 
    106 static void Collide(User *u, const Anope::string &id, const Anope::string &type)
    107 {
    108 	// Kill incoming user
    109 	IRCD->SendKill(Me, id, type);
    110 	// Quit colliding user
    111 	CollideKill(u, type);
    112 }
    113 
    114 User* User::OnIntroduce(const Anope::string &snick, const Anope::string &sident, const Anope::string &shost, const Anope::string &svhost, const Anope::string &sip, Server *sserver, const Anope::string &srealname, time_t ts, const Anope::string &smodes, const Anope::string &suid, NickCore *nc)
    115 {
    116 	// How IRCds handle collisions varies a lot, for safety well just always kill both sides
    117 	// With properly set qlines, this can almost never happen anyway
    118 
    119 	User *u = User::Find(snick, true);
    120 	if (u)
    121 	{
    122 		Collide(u, !suid.empty() ? suid : snick, "Nick collision");
    123 		return NULL;
    124 	}
    125 
    126 	if (!suid.empty())
    127 	{
    128 		u = User::Find(suid);
    129 		if (u)
    130 		{
    131 			Collide(u, suid, "ID collision");
    132 			return NULL;
    133 		}
    134 	}
    135 
    136 	return new User(snick, sident, shost, svhost, sip, sserver, srealname, ts, smodes, suid, nc);
    137 }
    138 
    139 void User::ChangeNick(const Anope::string &newnick, time_t ts)
    140 {
    141 	/* Sanity check to make sure we don't segfault */
    142 	if (newnick.empty())
    143 		throw CoreException("User::ChangeNick() got a bad argument");
    144 
    145 	this->super_admin = false;
    146 	Log(this, "nick") << "(" << this->realname << ") changed nick to " << newnick;
    147 
    148 	Anope::string old = this->nick;
    149 	this->timestamp = ts;
    150 
    151 	if (this->nick.equals_ci(newnick))
    152 		this->nick = newnick;
    153 	else
    154 	{
    155 		NickAlias *old_na = NickAlias::Find(this->nick);
    156 		if (old_na && (this->IsIdentified(true) || this->IsRecognized()))
    157 			old_na->last_seen = Anope::CurTime;
    158 
    159 		UserListByNick.erase(this->nick);
    160 
    161 		this->nick = newnick;
    162 
    163 		User* &other = UserListByNick[this->nick];
    164 		if (other)
    165 		{
    166 			CollideKill(this, "Nick collision");
    167 			CollideKill(other, "Nick collision");
    168 			return;
    169 		}
    170 		other = this;
    171 
    172 		on_access = false;
    173 		NickAlias *na = NickAlias::Find(this->nick);
    174 		if (na)
    175 			on_access = na->nc->IsOnAccess(this);
    176 
    177 		if (na && na->nc == this->Account())
    178 		{
    179 			na->last_seen = Anope::CurTime;
    180 			this->UpdateHost();
    181 		}
    182 	}
    183 
    184 	FOREACH_MOD(OnUserNickChange, (this, old));
    185 }
    186 
    187 void User::SetDisplayedHost(const Anope::string &shost)
    188 {
    189 	if (shost.empty())
    190 		throw CoreException("empty host? in MY services? it seems it's more likely than I thought.");
    191 
    192 	this->vhost = shost;
    193 
    194 	Log(this, "host") << "changed vhost to " << shost;
    195 
    196 	FOREACH_MOD(OnSetDisplayedHost, (this));
    197 
    198 	this->UpdateHost();
    199 }
    200 
    201 const Anope::string &User::GetDisplayedHost() const
    202 {
    203 	if (!this->vhost.empty())
    204 		return this->vhost;
    205 	else if (this->HasMode("CLOAK") && !this->GetCloakedHost().empty())
    206 		return this->GetCloakedHost();
    207 	else
    208 		return this->host;
    209 }
    210 
    211 void User::SetCloakedHost(const Anope::string &newhost)
    212 {
    213 	if (newhost.empty())
    214 		throw CoreException("empty host in User::SetCloakedHost");
    215 
    216 	chost = newhost;
    217 
    218 	Log(this, "host") << "changed cloaked host to " << newhost;
    219 
    220 	this->UpdateHost();
    221 }
    222 
    223 const Anope::string &User::GetCloakedHost() const
    224 {
    225 	return chost;
    226 }
    227 
    228 const Anope::string &User::GetUID() const
    229 {
    230 	if (!this->uid.empty() && IRCD->RequiresID)
    231 		return this->uid;
    232 	else
    233 		return this->nick;
    234 }
    235 
    236 void User::SetVIdent(const Anope::string &sident)
    237 {
    238 	this->vident = sident;
    239 
    240 	Log(this, "ident") << "changed vident to " << sident;
    241 
    242 	this->UpdateHost();
    243 }
    244 
    245 const Anope::string &User::GetVIdent() const
    246 {
    247 	if (!this->vident.empty())
    248 		return this->vident;
    249 	else
    250 		return this->ident;
    251 }
    252 
    253 void User::SetIdent(const Anope::string &sident)
    254 {
    255 	this->ident = sident;
    256 
    257 	Log(this, "ident") << "changed real ident to " << sident;
    258 
    259 	this->UpdateHost();
    260 }
    261 
    262 const Anope::string &User::GetIdent() const
    263 {
    264 	return this->ident;
    265 }
    266 
    267 Anope::string User::GetMask() const
    268 {
    269 	return this->nick + "!" + this->ident + "@" + this->host;
    270 }
    271 
    272 Anope::string User::GetDisplayedMask() const
    273 {
    274 	return this->nick + "!" + this->GetVIdent() + "@" + this->GetDisplayedHost();
    275 }
    276 
    277 void User::SetRealname(const Anope::string &srealname)
    278 {
    279 	if (srealname.empty())
    280 		throw CoreException("realname empty in SetRealname");
    281 
    282 	this->realname = srealname;
    283 	NickAlias *na = NickAlias::Find(this->nick);
    284 
    285 	if (na && (this->IsIdentified(true) || this->IsRecognized()))
    286 		na->last_realname = srealname;
    287 
    288 	Log(this, "realname") << "changed realname to " << srealname;
    289 }
    290 
    291 User::~User()
    292 {
    293 	UnsetExtensibles();
    294 
    295 	if (this->server != NULL)
    296 	{
    297 		if (this->server->IsSynced())
    298 			Log(this, "disconnect") << "(" << this->realname << ") disconnected from the network (" << this->server->GetName() << ")";
    299 		--this->server->users;
    300 	}
    301 
    302 	FOREACH_MOD(OnPreUserLogoff, (this));
    303 
    304 	ModeManager::StackerDel(this);
    305 	this->Logout();
    306 
    307 	if (this->HasMode("OPER"))
    308 		--OperCount;
    309 
    310 	while (!this->chans.empty())
    311 		this->chans.begin()->second->chan->DeleteUser(this);
    312 
    313 	UserListByNick.erase(this->nick);
    314 	if (!this->uid.empty())
    315 		UserListByUID.erase(this->uid);
    316 
    317 	FOREACH_MOD(OnPostUserLogoff, (this));
    318 }
    319 
    320 void User::SendMessage(BotInfo *source, const char *fmt, ...)
    321 {
    322 	va_list args;
    323 	char buf[BUFSIZE] = "";
    324 
    325 	const char *translated_message = Language::Translate(this, fmt);
    326 
    327 	va_start(args, fmt);
    328 	vsnprintf(buf, BUFSIZE - 1, translated_message, args);
    329 
    330 	this->SendMessage(source, Anope::string(buf));
    331 
    332 	va_end(args);
    333 }
    334 
    335 void User::SendMessage(BotInfo *source, const Anope::string &msg)
    336 {
    337 	const char *translated_message = Language::Translate(this, msg.c_str());
    338 
    339 	/* Send privmsg instead of notice if:
    340 	* - UsePrivmsg is enabled
    341 	* - The user is not registered and NSDefMsg is enabled
    342 	* - The user is registered and has set /ns set msg on
    343 	*/
    344 	bool send_privmsg = Config->UsePrivmsg && ((!this->nc && Config->DefPrivmsg) || (this->nc && this->nc->HasExt("MSG")));
    345 	sepstream sep(translated_message, '\n', true);
    346 	for (Anope::string tok; sep.GetToken(tok);)
    347 	{
    348 		if (send_privmsg)
    349 			IRCD->SendPrivmsg(source, this->GetUID(), "%s", tok.c_str());
    350 		else
    351 			IRCD->SendNotice(source, this->GetUID(), "%s", tok.c_str());
    352 	}
    353 }
    354 
    355 void User::Identify(NickAlias *na)
    356 {
    357 	if (this->nick.equals_ci(na->nick))
    358 	{
    359 		na->last_usermask = this->GetIdent() + "@" + this->GetDisplayedHost();
    360 		na->last_realhost = this->GetIdent() + "@" + this->host;
    361 		na->last_realname = this->realname;
    362 		na->last_seen = Anope::CurTime;
    363 	}
    364 
    365 	IRCD->SendLogin(this, na);
    366 
    367 	this->Login(na->nc);
    368 
    369 	FOREACH_MOD(OnNickIdentify, (this));
    370 
    371 	if (this->IsServicesOper())
    372 	{
    373 		if (!this->nc->o->ot->modes.empty())
    374 		{
    375 			this->SetModes(NULL, "%s", this->nc->o->ot->modes.c_str());
    376 			this->SendMessage(NULL, "Changing your usermodes to \002%s\002", this->nc->o->ot->modes.c_str());
    377 			UserMode *um = ModeManager::FindUserModeByName("OPER");
    378 			if (um && !this->HasMode("OPER") && this->nc->o->ot->modes.find(um->mchar) != Anope::string::npos)
    379 				IRCD->SendOper(this);
    380 		}
    381 		if (IRCD->CanSetVHost && !this->nc->o->vhost.empty())
    382 		{
    383 			this->SendMessage(NULL, "Changing your vhost to \002%s\002", this->nc->o->vhost.c_str());
    384 			this->SetDisplayedHost(this->nc->o->vhost);
    385 			IRCD->SendVhost(this, "", this->nc->o->vhost);
    386 		}
    387 	}
    388 }
    389 
    390 
    391 void User::Login(NickCore *core)
    392 {
    393 	if (!core || core == this->nc)
    394 		return;
    395 
    396 	this->Logout();
    397 	this->nc = core;
    398 	core->users.push_back(this);
    399 
    400 	this->UpdateHost();
    401 
    402 	if (this->server->IsSynced())
    403 		Log(this, "account") << "is now identified as " << this->nc->display;
    404 
    405 	FOREACH_MOD(OnUserLogin, (this));
    406 }
    407 
    408 void User::Logout()
    409 {
    410 	if (!this->nc)
    411 		return;
    412 
    413 	Log(this, "account") << "is no longer identified as " << this->nc->display;
    414 
    415 	std::list<User *>::iterator it = std::find(this->nc->users.begin(), this->nc->users.end(), this);
    416 	if (it != this->nc->users.end())
    417 		this->nc->users.erase(it);
    418 
    419 	this->nc = NULL;
    420 }
    421 
    422 NickCore *User::Account() const
    423 {
    424 	return this->nc;
    425 }
    426 
    427 bool User::IsIdentified(bool check_nick) const
    428 {
    429 	if (check_nick && this->nc)
    430 	{
    431 		NickAlias *na = NickAlias::Find(this->nick);
    432 		return na && *na->nc == *this->nc;
    433 	}
    434 
    435 	return this->nc ? true : false;
    436 }
    437 
    438 bool User::IsRecognized(bool check_secure) const
    439 {
    440 	if (check_secure && on_access)
    441 	{
    442 		const NickAlias *na = NickAlias::Find(this->nick);
    443 
    444 		if (!na || na->nc->HasExt("NS_SECURE"))
    445 			return false;
    446 	}
    447 
    448 	return on_access;
    449 }
    450 
    451 bool User::IsServicesOper()
    452 {
    453 	if (!this->nc || !this->nc->IsServicesOper())
    454 		// No opertype.
    455 		return false;
    456 	else if (this->nc->o->require_oper && !this->HasMode("OPER"))
    457 		return false;
    458 	else if (!this->nc->o->certfp.empty() && this->fingerprint != this->nc->o->certfp)
    459 		// Certfp mismatch
    460 		return false;
    461 	else if (!this->nc->o->hosts.empty())
    462 	{
    463 		bool match = false;
    464 		Anope::string match_host = this->GetIdent() + "@" + this->host;
    465 		for (unsigned i = 0; i < this->nc->o->hosts.size(); ++i)
    466 			if (Anope::Match(match_host, this->nc->o->hosts[i]))
    467 				match = true;
    468 		if (match == false)
    469 			return false;
    470 	}
    471 
    472 	EventReturn MOD_RESULT;
    473 	FOREACH_RESULT(IsServicesOper, MOD_RESULT, (this));
    474 	if (MOD_RESULT == EVENT_STOP)
    475 		return false;
    476 
    477 	return true;
    478 }
    479 
    480 bool User::HasCommand(const Anope::string &command)
    481 {
    482 	if (this->IsServicesOper())
    483 		return this->nc->o->ot->HasCommand(command);
    484 	return false;
    485 }
    486 
    487 bool User::HasPriv(const Anope::string &priv)
    488 {
    489 	if (this->IsServicesOper())
    490 		return this->nc->o->ot->HasPriv(priv);
    491 	return false;
    492 }
    493 
    494 void User::UpdateHost()
    495 {
    496 	if (this->host.empty())
    497 		return;
    498 
    499 	NickAlias *na = NickAlias::Find(this->nick);
    500 	on_access = false;
    501 	if (na)
    502 		on_access = na->nc->IsOnAccess(this);
    503 
    504 	if (na && (this->IsIdentified(true) || this->IsRecognized()))
    505 	{
    506 		Anope::string last_usermask = this->GetIdent() + "@" + this->GetDisplayedHost();
    507 		Anope::string last_realhost = this->GetIdent() + "@" + this->host;
    508 		na->last_usermask = last_usermask;
    509 		na->last_realhost = last_realhost;
    510 		// This is called on signon, and if users are introduced with an account it won't update
    511 		na->last_realname = this->realname;
    512 	}
    513 }
    514 
    515 bool User::HasMode(const Anope::string &mname) const
    516 {
    517 	return this->modes.count(mname);
    518 }
    519 
    520 void User::SetModeInternal(const MessageSource &source, UserMode *um, const Anope::string &param)
    521 {
    522 	if (!um)
    523 		return;
    524 
    525 	this->modes[um->name] = param;
    526 
    527 	if (um->name == "OPER")
    528 	{
    529 		++OperCount;
    530 
    531 		if (this->IsServicesOper())
    532 		{
    533 			if (!this->nc->o->ot->modes.empty())
    534 			{
    535 				this->SetModes(NULL, "%s", this->nc->o->ot->modes.c_str());
    536 				this->SendMessage(NULL, "Changing your usermodes to \002%s\002", this->nc->o->ot->modes.c_str());
    537 				UserMode *oper = ModeManager::FindUserModeByName("OPER");
    538 				if (oper && !this->HasMode("OPER") && this->nc->o->ot->modes.find(oper->mchar) != Anope::string::npos)
    539 					IRCD->SendOper(this);
    540 			}
    541 			if (IRCD->CanSetVHost && !this->nc->o->vhost.empty())
    542 			{
    543 				this->SendMessage(NULL, "Changing your vhost to \002%s\002", this->nc->o->vhost.c_str());
    544 				this->SetDisplayedHost(this->nc->o->vhost);
    545 				IRCD->SendVhost(this, "", this->nc->o->vhost);
    546 			}
    547 		}
    548 	}
    549 
    550 	if (um->name == "CLOAK" || um->name == "VHOST")
    551 		this->UpdateHost();
    552 
    553 	FOREACH_MOD(OnUserModeSet, (source, this, um->name));
    554 }
    555 
    556 void User::RemoveModeInternal(const MessageSource &source, UserMode *um)
    557 {
    558 	if (!um)
    559 		return;
    560 
    561 	this->modes.erase(um->name);
    562 
    563 	if (um->name == "OPER")
    564 	{
    565 		--OperCount;
    566 
    567 		// Don't let people de-oper and remain a SuperAdmin
    568 		this->super_admin = false;
    569 	}
    570 
    571 	if (um->name == "CLOAK" || um->name == "VHOST")
    572 	{
    573 		this->vhost.clear();
    574 		this->UpdateHost();
    575 	}
    576 
    577 	FOREACH_MOD(OnUserModeUnset, (source, this, um->name));
    578 }
    579 
    580 void User::SetMode(BotInfo *bi, UserMode *um, const Anope::string &param)
    581 {
    582 	if (!um || HasMode(um->name))
    583 		return;
    584 
    585 	ModeManager::StackerAdd(bi, this, um, true, param);
    586 	SetModeInternal(bi, um, param);
    587 }
    588 
    589 void User::SetMode(BotInfo *bi, const Anope::string &uname, const Anope::string &param)
    590 {
    591 	SetMode(bi, ModeManager::FindUserModeByName(uname), param);
    592 }
    593 
    594 void User::RemoveMode(BotInfo *bi, UserMode *um, const Anope::string &param)
    595 {
    596 	if (!um || !HasMode(um->name))
    597 		return;
    598 
    599 	ModeManager::StackerAdd(bi, this, um, false, param);
    600 	RemoveModeInternal(bi, um);
    601 }
    602 
    603 void User::RemoveMode(BotInfo *bi, const Anope::string &name, const Anope::string &param)
    604 {
    605 	RemoveMode(bi, ModeManager::FindUserModeByName(name), param);
    606 }
    607 
    608 void User::SetModes(BotInfo *bi, const char *umodes, ...)
    609 {
    610 	char buf[BUFSIZE] = "";
    611 	va_list args;
    612 	Anope::string modebuf, sbuf;
    613 	int add = -1;
    614 	va_start(args, umodes);
    615 	vsnprintf(buf, BUFSIZE - 1, umodes, args);
    616 	va_end(args);
    617 
    618 	spacesepstream sep(buf);
    619 	sep.GetToken(modebuf);
    620 	for (unsigned i = 0, end = modebuf.length(); i < end; ++i)
    621 	{
    622 		UserMode *um;
    623 
    624 		switch (modebuf[i])
    625 		{
    626 			case '+':
    627 				add = 1;
    628 				continue;
    629 			case '-':
    630 				add = 0;
    631 				continue;
    632 			default:
    633 				if (add == -1)
    634 					continue;
    635 				um = ModeManager::FindUserModeByChar(modebuf[i]);
    636 				if (!um)
    637 					continue;
    638 		}
    639 
    640 		if (add)
    641 		{
    642 			if (um->type == MODE_PARAM && sep.GetToken(sbuf))
    643 				this->SetMode(bi, um, sbuf);
    644 			else
    645 				this->SetMode(bi, um);
    646 		}
    647 		else
    648 			this->RemoveMode(bi, um);
    649 	}
    650 }
    651 
    652 void User::SetModesInternal(const MessageSource &source, const char *umodes, ...)
    653 {
    654 	char buf[BUFSIZE] = "";
    655 	va_list args;
    656 	Anope::string modebuf, sbuf;
    657 	int add = -1;
    658 	va_start(args, umodes);
    659 	vsnprintf(buf, BUFSIZE - 1, umodes, args);
    660 	va_end(args);
    661 
    662 	if (this->server && this->server->IsSynced() && Anope::string(buf) != "+")
    663 		Log(this, "mode") << "changes modes to " << buf;
    664 
    665 	spacesepstream sep(buf);
    666 	sep.GetToken(modebuf);
    667 	for (unsigned i = 0, end = modebuf.length(); i < end; ++i)
    668 	{
    669 		UserMode *um;
    670 
    671 		switch (modebuf[i])
    672 		{
    673 			case '+':
    674 				add = 1;
    675 				continue;
    676 			case '-':
    677 				add = 0;
    678 				continue;
    679 			default:
    680 				if (add == -1)
    681 					continue;
    682 				um = ModeManager::FindUserModeByChar(modebuf[i]);
    683 				if (!um)
    684 					continue;
    685 		}
    686 
    687 		if (add)
    688 		{
    689 			if (um->type == MODE_PARAM && sep.GetToken(sbuf))
    690 				this->SetModeInternal(source, um, sbuf);
    691 			else
    692 				this->SetModeInternal(source, um);
    693 		}
    694 		else
    695 			this->RemoveModeInternal(source, um);
    696 	}
    697 }
    698 
    699 Anope::string User::GetModes() const
    700 {
    701 	Anope::string m, params;
    702 
    703 	for (ModeList::const_iterator it = this->modes.begin(), it_end = this->modes.end(); it != it_end; ++it)
    704 	{
    705 		UserMode *um = ModeManager::FindUserModeByName(it->first);
    706 		if (um == NULL)
    707 			continue;
    708 
    709 		m += um->mchar;
    710 
    711 		if (!it->second.empty())
    712 			params += " " + it->second;
    713 	}
    714 
    715 	return m + params;
    716 }
    717 
    718 const User::ModeList &User::GetModeList() const
    719 {
    720 	return modes;
    721 }
    722 
    723 ChanUserContainer *User::FindChannel(Channel *c) const
    724 {
    725 	User::ChanUserList::const_iterator it = this->chans.find(c);
    726 	if (it != this->chans.end())
    727 		return it->second;
    728 	return NULL;
    729 }
    730 
    731 bool User::IsProtected()
    732 {
    733 	return this->HasMode("PROTECTED") || this->HasMode("GOD") || this->HasPriv("protected") || (this->server && this->server->IsULined());
    734 }
    735 
    736 void User::Kill(const MessageSource &source, const Anope::string &reason)
    737 {
    738 	Anope::string real_reason = source.GetName() + " (" + reason + ")";
    739 
    740 	IRCD->SendSVSKill(source, this, "%s", real_reason.c_str());
    741 }
    742 
    743 void User::KillInternal(const MessageSource &source, const Anope::string &reason)
    744 {
    745 	if (this->quit)
    746 	{
    747 		Log(LOG_DEBUG) << "Duplicate quit for " << this->nick;
    748 		return;
    749 	}
    750 
    751 	Log(this, "killed") << "was killed by " << source.GetName() << " (Reason: " << reason << ")";
    752 
    753 	this->Quit(reason);
    754 }
    755 
    756 void User::Quit(const Anope::string &reason)
    757 {
    758 	if (this->quit)
    759 	{
    760 		Log(LOG_DEBUG) << "Duplicate quit for " << this->nick;
    761 		return;
    762 	}
    763 
    764 	FOREACH_MOD(OnUserQuit, (this, reason));
    765 
    766 	this->quit = true;
    767 	quitting_users.push_back(this);
    768 }
    769 
    770 bool User::Quitting() const
    771 {
    772 	return this->quit;
    773 }
    774 
    775 Anope::string User::Mask() const
    776 {
    777 	Anope::string mask;
    778 	const Anope::string &mident = this->GetIdent();
    779 	const Anope::string &mhost = this->GetDisplayedHost();
    780 
    781 	if (mident[0] == '~')
    782 		mask = "*" + mident + "@";
    783 	else
    784 		mask = mident + "@";
    785 
    786 	sockaddrs addr(mhost);
    787 	if (addr.valid() && addr.sa.sa_family == AF_INET)
    788 	{
    789 		size_t dot = mhost.rfind('.');
    790 		mask += mhost.substr(0, dot) + (dot == Anope::string::npos ? "" : ".*");
    791 	}
    792 	else
    793 	{
    794 		size_t dot = mhost.find('.');
    795 		if (dot != Anope::string::npos && mhost.find('.', dot + 1) != Anope::string::npos)
    796 			mask += "*" + mhost.substr(dot);
    797 		else
    798 			mask += mhost;
    799 	}
    800 
    801 	return mask;
    802 }
    803 
    804 bool User::BadPassword()
    805 {
    806 	if (!Config->GetBlock("options")->Get<int>("badpasslimit"))
    807 		return false;
    808 
    809 	if (Config->GetBlock("options")->Get<time_t>("badpasstimeout") > 0 && this->invalid_pw_time > 0 && this->invalid_pw_time < Anope::CurTime - Config->GetBlock("options")->Get<time_t>("badpasstimeout"))
    810 		this->invalid_pw_count = 0;
    811 	++this->invalid_pw_count;
    812 	this->invalid_pw_time = Anope::CurTime;
    813 	if (this->invalid_pw_count >= Config->GetBlock("options")->Get<int>("badpasslimit"))
    814 	{
    815 		this->Kill(Me, "Too many invalid passwords");
    816 		return true;
    817 	}
    818 
    819 	return false;
    820 }
    821 
    822 User* User::Find(const Anope::string &name, bool nick_only)
    823 {
    824 	if (!nick_only && IRCD && IRCD->RequiresID)
    825 	{
    826 		user_map::iterator it = UserListByUID.find(name);
    827 		if (it != UserListByUID.end())
    828 			return it->second;
    829 
    830 		if (IRCD->AmbiguousID)
    831 			return NULL;
    832 	}
    833 
    834 	user_map::iterator it = UserListByNick.find(name);
    835 	if (it != UserListByNick.end())
    836 		return it->second;
    837 
    838 	return NULL;
    839 }
    840 
    841 void User::QuitUsers()
    842 {
    843 	for (std::list<User *>::iterator it = quitting_users.begin(), it_end = quitting_users.end(); it != it_end; ++it)
    844 		delete *it;
    845 	quitting_users.clear();
    846 }