diff --git a/src/Chan.cpp b/src/Chan.cpp index b1cd707f..23f949a8 100644 --- a/src/Chan.cpp +++ b/src/Chan.cpp @@ -149,21 +149,30 @@ void CChan::JoinUser(const CString& sKey) { } void CChan::AttachUser(CClient* pClient) { + + // Sending '/znc attach #channel' applies for all clients connected. + // Sending '/join #channel' only applies for the client that sent + // the command. This causes a de-sync with IRC events like QUIT + // displaying in the other clients server/status window. + + // Make '/join #channel' work the same as '/znc attach #channel' + CClient* pTarget = IsDetached() ? nullptr : pClient; + m_pNetwork->PutUser( ":" + m_pNetwork->GetIRCNick().GetNickMask() + " JOIN :" + GetName(), - pClient); + pTarget); if (!GetTopic().empty()) { m_pNetwork->PutUser(":" + m_pNetwork->GetIRCServer() + " 332 " + m_pNetwork->GetIRCNick().GetNick() + " " + GetName() + " :" + GetTopic(), - pClient); + pTarget); if (!GetTopicOwner().empty()) { m_pNetwork->PutUser(":" + m_pNetwork->GetIRCServer() + " 333 " + m_pNetwork->GetIRCNick().GetNick() + " " + GetName() + " " + GetTopicOwner() + " " + CString(GetTopicDate()), - pClient); + pTarget); } } @@ -176,10 +185,10 @@ void CChan::AttachUser(CClient* pClient) { const vector& vpClients = m_pNetwork->GetClients(); for (CClient* pEachClient : vpClients) { CClient* pThisClient; - if (!pClient) + if (!pTarget) pThisClient = pEachClient; else - pThisClient = pClient; + pThisClient = pTarget; for (map::iterator a = m_msNicks.begin(); a != m_msNicks.end(); ++a) { @@ -210,18 +219,18 @@ void CChan::AttachUser(CClient* pClient) { } } - if (pClient) // We only want to do this for one client + if (pTarget) break; } m_pNetwork->PutUser(":" + m_pNetwork->GetIRCServer() + " 366 " + m_pNetwork->GetIRCNick().GetNick() + " " + GetName() + " :End of /NAMES list.", - pClient); + pTarget); m_bDetached = false; // Send Buffer - SendBuffer(pClient); + SendBuffer(pTarget); } void CChan::DetachUser() { diff --git a/test/integration/tests/core.cpp b/test/integration/tests/core.cpp index d020ca3c..6038126a 100644 --- a/test/integration/tests/core.cpp +++ b/test/integration/tests/core.cpp @@ -1278,5 +1278,43 @@ TEST_F(ZNCTest, CAPDetached) { << "Client saw chghost even though all channels are detached"; } +// Test for CChan::AttachUser() +TEST_F(ZNCTest, JoinDetachedChannelMultiClient) { + auto znc = Run(); + auto ircd = ConnectIRCd(); + + auto client1 = LoginClient(); + ircd.Write(":server 001 nick :Hello"); + client1.Write("JOIN #test"); + ircd.ReadUntil("JOIN #test"); + ircd.Write(":nick JOIN :#test"); + ircd.Write(":server 353 nick #test :nick"); + ircd.Write(":server 366 nick #test :End of /NAMES list"); + client1.ReadUntil("End of /NAMES"); + + client1.Write("DETACH #test"); + client1.ReadUntil("Detached 1 channel"); + + auto client2 = LoginClient(); + client2.ReadUntil("001"); + + // Both client1 and client2 should be in #test + client2.Write("JOIN #test"); + + client1.ReadUntil(":nick JOIN :#test"); + client2.ReadUntil(":nick JOIN :#test"); + + client1.ReadUntil("353"); + client2.ReadUntil("353"); + + ircd.Write(":other!user@host JOIN :#test"); + client1.ReadUntil(":other!user@host JOIN :#test"); + client2.ReadUntil(":other!user@host JOIN :#test"); + + ircd.Write(":other!user@host QUIT :quit message"); + client1.ReadUntil(":other!user@host QUIT :quit message"); + client2.ReadUntil(":other!user@host QUIT :quit message"); +} + } // namespace } // namespace znc_inttest