From be35fc26160ec5308032df66ddef12b39a683e9b Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Mon, 22 Jan 2024 14:54:39 +0100 Subject: [PATCH] AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH --- includes/channel.hpp | 9 +- includes/client.hpp | 35 +++++-- includes/config.hpp | 19 ++++ includes/irc.hpp | 27 ++--- includes/message.hpp | 5 +- includes/server.hpp | 42 +++++--- srcs/channel.cpp | 4 +- srcs/client.cpp | 28 +++++- srcs/logs.cpp | 9 +- srcs/main.cpp | 4 +- srcs/message.cpp | 41 +++++++- srcs/server.cpp | 235 ++++++++++++++++++++++++++++++++++--------- 12 files changed, 360 insertions(+), 98 deletions(-) create mode 100644 includes/config.hpp diff --git a/includes/channel.hpp b/includes/channel.hpp index 921a218..7cab443 100644 --- a/includes/channel.hpp +++ b/includes/channel.hpp @@ -6,23 +6,28 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 10:34:25 by maldavid #+# #+# */ -/* Updated: 2024/01/21 11:41:51 by maldavid ### ########.fr */ +/* Updated: 2024/01/22 14:41:25 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ #ifndef __CHANNEL__ #define __CHANNEL__ +#include + namespace irc { class Channel { public: - Channel(); + Channel(const std::string& name); + + inline const std::string& getName() const { return _name; } ~Channel(); private: + const std::string _name; }; } diff --git a/includes/client.hpp b/includes/client.hpp index 88418e9..8ca6920 100644 --- a/includes/client.hpp +++ b/includes/client.hpp @@ -6,28 +6,51 @@ /* By: vvaas +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 10:33:17 by maldavid #+# #+# */ -/* Updated: 2024/01/22 01:29:55 by vvaas ### ########.fr */ +/* Updated: 2024/01/22 13:03:37 by maldavid ### ########.fr */ /* */ /******************************************************************************/ #ifndef __CLIENT__ #define __CLIENT__ + #include #include -#include + namespace irc { class Client { public: Client(int fd, sockaddr_in sock, int id); - inline int get_fd(void) { return (_fd); } - inline void set_fd(int new_fd) { _fd = new_fd; } - inline sockaddr_in get_sockaddr(void) { return (_s_data); } - inline std::string get_string_id(void) { std::stringstream out; out << _id; return (out.str()); } + + inline void newMsgInFlight(const std::string& msg) { _msg_in_flight = msg; } + + inline int getFD() const { return _fd; } + inline int getID() const { return _id; } + inline sockaddr_in getSockAddr() const { return _s_data; } + inline const std::string& getMsgInFlight() const { return _msg_in_flight; } + + std::string getNextMsg(); + + inline void setNewNickName(const std::string& name) { _nickname = name; } + inline void setNewUserName(const std::string& name) { _username = name; } + inline void setNewRealName(const std::string& name) { _realname = name; } + + inline const std::string& getNickName() const { return _nickname; } + inline const std::string& getUserName() const { return _username; } + inline const std::string& getRealName() const { return _realname; } + + void printUserHeader() const; + + inline void setFd(int new_fd) { _fd = new_fd; } + ~Client(); private: + std::string _nickname; + std::string _username; + std::string _realname; + std::string _msg_in_flight; sockaddr_in _s_data; int _fd; int _id; diff --git a/includes/config.hpp b/includes/config.hpp new file mode 100644 index 0000000..8a6c192 --- /dev/null +++ b/includes/config.hpp @@ -0,0 +1,19 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* config.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: maldavid +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/01/22 09:45:10 by maldavid #+# #+# */ +/* Updated: 2024/01/22 09:46:42 by maldavid ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef __CONFIG__ +#define __CONFIG__ + +#define INPUT_SIZE 1024 +#define LOGS_BUFFER_SIZE 4096 + +#endif diff --git a/includes/irc.hpp b/includes/irc.hpp index 9a52065..4a607e0 100644 --- a/includes/irc.hpp +++ b/includes/irc.hpp @@ -3,22 +3,23 @@ /* ::: :::::::: */ /* irc.hpp :+: :+: :+: */ /* +:+ +:+ +:+ */ -/* By: vvaas +#+ +:+ +#+ */ +/* By: vavaas +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ -/* Created: 2023/11/29 13:24:01 by vvaas #+# #+# */ -/* Updated: 2024/01/21 14:20:31 by vvaas ### ########.fr */ +/* Created: 2023/11/29 13:24:01 by vavaas #+# #+# */ +/* Updated: 2024/01/22 09:46:18 by maldavid ### ########.fr */ /* */ /******************************************************************************/ -#ifndef IRC_H -# define IRC_H +#ifndef __IRC__ +#define __IRC__ + +#include +#include +#include +#include +#include +#include +#include +#include -# include -# include -# include -# include -# include -# include -# include -# include #endif diff --git a/includes/message.hpp b/includes/message.hpp index 160a403..0f6874c 100644 --- a/includes/message.hpp +++ b/includes/message.hpp @@ -6,13 +6,14 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 11:35:21 by maldavid #+# #+# */ -/* Updated: 2024/01/21 12:04:43 by maldavid ### ########.fr */ +/* Updated: 2024/01/22 11:51:25 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ #ifndef __MESSAGE__ #define __MESSAGE__ +#include #include #include @@ -26,10 +27,12 @@ namespace irc inline unstd::SharedPtr getClient() const { return _client; } inline const std::string& getRawMsg() const { return _raw_msg; } inline const std::string& getCmd() const { return _command; } + inline const std::vector& getTokens() const { return _tokens; } ~Message(); private: + std::vector _tokens; const std::string _raw_msg; std::string _command; unstd::SharedPtr _client; diff --git a/includes/server.hpp b/includes/server.hpp index 5f24321..be730dd 100644 --- a/includes/server.hpp +++ b/includes/server.hpp @@ -6,7 +6,7 @@ /* By: vvaas +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 09:12:28 by maldavid #+# #+# */ -/* Updated: 2024/01/22 01:14:42 by vvaas ### ########.fr */ +/* Updated: 2024/01/22 12:16:31 by maldavid ### ########.fr */ /* */ /******************************************************************************/ @@ -20,31 +20,45 @@ #include #define MAX_USERS 20 + namespace irc { class Server { public: Server(int port, const std::string& password); - + void wait(); ~Server(); - inline unsigned long get_port(void) { return (htons(_port)); } + private: - void init_socket(void); - void init_socket_data(void); - void init_signal(void); + void initSocket(); + void initSocketData(); + void initSignal(); - void wait(void); - void add_client(int fd); + bool handleMessage(unstd::SharedPtr client); + + void handleInput(); + + // ugly as f*ck + void handleNick(unstd::SharedPtr client, const class Message& msg); + void handleUser(unstd::SharedPtr client, const class Message& msg); + void handleQuit(unstd::SharedPtr client, const class Message& msg); + void handlePart(unstd::SharedPtr client, const class Message& msg); + void handleJoin(unstd::SharedPtr client, const class Message& msg); + void handlePrivMsg(unstd::SharedPtr client, const class Message& msg); + void handleNotice(unstd::SharedPtr client, const class Message& msg); + void handleKick(unstd::SharedPtr client, const class Message& msg); + void handleMotD(unstd::SharedPtr client, const class Message& msg); + void handleTopic(unstd::SharedPtr client, const class Message& msg); + void handlePing(unstd::SharedPtr client, const class Message& msg); + void handleMode(unstd::SharedPtr client, const class Message& msg); - void handle_input(void); private: - sockaddr_in _s_data; - socklen_t _s_len; - fd_set _fd_set; - std::vector _channels; - std::vector _client; + std::vector > _client; + sockaddr_in _s_data; + socklen_t _s_len; + fd_set _fd_set; const std::string _password; const std::string _ip; const int _port; diff --git a/srcs/channel.cpp b/srcs/channel.cpp index 67387c7..c21b743 100644 --- a/srcs/channel.cpp +++ b/srcs/channel.cpp @@ -6,7 +6,7 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 10:36:21 by maldavid #+# #+# */ -/* Updated: 2024/01/21 10:36:49 by maldavid ### ########.fr */ +/* Updated: 2024/01/22 14:29:55 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ @@ -14,7 +14,7 @@ namespace irc { - Channel::Channel() + Channel::Channel(const std::string& name) : _name(name) { } diff --git a/srcs/client.cpp b/srcs/client.cpp index 4c3ec04..c4b9042 100644 --- a/srcs/client.cpp +++ b/srcs/client.cpp @@ -6,17 +6,39 @@ /* By: vvaas +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 10:35:52 by maldavid #+# #+# */ -/* Updated: 2024/01/22 01:27:30 by vvaas ### ########.fr */ +/* Updated: 2024/01/22 12:25:28 by maldavid ### ########.fr */ /* */ /******************************************************************************/ #include +#include +#include +#include namespace irc { - Client::Client(int fd, sockaddr_in sock, int id) : _s_data(sock), _fd(fd), _id(id) + Client::Client(int fd, sockaddr_in sock, int id) : _s_data(sock), _fd(fd), _id(id) {} + + void Client::printUserHeader() const { - + std::cout << AnsiColor::green << "[User " << _id; + if(!_realname.empty()) + std::cout << " " << _realname; + if(!_username.empty()) + std::cout << " {username " << _username << "}"; + if(!_nickname.empty()) + std::cout << " {nickname " << _nickname << "}"; + std::cout << "] : " << AnsiColor::reset; + } + + std::string Client::getNextMsg() + { + std::size_t finder = _msg_in_flight.find("\r\n"); + if(finder == std::string::npos) + logs::report(log_fatal_error, "client %d [getNextMsg()] : cannot get any other message, panic !", _id); + std::string msg = _msg_in_flight.substr(0, finder); + _msg_in_flight = _msg_in_flight.substr(finder + 2); + return msg; } Client::~Client() {} diff --git a/srcs/logs.cpp b/srcs/logs.cpp index 0f7f188..076fc86 100644 --- a/srcs/logs.cpp +++ b/srcs/logs.cpp @@ -6,7 +6,7 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 09:17:47 by maldavid #+# #+# */ -/* Updated: 2024/01/21 09:30:13 by maldavid ### ########.fr */ +/* Updated: 2024/01/22 09:46:54 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ @@ -15,8 +15,7 @@ #include #include #include - -#define BUFFER_SIZE 4096 +#include namespace irc { @@ -24,11 +23,11 @@ namespace irc { void report(LogType type, std::string message, ...) { - char buffer[BUFFER_SIZE]; + char buffer[LOGS_BUFFER_SIZE]; va_list al; va_start(al, message); - vsnprintf(buffer, BUFFER_SIZE, message.c_str(), al); + vsnprintf(buffer, LOGS_BUFFER_SIZE, message.c_str(), al); va_end(al); switch(type) diff --git a/srcs/main.cpp b/srcs/main.cpp index 5254c2b..8a616c1 100644 --- a/srcs/main.cpp +++ b/srcs/main.cpp @@ -6,7 +6,7 @@ /* By: vvaas +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/20 09:27:04 by maldavid #+# #+# */ -/* Updated: 2024/01/21 16:14:07 by vvaas ### ########.fr */ +/* Updated: 2024/01/22 09:30:16 by maldavid ### ########.fr */ /* */ /******************************************************************************/ @@ -33,5 +33,7 @@ int main(int ac, char** av) irc::logs::report(irc::log_fatal_error, "invalid port"); irc::Server server(port, av[2]); + server.wait(); + return 0; } diff --git a/srcs/message.cpp b/srcs/message.cpp index fdabfe3..1e788e4 100644 --- a/srcs/message.cpp +++ b/srcs/message.cpp @@ -6,18 +6,55 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 11:38:34 by maldavid #+# #+# */ -/* Updated: 2024/01/21 12:03:47 by maldavid ### ########.fr */ +/* Updated: 2024/01/22 13:02:56 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ #include #include +#include +#include namespace irc { + namespace details + { + void split(const std::string& s, std::vector& elems) + { + std::string token; + for(std::string::const_iterator it = s.begin(); it != s.end();) + { + if(std::isspace(*it)) + { + elems.push_back(token); + token.clear(); + while(std::isspace(*it) && it != s.end()) + it++; + } + else + { + token.push_back(*it); + it++; + } + } + elems.push_back(token); + } + + std::vector split(const std::string& s) + { + std::vector elems; + split(s, elems); + return elems; + } + } + Message::Message(unstd::SharedPtr client, const std::string& msg) : _raw_msg(msg), _client(client) { - + std::vector tokens = details::split(msg); + if(tokens.empty()) + return; + _command = tokens[0]; + _tokens = tokens; } Message::~Message() {} diff --git a/srcs/server.cpp b/srcs/server.cpp index 119c2ba..194d24f 100644 --- a/srcs/server.cpp +++ b/srcs/server.cpp @@ -6,7 +6,7 @@ /* By: vvaas +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 09:31:17 by maldavid #+# #+# */ -/* Updated: 2024/01/22 01:42:10 by vvaas ### ########.fr */ +/* Updated: 2024/01/22 14:54:21 by maldavid ### ########.fr */ /* */ /******************************************************************************/ @@ -19,114 +19,251 @@ #include #include #include - -/** Commands to handle - * NICK - * USER - * QUIT - * PART - * JOIN - * PRIVMSG - * NOTICE - * KICK - * MOTD - * TOPIC - * PING - * MODE - */ +#include +#include +#include namespace irc { Server::Server(int port, const std::string& password) : _s_len(sizeof(_s_data)), _password(password), _port(port), _active(true) { std::memset(&_s_data, 0, sizeof(sockaddr)); - init_socket(); - wait(); + initSocket(); } - void Server::init_socket_data(void) + void Server::initSocketData() { _s_data.sin_family = AF_INET; _s_data.sin_addr.s_addr = INADDR_ANY; _s_data.sin_port = htons(_port); _main_socket = socket(AF_INET, SOCK_STREAM, 0); // AF_INET == IPv4, SOCK_STREAM == TCP - if (_main_socket < 0) + if(_main_socket < 0) logs::report(log_fatal_error, "socket error"); logs::report(log_message, "socket succesfully started"); } - void Server::init_socket(void) + void Server::initSocket() { int opt = 0; - init_socket_data(); - if (setsockopt(_main_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) // SOL_SOCKET : modify socket only, SO_REUSEADDR : Reusable after program ends + initSocketData(); + if(setsockopt(_main_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) // SOL_SOCKET : modify socket only, SO_REUSEADDR : Reusable after program ends logs::report(log_fatal_error, "setsockopt() error (tout a pete)"); - if (bind(_main_socket, (struct sockaddr *)&_s_data, sizeof(_s_data)) != 0) // Bind _main_socket to localhost + + if(bind(_main_socket, reinterpret_cast(&_s_data), sizeof(_s_data)) != 0) // Bind _main_socket to localhost logs::report(log_fatal_error, "bind error"); logs::report(log_message, "bind successful, starting listen loop"); - if (listen(_main_socket, MAX_USERS) != 0) // init the listen with MAX_USERS + + if(listen(_main_socket, MAX_USERS) != 0) // init the listen with MAX_USERS logs::report(log_fatal_error, "listen error"); logs::report(log_message, "listen queue created successfully"); - logs::report(log_message, "Server is running"); + + logs::report(log_message, "server is up and running"); } - void Server::handle_input(void) + void Server::handleInput() { - char buffer[1024] = {0}; + char buffer[INPUT_SIZE] = { 0 }; - for (std::vector::iterator it = _client.begin(); it != _client.end(); it++) + for(std::vector >::iterator it = _client.begin(); it != _client.end(); ++it) { - if (!FD_ISSET(it->get_fd(), &_fd_set)) - continue ; - while (recv(it->get_fd(), buffer, 1024, 0) > 0) // read() but for socket fd + if(!FD_ISSET((*it)->getFD(), &_fd_set)) + continue; + + while(recv((*it)->getFD(), buffer, INPUT_SIZE, 0) > 0) // read() but for socket fd { - std::cout << AnsiColor::cyan << "[User " << it->get_string_id() << "] : " << AnsiColor::reset << buffer << std::flush; - memset(buffer, 0, sizeof(buffer)); // clear the buffer to avoid trash remaining + (*it)->newMsgInFlight(buffer); + while(handleMessage(*it)); + std::memset(buffer, 0, sizeof(buffer)); // clear the buffer to avoid trash remaining } - if (recv(it->get_fd(), buffer, 1024, 0) == 0) // recv return 0 if an user disconnect + + if(recv((*it)->getFD(), buffer, INPUT_SIZE, 0) == 0) // recv return 0 if an user disconnect { - std::cout << AnsiColor::cyan << "[User " << it->get_string_id() << "] : " << AnsiColor::reset << "Disconnected" << std::endl; - it = _client.erase(it) - 1; // magic bitch + logs::report(log_message, "User %d disconnected", (*it)->getID()); + it = _client.erase(it) - 1; } } } - void Server::wait(void) + void Server::wait() { int tmp; int fd = 0; int i = 0; socklen_t len = sizeof(sockaddr_in); - while (_active) + while(_active) { FD_ZERO(&_fd_set); FD_SET(_main_socket, &_fd_set); - for (std::vector::iterator it = _client.begin(); it != _client.end(); it++) - FD_SET(it->get_fd(), &_fd_set); + + for(std::vector >::iterator it = _client.begin(); it != _client.end(); ++it) + FD_SET((*it)->getFD(), &_fd_set); + tmp = select(MAX_USERS, &_fd_set, NULL, NULL, NULL); // SELECT blocks till a connection or message is received, and let only those in _fd_set - if (tmp < 0) + if(tmp < 0) logs::report(log_fatal_error, "select fd error"); - if (FD_ISSET(_main_socket, &_fd_set)) // if it's a new connection + + if(FD_ISSET(_main_socket, &_fd_set)) // if it's a new connection { sockaddr_in cli_sock; fd = accept(_main_socket, (sockaddr *)&cli_sock, &len); // adds the new connection - if (fd < 0) + if(fd < 0) logs::report(log_fatal_error, "accept() error"); - if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + if(fcntl(fd, F_SETFL, O_NONBLOCK) < 0) logs::report(log_fatal_error, "fcntl() error"); - _client.push_back(Client(fd, cli_sock, i++)); // put the client into the vector used in handle_input - std::cout << AnsiColor::cyan << "[User " << _client.back().get_string_id() << "] : " << AnsiColor::reset << "Connected" << std::endl; + + unstd::SharedPtr new_client(new Client(fd, cli_sock, i++)); + _client.push_back(new_client); // put the client into the vector used in handle_input + + logs::report(log_message, "User %d connected", _client.back()->getID()); } - handle_input(); + + handleInput(); } } + bool Server::handleMessage(unstd::SharedPtr client) + { + if(client->getMsgInFlight().empty()) // if there are no commands just return + return false; + + const Message msg(client, client->getNextMsg()); + + if(msg.getCmd() == "NICK") + handleNick(client, msg); + else if(msg.getCmd() == "USER") + handleUser(client, msg); + else if(msg.getCmd() == "QUIT") + handleQuit(client, msg); + else if(msg.getCmd() == "PART") + handlePart(client, msg); + else if(msg.getCmd() == "JOIN") + handleJoin(client, msg); + else if(msg.getCmd() == "PRIVMSG") + handlePrivMsg(client, msg); + else if(msg.getCmd() == "NOTICE") + handleNotice(client, msg); + else if(msg.getCmd() == "KICK") + handleKick(client, msg); + else if(msg.getCmd() == "MOTD") + handleMotD(client, msg); + else if(msg.getCmd() == "TOPIC") + handleTopic(client, msg); + else if(msg.getCmd() == "PING") + handlePing(client, msg); + else if(msg.getCmd() == "MODE") + handleMode(client, msg); + return true; + } + + void Server::handleNick(unstd::SharedPtr client, const Message& msg) + { + // TODO : handle nick collisions + if(msg.getTokens().size() != 2 && msg.getTokens().size() != 3) + { + logs::report(log_error, "NICK, invalid command '%s'", msg.getRawMsg().c_str()); + return; + } + client->printUserHeader(); + client->setNewNickName(msg.getTokens()[1]); + std::cout << "new nickname, " << client->getNickName() << std::endl; + } + + void Server::handleUser(unstd::SharedPtr client, const Message& msg) + { + if(msg.getTokens().size() != 5) + { + logs::report(log_error, "USER, invalid command '%s'", msg.getRawMsg().c_str()); + return; + } + client->printUserHeader(); + client->setNewUserName(msg.getTokens()[1]); + std::cout << "new username, " << client->getUserName() << std::endl; + + //client->printUserHeader(); + //client->setNewRealName(msg.getTokens()[1]); + //std::cout << "new realname, " << client->getRealName() << std::endl; + } + + void Server::handleQuit(unstd::SharedPtr client, const Message& msg) + { + (void)msg; + client->printUserHeader(); + std::cout << "quit" << std::endl; + } + + void Server::handlePart(unstd::SharedPtr client, const Message& msg) + { + (void)client; + (void)msg; + } + + void Server::handleJoin(unstd::SharedPtr client, const Message& msg) + { + if(msg.getTokens().size() > 3) + { + logs::report(log_error, "JOIN, invalid command '%s'", msg.getRawMsg().c_str()); + return; + } + + std::vector::const_iterator it; + for(it = _channels.begin(); it != _channels.end(); ++it) + { + if(msg.getTokens()[1] == it->getName()) + break; + } + if(it == _channels.end()) + _channels.push_back(Channel(msg.getTokens()[1])); + client->printUserHeader(); + std::cout << "joining new channel, " << msg.getTokens()[1] << std::endl; + } + + void Server::handlePrivMsg(unstd::SharedPtr client, const Message& msg) + { + (void)client; + (void)msg; + } + + void Server::handleNotice(unstd::SharedPtr client, const Message& msg) + { + (void)client; + (void)msg; + } + + void Server::handleKick(unstd::SharedPtr client, const Message& msg) + { + (void)client; + (void)msg; + } + + void Server::handleMotD(unstd::SharedPtr client, const Message& msg) + { + (void)client; + (void)msg; + } + + void Server::handleTopic(unstd::SharedPtr client, const Message& msg) + { + (void)client; + (void)msg; + } + + void Server::handlePing(unstd::SharedPtr client, const Message& msg) + { + (void)client; + (void)msg; + } + + void Server::handleMode(unstd::SharedPtr client, const Message& msg) + { + (void)client; + (void)msg; + } + Server::~Server() { if (_main_socket > 0) close(_main_socket); } - }