anope

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

nickserv.cpp (17073B)

      1 /* NickServ core functions
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "module.h"
     13 
     14 class NickServCollide;
     15 static std::set<NickServCollide *> collides;
     16 
     17 /** Timer for colliding nicks to force people off of nicknames
     18  */
     19 class NickServCollide : public Timer
     20 {
     21 	NickServService *service;
     22 	Reference<User> u;
     23 	time_t ts;
     24 	Reference<NickAlias> na;
     25 
     26  public:
     27 	NickServCollide(Module *me, NickServService *nss, User *user, NickAlias *nick, time_t delay) : Timer(me, delay), service(nss), u(user), ts(user->timestamp), na(nick)
     28 	{
     29 		collides.insert(this);
     30 	}
     31 
     32 	~NickServCollide()
     33 	{
     34 		collides.erase(this);
     35 	}
     36 
     37 	User *GetUser()
     38 	{
     39 		return u;
     40 	}
     41 
     42 	NickAlias *GetNick()
     43 	{
     44 		return na;
     45 	}
     46 
     47 	void Tick(time_t t) anope_override
     48 	{
     49 		if (!u || !na)
     50 			return;
     51 		/* If they identified or don't exist anymore, don't kill them. */
     52 		if (u->Account() == na->nc || u->timestamp > ts)
     53 			return;
     54 
     55 		service->Collide(u, na);
     56 	}
     57 };
     58 
     59 /** Timer for removing HELD status from nicks.
     60  */
     61 class NickServHeld : public Timer
     62 {
     63 	Reference<NickAlias> na;
     64 	Anope::string nick;
     65  public:
     66 	NickServHeld(Module *me, NickAlias *n, long l) : Timer(me, l), na(n), nick(na->nick)
     67 	{
     68 		n->Extend<bool>("HELD");
     69 	}
     70 
     71 	void Tick(time_t)
     72 	{
     73 		if (na)
     74 			na->Shrink<bool>("HELD");
     75 	}
     76 };
     77 
     78 class NickServRelease;
     79 static Anope::map<NickServRelease *> NickServReleases;
     80 
     81 /** Timer for releasing nicks to be available for use
     82  */
     83 class NickServRelease : public User, public Timer
     84 {
     85 	Anope::string nick;
     86 
     87  public:
     88 	NickServRelease(Module *me, NickAlias *na, time_t delay) : User(na->nick, Config->GetModule("nickserv")->Get<const Anope::string>("enforceruser", "user"),
     89 		Config->GetModule("nickserv")->Get<const Anope::string>("enforcerhost", Me->GetName()), "", "", Me, "Services Enforcer", Anope::CurTime, "", IRCD->UID_Retrieve(), NULL), Timer(me, delay), nick(na->nick)
     90 	{
     91 		/* Erase the current release timer and use the new one */
     92 		Anope::map<NickServRelease *>::iterator nit = NickServReleases.find(this->nick);
     93 		if (nit != NickServReleases.end())
     94 		{
     95 			IRCD->SendQuit(nit->second, "");
     96 			delete nit->second;
     97 		}
     98 
     99 		NickServReleases.insert(std::make_pair(this->nick, this));
    100 
    101 		IRCD->SendClientIntroduction(this);
    102 	}
    103 
    104 	~NickServRelease()
    105 	{
    106 		IRCD->SendQuit(this, "");
    107 		NickServReleases.erase(this->nick);
    108 	}
    109 
    110 	void Tick(time_t t) anope_override { }
    111 };
    112 
    113 class NickServCore : public Module, public NickServService
    114 {
    115 	Reference<BotInfo> NickServ;
    116 	std::vector<Anope::string> defaults;
    117 	ExtensibleItem<bool> held, collided;
    118 
    119 	void OnCancel(User *u, NickAlias *na)
    120 	{
    121 		if (collided.HasExt(na))
    122 		{
    123 			collided.Unset(na);
    124 
    125 			new NickServHeld(this, na, Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
    126 
    127 			if (IRCD->CanSVSHold)
    128 				IRCD->SendSVSHold(na->nick, Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
    129 			else
    130 				new NickServRelease(this, na, Config->GetModule("nickserv")->Get<time_t>("releasetimeout", "1m"));
    131 		}
    132 	}
    133 
    134  public:
    135 	NickServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR),
    136 		NickServService(this), held(this, "HELD"), collided(this, "COLLIDED")
    137 	{
    138 	}
    139 
    140 	~NickServCore()
    141 	{
    142 		OnShutdown();
    143 	}
    144 
    145 	void OnShutdown() anope_override
    146 	{
    147 		/* On shutdown, restart, or mod unload, remove all of our holds for nicks (svshold or qlines)
    148 		 * because some IRCds do not allow us to have these automatically expire
    149 		 */
    150 		for (nickalias_map::const_iterator it = NickAliasList->begin(); it != NickAliasList->end(); ++it)
    151 			this->Release(it->second);
    152 	}
    153 
    154 	void OnRestart() anope_override
    155 	{
    156 		OnShutdown();
    157 	}
    158 
    159 	void Validate(User *u) anope_override
    160 	{
    161 		NickAlias *na = NickAlias::Find(u->nick);
    162 		if (!na)
    163 			return;
    164 
    165 		EventReturn MOD_RESULT;
    166 		FOREACH_RESULT(OnNickValidate, MOD_RESULT, (u, na));
    167 		if (MOD_RESULT == EVENT_STOP)
    168 		{
    169 			this->Collide(u, na);
    170 			return;
    171 		}
    172 		else if (MOD_RESULT == EVENT_ALLOW)
    173 			return;
    174 
    175 		if (!na->nc->HasExt("NS_SECURE") && u->IsRecognized())
    176 		{
    177 			na->last_seen = Anope::CurTime;
    178 			na->last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
    179 			na->last_realname = u->realname;
    180 			return;
    181 		}
    182 
    183 		if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
    184 			return;
    185 
    186 		bool on_access = u->IsRecognized(false);
    187 
    188 		if (on_access || !na->nc->HasExt("KILL_IMMED"))
    189 		{
    190 			if (na->nc->HasExt("NS_SECURE"))
    191 				u->SendMessage(NickServ, NICK_IS_SECURE, Config->StrictPrivmsg.c_str(), NickServ->nick.c_str());
    192 			else
    193 				u->SendMessage(NickServ, NICK_IS_REGISTERED, Config->StrictPrivmsg.c_str(), NickServ->nick.c_str());
    194 		}
    195 		if (na->nc->HasExt("KILLPROTECT") && !on_access)
    196 		{
    197 			if (na->nc->HasExt("KILL_IMMED"))
    198 			{
    199 				u->SendMessage(NickServ, FORCENICKCHANGE_NOW);
    200 				this->Collide(u, na);
    201 			}
    202 			else if (na->nc->HasExt("KILL_QUICK"))
    203 			{
    204 				time_t killquick = Config->GetModule("nickserv")->Get<time_t>("killquick", "20s");
    205 				u->SendMessage(NickServ, _("If you do not change within %s, I will change your nick."), Anope::Duration(killquick, u->Account()).c_str());
    206 				new NickServCollide(this, this, u, na, killquick);
    207 			}
    208 			else
    209 			{
    210 				time_t kill = Config->GetModule("nickserv")->Get<time_t>("kill", "60s");
    211 				u->SendMessage(NickServ, _("If you do not change within %s, I will change your nick."), Anope::Duration(kill, u->Account()).c_str());
    212 				new NickServCollide(this, this, u, na, kill);
    213 			}
    214 		}
    215 
    216 	}
    217 
    218 	void OnUserLogin(User *u) anope_override
    219 	{
    220 		NickAlias *na = NickAlias::Find(u->nick);
    221 		if (na && *na->nc == u->Account() && !Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && !na->nc->HasExt("UNCONFIRMED"))
    222 			u->SetMode(NickServ, "REGISTERED");
    223 
    224 		const Anope::string &modesonid = Config->GetModule(this)->Get<Anope::string>("modesonid");
    225 		if (!modesonid.empty())
    226 			u->SetModes(NickServ, "%s", modesonid.c_str());
    227 	}
    228 
    229 	void Collide(User *u, NickAlias *na) anope_override
    230 	{
    231 		if (na)
    232 			collided.Set(na);
    233 
    234 		if (IRCD->CanSVSNick)
    235 		{
    236 			unsigned nicklen = Config->GetBlock("networkinfo")->Get<unsigned>("nicklen");
    237 			const Anope::string &guestprefix = Config->GetModule("nickserv")->Get<const Anope::string>("guestnickprefix", "Guest");
    238 
    239 			Anope::string guestnick;
    240 
    241 			int i = 0;
    242 			do
    243 			{
    244 				guestnick = guestprefix + stringify(static_cast<uint16_t>(rand()));
    245 				if (guestnick.length() > nicklen)
    246 					guestnick = guestnick.substr(0, nicklen);
    247 			}
    248 			while (User::Find(guestnick) && i++ < 10);
    249 
    250 			if (i == 11)
    251 				u->Kill(*NickServ, "Services nickname-enforcer kill");
    252 			else
    253 			{
    254 				u->SendMessage(*NickServ, _("Your nickname is now being changed to \002%s\002"), guestnick.c_str());
    255 				IRCD->SendForceNickChange(u, guestnick, Anope::CurTime);
    256 			}
    257 		}
    258 		else
    259 			u->Kill(*NickServ, "Services nickname-enforcer kill");
    260 	}
    261 
    262 	void Release(NickAlias *na) anope_override
    263 	{
    264 		if (held.HasExt(na))
    265 		{
    266 			if (IRCD->CanSVSHold)
    267 				IRCD->SendSVSHoldDel(na->nick);
    268 			else
    269 			{
    270 				User *u = User::Find(na->nick);
    271 				if (u && u->server == Me)
    272 				{
    273 					u->Quit();
    274 				}
    275 			}
    276 
    277 			held.Unset(na);
    278 		}
    279 		collided.Unset(na); /* clear pending collide */
    280 	}
    281 
    282 	void OnReload(Configuration::Conf *conf) anope_override
    283 	{
    284 		const Anope::string &nsnick = conf->GetModule(this)->Get<const Anope::string>("client");
    285 
    286 		if (nsnick.empty())
    287 			throw ConfigException(Module::name + ": <client> must be defined");
    288 
    289 		BotInfo *bi = BotInfo::Find(nsnick, true);
    290 		if (!bi)
    291 			throw ConfigException(Module::name + ": no bot named " + nsnick);
    292 
    293 		NickServ = bi;
    294 
    295 		spacesepstream(conf->GetModule(this)->Get<const Anope::string>("defaults", "ns_secure memo_signon memo_receive")).GetTokens(defaults);
    296 		if (defaults.empty())
    297 		{
    298 			defaults.push_back("NS_SECURE");
    299 			defaults.push_back("MEMO_SIGNON");
    300 			defaults.push_back("MEMO_RECEIVE");
    301 		}
    302 		else if (defaults[0].equals_ci("none"))
    303 			defaults.clear();
    304 	}
    305 
    306 	void OnDelNick(NickAlias *na) anope_override
    307 	{
    308 		User *u = User::Find(na->nick);
    309 		if (u && u->Account() == na->nc)
    310 		{
    311 			IRCD->SendLogout(u);
    312 			u->RemoveMode(NickServ, "REGISTERED");
    313 			u->Logout();
    314 		}
    315 	}
    316 
    317 	void OnDelCore(NickCore *nc) anope_override
    318 	{
    319 		Log(NickServ, "nick") << "Deleting nickname group " << nc->display;
    320 
    321 		/* Clean up this nick core from any users online */
    322 		for (std::list<User *>::iterator it = nc->users.begin(); it != nc->users.end();)
    323 		{
    324 			User *user = *it++;
    325 			IRCD->SendLogout(user);
    326 			user->RemoveMode(NickServ, "REGISTERED");
    327 			user->Logout();
    328 			FOREACH_MOD(OnNickLogout, (user));
    329 		}
    330 		nc->users.clear();
    331 	}
    332 
    333 	void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) anope_override
    334 	{
    335 		Log(LOG_NORMAL, "nick", NickServ) << "Changing " << nc->display << " nickname group display to " << newdisplay;
    336 	}
    337 
    338 	void OnNickIdentify(User *u) anope_override
    339 	{
    340 		Configuration::Block *block = Config->GetModule(this);
    341 
    342 		if (block->Get<bool>("modeonid", "yes"))
    343 
    344 			for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
    345 			{
    346 				ChanUserContainer *cc = it->second;
    347 				Channel *c = cc->chan;
    348 				if (c)
    349 					c->SetCorrectModes(u, true);
    350 			}
    351 
    352 		const Anope::string &modesonid = block->Get<const Anope::string>("modesonid");
    353 		if (!modesonid.empty())
    354 			u->SetModes(NickServ, "%s", modesonid.c_str());
    355 
    356 		if (block->Get<bool>("forceemail", "yes") && u->Account()->email.empty())
    357 		{
    358 			u->SendMessage(NickServ, _("You must now supply an e-mail for your nick.\n"
    359 							"This e-mail will allow you to retrieve your password in\n"
    360 							"case you forget it."));
    361 			u->SendMessage(NickServ, _("Type \002%s%s SET EMAIL \037e-mail\037\002 in order to set your e-mail.\n"
    362 							"Your privacy is respected; this e-mail won't be given to\n"
    363 							"any third-party person."), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str());
    364 		}
    365 
    366 		for (std::set<NickServCollide *>::iterator it = collides.begin(); it != collides.end(); ++it)
    367 		{
    368 			NickServCollide *c = *it;
    369 			if (c->GetUser() == u && c->GetNick() && c->GetNick()->nc == u->Account())
    370 			{
    371 				delete c;
    372 				break;
    373 			}
    374 		}
    375 	}
    376 
    377 	void OnNickGroup(User *u, NickAlias *target) anope_override
    378 	{
    379 		if (!target->nc->HasExt("UNCONFIRMED"))
    380 			u->SetMode(NickServ, "REGISTERED");
    381 	}
    382 
    383 	void OnNickUpdate(User *u) anope_override
    384 	{
    385 		for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it)
    386 		{
    387 			ChanUserContainer *cc = it->second;
    388 			Channel *c = cc->chan;
    389 			if (c)
    390 				c->SetCorrectModes(u, true);
    391 		}
    392 	}
    393 
    394 	void OnUserConnect(User *u, bool &exempt) anope_override
    395 	{
    396 		if (u->Quitting() || !u->server->IsSynced() || u->server->IsULined())
    397 			return;
    398 
    399 		const NickAlias *na = NickAlias::Find(u->nick);
    400 
    401 		const Anope::string &unregistered_notice = Config->GetModule(this)->Get<const Anope::string>("unregistered_notice");
    402 		if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && !unregistered_notice.empty() && !na && !u->Account())
    403 			u->SendMessage(NickServ, unregistered_notice.replace_all_cs("%n", u->nick));
    404 		else if (na && !u->IsIdentified(true))
    405 			this->Validate(u);
    406 	}
    407 
    408 	void OnPostUserLogoff(User *u) anope_override
    409 	{
    410 		NickAlias *na = NickAlias::Find(u->nick);
    411 		if (na)
    412 			OnCancel(u, na);
    413 	}
    414 
    415 	void OnServerSync(Server *s) anope_override
    416 	{
    417 		for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
    418 		{
    419 			User *u = it->second;
    420 
    421 			if (u->server == s)
    422 			{
    423 				if (u->HasMode("REGISTERED") && !u->IsIdentified(true))
    424 					u->RemoveMode(NickServ, "REGISTERED");
    425 				if (!u->IsIdentified())
    426 					this->Validate(u);
    427 			}
    428 		}
    429 	}
    430 
    431 	void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override
    432 	{
    433 		NickAlias *old_na = NickAlias::Find(oldnick), *na = NickAlias::Find(u->nick);
    434 		/* If the new nick isn't registered or it's registered and not yours */
    435 		if (!na || na->nc != u->Account())
    436 		{
    437 			/* Remove +r, but keep an account associated with the user */
    438 			u->RemoveMode(NickServ, "REGISTERED");
    439 
    440 			this->Validate(u);
    441 		}
    442 		else
    443 		{
    444 			/* Reset +r and re-send account (even though it really should be set at this point) */
    445 			IRCD->SendLogin(u, na);
    446 			if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && na->nc == u->Account() && !na->nc->HasExt("UNCONFIRMED"))
    447 				u->SetMode(NickServ, "REGISTERED");
    448 			Log(u, "", NickServ) << u->GetMask() << " automatically identified for group " << u->Account()->display;
    449 		}
    450 
    451 		if (!u->nick.equals_ci(oldnick) && old_na)
    452 			OnCancel(u, old_na);
    453 	}
    454 
    455 	void OnUserModeSet(const MessageSource &setter, User *u, const Anope::string &mname) anope_override
    456 	{
    457 		if (u->server->IsSynced() && mname == "REGISTERED" && !u->IsIdentified(true))
    458 			u->RemoveMode(NickServ, mname);
    459 	}
    460 
    461 	EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    462 	{
    463 		if (!params.empty() || source.c || source.service != *NickServ)
    464 			return EVENT_CONTINUE;
    465 		if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership"))
    466 			source.Reply(_("\002%s\002 allows you to register a nickname and\n"
    467 				"prevent others from using it. The following\n"
    468 				"commands allow for registration and maintenance of\n"
    469 				"nicknames; to use them, type \002%s%s \037command\037\002.\n"
    470 				"For more information on a specific command, type\n"
    471 				"\002%s%s %s \037command\037\002.\n"), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), source.command.c_str());
    472 		else
    473 			source.Reply(_("\002%s\002 allows you to register an account.\n"
    474 				"The following commands allow for registration and maintenance of\n"
    475 				"accounts; to use them, type \002%s%s \037command\037\002.\n"
    476 				"For more information on a specific command, type\n"
    477 				"\002%s%s %s \037command\037\002.\n"), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), Config->StrictPrivmsg.c_str(), NickServ->nick.c_str(), source.command.c_str());
    478 		return EVENT_CONTINUE;
    479 	}
    480 
    481 	void OnPostHelp(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    482 	{
    483 		if (!params.empty() || source.c || source.service != *NickServ)
    484 			return;
    485 		if (source.IsServicesOper())
    486 			source.Reply(_(" \n"
    487 				"Services Operators can also drop any nickname without needing\n"
    488 				"to identify for the nick, and may view the access list for\n"
    489 				"any nickname."));
    490 		time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
    491 		if (nickserv_expire >= 86400)
    492 			source.Reply(_(" \n"
    493 				"Accounts that are not used anymore are subject to\n"
    494 				"the automatic expiration, i.e. they will be deleted\n"
    495 				"after %d days if not used."), nickserv_expire / 86400);
    496 	}
    497 
    498 	void OnNickCoreCreate(NickCore *nc) anope_override
    499 	{
    500 		/* Set default flags */
    501 		for (unsigned i = 0; i < defaults.size(); ++i)
    502 			nc->Extend<bool>(defaults[i].upper());
    503 	}
    504 
    505 	void OnUserQuit(User *u, const Anope::string &msg) anope_override
    506 	{
    507 		if (u->server && !u->server->GetQuitReason().empty() && Config->GetModule(this)->Get<bool>("hidenetsplitquit"))
    508 			return;
    509 
    510 		/* Update last quit and last seen for the user */
    511 		NickAlias *na = NickAlias::Find(u->nick);
    512 		if (na && !na->nc->HasExt("NS_SUSPENDED") && (u->IsRecognized() || u->IsIdentified(true)))
    513 		{
    514 			na->last_seen = Anope::CurTime;
    515 			na->last_quit = msg;
    516 		}
    517 	}
    518 
    519 	void OnExpireTick() anope_override
    520 	{
    521 		if (Anope::NoExpire || Anope::ReadOnly)
    522 			return;
    523 
    524 		time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
    525 
    526 		for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; )
    527 		{
    528 			NickAlias *na = it->second;
    529 			++it;
    530 
    531 			User *u = User::Find(na->nick, true);
    532 			if (u && (u->IsIdentified(true) || u->IsRecognized()))
    533 				na->last_seen = Anope::CurTime;
    534 
    535 			bool expire = false;
    536 
    537 			if (nickserv_expire && Anope::CurTime - na->last_seen >= nickserv_expire)
    538 				expire = true;
    539 
    540 			FOREACH_MOD(OnPreNickExpire, (na, expire));
    541 
    542 			if (expire)
    543 			{
    544 				Log(LOG_NORMAL, "nickserv/expire", NickServ) << "Expiring nickname " << na->nick << " (group: " << na->nc->display << ") (e-mail: " << (na->nc->email.empty() ? "none" : na->nc->email) << ")";
    545 				FOREACH_MOD(OnNickExpire, (na));
    546 				delete na;
    547 			}
    548 		}
    549 	}
    550 
    551 	void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override
    552 	{
    553 		if (!na->nc->HasExt("UNCONFIRMED"))
    554 		{
    555 			time_t nickserv_expire = Config->GetModule(this)->Get<time_t>("expire", "21d");
    556 			if (!na->HasExt("NS_NO_EXPIRE") && nickserv_expire && !Anope::NoExpire && (source.HasPriv("nickserv/auspex") || na->last_seen != Anope::CurTime))
    557 				info[_("Expires")] = Anope::strftime(na->last_seen + nickserv_expire, source.GetAccount());
    558 		}
    559 		else
    560 		{
    561 			time_t unconfirmed_expire = Config->GetModule("ns_register")->Get<time_t>("unconfirmedexpire", "1d");
    562 			info[_("Expires")] = Anope::strftime(na->time_registered + unconfirmed_expire, source.GetAccount());
    563 		}
    564 	}
    565 };
    566 
    567 MODULE_INIT(NickServCore)