mirror of
https://github.com/znc/znc.git
synced 2026-05-06 13:32:36 +02:00
Merge pull request #957 from jpnurmi/cap-notify
Add cap-notify & away-notify support
This commit is contained in:
@@ -95,6 +95,8 @@ public:
|
||||
m_bGotNick(false),
|
||||
m_bGotUser(false),
|
||||
m_bInCap(false),
|
||||
m_bCapNotify(false),
|
||||
m_bAwayNotify(false),
|
||||
m_bNamesx(false),
|
||||
m_bUHNames(false),
|
||||
m_bAway(false),
|
||||
@@ -111,12 +113,25 @@ public:
|
||||
m_sNetwork(""),
|
||||
m_sIdentifier(""),
|
||||
m_spAuth(),
|
||||
m_ssAcceptedCaps()
|
||||
m_ssAcceptedCaps(),
|
||||
m_mCoreCaps({{"multi-prefix", {false, [this](bool bVal) { m_bNamesx = bVal; }}},
|
||||
{"userhost-in-names", {false, [this](bool bVal) { m_bUHNames = bVal; }}},
|
||||
{"echo-message", {false, [this](bool bVal) { m_bEchoMessage = bVal; }}},
|
||||
{"server-time", {false, [this](bool bVal) { m_bServerTime = bVal; }}},
|
||||
{"batch", {false, [this](bool bVal) { m_bBatch = bVal; }}},
|
||||
{"cap-notify", {false, [this](bool bVal) { m_bCapNotify = bVal; }}},
|
||||
{"away-notify", {true, [this](bool bVal) { m_bAwayNotify = bVal; }}},
|
||||
})
|
||||
{
|
||||
EnableReadLine();
|
||||
// RFC says a line can have 512 chars max, but we are
|
||||
// a little more gentle ;)
|
||||
SetMaxBufferThreshold(1024);
|
||||
|
||||
// For compatibility with older clients
|
||||
m_mCoreCaps["znc.in/server-time-iso"] = m_mCoreCaps["server-time"];
|
||||
m_mCoreCaps["znc.in/batch"] = m_mCoreCaps["batch"];
|
||||
m_mCoreCaps["znc.in/self-message"] = {false, [this](bool bVal) { m_bSelfMessage = bVal; }};
|
||||
}
|
||||
|
||||
virtual ~CClient();
|
||||
@@ -131,6 +146,8 @@ public:
|
||||
CString GetNick(bool bAllowIRCNick = true) const;
|
||||
CString GetNickMask() const;
|
||||
CString GetIdentifier() const { return m_sIdentifier; }
|
||||
bool HasCapNotify() const { return m_bCapNotify; }
|
||||
bool HasAwayNotify() const { return m_bAwayNotify; }
|
||||
bool HasNamesx() const { return m_bNamesx; }
|
||||
bool HasUHNames() const { return m_bUHNames; }
|
||||
bool IsAway() const { return m_bAway; }
|
||||
@@ -160,6 +177,9 @@ public:
|
||||
|
||||
bool IsCapEnabled(const CString& sCap) const { return 1 == m_ssAcceptedCaps.count(sCap); }
|
||||
|
||||
void NotifyServerDependentCaps(const SCString& ssCaps);
|
||||
void ClearServerDependentCaps();
|
||||
|
||||
void ReadLine(const CString& sData) override;
|
||||
bool SendMotd();
|
||||
void HelpUser(const CString& sFilter = "");
|
||||
@@ -191,6 +211,8 @@ protected:
|
||||
bool m_bGotNick;
|
||||
bool m_bGotUser;
|
||||
bool m_bInCap;
|
||||
bool m_bCapNotify;
|
||||
bool m_bAwayNotify;
|
||||
bool m_bNamesx;
|
||||
bool m_bUHNames;
|
||||
bool m_bAway;
|
||||
@@ -208,6 +230,13 @@ protected:
|
||||
CString m_sIdentifier;
|
||||
std::shared_ptr<CAuthBase> m_spAuth;
|
||||
SCString m_ssAcceptedCaps;
|
||||
// The capabilities supported by the ZNC core - capability names mapped
|
||||
// to a pair which contains a bool describing whether the capability is
|
||||
// server-dependent, and a capability value change handler.
|
||||
std::map<CString, std::pair<bool, std::function<void(bool bVal)>>> m_mCoreCaps;
|
||||
// A subset of CIRCSock::GetAcceptedCaps(), the caps that can be listed
|
||||
// in CAP LS and may be notified to the client with CAP NEW (cap-notify).
|
||||
SCString m_ssServerDependentCaps;
|
||||
|
||||
friend class ClientTest;
|
||||
};
|
||||
|
||||
@@ -101,9 +101,11 @@ public:
|
||||
CIRCNetwork* GetNetwork() const { return m_pNetwork; }
|
||||
bool HasNamesx() const { return m_bNamesx; }
|
||||
bool HasUHNames() const { return m_bUHNames; }
|
||||
bool HasAwayNotify() const { return m_bAwayNotify; }
|
||||
const std::set<unsigned char>& GetUserModes() const { return m_scUserModes; }
|
||||
// This is true if we are past raw 001
|
||||
bool IsAuthed() const { return m_bAuthed; }
|
||||
const SCString& GetAcceptedCaps() const { return m_ssAcceptedCaps; }
|
||||
bool IsCapAccepted(const CString& sCap) { return 1 == m_ssAcceptedCaps.count(sCap); }
|
||||
const MCString& GetISupport() const { return m_mISupport; }
|
||||
CString GetISupport(const CString& sKey, const CString& sDefault = "") const;
|
||||
@@ -126,6 +128,7 @@ protected:
|
||||
bool m_bAuthed;
|
||||
bool m_bNamesx;
|
||||
bool m_bUHNames;
|
||||
bool m_bAwayNotify;
|
||||
CString m_sPerms;
|
||||
CString m_sPermModes;
|
||||
std::set<unsigned char> m_scUserModes;
|
||||
|
||||
@@ -874,27 +874,20 @@ void CClient::HandleCap(const CString& sLine)
|
||||
//TODO support ~ and = modifiers
|
||||
CString sSubCmd = sLine.Token(1);
|
||||
|
||||
std::map<CString, std::function<void(bool bVal)>> mCoreCaps = {
|
||||
{"multi-prefix", [this](bool bVal) { m_bNamesx = bVal; }},
|
||||
{"userhost-in-names", [this](bool bVal) { m_bUHNames = bVal; }},
|
||||
{"echo-message", [this](bool bVal) { m_bEchoMessage = bVal; }},
|
||||
{"server-time", [this](bool bVal) { m_bServerTime = bVal; }},
|
||||
{"batch", [this](bool bVal) { m_bBatch = bVal; }},
|
||||
};
|
||||
// For compatibility with older clients
|
||||
mCoreCaps["znc.in/server-time-iso"] = mCoreCaps["server-time"];
|
||||
mCoreCaps["znc.in/batch"] = mCoreCaps["batch"];
|
||||
mCoreCaps["znc.in/self-message"] = [this](bool bVal) { m_bSelfMessage = bVal; };
|
||||
|
||||
if (sSubCmd.Equals("LS")) {
|
||||
SCString ssOfferCaps;
|
||||
for (const auto& it : mCoreCaps) {
|
||||
ssOfferCaps.insert(it.first);
|
||||
for (const auto& it : m_mCoreCaps) {
|
||||
bool bServerDependent = std::get<0>(it.second);
|
||||
if (!bServerDependent || m_ssServerDependentCaps.count(it.first) > 0)
|
||||
ssOfferCaps.insert(it.first);
|
||||
}
|
||||
GLOBALMODULECALL(OnClientCapLs(this, ssOfferCaps), NOTHING);
|
||||
CString sRes = CString(" ").Join(ssOfferCaps.begin(), ssOfferCaps.end());
|
||||
RespondCap("LS :" + sRes);
|
||||
m_bInCap = true;
|
||||
if (sLine.Token(2).ToInt() >= 302) {
|
||||
m_bCapNotify = true;
|
||||
}
|
||||
} else if (sSubCmd.Equals("END")) {
|
||||
m_bInCap = false;
|
||||
if (!IsAttached()) {
|
||||
@@ -914,7 +907,12 @@ void CClient::HandleCap(const CString& sLine)
|
||||
if (sCap.TrimPrefix("-"))
|
||||
bVal = false;
|
||||
|
||||
bool bAccepted = mCoreCaps.count(sCap) > 0;
|
||||
bool bAccepted = false;
|
||||
const auto& it = m_mCoreCaps.find(sCap);
|
||||
if (m_mCoreCaps.end() != it) {
|
||||
bool bServerDependent = std::get<0>(it->second);
|
||||
bAccepted = !bServerDependent || m_ssServerDependentCaps.count(sCap) > 0;
|
||||
}
|
||||
GLOBALMODULECALL(IsClientCapSupported(this, sCap, bVal), &bAccepted);
|
||||
|
||||
if (!bAccepted) {
|
||||
@@ -931,9 +929,10 @@ void CClient::HandleCap(const CString& sLine)
|
||||
if (sCap.TrimPrefix("-"))
|
||||
bVal = false;
|
||||
|
||||
auto handler_it = mCoreCaps.find(sCap);
|
||||
if (mCoreCaps.end() != handler_it) {
|
||||
handler_it->second(bVal);
|
||||
auto handler_it = m_mCoreCaps.find(sCap);
|
||||
if (m_mCoreCaps.end() != handler_it) {
|
||||
const auto& handler = std::get<1>(handler_it->second);
|
||||
handler(bVal);
|
||||
}
|
||||
GLOBALMODULECALL(OnClientCapRequest(this, sCap, bVal), NOTHING);
|
||||
|
||||
@@ -996,3 +995,39 @@ void CClient::ParseIdentifier(const CString& sAuthLine) {
|
||||
m_sUser = sAuthLine;
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::NotifyServerDependentCaps(const SCString& ssCaps)
|
||||
{
|
||||
for (const CString& sCap : ssCaps) {
|
||||
const auto& it = m_mCoreCaps.find(sCap);
|
||||
if (m_mCoreCaps.end() != it) {
|
||||
bool bServerDependent = std::get<0>(it->second);
|
||||
if (bServerDependent) {
|
||||
m_ssServerDependentCaps.insert(sCap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HasCapNotify() && !m_ssServerDependentCaps.empty()) {
|
||||
CString sCaps = CString(" ").Join(m_ssServerDependentCaps.begin(), m_ssServerDependentCaps.end());
|
||||
PutClient(":irc.znc.in CAP " + GetNick() + " NEW :" + sCaps);
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::ClearServerDependentCaps()
|
||||
{
|
||||
if (HasCapNotify() && !m_ssServerDependentCaps.empty()) {
|
||||
CString sCaps = CString(" ").Join(m_ssServerDependentCaps.begin(), m_ssServerDependentCaps.end());
|
||||
PutClient(":irc.znc.in CAP " + GetNick() + " DEL :" + sCaps);
|
||||
|
||||
for (const CString& sCap : m_ssServerDependentCaps) {
|
||||
const auto& it = m_mCoreCaps.find(sCap);
|
||||
if (m_mCoreCaps.end() != it) {
|
||||
const auto& handler = std::get<1>(it->second);
|
||||
handler(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_ssServerDependentCaps.clear();
|
||||
}
|
||||
|
||||
@@ -567,6 +567,10 @@ void CIRCNetwork::ClientConnected(CClient *pClient) {
|
||||
|
||||
size_t uIdx, uSize;
|
||||
|
||||
if (m_pIRCSock) {
|
||||
pClient->NotifyServerDependentCaps(m_pIRCSock->GetAcceptedCaps());
|
||||
}
|
||||
|
||||
pClient->SetPlaybackActive(true);
|
||||
|
||||
if (m_RawBuffer.IsEmpty()) {
|
||||
@@ -659,6 +663,7 @@ void CIRCNetwork::ClientDisconnected(CClient *pClient) {
|
||||
if (it != m_vClients.end()) {
|
||||
m_vClients.erase(it);
|
||||
}
|
||||
pClient->ClearServerDependentCaps();
|
||||
}
|
||||
|
||||
CUser* CIRCNetwork::GetUser() const {
|
||||
@@ -1223,6 +1228,10 @@ void CIRCNetwork::SetIRCSocket(CIRCSock* pIRCSock) {
|
||||
}
|
||||
|
||||
void CIRCNetwork::IRCConnected() {
|
||||
const SCString& ssCaps = m_pIRCSock->GetAcceptedCaps();
|
||||
for (CClient* pClient : m_vClients) {
|
||||
pClient->NotifyServerDependentCaps(ssCaps);
|
||||
}
|
||||
if (m_uJoinDelay > 0) {
|
||||
m_pJoinTimer->Delay(m_uJoinDelay);
|
||||
} else {
|
||||
@@ -1231,6 +1240,9 @@ void CIRCNetwork::IRCConnected() {
|
||||
}
|
||||
|
||||
void CIRCNetwork::IRCDisconnected() {
|
||||
for (CClient* pClient : m_vClients) {
|
||||
pClient->ClearServerDependentCaps();
|
||||
}
|
||||
m_pIRCSock = nullptr;
|
||||
|
||||
SetIRCServer("");
|
||||
|
||||
@@ -60,6 +60,7 @@ CIRCSock::CIRCSock(CIRCNetwork* pNetwork)
|
||||
m_bAuthed(false),
|
||||
m_bNamesx(false),
|
||||
m_bUHNames(false),
|
||||
m_bAwayNotify(false),
|
||||
m_sPerms("*!@%+"),
|
||||
m_sPermModes("qaohv"),
|
||||
m_scUserModes(),
|
||||
@@ -809,7 +810,7 @@ void CIRCSock::ReadLine(const CString& sData) {
|
||||
sArgs.Split(" ", vsTokens, false);
|
||||
|
||||
for (const CString& sCap : vsTokens) {
|
||||
if (OnServerCapAvailable(sCap) || sCap == "multi-prefix" || sCap == "userhost-in-names") {
|
||||
if (OnServerCapAvailable(sCap) || sCap == "multi-prefix" || sCap == "userhost-in-names" || sCap == "away-notify") {
|
||||
m_ssPendingCaps.insert(sCap);
|
||||
}
|
||||
}
|
||||
@@ -820,6 +821,8 @@ void CIRCSock::ReadLine(const CString& sData) {
|
||||
m_bNamesx = true;
|
||||
} else if ("userhost-in-names" == sArgs) {
|
||||
m_bUHNames = true;
|
||||
} else if ("away-notify" == sArgs) {
|
||||
m_bAwayNotify = true;
|
||||
}
|
||||
m_ssAcceptedCaps.insert(sArgs);
|
||||
} else if (sSubCmd == "NAK") {
|
||||
@@ -836,6 +839,14 @@ void CIRCSock::ReadLine(const CString& sData) {
|
||||
} else if (sCmd.Equals("INVITE")) {
|
||||
IRCSOCKMODULECALL(OnInvite(Nick, sLine.Token(3).TrimPrefix_n(":")), &bReturn);
|
||||
if (bReturn) return;
|
||||
} else if (sCmd.Equals("AWAY")) {
|
||||
const vector<CClient*>& vClients = m_pNetwork->GetClients();
|
||||
for (CClient* pClient : vClients) {
|
||||
if (pClient->HasAwayNotify()) {
|
||||
m_pNetwork->PutUser(sLine, pClient);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user