diff options
-rw-r--r-- | include/protocol.h | 30 | ||||
-rw-r--r-- | src/process.cpp | 123 | ||||
-rw-r--r-- | src/protocol.cpp | 7 |
3 files changed, 134 insertions, 26 deletions
diff --git a/include/protocol.h b/include/protocol.h index 1e3653c85..1f3b8c026 100644 --- a/include/protocol.h +++ b/include/protocol.h @@ -39,7 +39,7 @@ class CoreExport IRCDProto : public Service virtual void SendNumericInternal(int numeric, const Anope::string &dest, const Anope::string &buf); const Anope::string &GetProtocolName(); - virtual void Parse(const Anope::string &, Anope::string &, Anope::string &, std::vector<Anope::string> &); + virtual bool Parse(const Anope::string &, Anope::map<Anope::string> &, Anope::string &, Anope::string &, std::vector<Anope::string> &); virtual Anope::string Format(const Anope::string &source, const Anope::string &message); /* Modes used by default by our clients */ @@ -278,11 +278,39 @@ class CoreExport IRCDMessage : public Service IRCDMessage(Module *owner, const Anope::string &n, unsigned p = 0); unsigned GetParamCount() const; virtual void Run(MessageSource &, const std::vector<Anope::string> ¶ms) = 0; + virtual void Run(MessageSource &, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags); void SetFlag(IRCDMessageFlag f) { flags.insert(f); } bool HasFlag(IRCDMessageFlag f) const { return flags.count(f); } }; +/** MessageTokenizer allows tokens in the IRC wire format to be read from a string */ +class CoreExport MessageTokenizer +{ +private: + /** The message we are parsing tokens from. */ + Anope::string message; + + /** The current position within the message. */ + Anope::string::size_type position; + + public: + /** Create a tokenstream and fill it with the provided data. */ + MessageTokenizer(const Anope::string &msg); + + /** Retrieve the next \<middle> token in the message. + * @param token The next token available, or an empty string if none remain. + * @return True if a token was retrieved; otherwise, false. + */ + bool GetMiddle(Anope::string &token); + + /** Retrieve the next \<trailing> token in the message. + * @param token The next token available, or an empty string if none remain. + * @return True if a token was retrieved; otherwise, false. + */ + bool GetTrailing(Anope::string &token); +}; + extern CoreExport IRCDProto *IRCD; #endif // PROTOCOL_H diff --git a/src/process.cpp b/src/process.cpp index 43f504f51..2bec1a83d 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -24,13 +24,21 @@ void Anope::Process(const Anope::string &buffer) if (buffer.empty()) return; + Anope::map<Anope::string> tags; Anope::string source, command; std::vector<Anope::string> params; - IRCD->Parse(buffer, source, command, params); + if (!IRCD->Parse(buffer, tags, source, command, params)) + return; if (Anope::ProtocolDebug) { + if (tags.empty()) + Log() << "No tags"; + else + for (Anope::map<Anope::string>::const_iterator it = tags.begin(); it != tags.end(); ++it) + Log() << "tags " << it->first << ": " << it->second; + Log() << "Source : " << (source.empty() ? "No source" : source); Log() << "Command: " << command; @@ -41,12 +49,6 @@ void Anope::Process(const Anope::string &buffer) Log() << "params " << i << ": " << params[i]; } - if (command.empty()) - { - Log(LOG_DEBUG) << "No command? " << buffer; - return; - } - static const Anope::string proto_name = ModuleManager::FindFirstOf(PROTOCOL) ? ModuleManager::FindFirstOf(PROTOCOL)->name : ""; MessageSource src(source); @@ -70,34 +72,55 @@ void Anope::Process(const Anope::string &buffer) else if (m->HasFlag(IRCDMESSAGE_REQUIRE_SERVER) && !source.empty() && !src.GetServer()) Log(LOG_DEBUG) << "unexpected non-server source " << source << " for " << command; else - m->Run(src, params); + m->Run(src, params, tags); } -void IRCDProto::Parse(const Anope::string &buffer, Anope::string &source, Anope::string &command, std::vector<Anope::string> ¶ms) +bool IRCDProto::Parse(const Anope::string &buffer, Anope::map<Anope::string> &tags, Anope::string &source, Anope::string &command, std::vector<Anope::string> ¶ms) { - spacesepstream sep(buffer); - - if (buffer[0] == ':') - { - sep.GetToken(source); - source.erase(0, 1); - } + MessageTokenizer tokens(buffer); - sep.GetToken(command); + // This will always exist because of the check in Anope::Process. + Anope::string token; + tokens.GetMiddle(token); - for (Anope::string token; sep.GetToken(token);) + if (token[0] == '@') { - if (token[0] == ':') + // The line begins with message tags. + sepstream tagstream(token.substr(1), ';'); + while (tagstream.GetToken(token)) { - if (!sep.StreamEnd()) - params.push_back(token.substr(1) + " " + sep.GetRemaining()); + const Anope::string::size_type valsep = token.find('='); + if (valsep == Anope::string::npos) + { + // Tag has no value. + tags[token]; + } else - params.push_back(token.substr(1)); - break; + { + // Tag has a value + tags[token.substr(0, valsep)] = token.substr(valsep + 1); + } } - else - params.push_back(token); + + if (!tokens.GetMiddle(token)) + return false; + } + + if (token[0] == ':') + { + source = token.substr(1); + if (!tokens.GetMiddle(token)) + return false; } + + // Store the command name. + command = token; + + // Retrieve all of the parameters. + while (tokens.GetTrailing(token)) + params.push_back(token); + + return true; } Anope::string IRCDProto::Format(const Anope::string &source, const Anope::string &message) @@ -107,3 +130,53 @@ Anope::string IRCDProto::Format(const Anope::string &source, const Anope::string else return message; } + +MessageTokenizer::MessageTokenizer(const Anope::string &msg) + : message(msg) + , position(0) +{ +} + +bool MessageTokenizer::GetMiddle(Anope::string &token) +{ + // If we are past the end of the string we can't do anything. + if (position >= message.length()) + { + token.clear(); + return false; + } + + // If we can't find another separator this is the last token in the message. + Anope::string::size_type separator = message.find(' ', position); + if (separator == Anope::string::npos) + { + token = message.substr(position); + position = message.length(); + return true; + } + + token = message.substr(position, separator - position); + position = message.find_first_not_of(' ', separator); + return true; +} + +bool MessageTokenizer::GetTrailing(Anope::string &token) +{ + // If we are past the end of the string we can't do anything. + if (position >= message.length()) + { + token.clear(); + return false; + } + + // If this is true then we have a <trailing> token! + if (message[position] == ':') + { + token = message.substr(position + 1); + position = message.length(); + return true; + } + + // There is no <trailing> token so it must be a <middle> token. + return GetMiddle(token); +} diff --git a/src/protocol.cpp b/src/protocol.cpp index 3daa9f136..dd2595992 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -502,3 +502,10 @@ unsigned IRCDMessage::GetParamCount() const { return this->param_count; } + +void IRCDMessage::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) +{ + // Most IRCds don't support message tags yet so use the tagless variant. + Run(source, params); +} + |