From 352e24cba9aadc0a2ed66e88e1439a1c2a20f2f5 Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Wed, 2 Jun 2021 04:05:55 +0100 Subject: Fix renicking duplicate users when the casemapping changes. --- src/modules/m_codepage.cpp | 47 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) (limited to 'src/modules/m_codepage.cpp') diff --git a/src/modules/m_codepage.cpp b/src/modules/m_codepage.cpp index 958bd07f7..40d63de37 100644 --- a/src/modules/m_codepage.cpp +++ b/src/modules/m_codepage.cpp @@ -79,17 +79,44 @@ class ModuleCodepage hashmap.swap(newhash); } + void ChangeNick(User* user, const std::string& message) + { + user->WriteNumeric(RPL_SAVENICK, user->uuid, message); + user->ChangeNick(user->uuid); + } + void CheckDuplicateNick() { - insp::flat_set duplicates; - const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers(); - for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); ++iter) + user_hash duplicates; + const user_hash& users = ServerInstance->Users->GetUsers(); + for (user_hash::const_iterator iter = users.begin(); iter != users.end(); ++iter) { - LocalUser* user = *iter; - if (user->nick != user->uuid && !duplicates.insert(user->nick).second) + User* user = iter->second; + if (user->nick == user->uuid) + continue; // UUID users are always unique. + + std::pair check = duplicates.insert(std::make_pair(user->nick, user)); + if (check.second) + continue; // No duplicate. + + User* otheruser = check.first->second; + if (otheruser->age < user->age) { - user->WriteNumeric(RPL_SAVENICK, user->uuid, "Your nickname is no longer available."); - user->ChangeNick(user->uuid); + // The other user connected first. + ChangeNick(user, "Your nickname is no longer available."); + } + else if (otheruser->age > user->age) + { + // The other user connected last. + ChangeNick(otheruser, "Your nickname is no longer available."); + check.first->second = user; + } + else + { + // Both connected at the same time. + ChangeNick(user, "Your nickname is no longer available."); + ChangeNick(otheruser, "Your nickname is no longer available."); + duplicates.erase(check.first); } } } @@ -101,10 +128,7 @@ class ModuleCodepage { LocalUser* user = *iter; if (user->nick != user->uuid && !ServerInstance->IsNick(user->nick)) - { - user->WriteNumeric(RPL_SAVENICK, user->uuid, "Your nickname is no longer valid."); - user->ChangeNick(user->uuid); - } + ChangeNick(user, "Your nickname is no longer valid."); } } @@ -215,6 +239,7 @@ class ModuleCodepage ServerInstance->Config->CaseMapping = name; national_case_insensitive_map = casemap; + CheckDuplicateNick(); CheckRehash(newcasemap); ServerInstance->ISupport.Build(); -- cgit v1.3.1-10-gc9f91 From 26a1c1955f926b54acdafeaa7b8132e53e90bdeb Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Fri, 4 Nov 2022 12:24:12 +0000 Subject: Fix destroying duplicate channels when the casemapping changes. --- src/modules/m_codepage.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'src/modules/m_codepage.cpp') diff --git a/src/modules/m_codepage.cpp b/src/modules/m_codepage.cpp index 40d63de37..bcaa7e78c 100644 --- a/src/modules/m_codepage.cpp +++ b/src/modules/m_codepage.cpp @@ -79,12 +79,63 @@ class ModuleCodepage hashmap.swap(newhash); } + void DestroyChannel(Channel* chan) + { + // Remove all of the users from the channel. Using KICK here will mean + // the user's client will probably attempt to rejoin and will enter the + // succeeding channel. Unfortunately this is the best we can do for now. + while (!chan->userlist.empty()) + chan->KickUser(ServerInstance->FakeClient, chan->userlist.begin(), "This channel does not exist anymore."); + + // Remove all modes from the channel just in case one of them keeps the channel open. + Modes::ChangeList changelist; + const ModeParser::ModeHandlerMap& chanmodes = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL); + for (ModeParser::ModeHandlerMap::const_iterator i = chanmodes.begin(); i != chanmodes.end(); ++i) + i->second->RemoveMode(chan, changelist); + ServerInstance->Modes->Process(ServerInstance->FakeClient, chan, NULL, changelist, ModeParser::MODE_LOCALONLY); + + // The channel will be destroyed automatically by CheckDestroy. + } + void ChangeNick(User* user, const std::string& message) { user->WriteNumeric(RPL_SAVENICK, user->uuid, message); user->ChangeNick(user->uuid); } + void CheckDuplicateChan() + { + chan_hash duplicates; + const chan_hash& chans = ServerInstance->GetChans(); + for (chan_hash::const_iterator iter = chans.begin(); iter != chans.end(); ++iter) + { + Channel* chan = iter->second; + std::pair check = duplicates.insert(std::make_pair(chan->name, chan)); + if (check.second) + continue; // No duplicate. + + Channel* otherchan = check.first->second; + if (otherchan->age < chan->age) + { + // The other channel was created first. + DestroyChannel(chan); + } + else if (otherchan->age > chan->age) + { + // The other channel was created last. + DestroyChannel(otherchan); + check.first->second = chan; + } + else + { + // Both created at the same time. + DestroyChannel(chan); + DestroyChannel(otherchan); + duplicates.erase(check.first); + } + } + } + void CheckDuplicateNick() { user_hash duplicates; @@ -157,6 +208,7 @@ class ModuleCodepage ServerInstance->Config->CaseMapping = origcasemapname; national_case_insensitive_map = origcasemap; + CheckDuplicateChan(); CheckDuplicateNick(); CheckRehash(casemap); @@ -239,6 +291,7 @@ class ModuleCodepage ServerInstance->Config->CaseMapping = name; national_case_insensitive_map = casemap; + CheckDuplicateChan(); CheckDuplicateNick(); CheckRehash(newcasemap); -- cgit v1.3.1-10-gc9f91