mirror of
https://github.com/anope/anope.git
synced 2026-06-29 00:06:39 +02:00
Update the core message parser to allow parsing IRCv3 message tags.
This commit is contained in:
+29
-1
@@ -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
|
||||
|
||||
+99
-26
@@ -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);
|
||||
MessageTokenizer tokens(buffer);
|
||||
|
||||
if (buffer[0] == ':')
|
||||
// This will always exist because of the check in Anope::Process.
|
||||
Anope::string token;
|
||||
tokens.GetMiddle(token);
|
||||
|
||||
if (token[0] == '@')
|
||||
{
|
||||
sep.GetToken(source);
|
||||
source.erase(0, 1);
|
||||
}
|
||||
|
||||
sep.GetToken(command);
|
||||
|
||||
for (Anope::string token; sep.GetToken(token);)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user