diff options
| author | Adam- <Adam-@5417fbe8-f217-4b02-8779-1006273d7864> | 2010-05-22 07:40:22 +0000 |
|---|---|---|
| committer | Adam- <Adam-@5417fbe8-f217-4b02-8779-1006273d7864> | 2010-05-22 07:40:22 +0000 |
| commit | d5f036017510dad134fd9c8d471135f774f1fe17 (patch) | |
| tree | ee037c32da9ad223cd660af9f5ed36133a517bec /src/users.c | |
| parent | fae2710ba7e068edb68baf26f901af564741e5bf (diff) | |
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
Diffstat (limited to 'src/users.c')
| -rw-r--r-- | src/users.c | 133 |
1 files changed, 106 insertions, 27 deletions
diff --git a/src/users.c b/src/users.c index c1532bc27..26e50a40d 100644 --- a/src/users.c +++ b/src/users.c @@ -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; |
