summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/message.h18
-rw-r--r--src/coremods/core_privmsg.cpp74
-rw-r--r--src/modules/m_anticaps.cpp21
-rw-r--r--src/modules/m_blockcaps.cpp19
-rw-r--r--src/modules/m_delayjoin.cpp12
-rw-r--r--src/modules/m_noctcp.cpp5
6 files changed, 118 insertions, 31 deletions
diff --git a/include/message.h b/include/message.h
index 8c7c7592a..7a968d866 100644
--- a/include/message.h
+++ b/include/message.h
@@ -56,6 +56,24 @@ class CoreExport MessageDetails
/** The type of message. */
const MessageType type;
+ /** Determines whether the specified message is a CTCP. If the specified message
+ * is a CTCP then the CTCP name and CTCP body are extracted and stored in the
+ * name and body references.
+ * @param name The location to store the parsed CTCP name.
+ * @param body The location to store the parsed CTCP body.
+ */
+ virtual bool IsCTCP(std::string& name, std::string& body) const = 0;
+
+ /** Determines whether the specified message is a CTCP. If the specified message
+ * is a CTCP then the CTCP name is extracted and stored in the name reference.
+ * @param name The location to store the parsed CTCP name.
+ */
+ virtual bool IsCTCP(std::string& name) const = 0;
+
+ /** Determines whether the specified message is a CTCP. */
+ virtual bool IsCTCP() const = 0;
+
+ protected:
MessageDetails(MessageType mt, const std::string& msg, const ClientProtocol::TagMap& tags)
: echo(true)
, echo_original(false)
diff --git a/src/coremods/core_privmsg.cpp b/src/coremods/core_privmsg.cpp
index 2daeef3ad..65609df00 100644
--- a/src/coremods/core_privmsg.cpp
+++ b/src/coremods/core_privmsg.cpp
@@ -21,6 +21,74 @@
#include "inspircd.h"
+class MessageDetailsImpl : public MessageDetails
+{
+public:
+ MessageDetailsImpl(MessageType mt, const std::string& msg, const ClientProtocol::TagMap& tags)
+ : MessageDetails(mt, msg, tags)
+ {
+ }
+
+ bool IsCTCP(std::string& name, std::string& body) const CXX11_OVERRIDE
+ {
+ if (!this->IsCTCP())
+ return false;
+
+ size_t end_of_name = text.find(' ', 2);
+ size_t end_of_ctcp = *text.rbegin() == '\x1' ? 1 : 0;
+ if (end_of_name == std::string::npos)
+ {
+ // The CTCP only contains a name.
+ name.assign(text, 1, text.length() - 1 - end_of_ctcp);
+ body.clear();
+ return true;
+ }
+
+ // The CTCP contains a name and a body.
+ name.assign(text, 1, end_of_name - 1);
+
+ size_t start_of_body = text.find_first_not_of(' ', end_of_name + 1);
+ if (start_of_body == std::string::npos)
+ {
+ // The CTCP body is provided but empty.
+ body.clear();
+ return true;
+ }
+
+ // The CTCP body provided was non-empty.
+ body.assign(text, start_of_body, text.length() - start_of_body - end_of_ctcp);
+ return true;
+ }
+
+ bool IsCTCP(std::string& name) const CXX11_OVERRIDE
+ {
+ if (!this->IsCTCP())
+ return false;
+
+ size_t end_of_name = text.find(' ', 2);
+ if (end_of_name == std::string::npos)
+ {
+ // The CTCP only contains a name.
+ size_t end_of_ctcp = *text.rbegin() == '\x1' ? 1 : 0;
+ name.assign(text, 1, text.length() - 1 - end_of_ctcp);
+ return true;
+ }
+
+ // The CTCP contains a name and a body.
+ name.assign(text, 1, end_of_name - 1);
+ return true;
+ }
+
+ bool IsCTCP() const CXX11_OVERRIDE
+ {
+ // According to draft-oakley-irc-ctcp-02 a valid CTCP must begin with SOH and
+ // contain at least one octet which is not NUL, SOH, CR, LF, or SPACE. As most
+ // of these are restricted at the protocol level we only need to check for SOH
+ // and SPACE.
+ return (text.length() >= 2) && (text[0] == '\x1') && (text[1] != '\x1') && (text[1] != ' ');
+ }
+};
+
class MessageCommandBase : public Command
{
ChanModeReference moderatedmode;
@@ -94,7 +162,7 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const Params& parameters
std::string servername(parameters[0], 1);
MessageTarget msgtarget(&servername);
- MessageDetails msgdetails(mt, parameters[1], parameters.GetTags());
+ MessageDetailsImpl msgdetails(mt, parameters[1], parameters.GetTags());
ModResult MOD_RESULT;
FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, msgtarget, msgdetails));
@@ -153,7 +221,7 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const Params& parameters
}
MessageTarget msgtarget(chan, status);
- MessageDetails msgdetails(mt, parameters[1], parameters.GetTags());
+ MessageDetailsImpl msgdetails(mt, parameters[1], parameters.GetTags());
msgdetails.exemptions.insert(user);
ModResult MOD_RESULT;
@@ -229,7 +297,7 @@ CmdResult MessageCommandBase::HandleMessage(User* user, const Params& parameters
}
MessageTarget msgtarget(dest);
- MessageDetails msgdetails(mt, parameters[1], parameters.GetTags());
+ MessageDetailsImpl msgdetails(mt, parameters[1], parameters.GetTags());
ModResult MOD_RESULT;
diff --git a/src/modules/m_anticaps.cpp b/src/modules/m_anticaps.cpp
index 755ab8bfa..6cb9b940b 100644
--- a/src/modules/m_anticaps.cpp
+++ b/src/modules/m_anticaps.cpp
@@ -216,23 +216,18 @@ class ModuleAntiCaps : public Module
// If the user is exempt from anticaps then we don't need
// to do anything else.
ModResult result = CheckExemption::Call(exemptionprov, user, channel, "anticaps");
- if (result == MOD_RES_ALLOW)
+ if (result == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;
// If the message is a CTCP then we skip it unless it is
- // an ACTION in which case we skip the prefix and suffix.
- std::string::const_iterator text_begin = details.text.begin();
- std::string::const_iterator text_end = details.text.end();
- if (details.text[0] == '\1')
+ // an ACTION in which case we just check against the body.
+ std::string ctcpname;
+ std::string msgbody(details.text);
+ if (details.IsCTCP(ctcpname, msgbody))
{
// If the CTCP is not an action then skip it.
- if (details.text.compare(0, 8, "\1ACTION ", 8))
+ if (!irc::equals(ctcpname, "ACTION"))
return MOD_RES_PASSTHRU;
-
- // Skip the CTCP message characters.
- text_begin += 8;
- if (*details.text.rbegin() == '\1')
- text_end -= 1;
}
// Retrieve the anticaps config. This should never be
@@ -243,14 +238,14 @@ class ModuleAntiCaps : public Module
// If the message is shorter than the minimum length then
// we don't need to do anything else.
- size_t length = std::distance(text_begin, text_end);
+ size_t length = msgbody.length();
if (length < config->minlen)
return MOD_RES_PASSTHRU;
// Count the characters to see how many upper case and
// ignored (non upper or lower) characters there are.
size_t upper = 0;
- for (std::string::const_iterator iter = text_begin; iter != text_end; ++iter)
+ for (std::string::const_iterator iter = msgbody.begin(); iter != msgbody.end(); ++iter)
{
unsigned char chr = static_cast<unsigned char>(*iter);
if (uppercase.test(chr))
diff --git a/src/modules/m_blockcaps.cpp b/src/modules/m_blockcaps.cpp
index b79e126a3..fa780427c 100644
--- a/src/modules/m_blockcaps.cpp
+++ b/src/modules/m_blockcaps.cpp
@@ -60,31 +60,26 @@ public:
if (!c->GetExtBanStatus(user, 'B').check(!c->IsModeSet(bc)))
{
// If the message is a CTCP then we skip it unless it is
- // an ACTION in which case we strip the prefix and suffix.
- std::string::const_iterator text_begin = details.text.begin();
- std::string::const_iterator text_end = details.text.end();
- if (details.text[0] == '\1')
+ // an ACTION in which case we just check against the body.
+ std::string ctcpname;
+ std::string message(details.text);
+ if (details.IsCTCP(ctcpname, message))
{
// If the CTCP is not an action then skip it.
- if (details.text.compare(0, 8, "\1ACTION ", 8))
+ if (!irc::equals(ctcpname, "ACTION"))
return MOD_RES_PASSTHRU;
-
- // Skip the CTCP message characters.
- text_begin += 8;
- if (*details.text.rbegin() == '\1')
- text_end -= 1;
}
// If the message is shorter than the minimum length
// then we don't need to do anything else.
- size_t length = std::distance(text_begin, text_end);
+ size_t length = message.length();
if (length < minlen)
return MOD_RES_PASSTHRU;
// Count the characters to see how many upper case and
// ignored (non upper or lower) characters there are.
size_t upper = 0;
- for (std::string::const_iterator iter = text_begin; iter != text_end; ++iter)
+ for (std::string::const_iterator iter = message.begin(); iter != message.end(); ++iter)
{
unsigned char chr = static_cast<unsigned char>(*iter);
if (uppercase.test(chr))
diff --git a/src/modules/m_delayjoin.cpp b/src/modules/m_delayjoin.cpp
index 7c557eb35..d157b97bc 100644
--- a/src/modules/m_delayjoin.cpp
+++ b/src/modules/m_delayjoin.cpp
@@ -91,6 +91,16 @@ class ModuleDelayJoin : public Module
ModResult OnRawMode(User* user, Channel* channel, ModeHandler* mh, const std::string& param, bool adding) CXX11_OVERRIDE;
};
+// TODO: make RevealUser accessible from DelayJoinMode and get rid of this.
+class DummyMessageDetails : public MessageDetails
+{
+public:
+ DummyMessageDetails() : MessageDetails(MSG_PRIVMSG, "", ClientProtocol::TagMap()) { }
+ bool IsCTCP(std::string& name, std::string& body) const CXX11_OVERRIDE { return false; }
+ bool IsCTCP(std::string&) const CXX11_OVERRIDE { return false; }
+ bool IsCTCP() const CXX11_OVERRIDE { return false; }
+};
+
ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding)
{
/* no change */
@@ -104,7 +114,7 @@ ModeAction DelayJoinMode::OnModeChange(User* source, User* dest, Channel* channe
* they remain permanently invisible on this channel!
*/
MessageTarget msgtarget(channel, 0);
- MessageDetails msgdetails(MSG_PRIVMSG, "", ClientProtocol::TagMap());
+ DummyMessageDetails msgdetails;
const Channel::MemberMap& users = channel->GetUsers();
for (Channel::MemberMap::const_iterator n = users.begin(); n != users.end(); ++n)
{
diff --git a/src/modules/m_noctcp.cpp b/src/modules/m_noctcp.cpp
index dbfe55eac..54960a88e 100644
--- a/src/modules/m_noctcp.cpp
+++ b/src/modules/m_noctcp.cpp
@@ -43,10 +43,11 @@ class ModuleNoCTCP : public Module
{
if ((target.type == MessageTarget::TYPE_CHANNEL) && (IS_LOCAL(user)))
{
- Channel* c = target.Get<Channel>();
- if ((details.text.empty()) || (details.text[0] != '\001') || (!strncmp(details.text.c_str(),"\1ACTION ", 8)) || (details.text == "\1ACTION\1") || (details.text == "\1ACTION"))
+ std::string ctcpname;
+ if (!details.IsCTCP(ctcpname) || irc::equals(ctcpname, "ACTION"))
return MOD_RES_PASSTHRU;
+ Channel* c = target.Get<Channel>();
ModResult res = CheckExemption::Call(exemptionprov, user, c, "noctcp");
if (res == MOD_RES_ALLOW)
return MOD_RES_PASSTHRU;