ft_irc/srcs/IrcParser.cpp

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);
}