Merge pull request #957 from jpnurmi/cap-notify

Add cap-notify & away-notify support
This commit is contained in:
J-P Nurmi
2015-07-12 19:34:31 +02:00
5 changed files with 110 additions and 20 deletions

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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("");

View File

@@ -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;
}
}