first commit
This commit is contained in:
commit
332cb7501a
|
|
@ -0,0 +1,132 @@
|
||||||
|
# ft_irc parsing module
|
||||||
|
|
||||||
|
Partie parsing C++98 pour `ft_irc`.
|
||||||
|
|
||||||
|
## Ce que ça fait
|
||||||
|
|
||||||
|
- accumule les données TCP reçues dans un buffer par client ;
|
||||||
|
- extrait les lignes complètes terminées par `\r\n` ou `\n` ;
|
||||||
|
- parse une ligne IRC en :
|
||||||
|
- prefix optionnel ;
|
||||||
|
- command ;
|
||||||
|
- params ;
|
||||||
|
- trailing param après `:`, qui peut contenir des espaces ;
|
||||||
|
- normalise les commandes en uppercase ;
|
||||||
|
- fournit une validation minimale du nombre de paramètres.
|
||||||
|
|
||||||
|
## Ce que ça ne fait volontairement pas
|
||||||
|
|
||||||
|
Le parser ne doit pas gérer la logique métier :
|
||||||
|
|
||||||
|
- vérifier le password ;
|
||||||
|
- vérifier si le nickname existe déjà ;
|
||||||
|
- créer les channels ;
|
||||||
|
- vérifier les permissions operator ;
|
||||||
|
- appliquer les modes ;
|
||||||
|
- envoyer les numeric replies.
|
||||||
|
|
||||||
|
Tout ça appartient aux handlers de commandes.
|
||||||
|
|
||||||
|
## Utilisation dans ton Client
|
||||||
|
|
||||||
|
Chaque client doit avoir son propre `ParseBuffer`.
|
||||||
|
|
||||||
|
Exemple :
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Dans ta classe Client
|
||||||
|
ParseBuffer _parseBuffer;
|
||||||
|
```
|
||||||
|
|
||||||
|
Quand `poll()` indique que le fd client est lisible :
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
char buf[4096];
|
||||||
|
ssize_t n = recv(clientFd, buf, sizeof(buf), 0);
|
||||||
|
|
||||||
|
if (n <= 0)
|
||||||
|
{
|
||||||
|
// déconnexion ou erreur
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.getParseBuffer().append(buf, n);
|
||||||
|
|
||||||
|
std::vector<IrcMessage> messages = client.getParseBuffer().extractMessages();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < messages.size(); ++i)
|
||||||
|
{
|
||||||
|
IrcMessage &msg = messages[i];
|
||||||
|
|
||||||
|
if (!msg.isValid())
|
||||||
|
{
|
||||||
|
// selon l'erreur:
|
||||||
|
// - ignorer
|
||||||
|
// - répondre ERR_UNKNOWNCOMMAND
|
||||||
|
// - fermer si line too long
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dispatcher.handle(client, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compilation test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
c++ -Wall -Wextra -Werror -std=c++98 \
|
||||||
|
-Iincludes \
|
||||||
|
srcs/IrcMessage.cpp \
|
||||||
|
srcs/IrcParser.cpp \
|
||||||
|
srcs/ParseBuffer.cpp \
|
||||||
|
srcs/CommandValidator.cpp \
|
||||||
|
test/main.cpp \
|
||||||
|
-o parser_test
|
||||||
|
|
||||||
|
./parser_test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Format IRC géré
|
||||||
|
|
||||||
|
```txt
|
||||||
|
COMMAND param1 param2 :trailing avec espaces
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemples :
|
||||||
|
|
||||||
|
```txt
|
||||||
|
PASS secret
|
||||||
|
NICK jeremy
|
||||||
|
USER jeremy 0 * :Jeremy Real Name
|
||||||
|
JOIN #42
|
||||||
|
PRIVMSG #42 :salut les gars
|
||||||
|
MODE #42 +it
|
||||||
|
MODE #42 +k password
|
||||||
|
KICK #42 bob :raison du kick
|
||||||
|
```
|
||||||
|
|
||||||
|
## Intégration recommandée
|
||||||
|
|
||||||
|
Architecture propre :
|
||||||
|
|
||||||
|
```txt
|
||||||
|
Client
|
||||||
|
- fd
|
||||||
|
- nick
|
||||||
|
- username
|
||||||
|
- ParseBuffer
|
||||||
|
|
||||||
|
Server
|
||||||
|
- poll loop
|
||||||
|
- accept
|
||||||
|
- recv
|
||||||
|
- donne les IrcMessage au dispatcher
|
||||||
|
|
||||||
|
CommandDispatcher
|
||||||
|
- map command -> handler
|
||||||
|
- PASS/NICK/USER/JOIN/PRIVMSG/MODE/etc.
|
||||||
|
|
||||||
|
Parser
|
||||||
|
- aucune logique métier
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef COMMANDVALIDATOR_HPP
|
||||||
|
# define COMMANDVALIDATOR_HPP
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
# include "IrcMessage.hpp"
|
||||||
|
|
||||||
|
class CommandValidator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CommandValidator(void);
|
||||||
|
CommandValidator(const CommandValidator &other);
|
||||||
|
CommandValidator &operator=(const CommandValidator &other);
|
||||||
|
~CommandValidator(void);
|
||||||
|
|
||||||
|
static bool hasEnoughParams(const IrcMessage &msg);
|
||||||
|
static bool needsRegistration(const std::string &command);
|
||||||
|
static bool isKnownCommand(const std::string &command);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef IRCMESSAGE_HPP
|
||||||
|
# define IRCMESSAGE_HPP
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
# include <vector>
|
||||||
|
|
||||||
|
class IrcMessage
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string _prefix;
|
||||||
|
std::string _command;
|
||||||
|
std::vector<std::string> _params;
|
||||||
|
std::string _raw;
|
||||||
|
bool _valid;
|
||||||
|
std::string _error;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IrcMessage(void);
|
||||||
|
IrcMessage(const IrcMessage &other);
|
||||||
|
IrcMessage &operator=(const IrcMessage &other);
|
||||||
|
~IrcMessage(void);
|
||||||
|
|
||||||
|
void setPrefix(const std::string &prefix);
|
||||||
|
void setCommand(const std::string &command);
|
||||||
|
void addParam(const std::string ¶m);
|
||||||
|
void setRaw(const std::string &raw);
|
||||||
|
void setValid(bool valid);
|
||||||
|
void setError(const std::string &error);
|
||||||
|
|
||||||
|
const std::string &getPrefix(void) const;
|
||||||
|
const std::string &getCommand(void) const;
|
||||||
|
const std::vector<std::string> &getParams(void) const;
|
||||||
|
const std::string &getRaw(void) const;
|
||||||
|
bool isValid(void) const;
|
||||||
|
const std::string &getError(void) const;
|
||||||
|
|
||||||
|
bool hasPrefix(void) const;
|
||||||
|
bool hasParam(size_t index) const;
|
||||||
|
const std::string ¶m(size_t index) const;
|
||||||
|
size_t paramCount(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef IRCPARSER_HPP
|
||||||
|
# define IRCPARSER_HPP
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
# include <vector>
|
||||||
|
# include "IrcMessage.hpp"
|
||||||
|
|
||||||
|
class IrcParser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static std::string trimLeft(const std::string &s);
|
||||||
|
static std::string trimRight(const std::string &s);
|
||||||
|
static std::string toUpper(const std::string &s);
|
||||||
|
static bool isSpace(char c);
|
||||||
|
static bool isCommandChar(char c);
|
||||||
|
static bool isValidCommand(const std::string &command);
|
||||||
|
static void parseParams(const std::string &s, size_t pos, IrcMessage &msg);
|
||||||
|
|
||||||
|
public:
|
||||||
|
IrcParser(void);
|
||||||
|
IrcParser(const IrcParser &other);
|
||||||
|
IrcParser &operator=(const IrcParser &other);
|
||||||
|
~IrcParser(void);
|
||||||
|
|
||||||
|
static IrcMessage parseLine(const std::string &line);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef PARSEBUFFER_HPP
|
||||||
|
# define PARSEBUFFER_HPP
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
# include <vector>
|
||||||
|
# include "IrcMessage.hpp"
|
||||||
|
|
||||||
|
class ParseBuffer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string _buffer;
|
||||||
|
size_t _maxLineSize;
|
||||||
|
|
||||||
|
bool extractOneLine(std::string &line);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ParseBuffer(void);
|
||||||
|
explicit ParseBuffer(size_t maxLineSize);
|
||||||
|
ParseBuffer(const ParseBuffer &other);
|
||||||
|
ParseBuffer &operator=(const ParseBuffer &other);
|
||||||
|
~ParseBuffer(void);
|
||||||
|
|
||||||
|
void append(const char *data, size_t len);
|
||||||
|
std::vector<IrcMessage> extractMessages(void);
|
||||||
|
void clear(void);
|
||||||
|
|
||||||
|
const std::string &raw(void) const;
|
||||||
|
size_t size(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef SERVER_HPP
|
||||||
|
# define SERVER_HPP
|
||||||
|
|
||||||
|
# include <string>
|
||||||
|
# include <vector>
|
||||||
|
# include <map>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <poll.h>
|
||||||
|
# include <fcntl.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
# include "ParseBuffer.hpp"
|
||||||
|
# include <stdexcept>
|
||||||
|
# include <cstring>
|
||||||
|
# include <cerrno>
|
||||||
|
|
||||||
|
struct ConnectedUser
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
std::string nick;
|
||||||
|
std::string username;
|
||||||
|
std::string realname;
|
||||||
|
std::string hostname;
|
||||||
|
bool passOk;
|
||||||
|
bool nickOk;
|
||||||
|
bool userOk;
|
||||||
|
ParseBuffer parseBuffer;
|
||||||
|
|
||||||
|
explicit ConnectedUser(int fd);
|
||||||
|
bool isRegistered(void) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Server
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int _serverFd;
|
||||||
|
int _port;
|
||||||
|
std::string _password;
|
||||||
|
std::vector<struct pollfd> _fds;
|
||||||
|
std::map<int, ConnectedUser> _users;
|
||||||
|
|
||||||
|
void acceptNew(void);
|
||||||
|
void handleRecv(int fd);
|
||||||
|
void removeUser(int fd);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Server(int port, const std::string &password);
|
||||||
|
Server(const Server &other);
|
||||||
|
Server &operator=(const Server &other);
|
||||||
|
~Server(void);
|
||||||
|
|
||||||
|
void start(void);
|
||||||
|
void run(void);
|
||||||
|
void sendReply(int fd, const std::string &msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "CommandValidator.hpp"
|
||||||
|
|
||||||
|
CommandValidator::CommandValidator(void) {}
|
||||||
|
CommandValidator::CommandValidator(const CommandValidator &other) { (void)other; }
|
||||||
|
CommandValidator &CommandValidator::operator=(const CommandValidator &other) { (void)other; return (*this); }
|
||||||
|
CommandValidator::~CommandValidator(void) {}
|
||||||
|
|
||||||
|
bool CommandValidator::isKnownCommand(const std::string &cmd)
|
||||||
|
{
|
||||||
|
return (cmd == "PASS"
|
||||||
|
|| cmd == "NICK"
|
||||||
|
|| cmd == "USER"
|
||||||
|
|| cmd == "JOIN"
|
||||||
|
|| cmd == "PART"
|
||||||
|
|| cmd == "PRIVMSG"
|
||||||
|
|| cmd == "NOTICE"
|
||||||
|
|| cmd == "QUIT"
|
||||||
|
|| cmd == "PING"
|
||||||
|
|| cmd == "PONG"
|
||||||
|
|| cmd == "KICK"
|
||||||
|
|| cmd == "INVITE"
|
||||||
|
|| cmd == "TOPIC"
|
||||||
|
|| cmd == "MODE");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is only syntactic/minimal validation.
|
||||||
|
The real command handler still checks:
|
||||||
|
- password correctness
|
||||||
|
- nickname already used
|
||||||
|
- channel exists
|
||||||
|
- permissions/operator status
|
||||||
|
- invite-only/key/limit modes
|
||||||
|
*/
|
||||||
|
bool CommandValidator::hasEnoughParams(const IrcMessage &msg)
|
||||||
|
{
|
||||||
|
const std::string &cmd = msg.getCommand();
|
||||||
|
|
||||||
|
if (cmd == "PASS") return (msg.paramCount() >= 1);
|
||||||
|
if (cmd == "NICK") return (msg.paramCount() >= 1);
|
||||||
|
if (cmd == "USER") return (msg.paramCount() >= 4);
|
||||||
|
if (cmd == "JOIN") return (msg.paramCount() >= 1);
|
||||||
|
if (cmd == "PART") return (msg.paramCount() >= 1);
|
||||||
|
if (cmd == "PRIVMSG") return (msg.paramCount() >= 2);
|
||||||
|
if (cmd == "NOTICE") return (msg.paramCount() >= 2);
|
||||||
|
if (cmd == "QUIT") return (true);
|
||||||
|
if (cmd == "PING") return (msg.paramCount() >= 1);
|
||||||
|
if (cmd == "PONG") return (msg.paramCount() >= 1);
|
||||||
|
if (cmd == "KICK") return (msg.paramCount() >= 2);
|
||||||
|
if (cmd == "INVITE") return (msg.paramCount() >= 2);
|
||||||
|
if (cmd == "TOPIC") return (msg.paramCount() >= 1);
|
||||||
|
if (cmd == "MODE") return (msg.paramCount() >= 1);
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Before registration, usually only PASS, NICK, USER, PING/PONG/QUIT are useful.
|
||||||
|
*/
|
||||||
|
bool CommandValidator::needsRegistration(const std::string &cmd)
|
||||||
|
{
|
||||||
|
return !(cmd == "PASS"
|
||||||
|
|| cmd == "NICK"
|
||||||
|
|| cmd == "USER"
|
||||||
|
|| cmd == "PING"
|
||||||
|
|| cmd == "PONG"
|
||||||
|
|| cmd == "QUIT");
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#include "IrcMessage.hpp"
|
||||||
|
|
||||||
|
IrcMessage::IrcMessage(void) : _valid(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IrcMessage::IrcMessage(const IrcMessage &other)
|
||||||
|
{
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrcMessage &IrcMessage::operator=(const IrcMessage &other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
_prefix = other._prefix;
|
||||||
|
_command = other._command;
|
||||||
|
_params = other._params;
|
||||||
|
_raw = other._raw;
|
||||||
|
_valid = other._valid;
|
||||||
|
_error = other._error;
|
||||||
|
}
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
IrcMessage::~IrcMessage(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IrcMessage::setPrefix(const std::string &prefix) { _prefix = prefix; }
|
||||||
|
void IrcMessage::setCommand(const std::string &command) { _command = command; }
|
||||||
|
void IrcMessage::addParam(const std::string ¶m) { _params.push_back(param); }
|
||||||
|
void IrcMessage::setRaw(const std::string &raw) { _raw = raw; }
|
||||||
|
void IrcMessage::setValid(bool valid) { _valid = valid; }
|
||||||
|
void IrcMessage::setError(const std::string &error) { _error = error; }
|
||||||
|
|
||||||
|
const std::string &IrcMessage::getPrefix(void) const { return (_prefix); }
|
||||||
|
const std::string &IrcMessage::getCommand(void) const { return (_command); }
|
||||||
|
const std::vector<std::string> &IrcMessage::getParams(void) const { return (_params); }
|
||||||
|
const std::string &IrcMessage::getRaw(void) const { return (_raw); }
|
||||||
|
bool IrcMessage::isValid(void) const { return (_valid); }
|
||||||
|
const std::string &IrcMessage::getError(void) const { return (_error); }
|
||||||
|
|
||||||
|
bool IrcMessage::hasPrefix(void) const
|
||||||
|
{
|
||||||
|
return (!_prefix.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IrcMessage::hasParam(size_t index) const
|
||||||
|
{
|
||||||
|
return (index < _params.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &IrcMessage::param(size_t index) const
|
||||||
|
{
|
||||||
|
return (_params[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t IrcMessage::paramCount(void) const
|
||||||
|
{
|
||||||
|
return (_params.size());
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
#include "ParseBuffer.hpp"
|
||||||
|
#include "IrcParser.hpp"
|
||||||
|
|
||||||
|
ParseBuffer::ParseBuffer(void) : _maxLineSize(512)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseBuffer::ParseBuffer(size_t maxLineSize) : _maxLineSize(maxLineSize)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseBuffer::ParseBuffer(const ParseBuffer &other)
|
||||||
|
{
|
||||||
|
*this = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseBuffer &ParseBuffer::operator=(const ParseBuffer &other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
_buffer = other._buffer;
|
||||||
|
_maxLineSize = other._maxLineSize;
|
||||||
|
}
|
||||||
|
return (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseBuffer::~ParseBuffer(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseBuffer::append(const char *data, size_t len)
|
||||||
|
{
|
||||||
|
if (data && len > 0)
|
||||||
|
_buffer.append(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Extract lines terminated by:
|
||||||
|
- "\r\n" standard IRC
|
||||||
|
- "\n" useful for netcat/manual tests
|
||||||
|
|
||||||
|
The returned line does not include CRLF/LF.
|
||||||
|
*/
|
||||||
|
bool ParseBuffer::extractOneLine(std::string &line)
|
||||||
|
{
|
||||||
|
size_t lf = _buffer.find('\n');
|
||||||
|
|
||||||
|
if (lf == std::string::npos)
|
||||||
|
return (false);
|
||||||
|
|
||||||
|
line = _buffer.substr(0, lf);
|
||||||
|
if (!line.empty() && line[line.size() - 1] == '\r')
|
||||||
|
line.erase(line.size() - 1);
|
||||||
|
|
||||||
|
_buffer.erase(0, lf + 1);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<IrcMessage> ParseBuffer::extractMessages(void)
|
||||||
|
{
|
||||||
|
std::vector<IrcMessage> messages;
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (extractOneLine(line))
|
||||||
|
{
|
||||||
|
IrcMessage msg;
|
||||||
|
|
||||||
|
if (line.size() + 2 > _maxLineSize)
|
||||||
|
{
|
||||||
|
msg.setRaw(line);
|
||||||
|
msg.setError("line too long");
|
||||||
|
msg.setValid(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
msg = IrcParser::parseLine(line);
|
||||||
|
messages.push_back(msg);
|
||||||
|
}
|
||||||
|
return (messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseBuffer::clear(void)
|
||||||
|
{
|
||||||
|
_buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &ParseBuffer::raw(void) const
|
||||||
|
{
|
||||||
|
return (_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ParseBuffer::size(void) const
|
||||||
|
{
|
||||||
|
return (_buffer.size());
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include "Server.hpp"
|
||||||
|
|
||||||
|
void Server::start(void)
|
||||||
|
{
|
||||||
|
_serverFd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (_serverFd == -1)
|
||||||
|
throw std::runtime_error(strerror(errno));
|
||||||
|
int opt = 1;
|
||||||
|
if (setsockopt(_serverFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
|
||||||
|
{
|
||||||
|
close(_serverFd);
|
||||||
|
throw std::runtime_error(strerror(errno));
|
||||||
|
}
|
||||||
|
if (fcntl(_serverFd, F_SETFL, O_NONBLOCK) == -1)
|
||||||
|
{
|
||||||
|
close(_serverFd);
|
||||||
|
throw std::runtime_error(strerror(errno));
|
||||||
|
}
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(_port);
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
if (bind(_serverFd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
|
||||||
|
{
|
||||||
|
close(_serverFd);
|
||||||
|
throw std::runtime_error(strerror(errno));
|
||||||
|
}
|
||||||
|
if (listen(_serverFd, SOMAXCONN) == -1)
|
||||||
|
{
|
||||||
|
close(_serverFd);
|
||||||
|
throw std::runtime_error(strerror(errno));
|
||||||
|
}
|
||||||
|
struct pollfd pollfd;
|
||||||
|
pollfd.fd = _serverFd;
|
||||||
|
pollfd.events = POLLIN;
|
||||||
|
pollfd.revents = 0;
|
||||||
|
_fds.push_back(pollfd);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include "ParseBuffer.hpp"
|
||||||
|
#include "CommandValidator.hpp"
|
||||||
|
|
||||||
|
static void printMessage(const IrcMessage &msg)
|
||||||
|
{
|
||||||
|
std::cout << "raw=[" << msg.getRaw() << "]\n";
|
||||||
|
std::cout << "valid=" << (msg.isValid() ? "yes" : "no") << "\n";
|
||||||
|
if (!msg.getError().empty())
|
||||||
|
std::cout << "error=" << msg.getError() << "\n";
|
||||||
|
if (msg.hasPrefix())
|
||||||
|
std::cout << "prefix=" << msg.getPrefix() << "\n";
|
||||||
|
std::cout << "command=" << msg.getCommand() << "\n";
|
||||||
|
for (size_t i = 0; i < msg.paramCount(); ++i)
|
||||||
|
std::cout << "param[" << i << "]=[" << msg.param(i) << "]\n";
|
||||||
|
std::cout << "known=" << (CommandValidator::isKnownCommand(msg.getCommand()) ? "yes" : "no") << "\n";
|
||||||
|
std::cout << "enough_params=" << (CommandValidator::hasEnoughParams(msg) ? "yes" : "no") << "\n";
|
||||||
|
std::cout << "-----\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
ParseBuffer buffer;
|
||||||
|
std::string chunk1 = "PASS secret\r\nNICK je";
|
||||||
|
std::string chunk2 = "remy\r\nUSER jeremy 0 * :Jeremy Real Name\r\n";
|
||||||
|
std::string chunk3 = "JOIN #42\r\nPRIVMSG #42 :salut les gars ca va ?\r\n";
|
||||||
|
std::string chunk4 = ":nick!user@host PRIVMSG jeremy :hello from prefixed line\r\n";
|
||||||
|
|
||||||
|
buffer.append(chunk1.c_str(), chunk1.size());
|
||||||
|
std::vector<IrcMessage> messages = buffer.extractMessages();
|
||||||
|
for (size_t i = 0; i < messages.size(); ++i)
|
||||||
|
printMessage(messages[i]);
|
||||||
|
|
||||||
|
buffer.append(chunk2.c_str(), chunk2.size());
|
||||||
|
messages = buffer.extractMessages();
|
||||||
|
for (size_t i = 0; i < messages.size(); ++i)
|
||||||
|
printMessage(messages[i]);
|
||||||
|
|
||||||
|
buffer.append(chunk3.c_str(), chunk3.size());
|
||||||
|
buffer.append(chunk4.c_str(), chunk4.size());
|
||||||
|
messages = buffer.extractMessages();
|
||||||
|
for (size_t i = 0; i < messages.size(); ++i)
|
||||||
|
printMessage(messages[i]);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue