From 4ed833abbd73773a4cb0a5c825d6056edf7562f8 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 18 Feb 2011 13:19:08 +0100 Subject: [PATCH 1/6] Remove a useless lookup All the places that add entries to the CTCPReplies map use CString::Token(0) to split the first token away. This means it is impossible for this to contain spaces. Now this means that it is pointless to look up the full CTCP request in the CTCPReplies map because it can't contain any matching item. Signed-off-by: Uli Schlachter --- IRCSock.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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; From e7fc8fa907ee7841372285de57118844abf0f883 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 18 Feb 2011 13:06:28 +0100 Subject: [PATCH 2/6] CUser::AddCTCPReply(): Reject CTCP requests containing spaces CTCP requests can't contain spaces so it's useless to specify rules for those. This doesn't affect any of the existing callers because those use Token(0) for generating the first argument to this function. Signed-off-by: Uli Schlachter --- User.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/User.cpp b/User.cpp index 1c06570b..31d2b62a 100644 --- a/User.cpp +++ b/User.cpp @@ -1253,10 +1253,14 @@ 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; } From 2ccafaf516ef00e3ee39c1cd261dad30bfbdb4d2 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 18 Feb 2011 13:33:44 +0100 Subject: [PATCH 3/6] Add CTable::empty It shouldn't make any real difference (especially not for std::vector), but "empty()" is better than using "size() == 0". Signed-off-by: Uli Schlachter --- Utils.cpp | 2 +- Utils.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) 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; From c1bb18b9e80c6f05541811064c95b8c6e2ce7526 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 18 Feb 2011 13:34:47 +0100 Subject: [PATCH 4/6] Add CUser::DelCTCPReply() This function is the counterpart for CUser::AddCTCPReply(). Guess what? It removes an entry from CUser::m_mssCTCPReplies again! It's magic! Signed-off-by: Uli Schlachter --- User.cpp | 4 ++++ User.h | 1 + 2 files changed, 5 insertions(+) diff --git a/User.cpp b/User.cpp index 31d2b62a..0ab5ed76 100644 --- a/User.cpp +++ b/User.cpp @@ -1265,6 +1265,10 @@ bool CUser::AddCTCPReply(const CString& sCTCP, const CString& 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; } From 3f0e200073ed73a07c9c9c706e9bd047ebe90f85 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 18 Feb 2011 13:35:28 +0100 Subject: [PATCH 5/6] Admin: Add functions for listing/adding/removing CTCP replies Signed-off-by: Uli Schlachter --- modules/admin.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/modules/admin.cpp b/modules/admin.cpp index 5a47bede..2efe0aba 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(); @@ -674,6 +677,72 @@ class CAdminMod : public CModule { PutModule("Closed user's IRC connection."); } + void ListCTCP(const CString& sLine) { + CString sUsername = sLine.Token(1); + + 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()) { + 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); + + 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 +859,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() {} From 4faad67f8198ab8186cad951c172bb5856e3ad3a Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Fri, 18 Feb 2011 14:47:52 +0100 Subject: [PATCH 6/6] admin: Allow omitting of the user name with some commands This changes admin to assume that an "empty username" was specified if some arguments is empty which shouldn't be empty. This empty username is then interpreted as meaning the current user. Signed-off-by: Uli Schlachter --- modules/admin.cpp | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/modules/admin.cpp b/modules/admin.cpp index 2efe0aba..c8a3356d 100644 --- a/modules/admin.cpp +++ b/modules/admin.cpp @@ -614,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; @@ -632,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; @@ -658,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; @@ -678,9 +688,12 @@ class CAdminMod : public CModule { } void ListCTCP(const CString& sLine) { - 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) return; @@ -703,10 +716,15 @@ class CAdminMod : public CModule { } void AddCTCP(const CString& sLine) { - CString sUsername = sLine.Token(1); + 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."); @@ -714,7 +732,7 @@ class CAdminMod : public CModule { return; } - CUser* pUser = GetUser(sUsername); + CUser* pUser = GetUser(sUserName); if (!pUser) return; @@ -725,10 +743,14 @@ class CAdminMod : public CModule { } void DelCTCP(const CString& sLine) { - CString sUsername = sLine.Token(1); - CString sCTCPRequest = sLine.Token(2); + CString sUserName = sLine.Token(1); + CString sCTCPRequest = sLine.Token(2, true); - CUser* pUser = GetUser(sUsername); + if (sCTCPRequest.empty()) { + sCTCPRequest = sUserName; + sUserName = m_pUser->GetUserName(); + } + CUser* pUser = GetUser(sUserName); if (!pUser) return;