diff options
-rw-r--r-- | include/message.h | 18 | ||||
-rw-r--r-- | src/coremods/core_privmsg.cpp | 74 | ||||
-rw-r--r-- | src/modules/m_anticaps.cpp | 21 | ||||
-rw-r--r-- | src/modules/m_blockcaps.cpp | 19 | ||||
-rw-r--r-- | src/modules/m_delayjoin.cpp | 12 | ||||
-rw-r--r-- | src/modules/m_noctcp.cpp | 5 |
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 ¶meter, 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; |