1
0
mirror of https://github.com/anope/anope.git synced 2026-07-07 04:03:12 +02:00

Rewrote the nick colliding/releaseing/canceling system, fixes many many bugs on IRCds without svsnick and/or svshold

git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@2975 5417fbe8-f217-4b02-8779-1006273d7864
This commit is contained in:
Adam-
2010-05-22 07:40:22 +00:00
parent fae2710ba7
commit d5f0360175
20 changed files with 237 additions and 287 deletions
+22 -5
View File
@@ -11,11 +11,16 @@ enum NickNameFlag
NS_FORBIDDEN,
/* Nick never expires */
NS_NO_EXPIRE,
/* Nick is being held after a kill */
NS_KILL_HELD,
/* SVSNICK has been sent but nick has not yet changed.
* An enforcer will be introduced when it does change. */
NS_GUESTED,
/* This nick is being held after a kill by an enforcer client
* or is being SVSHeld. Used by ns_release to determin if something
* should be allowed to be released
*/
NS_HELD,
/* We are taking over this nick, either by SVSNICK or KILL.
* We are waiting for the confirmation of either of these actions to
* proceed. This is checked in NickAlias::OnCancel
*/
NS_COLLIDED,
NS_END
};
@@ -106,6 +111,18 @@ class CoreExport NickAlias : public Extensible, public Flags<NickNameFlag>
time_t last_seen; /* When it was seen online for the last time */
NickCore *nc; /* I'm an alias of this */
HostInfo hostinfo;
/** Release a nick
* See the comment in users.cpp
*/
void Release();
/** This function is called when a user on this nick either disconnects or changes nick.
* Note that the user isnt necessarially identified to this nick
* See the comment in users.cpp
* @param u The user
*/
void OnCancel(User *u);
};
class CoreExport NickCore : public Extensible, public Flags<NickCoreFlag>
-5
View File
@@ -371,23 +371,18 @@ E NickAlias *nalists[1024];
E NickCore *nclists[1024];
E NickRequest *nrlists[1024];
E NickRequest *findrequestnick(const char *nick);
E unsigned int guestnum;
E void insert_requestnick(NickRequest * nr);
E void alpha_insert_alias(NickAlias * na);
E void insert_core(NickCore * nc);
E void get_aliases_stats(long *nrec, long *memuse);
E void get_core_stats(long *nrec, long *memuse);
E void collide(NickAlias * na, int from_timeout);
E void del_ns_timeout(NickAlias * na, int type);
E void change_core_display(NickCore * nc);
E void change_core_display(NickCore * nc, const char *newdisplay);
E void release(NickAlias * na, int from_timeout);
E int do_setmodes(User * u);
E void ns_init();
E void nickserv(User * u, char *buf);
E int validate_user(User * u);
E void cancel_user(User * u);
E void expire_nicks();
E void expire_requests();
E NickAlias *findnick(const char *nick);
+13 -26
View File
@@ -1159,65 +1159,52 @@ class CoreExport Anope
*/
class NickServCollide : public Timer
{
public:
/* NickAlias of the nick who were kicking off */
NickAlias *na;
/* Return for the std::map::insert */
std::pair<std::map<NickAlias *, NickServCollide *>::iterator, bool> it;
/* The nick */
std::string nick;
public:
/** Default constructor
* @param nickalias The nick alias were kicking off
* @param _nick The nick were colliding
* @param delay How long to delay before kicking the user off the nick
*/
NickServCollide(NickAlias *nickalias, time_t delay);
NickServCollide(const std::string &_nick, time_t delay);
/** Default destructor
*/
~NickServCollide();
virtual ~NickServCollide();
/** Called when the delay is up
* @param t The current time
*/
void Tick(time_t t);
/** Clear all timers for a nick
* @param na The nick to remove the timers for
*/
static void ClearTimers(NickAlias *na);
};
/** Timers for releasing nicks to be available for use
*/
class NickServRelease : public Timer
{
public:
/* The nick */
NickAlias *na;
std::string nick;
public:
/* The uid of the services enforcer client (used for TS6 ircds) */
std::string uid;
/* Return for std::map::insert */
std::pair<std::map<NickAlias *, NickServRelease *>::iterator, bool> it;
/** Default constructor
* @param nickalias The nick
* @param _nick The nick
* @param _uid the uid of the enforcer, if any
* @param delay The delay before the nick is released
*/
NickServRelease(NickAlias *nickalias, time_t delay);
NickServRelease(const std::string &_nick, const std::string &_uid, time_t delay);
/** Default destructor
*/
~NickServRelease();
virtual ~NickServRelease();
/** Called when the delay is up
* @param t The current time
*/
void Tick(time_t t);
/** Clear all timers for a nick
* @param na The nick to remove the timers for
* @param dorelase true to actually call release(), false to just remove the timers
*/
static void ClearTimers(NickAlias *na, bool dorelease = false);
};
/** A timer used to keep the BotServ bot/ChanServ in the channel
+7
View File
@@ -130,6 +130,7 @@ class CoreExport User : public Extensible
/** Get the full mask ( nick!ident@realhost ) of a user
*/
const std::string GetMask();
/** Updates the realname of the user record.
*/
void SetRealname(const std::string &realname);
@@ -143,6 +144,12 @@ class CoreExport User : public Extensible
virtual void SendMessage(const std::string &source, const char *fmt, ...);
virtual void SendMessage(const std::string &source, const std::string &msg);
/** Collide a nick
* See the comment in users.cpp
* @param na The nick
*/
void Collide(NickAlias *na);
/** Check if the user should become identified because
* their svid matches the one stored in their nickcore
* @param svid Services id
+3
View File
@@ -64,7 +64,10 @@ BotInfo::BotInfo(const std::string &nnick, const std::string &nuser, const std::
// If we're synchronised with the uplink already, call introduce_user() for this bot.
if (serv_uplink && serv_uplink->sync == SSYNC_DONE)
{
ircdproto->SendClientIntroduction(this->nick, this->user, this->host, this->real, ircd->pseudoclient_mode, this->uid);
ircdproto->SendSQLine(this->nick, "Reserved for services");
}
}
BotInfo::~BotInfo()
+1
View File
@@ -274,6 +274,7 @@ class CommandBSBot : public Command
bi->uid = ts6_uid_retrieve();
}
ircdproto->SendClientIntroduction(bi->nick, bi->user, bi->host, bi->real, ircd->pseudoclient_mode, bi->uid);
ircdproto->SendSQLine(bi->nick, "Reserved for services");
bi->RejoinAll();
}
+1 -1
View File
@@ -65,7 +65,7 @@ class CommandNSForbid : public Command
if (curr)
{
notice_lang(Config.s_NickServ, curr, FORCENICKCHANGE_NOW);
collide(na, 0);
curr->Collide(na);
}
-7
View File
@@ -15,9 +15,6 @@
#include "module.h"
#define TO_COLLIDE 0 /* Collide the user with this nick */
#define TO_RELEASE 1 /* Release a collided nick */
class CommandNSIdentify : public Command
{
public:
@@ -94,10 +91,6 @@ class CommandNSIdentify : public Command
if (u->IsIdentified())
check_memos(u);
/* Clear any timers */
if (na->nc->HasFlag(NI_KILLPROTECT))
del_ns_timeout(na, TO_COLLIDE);
}
return MOD_CONT;
}
-8
View File
@@ -15,9 +15,6 @@
#include "module.h"
#define TO_COLLIDE 0 /* Collide the user with this nick */
#define TO_RELEASE 1 /* Release a collided nick */
class CommandNSLogout : public Command
{
public:
@@ -44,7 +41,6 @@ class CommandNSLogout : public Command
if (nick && !param.empty() && param == "REVALIDATE")
{
cancel_user(u2);
validate_user(u2);
}
@@ -57,10 +53,6 @@ class CommandNSLogout : public Command
else
notice_lang(Config.s_NickServ, u, NICK_LOGOUT_SUCCEEDED);
/* Clear any timers again */
if (na && u->Account()->HasFlag(NI_KILLPROTECT))
del_ns_timeout(na, TO_COLLIDE);
ircdproto->SendAccountLogout(u2, u2->Account());
ircdproto->SendUnregisteredNick(u2);
+2 -2
View File
@@ -49,7 +49,7 @@ class CommandNSRecover : public Command
char relstr[192];
notice_lang(Config.s_NickServ, u2, FORCENICKCHANGE_NOW);
collide(na, 0);
u2->Collide(na);
/* Convert Config.NSReleaseTimeout seconds to string format */
duration(na->nc, relstr, sizeof(relstr), Config.NSReleaseTimeout);
@@ -74,7 +74,7 @@ class CommandNSRecover : public Command
char relstr[192];
notice_lang(Config.s_NickServ, u2, FORCENICKCHANGE_NOW);
collide(na, 0);
u2->Collide(na);
/* Convert Config.NSReleaseTimeout seconds to string format */
duration(na->nc, relstr, sizeof(relstr), Config.NSReleaseTimeout);
+3 -3
View File
@@ -35,14 +35,14 @@ class CommandNSRelease : public Command
notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
else if (na->nc->HasFlag(NI_SUSPENDED))
notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, na->nick);
else if (!(na->HasFlag(NS_KILL_HELD)))
else if (!(na->HasFlag(NS_HELD)))
notice_lang(Config.s_NickServ, u, NICK_RELEASE_NOT_HELD, nick);
else if (!pass.empty())
{
int res = enc_check_password(pass, na->nc->pass);
if (res == 1)
{
release(na, 0);
na->Release();
notice_lang(Config.s_NickServ, u, NICK_RELEASED);
}
else
@@ -60,7 +60,7 @@ class CommandNSRelease : public Command
{
if (u->Account() == na->nc || (!(na->nc->HasFlag(NI_SECURE)) && is_on_access(u, na->nc)))
{
release(na, 0);
na->Release();
notice_lang(Config.s_NickServ, u, NICK_RELEASED);
}
else
+4 -3
View File
@@ -70,11 +70,12 @@ class CommandNSSuspend : public Command
if (na2->last_quit)
delete [] na2->last_quit;
na2->last_quit = sstrdup(reason);
/* removes nicktracking */
if ((u2 = finduser(na2->nick)))
{
u2->Logout();
/* force guestnick */
collide(na2, 0);
u2->Collide(na2);
}
}
}
+3
View File
@@ -43,7 +43,10 @@ void introduce_user(const std::string &user)
{
ci::string ci_bi_nick(bi->nick.c_str());
if (user.empty() || ci_bi_nick == user)
{
ircdproto->SendClientIntroduction(bi->nick, bi->user, bi->host, bi->real, ircd->pseudoclient_mode, bi->uid);
ircdproto->SendSQLine(bi->nick, "Reserved for services");
}
}
}
}
+1 -1
View File
@@ -300,7 +300,7 @@ int m_whois(const char *source, const char *who)
ircdproto->SendNumeric(Config.ServerName, 317, source, "%s %ld %ld :seconds idle, signon time", bi->nick.c_str(), time(NULL) - bi->lastmsg, start_time);
ircdproto->SendNumeric(Config.ServerName, 318, source, "%s :End of /WHOIS list.", who);
}
else if (!ircd->svshold && (na = findnick(who)) && na->HasFlag(NS_KILL_HELD))
else if (!ircd->svshold && (na = findnick(who)) && na->HasFlag(NS_HELD))
{
/* We have a nick enforcer client here that we need to respond to.
* We can't just say it doesn't exist here, even tho it does for
+46 -4
View File
@@ -80,10 +80,6 @@ NickAlias::~NickAlias()
{
User *u = NULL;
/* First thing to do: remove any timeout belonging to the nick we're deleting */
NickServCollide::ClearTimers(this);
NickServRelease::ClearTimers(this, true);
FOREACH_MOD(I_OnDelNick, OnDelNick(this));
/* Second thing to do: look for an user using the alias
@@ -130,3 +126,49 @@ NickAlias::~NickAlias()
delete [] this->last_quit;
}
/** Release a nick from being held. This can be called from the core (ns_release)
* or from a timer used when forcing clients off of nicks. Note that if this is called
* from a timer, ircd->svshold is NEVER true
*/
void NickAlias::Release()
{
if (this->HasFlag(NS_HELD))
{
if (ircd->svshold)
{
ircdproto->SendSVSHoldDel(this->nick);
}
else
{
ircdproto->SendQuit(this->nick, NULL);
}
this->UnsetFlag(NS_HELD);
}
}
/** Called when a user gets off this nick
* See the comment in users.cpp for User::Collide()
* @param u The user
*/
void NickAlias::OnCancel(User *)
{
if (this->HasFlag(NS_COLLIDED))
{
this->SetFlag(NS_HELD);
this->UnsetFlag(NS_COLLIDED);
if (ircd->svshold)
{
ircdproto->SendSVSHold(this->nick);
}
else
{
std::string uid = (ircd->ts6 ? ts6_uid_retrieve() : "");
ircdproto->SendClientIntroduction(this->nick, Config.NSEnforcerUser, Config.NSEnforcerHost, "Services Enforcer", "+", uid);
new NickServRelease(this->nick, uid, Config.NSReleaseTimeout);
}
}
}
+24 -191
View File
@@ -24,106 +24,60 @@ NickAlias *nalists[1024];
NickCore *nclists[1024];
NickRequest *nrlists[1024];
unsigned int guestnum; /* Current guest number */
static std::map<std::string, NickServCollide *> NickServCollides;
static std::map<std::string, NickServRelease *> NickServReleases;
#define TO_COLLIDE 0 /* Collide the user with this nick */
#define TO_RELEASE 1 /* Release a collided nick */
/*************************************************************************/
static std::map<NickAlias *, NickServCollide *> NickServCollides;
static std::map<NickAlias *, NickServRelease *> NickServReleases;
NickServCollide::NickServCollide(NickAlias *nickalias, time_t delay) : Timer(delay), na(nickalias)
NickServCollide::NickServCollide(const std::string &_nick, time_t delay) : Timer(delay), nick(_nick)
{
/* Erase the current collide and use the new one */
std::map<NickAlias *, NickServCollide *>::iterator nit = NickServCollides.find(nickalias);
std::map<std::string, NickServCollide *>::iterator nit = NickServCollides.find(nick);
if (nit != NickServCollides.end())
{
delete nit->second;
}
it = NickServCollides.insert(std::make_pair(nickalias, this));
NickServCollides.insert(std::make_pair(nick, this));
}
NickServCollide::~NickServCollide()
{
if (it.second)
{
NickServCollides.erase(it.first);
}
NickServCollides.erase(nick);
}
void NickServCollide::Tick(time_t ctime)
{
/* If they identified or don't exist anymore, don't kill them. */
User *u = finduser(na->nick);
if (!u || u->Account() == na->nc || u->my_signon > this->GetSetTime())
User *u = finduser(nick);
NickAlias *na = findnick(nick);
if (!u || !na || u->Account() == na->nc || u->my_signon > this->GetSetTime())
return;
/* The RELEASE timeout will always add to the beginning of the
* list, so we won't see it. Which is fine because it can't be
* triggered yet anyway. */
collide(na, 1);
u->Collide(na);
}
void NickServCollide::ClearTimers(NickAlias *na)
{
std::map<NickAlias *, NickServCollide *>::iterator i = NickServCollides.find(na);
if (i != NickServCollides.end())
{
delete i->second;
}
}
NickServRelease::NickServRelease(NickAlias *nickalias, time_t delay) : Timer(delay), na(nickalias)
NickServRelease::NickServRelease(const std::string &_nick, const std::string &_uid, time_t delay) : Timer(delay), nick(_nick), uid(_uid)
{
/* Erase the current release timer and use the new one */
std::map<NickAlias *, NickServRelease *>::iterator nit = NickServReleases.find(nickalias);
std::map<std::string, NickServRelease *>::iterator nit = NickServReleases.find(nick);
if (nit != NickServReleases.end())
{
delete nit->second;
}
it = NickServReleases.insert(std::make_pair(nickalias, this));
NickServReleases.insert(std::make_pair(nick, this));
}
NickServRelease::~NickServRelease()
{
if (it.second)
{
NickServReleases.erase(it.first);
}
NickServReleases.erase(nick);
}
void NickServRelease::Tick(time_t ctime)
{
if (ircd->svshold)
{
ircdproto->SendSVSHoldDel(na->nick);
}
else
{
if (ircd->ts6 && !uid.empty())
ircdproto->SendQuit(uid.c_str(), NULL);
else
ircdproto->SendQuit(na->nick, NULL);
}
na->UnsetFlag(NS_KILL_HELD);
}
NickAlias *na = findnick(nick);
void NickServRelease::ClearTimers(NickAlias *na, bool dorelease)
{
std::map<NickAlias *, NickServRelease *>::iterator i = NickServReleases.find(na);
if (i != NickServReleases.end())
{
if (dorelease)
release(na, 1);
delete i->second;
}
if (na)
na->Release();
}
/*************************************************************************/
@@ -217,9 +171,6 @@ void get_core_stats(long *nrec, long *memuse)
void ns_init()
{
moduleAddNickServCmds();
guestnum = time(NULL);
while (guestnum > 9999999)
guestnum -= 10000000;
}
/*************************************************************************/
@@ -265,11 +216,11 @@ int validate_user(User * u)
{
NickAlias *na;
NickRequest *nr;
NickServCollide *t;
if ((nr = findrequestnick(u->nick.c_str())))
{
notice_lang(Config.s_NickServ, u, NICK_IS_PREREG);
return 0;
}
if (!(na = findnick(u->nick)))
@@ -278,14 +229,14 @@ int validate_user(User * u)
if (na->HasFlag(NS_FORBIDDEN))
{
notice_lang(Config.s_NickServ, u, NICK_MAY_NOT_BE_USED);
collide(na, 0);
u->Collide(na);
return 0;
}
if (na->nc->HasFlag(NI_SUSPENDED))
{
notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, u->nick.c_str());
collide(na, 0);
u->Collide(na);
return 0;
}
@@ -315,17 +266,17 @@ int validate_user(User * u)
if (na->nc->HasFlag(NI_KILL_IMMED))
{
notice_lang(Config.s_NickServ, u, FORCENICKCHANGE_NOW);
collide(na, 0);
u->Collide(na);
}
else if (na->nc->HasFlag(NI_KILL_QUICK))
{
notice_lang(Config.s_NickServ, u, FORCENICKCHANGE_IN_20_SECONDS);
t = new NickServCollide(na, 20);
new NickServCollide(na->nick, 20);
}
else
{
notice_lang(Config.s_NickServ, u, FORCENICKCHANGE_IN_1_MINUTE);
t = new NickServCollide(na, 60);
new NickServCollide(na->nick, 60);
}
}
@@ -334,44 +285,6 @@ int validate_user(User * u)
/*************************************************************************/
/* Cancel validation flags for a nick (i.e. when the user with that nick
* signs off or changes nicks). Also cancels any impending collide. */
void cancel_user(User * u)
{
NickAlias *na = findnick(u->nick);
NickServRelease *t;
std::string uid;
if (na)
{
if (na->HasFlag(NS_GUESTED))
{
if (ircd->svshold)
{
ircdproto->SendSVSHold(na->nick);
}
else if (ircd->svsnick)
{
uid = ts6_uid_retrieve();
ircdproto->SendClientIntroduction(u->nick, Config.NSEnforcerUser, Config.NSEnforcerHost, "Services Enforcer", "+", uid);
t = new NickServRelease(na, Config.NSReleaseTimeout);
t->uid = uid;
}
else
{
ircdproto->SendSVSKill(findbot(Config.s_NickServ), u, "Please do not use a registered nickname without identifying");
}
na->SetFlag(NS_KILL_HELD);
na->UnsetFlag(NS_GUESTED);
}
NickServCollide::ClearTimers(na);
}
}
/*************************************************************************/
/* Remove all nicks which have expired. Also update last-seen time for all
* nicks.
*/
@@ -699,86 +612,6 @@ void change_core_display(NickCore * nc)
}
/*************************************************************************/
/* Collide a nick.
*
* When connected to a network using DALnet servers, version 4.4.15 and above,
* Services is now able to force a nick change instead of killing the user.
* The new nick takes the form "Guest######". If a nick change is forced, we
* do not introduce the enforcer nick until the user's nick actually changes.
* This is watched for and done in cancel_user(). -TheShadow
*/
void collide(NickAlias * na, int from_timeout)
{
if (!from_timeout)
NickServCollide::ClearTimers(na);
/* Old system was unsure since there can be more than one collide
* per second. So let use another safer method.
* --lara
*/
/* So you should check the length of Config.NSGUestNickPrefix, eh Lara?
* --Certus
*/
if (ircd->svsnick)
{
User *u = finduser(na->nick);
if (!u)
return;
std::string guestnick;
/* We need to make sure the guestnick is free -- heinz */
do
{
char randbuf[17];
snprintf(randbuf, sizeof(randbuf), "%d", getrandom16());
guestnick = Config.NSGuestNickPrefix;
guestnick += randbuf;
}
while (finduser(guestnick.c_str()));
notice_lang(Config.s_NickServ, finduser(na->nick), FORCENICKCHANGE_CHANGING, guestnick.c_str());
ircdproto->SendForceNickChange(u, guestnick.c_str(), time(NULL));
na->SetFlag(NS_GUESTED);
}
else
{
kill_user(Config.s_NickServ, na->nick, "Services nickname-enforcer kill");
}
}
/*************************************************************************/
/* Release hold on a nick. */
void release(NickAlias * na, int from_timeout)
{
if (!from_timeout)
NickServRelease::ClearTimers(na);
if (ircd->svshold)
{
ircdproto->SendSVSHoldDel(na->nick);
}
else
{
ircdproto->SendQuit(na->nick, NULL);
}
na->UnsetFlag(NS_KILL_HELD);
}
/*************************************************************************/
void del_ns_timeout(NickAlias * na, int type)
{
if (type == TO_COLLIDE)
NickServCollide::ClearTimers(na);
else if (type == TO_RELEASE)
NickServRelease::ClearTimers(na);
}
/*************************************************************************/
/*********************** NickServ command routines ***********************/
/*************************************************************************/
-1
View File
@@ -261,7 +261,6 @@ class BahamutIRCdProto : public IRCDProto
{
EnforceQlinedNick(nick, Config.s_BotServ);
send_cmd(NULL, "NICK %s 1 %ld %s %s %s %s 0 0 :%s", nick.c_str(), static_cast<long>(time(NULL)), modes, user.c_str(), host.c_str(), Config.ServerName, real.c_str());
SendSQLine(nick, "Reserved for services");
}
/* SVSMODE +d */
-1
View File
@@ -209,7 +209,6 @@ class RatboxProto : public IRCDTS6Proto
{
EnforceQlinedNick(nick, NULL);
send_cmd(TS6SID, "UID %s 1 %ld %s %s %s 0 %s :%s", nick.c_str(), static_cast<long>(time(NULL)), modes, user.c_str(), host.c_str(), uid.c_str(), real.c_str());
SendSQLine(nick, "Reserved for services");
}
void SendPartInternal(BotInfo *bi, const char *chan, const char *buf)
+1 -2
View File
@@ -197,9 +197,8 @@ class UnrealIRCdProto : public IRCDProto
void SendClientIntroduction(const std::string &nick, const std::string &user, const std::string &host, const std::string &real, const char *modes, const std::string &uid)
{
EnforceQlinedNick(nick, Config.s_BotServ);
EnforceQlinedNick(nick, Config.ServerName);
send_cmd(NULL, "& %s 1 %ld %s %s %s 0 %s %s%s :%s", nick.c_str(), static_cast<long>(time(NULL)), user.c_str(), host.c_str(), Config.ServerName, modes, host.c_str(), myIrcd->nickip ? " *" : " ", real.c_str());
SendSQLine(nick, "Reserved for services");
}
void SendKickInternal(BotInfo *source, Channel *chan, User *user, const char *buf)
+106 -27
View File
@@ -14,6 +14,7 @@
#include "services.h"
#include "modules.h"
#include "language.h"
#define HASH(nick) (((nick)[0]&31)<<5 | ((nick)[1]&31))
User *userlist[1024];
@@ -223,13 +224,11 @@ void User::SetRealname(const std::string &srealname)
User::~User()
{
char *srealname;
Alog(LOG_DEBUG_2) << "User::~User() called";
if (Config.LogUsers)
{
srealname = normalizeBuffer(this->realname);
const char *srealname = normalizeBuffer(this->realname);
Alog() << "LOGUSERS: " << this->GetMask() << (ircd->vhost ? " => " : " ")
<< (ircd->vhost ? this->GetDisplayedHost() : "")
@@ -244,6 +243,22 @@ User::~User()
if (is_oper(this))
opcnt--;
while (!this->chans.empty())
{
this->chans.front()->chan->DeleteUser(this);
}
if (this->prev)
this->prev->next = this->next;
else
userlist[HASH(this->nick)] = this->next;
if (this->next)
this->next->prev = this->prev;
NickAlias *na = findnick(this->nick);
if (na)
na->OnCancel(this);
Alog(LOG_DEBUG_2) << "User::~User(): free user data";
@@ -257,26 +272,6 @@ User::~User()
if (this->hostip)
delete [] this->hostip;
Alog(LOG_DEBUG_2) << "User::~User(): remove from channels";
while (!this->chans.empty())
{
this->chans.front()->chan->DeleteUser(this);
}
/* Cancel pending nickname enforcers, etc */
cancel_user(this);
Alog(LOG_DEBUG_2) << "User::~User(): delete from list";
if (this->prev)
this->prev->next = this->next;
else
userlist[HASH(this->nick)] = this->next;
if (this->next)
this->next->prev = this->prev;
Alog(LOG_DEBUG_2) << "User::~User() done";
}
@@ -315,6 +310,88 @@ void User::SendMessage(const std::string &source, const std::string &msg)
}
}
/** Collides a nick.
*
* First, it marks the nick (if the user is on a registered nick, we don't use it without but it could be)
* as COLLIDED, this is checked in NickAlias::OnCancel.
*
* Then it does one of two things.
*
* 1. This will force change the users nick to the guest nick. This gets processed by the IRCd and comes
* back to call do_nick. do_nick changes the nick of the use to the new one, then calls NickAlias::OnCancel
* with the users old nick's nickalias (if there is one).
*
* 2. Calls kill_user, which will either delete the user immediatly or kill them, wait for the QUIT,
* then delete the user then. Users destructor then calls NickAlias::OnCancel
*
* NickAlias::OnCancel checks for NS_COLLIDED, it then does one of two things.
*
* 1. If supported, we send a SVSHold for the user. We are done here, the IRCds expires this at the time we give it.
*
* 2. We create a new client with SendClientIntroduction(). Note that is it important that this is called either after the
* user has been removed from our internal list of user or after the users nick has been updated completely internally.
* This is beacuse SendClientIntroduction will destroy any users we think are currently on the nickname (which causes a
* lot of problems, eg, deleting the user which recalls OnCancel), whether they really are or not. We then create a
* release timer for this new client that waits and later on sends a QUIT for the client. Release timers are never used
* for SVSHolds. Ever.
*
*
* Note that now for the timers we only store the users name, not the NickAlias* pointer. We never remove timers when
* a user changes nick or a nick is deleted, the timers must assume that either of these may have happend.
*
* Storing NickAlias* pointers caused quite a problem, some of which are:
*
* Having a valid timer alive that calls User::Collide would either:
*
* 1. Kill the user, causing users destructor to cancel all timers for the nick (as it should, it has no way of knowing
* if we are in a timer or not) which would delete the currently active timer while it was running, causing TimerManager
* to explode.
*
* 2. Force a user off of their nick, this would call NickAlias::Cancel before updating the user internally (to cancel the
* current nicks timers, granted we could have easially saved this and called it after) which could possibly try to
* introduce an enforcer nick. We would then check to see if the nick is already in use (it is, internally) and send
* a kill for that nick. That may in turn delete the user immediatly, calling users destructor, which would attempt to
* delete the timer, causing TimerManager to explode.
*
* Additionally, if we marked the timer as "in use" so that calling the ClearTimer function wouldn't delete them, users
* destructor would then call NickAlias::OnCancel, which would (at this point, it was unsetting GUESTED after introducing
* the new client) introduce the same new client again, without actually deleting the originial user, causing an infinite
* loop.
*
* This is why we remove NS_GUESTED first in NickAlias::OnCancel before introducing a new client, although this should
* not happen anymore. If I must emphasize this again, users need to be GONE from the internal list before calling
* NickAlias::OnCancel. NickAlias::OnCancel intentionally reffers to this->nick, not the user passed to it. They *can*
* (but not always) be different, depending if the user changed nicks or disconnected.
*
*
* Adam
*/
void User::Collide(NickAlias *na)
{
if (na)
na->SetFlag(NS_COLLIDED);
if (ircd->svsnick)
{
std::string guestnick;
do
{
char randbuf[17];
snprintf(randbuf, sizeof(randbuf), "%d", getrandom16());
guestnick = std::string(Config.NSGuestNickPrefix) + std::string(randbuf);
}
while (finduser(guestnick.c_str()));
notice_lang(Config.s_NickServ, this, FORCENICKCHANGE_CHANGING, guestnick.c_str());
ircdproto->SendForceNickChange(this, guestnick.c_str(), time(NULL));
}
else
{
kill_user(Config.s_NickServ, this->nick, "Services nickname-enforcer kill");
}
}
/** Check if the user should become identified because
* their svid matches the one stored in their nickcore
* @param svid Services id
@@ -749,7 +826,6 @@ User *do_nick(const char *source, const char *nick, const char *username, const
NickAlias *old_na; /* Old nick rec */
int nc_changed = 1; /* Did nick core change? */
char *logrealname;
std::string oldnick; /* stores the old nick of the user, so we can pass it to OnUserNickChange */
if (!*source) {
char ipbuf[16];
@@ -875,16 +951,19 @@ User *do_nick(const char *source, const char *nick, const char *username, const
user->my_signon = time(NULL);
old_na = findnick(user->nick);
if (old_na) {
if (old_na)
{
if (user->IsRecognized())
old_na->last_seen = time(NULL);
cancel_user(user);
}
oldnick = user->nick;
std::string oldnick = user->nick;
user->SetNewNick(nick);
FOREACH_MOD(I_OnUserNickChange, OnUserNickChange(user, oldnick.c_str()));
if (old_na)
old_na->OnCancel(user);
if ((old_na ? old_na->nc : NULL) == user->Account())
nc_changed = 0;