143 lines
2.9 KiB
C++
143 lines
2.9 KiB
C++
#include "IrcParser.hpp"
|
|
#include <cctype>
|
|
|
|
IrcParser::IrcParser(void) {}
|
|
IrcParser::IrcParser(const IrcParser &other) { (void)other; }
|
|
IrcParser &IrcParser::operator=(const IrcParser &other) { (void)other; return (*this); }
|
|
IrcParser::~IrcParser(void) {}
|
|
|
|
bool IrcParser::isSpace(char c)
|
|
{
|
|
return (c == ' ' || c == '\t');
|
|
}
|
|
|
|
std::string IrcParser::trimLeft(const std::string &s)
|
|
{
|
|
size_t i = 0;
|
|
|
|
while (i < s.size() && isSpace(s[i]))
|
|
i++;
|
|
return (s.substr(i));
|
|
}
|
|
|
|
std::string IrcParser::trimRight(const std::string &s)
|
|
{
|
|
size_t end = s.size();
|
|
|
|
while (end > 0 && (s[end - 1] == '\r' || s[end - 1] == '\n' || isSpace(s[end - 1])))
|
|
end--;
|
|
return (s.substr(0, end));
|
|
}
|
|
|
|
std::string IrcParser::toUpper(const std::string &s)
|
|
{
|
|
std::string out = s;
|
|
|
|
for (size_t i = 0; i < out.size(); ++i)
|
|
out[i] = static_cast<char>(std::toupper(static_cast<unsigned char>(out[i])));
|
|
return (out);
|
|
}
|
|
|
|
bool IrcParser::isCommandChar(char c)
|
|
{
|
|
return (std::isalpha(static_cast<unsigned char>(c)) || std::isdigit(static_cast<unsigned char>(c)));
|
|
}
|
|
|
|
bool IrcParser::isValidCommand(const std::string &command)
|
|
{
|
|
if (command.empty())
|
|
return (false);
|
|
for (size_t i = 0; i < command.size(); ++i)
|
|
{
|
|
if (!isCommandChar(command[i]))
|
|
return (false);
|
|
}
|
|
return (true);
|
|
}
|
|
|
|
/*
|
|
IRC params rule:
|
|
- parameters are separated by spaces
|
|
- if a parameter starts with ':', it is the trailing parameter
|
|
- the trailing parameter may contain spaces and is always the last param
|
|
|
|
Example:
|
|
PRIVMSG #chan :hello les gars
|
|
params[0] = "#chan"
|
|
params[1] = "hello les gars"
|
|
*/
|
|
void IrcParser::parseParams(const std::string &s, size_t pos, IrcMessage &msg)
|
|
{
|
|
while (pos < s.size())
|
|
{
|
|
while (pos < s.size() && isSpace(s[pos]))
|
|
pos++;
|
|
if (pos >= s.size())
|
|
break;
|
|
|
|
if (s[pos] == ':')
|
|
{
|
|
msg.addParam(s.substr(pos + 1));
|
|
break;
|
|
}
|
|
|
|
size_t start = pos;
|
|
while (pos < s.size() && !isSpace(s[pos]))
|
|
pos++;
|
|
msg.addParam(s.substr(start, pos - start));
|
|
}
|
|
}
|
|
|
|
IrcMessage IrcParser::parseLine(const std::string &line)
|
|
{
|
|
IrcMessage msg;
|
|
std::string s;
|
|
size_t pos;
|
|
size_t start;
|
|
|
|
msg.setRaw(line);
|
|
s = trimRight(trimLeft(line));
|
|
|
|
if (s.empty())
|
|
{
|
|
msg.setError("empty line");
|
|
return (msg);
|
|
}
|
|
|
|
/*
|
|
Optional prefix:
|
|
:nick!user@host COMMAND params...
|
|
In ft_irc, clients usually do not send prefixes, but accepting it makes the parser robust.
|
|
*/
|
|
pos = 0;
|
|
if (s[pos] == ':')
|
|
{
|
|
size_t prefixEnd = s.find(' ', pos);
|
|
if (prefixEnd == std::string::npos)
|
|
{
|
|
msg.setError("prefix without command");
|
|
return (msg);
|
|
}
|
|
msg.setPrefix(s.substr(1, prefixEnd - 1));
|
|
pos = prefixEnd + 1;
|
|
while (pos < s.size() && isSpace(s[pos]))
|
|
pos++;
|
|
}
|
|
|
|
start = pos;
|
|
while (pos < s.size() && !isSpace(s[pos]))
|
|
pos++;
|
|
|
|
msg.setCommand(toUpper(s.substr(start, pos - start)));
|
|
|
|
if (!isValidCommand(msg.getCommand()))
|
|
{
|
|
msg.setError("invalid command");
|
|
return (msg);
|
|
}
|
|
|
|
parseParams(s, pos, msg);
|
|
msg.setValid(true);
|
|
return (msg);
|
|
}
|