mirror of
https://github.com/znc/znc.git
synced 2026-07-04 00:41:38 +02:00
Implement chghost capability
Interaction with extended-join doesn't yet work correctly, because ZNC doesn't keep track of everyone's real names
This commit is contained in:
@@ -119,6 +119,7 @@ class CClient : public CIRCSocket {
|
||||
bool HasExtendedJoin() const { return m_bExtendedJoin; }
|
||||
bool HasNamesx() const { return m_bNamesx; }
|
||||
bool HasUHNames() const { return m_bUHNames; }
|
||||
bool HasChgHost() const { return m_bChgHost; }
|
||||
bool IsAway() const { return m_bAway; }
|
||||
bool HasServerTime() const { return m_bServerTime; }
|
||||
bool HasBatch() const { return m_bBatch; }
|
||||
@@ -289,6 +290,7 @@ class CClient : public CIRCSocket {
|
||||
bool m_bExtendedJoin;
|
||||
bool m_bNamesx;
|
||||
bool m_bUHNames;
|
||||
bool m_bChgHost;
|
||||
bool m_bAway;
|
||||
bool m_bServerTime;
|
||||
bool m_bBatch;
|
||||
|
||||
@@ -131,6 +131,7 @@ class CIRCSock : public CIRCSocket {
|
||||
unsigned int GetMaxNickLen() const { return m_uMaxNickLen; }
|
||||
EChanModeArgs GetModeType(char cMode) const;
|
||||
char GetPermFromMode(char cMode) const;
|
||||
char GetModeFromPerm(char cPerm) const;
|
||||
const std::map<char, EChanModeArgs>& GetChanModes() const {
|
||||
return m_mceChanModes;
|
||||
}
|
||||
@@ -177,6 +178,7 @@ class CIRCSock : public CIRCSocket {
|
||||
bool OnActionMessage(CActionMessage& Message);
|
||||
bool OnAwayMessage(CMessage& Message);
|
||||
bool OnCapabilityMessage(CMessage& Message);
|
||||
bool OnChgHostMessage(CChgHostMessage& Message);
|
||||
bool OnCTCPMessage(CCTCPMessage& Message);
|
||||
bool OnErrorMessage(CMessage& Message);
|
||||
bool OnInviteMessage(CMessage& Message);
|
||||
|
||||
@@ -67,6 +67,7 @@ class CMessage {
|
||||
Action,
|
||||
Away,
|
||||
Capability,
|
||||
ChgHost,
|
||||
CTCP,
|
||||
Error,
|
||||
Invite,
|
||||
@@ -121,6 +122,7 @@ class CMessage {
|
||||
*/
|
||||
VCString GetParamsSplit(unsigned int uIdx, unsigned int uLen = -1) const;
|
||||
void SetParams(const VCString& vsParams);
|
||||
void SetParams(VCString&& vsParams);
|
||||
|
||||
/// @deprecated use GetParamsColon() instead.
|
||||
CString GetParams(unsigned int uIdx, unsigned int uLen = -1) const
|
||||
@@ -333,4 +335,13 @@ class CTopicMessage : public CTargetMessage {
|
||||
};
|
||||
REGISTER_ZNC_MESSAGE(CTopicMessage);
|
||||
|
||||
class CChgHostMessage : public CMessage {
|
||||
public:
|
||||
CString GetNewIdent() const { return GetParam(0); }
|
||||
void SetNewIdent(const CString& sIdent) { SetParam(0, sIdent); }
|
||||
CString GetNewHost() const { return GetParam(1); }
|
||||
void SetNewHost(const CString& sHost) { SetParam(1, sHost); }
|
||||
};
|
||||
REGISTER_ZNC_MESSAGE(CChgHostMessage);
|
||||
|
||||
#endif // !ZNC_MESSAGE_H
|
||||
|
||||
+4
-1
@@ -75,7 +75,8 @@ using std::vector;
|
||||
} \
|
||||
}
|
||||
|
||||
CClient::CClient() : CIRCSocket(),
|
||||
CClient::CClient()
|
||||
: CIRCSocket(),
|
||||
m_bGotPass(false),
|
||||
m_bGotNick(false),
|
||||
m_bGotUser(false),
|
||||
@@ -87,6 +88,7 @@ CClient::CClient() : CIRCSocket(),
|
||||
m_bExtendedJoin(false),
|
||||
m_bNamesx(false),
|
||||
m_bUHNames(false),
|
||||
m_bChgHost(false),
|
||||
m_bAway(false),
|
||||
m_bServerTime(false),
|
||||
m_bBatch(false),
|
||||
@@ -762,6 +764,7 @@ CClient::CoreCaps() {
|
||||
}},
|
||||
{"cap-notify",
|
||||
[](CClient* pClient, bool bVal) { pClient->m_bCapNotify = bVal; }},
|
||||
{"chghost", [](CClient* pClient, bool bVal) { pClient->m_bChgHost = bVal; }},
|
||||
};
|
||||
|
||||
// For compatibility with older clients
|
||||
|
||||
+80
-2
@@ -185,6 +185,9 @@ void CIRCSock::ReadLine(const CString& sData) {
|
||||
case CMessage::Type::Capability:
|
||||
bReturn = OnCapabilityMessage(Message);
|
||||
break;
|
||||
case CMessage::Type::ChgHost:
|
||||
bReturn = OnChgHostMessage(Message);
|
||||
break;
|
||||
case CMessage::Type::CTCP:
|
||||
bReturn = OnCTCPMessage(Message);
|
||||
break;
|
||||
@@ -380,8 +383,8 @@ bool CIRCSock::OnCapabilityMessage(CMessage& Message) {
|
||||
{"userhost-in-names", [this](bool bVal) { m_bUHNames = bVal; }},
|
||||
{"cap-notify", [](bool bVal) {}},
|
||||
{"server-time", [this](bool bVal) { m_bServerTime = bVal; }},
|
||||
{"znc.in/server-time-iso",
|
||||
[this](bool bVal) { m_bServerTime = bVal; }},
|
||||
{"znc.in/server-time-iso", [this](bool bVal) { m_bServerTime = bVal; }},
|
||||
{"chghost", [](bool) {}},
|
||||
};
|
||||
|
||||
auto RemoveCap = [&](const CString& sCap) {
|
||||
@@ -514,6 +517,69 @@ bool CIRCSock::OnCTCPMessage(CCTCPMessage& Message) {
|
||||
return (pChan && pChan->IsDetached());
|
||||
}
|
||||
|
||||
bool CIRCSock::OnChgHostMessage(CChgHostMessage& Message) {
|
||||
// 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
|
||||
//
|
||||
// TODO: make CNick store modes (v, o) instead of perm chars (+, @), that
|
||||
// would simplify this
|
||||
bool bNeedEmulate = false;
|
||||
for (CClient* pClient : m_pNetwork->GetClients()) {
|
||||
if (pClient->HasChgHost()) {
|
||||
pClient->PutClient(Message);
|
||||
} else {
|
||||
bNeedEmulate = true;
|
||||
pClient->PutClient(CMessage(Message.GetNick(), "QUIT",
|
||||
{"Changing hostname"},
|
||||
Message.GetTags()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!bNeedEmulate) return true;
|
||||
|
||||
CNick NewNick = Message.GetNick();
|
||||
NewNick.SetIdent(Message.GetNewIdent());
|
||||
NewNick.SetHost(Message.GetNewHost());
|
||||
|
||||
for (CChan* pChan : m_pNetwork->GetChans()) {
|
||||
if (CNick* pNick = pChan->FindNick(Message.GetNick().GetNick())) {
|
||||
pNick->SetIdent(Message.GetNewIdent());
|
||||
pNick->SetHost(Message.GetNewHost());
|
||||
}
|
||||
|
||||
CTargetMessage ModeMsg;
|
||||
ModeMsg.SetNick(CNick(":irc.znc.in"));
|
||||
ModeMsg.SetTags(Message.GetTags());
|
||||
ModeMsg.SetCommand("MODE");
|
||||
VCString vsModeParams = {pChan->GetName(), "+"};
|
||||
if (CNick* pNick = pChan->FindNick(NewNick.GetNick())) {
|
||||
for (char cPerm : pNick->GetPermStr()) {
|
||||
char cMode = GetModeFromPerm(cPerm);
|
||||
if (cMode) {
|
||||
vsModeParams[1].append(1, cMode);
|
||||
vsModeParams.push_back(NewNick.GetNick());
|
||||
}
|
||||
}
|
||||
}
|
||||
ModeMsg.SetParams(std::move(vsModeParams));
|
||||
|
||||
for (CClient* pClient : m_pNetwork->GetClients()) {
|
||||
if (!pClient->HasChgHost()) {
|
||||
// TODO: send account name and real name too, for
|
||||
// extended-join
|
||||
pClient->PutClient(CMessage(NewNick, "JOIN", {pChan->GetName()},
|
||||
Message.GetTags()));
|
||||
if (ModeMsg.GetParams().size() > 2) {
|
||||
pClient->PutClient(ModeMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIRCSock::OnErrorMessage(CMessage& Message) {
|
||||
// ERROR :Closing Link: nick[24.24.24.24] (Excess Flood)
|
||||
CString sError = Message.GetParam(0);
|
||||
@@ -1517,6 +1583,18 @@ char CIRCSock::GetPermFromMode(char cMode) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char CIRCSock::GetModeFromPerm(char cPerm) const {
|
||||
if (m_sPermModes.size() == m_sPerms.size()) {
|
||||
for (unsigned int a = 0; a < m_sPermModes.size(); a++) {
|
||||
if (m_sPerms[a] == cPerm) {
|
||||
return m_sPermModes[a];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CIRCSock::EChanModeArgs CIRCSock::GetModeType(char cMode) const {
|
||||
map<char, EChanModeArgs>::const_iterator it =
|
||||
m_mceChanModes.find(cMode);
|
||||
|
||||
@@ -81,6 +81,16 @@ void CMessage::SetParams(const VCString& vsParams) {
|
||||
}
|
||||
}
|
||||
|
||||
void CMessage::SetParams(VCString&& vsParams) {
|
||||
m_vsParams = std::move(vsParams);
|
||||
m_bColon = false;
|
||||
|
||||
if (m_eType == Type::Text || m_eType == Type::Notice ||
|
||||
m_eType == Type::Action || m_eType == Type::CTCP) {
|
||||
InitType();
|
||||
}
|
||||
}
|
||||
|
||||
CString CMessage::GetParam(unsigned int uIdx) const {
|
||||
if (uIdx >= m_vsParams.size()) {
|
||||
return "";
|
||||
@@ -270,6 +280,7 @@ void CMessage::InitType() {
|
||||
{"ACCOUNT", Type::Account},
|
||||
{"AWAY", Type::Away},
|
||||
{"CAP", Type::Capability},
|
||||
{"CHGHOST", Type::ChgHost},
|
||||
{"ERROR", Type::Error},
|
||||
{"INVITE", Type::Invite},
|
||||
{"JOIN", Type::Join},
|
||||
|
||||
@@ -745,5 +745,56 @@ TEST_F(ZNCTest, CapReqWithoutLs) {
|
||||
ASSERT_THAT(client.ReadRemainder().toStdString(), Not(HasSubstr("Welcome")));
|
||||
}
|
||||
|
||||
TEST_F(ZNCTest, ChgHostEmulation) {
|
||||
auto znc = Run();
|
||||
auto ircd = ConnectIRCd();
|
||||
ircd.Write("CAP user LS :chghost");
|
||||
ircd.ReadUntil("CAP REQ :chghost");
|
||||
ircd.Write("CAP user ACK :chghost");
|
||||
|
||||
auto client1 = LoginClient();
|
||||
auto client2 = LoginClient();
|
||||
client2.Write("CAP REQ :chghost");
|
||||
client2.ReadUntil("ACK");
|
||||
|
||||
ircd.Write(":user!oldident@oldhost JOIN #chan");
|
||||
|
||||
ircd.Write(":user!oldident@oldhost CHGHOST newident newhost");
|
||||
client1.ReadUntil(":user!oldident@oldhost QUIT :Changing hostname");
|
||||
client1.ReadUntil(":user!newident@newhost JOIN #chan");
|
||||
ASSERT_THAT(client1.ReadRemainder().toStdString(), Not(HasSubstr("MODE")));
|
||||
client2.ReadUntil(":user!oldident@oldhost CHGHOST newident newhost");
|
||||
client2.Close();
|
||||
|
||||
ircd.Write(":server MODE #chan +v user");
|
||||
client1.ReadUntil("MODE");
|
||||
ircd.Write(":user!newident@newhost CHGHOST ident-2 host-2");
|
||||
client1.ReadUntil(":irc.znc.in MODE #chan +v user");
|
||||
|
||||
ircd.Write(":server MODE #chan +o user");
|
||||
client1.ReadUntil("MODE");
|
||||
ircd.Write(":user!ident-2@host-2 CHGHOST ident-3 host-3");
|
||||
client1.ReadUntil(":irc.znc.in MODE #chan +ov user user");
|
||||
}
|
||||
|
||||
TEST_F(ZNCTest, ChgHostOnce) {
|
||||
auto znc = Run();
|
||||
auto ircd = ConnectIRCd();
|
||||
ircd.Write("CAP user LS :chghost");
|
||||
ircd.ReadUntil("CAP REQ :chghost");
|
||||
ircd.Write("CAP user ACK :chghost");
|
||||
|
||||
auto client = LoginClient();
|
||||
client.Write("CAP REQ :chghost");
|
||||
client.ReadUntil("ACK");
|
||||
|
||||
ircd.Write(":user!oldident@oldhost JOIN #chan");
|
||||
ircd.Write(":user!oldident@oldhost JOIN #chan2");
|
||||
ircd.Write(":user!oldident@oldhost CHGHOST newident newhost");
|
||||
client.ReadUntil("CHGHOST");
|
||||
ASSERT_THAT(client.ReadRemainder().toStdString(),
|
||||
Not(HasSubstr("CHGHOST")));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace znc_inttest
|
||||
|
||||
Reference in New Issue
Block a user