diff --git a/IRCSock.cpp b/IRCSock.cpp index 871e5071..0e0237fe 100644 --- a/IRCSock.cpp +++ b/IRCSock.cpp @@ -822,15 +822,11 @@ bool CIRCSock::OnPrivCTCP(CNick& Nick, CString& sMessage) { bool CIRCSock::OnGeneralCTCP(CNick& Nick, CString& sMessage) { const MCString& mssCTCPReplies = m_pUser->GetCTCPReplies(); - MCString::const_iterator it = mssCTCPReplies.find(sMessage.AsUpper()); CString sQuery = sMessage.Token(0).AsUpper(); + MCString::const_iterator it = mssCTCPReplies.find(sQuery); bool bHaveReply = false; CString sReply; - if (it == mssCTCPReplies.end()) { - it = mssCTCPReplies.find(sQuery); - } - if (it != mssCTCPReplies.end()) { sReply = m_pUser->ExpandString(it->second); bHaveReply = true; diff --git a/User.cpp b/User.cpp index 1c06570b..0ab5ed76 100644 --- a/User.cpp +++ b/User.cpp @@ -1253,14 +1253,22 @@ void CUser::SetIRCNick(const CNick& n) { } bool CUser::AddCTCPReply(const CString& sCTCP, const CString& sReply) { + // Reject CTCP requests containing spaces + if (sCTCP.find_first_of(' ') != CString::npos) { + return false; + } + // Reject empty CTCP requests if (sCTCP.empty()) { return false; } - m_mssCTCPReplies[sCTCP.AsUpper()] = sReply; return true; } +bool CUser::DelCTCPReply(const CString& sCTCP) { + return m_mssCTCPReplies.erase(sCTCP) > 0; +} + bool CUser::SetStatusPrefix(const CString& s) { if ((!s.empty()) && (s.length() < 6) && (s.find(' ') == CString::npos)) { m_sStatusPrefix = (s.empty()) ? "*" : s; diff --git a/User.h b/User.h index f281d941..fc483d72 100644 --- a/User.h +++ b/User.h @@ -155,6 +155,7 @@ public: void SetIRCServer(const CString& s); void SetQuitMsg(const CString& s); bool AddCTCPReply(const CString& sCTCP, const CString& sReply); + bool DelCTCPReply(const CString& sCTCP); bool SetBufferCount(unsigned int u, bool bForce = false); void SetKeepBuffer(bool b); void SetChanPrefixes(const CString& s) { m_sChanPrefixes = s; } diff --git a/Utils.cpp b/Utils.cpp index 46e1ac02..627ce7de 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -390,7 +390,7 @@ bool CTable::SetCell(const CString& sColumn, const CString& sValue, unsigned int bool CTable::GetLine(unsigned int uIdx, CString& sLine) const { stringstream ssRet; - if (!size()) { + if (empty()) { return false; } diff --git a/Utils.h b/Utils.h index f1189396..1b67a0a5 100644 --- a/Utils.h +++ b/Utils.h @@ -195,6 +195,9 @@ public: /// @return The number of rows in this table, not counting the header. using vector >::size; + + /// @return True if this table doesn't contain any rows. + using vector >::empty; private: unsigned int GetColumnIndex(const CString& sName) const; diff --git a/modules/admin.cpp b/modules/admin.cpp index 5a47bede..c8a3356d 100644 --- a/modules/admin.cpp +++ b/modules/admin.cpp @@ -49,7 +49,10 @@ class CAdminMod : public CModule { {"Disconnect", "username", "Disconnects the user from their IRC server"}, {"LoadModule", "username modulename", "Loads a Module for a user"}, {"UnLoadModule", "username modulename", "Removes a Module of a user"}, - {"ListMods", "username", "Get the list of modules for a user"} + {"ListMods", "username", "Get the list of modules for a user"}, + {"ListCTCPs", "username", "List the configured CTCP replies"}, + {"AddCTCP", "username ctcp [reply]", "Configure a new CTCP reply"}, + {"DelCTCP", "username ctcp", "Remove a CTCP reply"} }; for (unsigned int i = 0; i != ARRAY_SIZE(help); ++i) { CmdTable.AddRow(); @@ -611,8 +614,12 @@ class CAdminMod : public CModule { void AddServer(const CString& sLine) { CString sUsername = sLine.Token(1); - const CString sServer = sLine.Token(2, true); + CString sServer = sLine.Token(2, true); + if (sServer.empty()) { + sServer = sUsername; + sUsername = m_pUser->GetUserName(); + } if (sServer.empty()) { PutModule("Usage: addserver "); return; @@ -629,9 +636,12 @@ class CAdminMod : public CModule { } void ReconnectUser(const CString& sLine) { - const CString sUsername = sLine.Token(1); + CString sUserName = sLine.Token(1, true); - CUser* pUser = GetUser(sUsername); + if (sUserName.empty()) { + sUserName = m_pUser->GetUserName(); + } + CUser* pUser = GetUser(sUserName); if (!pUser) { PutModule("User not found."); return; @@ -655,9 +665,12 @@ class CAdminMod : public CModule { } void DisconnectUser(const CString& sLine) { - const CString sUsername = sLine.Token(1); + CString sUserName = sLine.Token(1, true); - CUser* pUser = GetUser(sUsername); + if (sUserName.empty()) { + sUserName = m_pUser->GetUserName(); + } + CUser* pUser = GetUser(sUserName); if (!pUser) { PutModule("User not found."); return; @@ -674,6 +687,84 @@ class CAdminMod : public CModule { PutModule("Closed user's IRC connection."); } + void ListCTCP(const CString& sLine) { + CString sUserName = sLine.Token(1, true); + + if (sUserName.empty()) { + sUserName = m_pUser->GetUserName(); + } + CUser* pUser = GetUser(sUserName); + if (!pUser) + return; + + const MCString& msCTCPReplies = pUser->GetCTCPReplies(); + CTable Table; + Table.AddColumn("Request"); + Table.AddColumn("Reply"); + for (MCString::const_iterator it = msCTCPReplies.begin(); it != msCTCPReplies.end(); it++) { + Table.AddRow(); + Table.SetCell("Request", it->first); + Table.SetCell("Reply", it->second); + } + + if (Table.empty()) { + PutModule("No CTCP replies for user [" + pUser->GetUserName() + "] configured!"); + } else { + PutModule("CTCP replies for user [" + pUser->GetUserName() + "]:"); + PutModule(Table); + } + } + + void AddCTCP(const CString& sLine) { + CString sUserName = sLine.Token(1); + CString sCTCPRequest = sLine.Token(2); + CString sCTCPReply = sLine.Token(3, true); + + if (sCTCPRequest.empty()) { + sCTCPRequest = sUserName; + sCTCPReply = sLine.Token(2, true); + sUserName = m_pUser->GetUserName(); + } + if (sCTCPRequest.empty()) { + PutModule("Usage: AddCTCP [user] [request] [reply]"); + PutModule("This will cause ZNC to reply to the CTCP instead of forwarding it to clients."); + PutModule("An empty reply will cause the CTCP request to be blocked."); + return; + } + + CUser* pUser = GetUser(sUserName); + if (!pUser) + return; + + if (pUser->AddCTCPReply(sCTCPRequest, sCTCPReply)) + PutModule("Added!"); + else + PutModule("Error!"); + } + + void DelCTCP(const CString& sLine) { + CString sUserName = sLine.Token(1); + CString sCTCPRequest = sLine.Token(2, true); + + if (sCTCPRequest.empty()) { + sCTCPRequest = sUserName; + sUserName = m_pUser->GetUserName(); + } + CUser* pUser = GetUser(sUserName); + if (!pUser) + return; + + if (sCTCPRequest.empty()) { + PutModule("Usage: DelCTCP [user] [request]"); + return; + } + + if (pUser->DelCTCPReply(sCTCPRequest)) + PutModule("Successfully removed [" + sCTCPRequest + "]"); + else + PutModule("Error: [" + sCTCPRequest + "] not found!"); + } + void LoadModuleForUser(const CString& sLine) { CString sUsername = sLine.Token(1); CString sModName = sLine.Token(2); @@ -790,6 +881,9 @@ public: fnmap_["loadmodule"] = &CAdminMod::LoadModuleForUser; fnmap_["unloadmodule"] = &CAdminMod::UnLoadModuleForUser; fnmap_["listmods"] = &CAdminMod::ListModuleForUser; + fnmap_["listctcps"] = &CAdminMod::ListCTCP; + fnmap_["addctcp"] = &CAdminMod::AddCTCP; + fnmap_["delctcp"] = &CAdminMod::DelCTCP; } virtual ~CAdminMod() {}