Merge pull request #1961 from RealKindOne/fix-chghost/away-notify

Make account-notify, away-notify and chghost only send to client if the nick is in one of channels attached to a client

Fix #1826
This commit is contained in:
Alexey Sokolov
2025-07-07 08:33:31 +01:00
committed by GitHub
3 changed files with 84 additions and 0 deletions
+2
View File
@@ -171,6 +171,8 @@ class CIRCSock : public CIRCSocket {
// TODO move this function to CIRCNetwork and make it non-static?
static bool IsFloodProtected(double fRate);
bool IsNickVisibleInAttachedChannels(const CString& sNick) const;
private:
// Message Handlers
bool OnAccountMessage(CMessage& Message);
+25
View File
@@ -322,8 +322,25 @@ static void FixupChanNick(CNick& Nick, CChan* pChan) {
}
}
// #1826: CAP away-notify clients shouldn't receive notifications if all shared
// channels are detached
// This applies to account, away-notify, and chghost.
bool CIRCSock::IsNickVisibleInAttachedChannels(const CString& sNick) const {
const vector<CChan*>& vChans = m_pNetwork->GetChans();
for (const CChan* pChan : vChans) {
if (!pChan->IsDetached() && pChan->FindNick(sNick)) {
return true;
}
}
return false;
}
bool CIRCSock::OnAccountMessage(CMessage& Message) {
// TODO: IRCSOCKMODULECALL(OnAccountMessage(Message)) ?
// Do not send ACCOUNT if all shared channels are detached.
if (!IsNickVisibleInAttachedChannels(Message.GetNick().GetNick())) {
return true;
}
return false;
}
@@ -377,6 +394,10 @@ bool CIRCSock::OnActionMessage(CActionMessage& Message) {
bool CIRCSock::OnAwayMessage(CMessage& Message) {
// TODO: IRCSOCKMODULECALL(OnAwayMessage(Message)) ?
// Do not send away-notify if all shared channels are detached.
if (!IsNickVisibleInAttachedChannels(Message.GetNick().GetNick())) {
return true;
}
return false;
}
@@ -570,6 +591,10 @@ bool CIRCSock::OnCTCPMessage(CCTCPMessage& Message) {
}
bool CIRCSock::OnChgHostMessage(CChgHostMessage& Message) {
// Do not send chghost if all shared channels are detached.
if (!IsNickVisibleInAttachedChannels(Message.GetNick().GetNick())) {
return true;
}
// The emulation of QUIT+JOIN would be cleaner inside CClient::PutClient()
// but computation of new modes is difficult enough so that I don't want to
// repeat it for every client
+57
View File
@@ -292,6 +292,10 @@ TEST_F(ZNCTest, AwayNotify) {
client.ReadUntil("CAP user NEW :away-notify");
client.Write("CAP REQ :away-notify");
client.ReadUntil("ACK :away-notify");
// Fix for #1826 breaks this test. Join channel so this test does not fail.
client.Write(":nick JOIN #test");
ircd.ReadUntil("JOIN #test");
ircd.Write(":x!y@z JOIN #test");
ircd.Write(":x!y@z AWAY :reason");
client.ReadUntil(":x!y@z AWAY :reason");
ircd.Close();
@@ -1221,5 +1225,58 @@ TEST_F(ZNCTest, DisconnectedTagmsgCrash) {
client.ReadUntil("AddServer");
}
// https://github.com/znc/znc/issues/1826
TEST_F(ZNCTest, CAPDetached) {
auto znc = Run();
auto ircd = ConnectIRCd();
ircd.Write("CAP user LS :away-notify");
ircd.ReadUntil("CAP REQ :away-notify");
ircd.Write("CAP user ACK :away-notify");
ircd.Write("CAP user LS :chghost");
ircd.ReadUntil("CAP REQ :chghost");
ircd.Write("CAP user ACK :chghost");
ircd.Write("CAP user LS :account-notify");
ircd.ReadUntil("CAP REQ :account-notify");
ircd.Write("CAP user ACK :account-notify");
auto client = LoginClient();
client.Write("CAP LS");
client.Write("CAP REQ :cap-notify");
client.ReadUntil("ACK :cap-notify");
client.Write("CAP REQ :away-notify");
client.ReadUntil("ACK :away-notify");
client.Write("CAP REQ :chghost");
client.ReadUntil("ACK :chghost");
client.Write("CAP REQ :account-notify");
client.ReadUntil("ACK :account-notify");
client.Write("CAP END");
client.Write(":nick!ident@host JOIN #test");
ircd.Write(":test!test@test JOIN #test");
client.ReadUntil(":test!test@test JOIN #test");
ircd.Write("353 nick = #test :nick!ident@host test!test@test");
ircd.Write("366 nick #test :End of /NAMES list.");
client.ReadUntil("#test :End of /NAMES");
client.Write("znc detach #test");
client.ReadUntil(":*status!status@znc.in PRIVMSG nick :Detached 1 channel");
ircd.Write(":test!test@test ACCOUNT test");
EXPECT_THAT(client.ReadRemainder().toStdString(), Not(HasSubstr(":test!test@test ACCOUNT test")))
<< "Client saw account-notify even though all channels are detached";
ircd.Write(":test!test@test AWAY :going away");
EXPECT_THAT(client.ReadRemainder().toStdString(), Not(HasSubstr(":test!test@test AWAY :going away")))
<< "Client saw away-notify even though all channels are detached";
ircd.Write(":test!test@test CHGHOST test detached.test");
EXPECT_THAT(client.ReadRemainder().toStdString(), Not(HasSubstr(":test!test@test CHGHOST test detached.test")))
<< "Client saw chghost even though all channels are detached";
}
} // namespace
} // namespace znc_inttest