Merge branch 'master' into raph

This commit is contained in:
Raphaël 2025-05-24 18:03:06 +02:00 committed by GitHub
commit 520814d6bd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 357 additions and 225 deletions

View file

@ -1,14 +1,14 @@
# **************************************************************************** # #******************************************************************************#
# # # #
# ::: :::::::: # # ::: :::::::: #
# Makefile :+: :+: :+: # # Makefile :+: :+: :+: #
# +:+ +:+ +:+ # # +:+ +:+ +:+ #
# By: sben-tay <sben-tay@student.42.fr> +#+ +:+ +#+ # # By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ # # +#+#+#+#+#+ +#+ #
# Created: 2025/05/02 15:40:00 by rparodi #+# #+# # # Created: 2025/05/02 15:40:00 by rparodi #+# #+# #
# Updated: 2025/05/24 16:48:43 by rparodi ### ########.fr # # Updated: 2025/05/24 16:48:43 by rparodi ### ########.fr #
# # # #
# **************************************************************************** # #******************************************************************************#
# Name # Name
@ -85,30 +85,37 @@ debug: CXXFLAGS += -g
# debug: CXXFLAGS += -fsanitize=address # debug: CXXFLAGS += -fsanitize=address
debug: re debug: re
PORT ?= 4243
test: debug test: debug
@printf '$(GREY) now running with\n\t- Port:\t\t$(GREEN)4243$(GREY)\n\t- Password:\t$(GREEN)irc$(END)\n' @printf '$(GREY) now running with\n\t- Port:\t\t$(GREEN)$(PORT)$(GREY)\n\t- Password:\t$(GREEN)irc$(END)\n'
@if tmux has-session -t $(SESSION) 2>/dev/null; then \ @if tmux has-session -t $(SESSION) 2>/dev/null; then \
tmux kill-session -t $(SESSION); \ tmux kill-session -t $(SESSION); \
fi fi
@tmux new-session -d -s $(SESSION) \ @tmux new-session -d -s $(SESSION) \
'bash -lc "./$(NAME) 4243 irc; exec bash"' 'bash -lc "./$(NAME) $(PORT) irc; exec bash"'
@tmux split-window -h -p 70 -t $(SESSION):0 \ @tmux split-window -h -p 70 -t $(SESSION):0 \
'bash -lc "irssi -c localhost -p 4243 -w irc || exec yes \"irssi exit code: $?\""' 'bash -lc "irssi -c localhost -p $(PORT) -w irc || exec yes \"irssi exit code: $?\""'
@tmux split-window -v -p 50 -t $(SESSION):0.1 \ @tmux split-window -v -p 50 -t $(SESSION):0.1 \
'bash -lc "nc localhost 4243 || exec yes \"netcat exit code: $?\""' 'bash -lc "nc localhost $(PORT) || exec yes \"netcat exit code: $?\""'
@tmux split-window -v -p 50 -t $(SESSION):0.2 \
'bash -lc "nc localhost $(PORT) || exec yes \"netcat exit code: $?\""'
@tmux split-window -v -p 50 -t $(SESSION):0.3 \
'bash -lc "nc localhost $(PORT) || exec yes \"netcat exit code: $?\""'
@tmux attach -t $(SESSION) @tmux attach -t $(SESSION)
run: re run: re
@printf '$(GREY) now running with\n\t- Port:\t\t$(GREEN)4243$(GREY)\n\t- Password:\t$(GREEN)irc$(END)\n' @printf '$(GREY) now running with\n\t- Port:\t\t$(GREEN)$(PORT)$(GREY)\n\t- Password:\t$(GREEN)irc$(END)\n'
@if tmux has-session -t $(SESSION) 2>/dev/null; then \ @if tmux has-session -t $(SESSION) 2>/dev/null; then \
tmux kill-session -t $(SESSION); \ tmux kill-session -t $(SESSION); \
fi fi
@tmux new-session -d -s $(SESSION) \ @tmux new-session -d -s $(SESSION) \
'bash -lc "./$(NAME) 4243 irc; exec bash"' 'bash -lc "./$(NAME) $(PORT) irc; exec bash"'
@tmux split-window -h -p 70 -t $(SESSION):0 \ @tmux split-window -h -p 70 -t $(SESSION):0 \
'bash -lc "irssi -c localhost -p 4243 -w irc || exec yes \"irssi exit code: $?\""' 'bash -lc "irssi -c localhost -p $(PORT) -w irc || exec yes \"irssi exit code: $?\""'
@tmux split-window -v -p 50 -t $(SESSION):0.1 \ @tmux split-window -v -p 50 -t $(SESSION):0.1 \
'bash -lc "nc localhost 4243 || exec yes \"netcat exit code: $?\""' 'bash -lc "nc localhost $(PORT) || exec yes \"netcat exit code: $?\""'
@tmux attach -t $(SESSION) @tmux attach -t $(SESSION)
# Header # Header

View file

@ -10,11 +10,13 @@ class "main()"
' ======================== ' ========================
class Server { class Server {
- _port : int - _port : int
- _serverFd : int
- _password : string - _password : string
- _server_fd : int - _pollManager : PollManager
- _poll : PollManager - _users : map<int, User*>
+ Server(port : int, password : string) + Server(port : int, password : string)
+ ~Server()
+ start() : void + start() : void
+ getPort() : int + getPort() : int
+ showInfo() : void + showInfo() : void
@ -25,38 +27,42 @@ class Server {
' ============================ ' ============================
class PollManager { class PollManager {
- _fds : vector<pollfd> - _fds : vector<pollfd>
- _users : map<int, User>
+ addUser(fd : int) : void + PollManager()
+ removeUser(fd : int) : void + ~PollManager()
+ updateUser(fd : int, events : short) : void + setServerFd(fd : int) : void
+ pollLoop(server_fd : int) : void + addClient(fd : short unsigned) : void
+ readFromUser(fd : int) : void + removeClient(fd : short unsigned) : void
+ writeToUser(fd : int) : void + updateServer(fd : short unsigned) : void
+ pollLoop(server_fd : int, newClients : vector<int>, disconnected : vector<int>, readyClients : vector<pair<int, string>>) : void
} }
' ======================== ' ========================
' CLASS: User ' CLASS: User
' ======================== ' ========================
class User { class User {
- _fd : int - _fd : short unsigned int
- _nickname : string
- _username : string
- _hostname : string
- _readBuffer : string
- _writeBuffer : string
- _registered : bool - _registered : bool
- _nickname : string
- _hostname : string
- _read_buffer : string
- _write_buffer : string
- _username : string
- _hasNick : bool
- _hasUser : bool
+ getFd() : int + User(fd : short unsigned int)
+ getName() : string + getFd() : short unsigned int
+ setNickname(name : string) : void
+ appendToReadBuffer(data : string) : void
+ appendToWriteBuffer(data : string) : void
+ extractFullCommand() : string
+ isReadyToSend() : bool + isReadyToSend() : bool
+ isRegistered() : bool + isRegistered() : bool
+ getName() : string
+ extractFullCommand() : string
+ appendToReadBuffer(data : string) : void
+ appendToWriteBuffer(data : string) : void
+ setNickname(nickname : string) : void
+ setUsername(username : string) : void
+ checkRegistration() : void
} }
' ======================== ' ========================
' CLASS: Channel ' CLASS: Channel
' ======================== ' ========================

View file

@ -1,14 +1,14 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* PollManager.hpp :+: :+: :+: */ /* PollManager.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: sben-tay <sben-tay@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/19 19:15:13 by omoudni #+# #+# */ /* Created: 2025/05/19 19:15:13 by omoudni #+# #+# */
/* Updated: 2025/05/20 17:22:59 by sben-tay ### ########.fr */ /* Updated: 2025/05/22 17:30:00 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#pragma once #pragma once
@ -16,19 +16,18 @@
#include <vector> #include <vector>
#include <string> #include <string>
class PollManager { class PollManager
{
public: public:
PollManager(); PollManager();
~PollManager(); ~PollManager();
void addClient(int fd); void setServerFd(int fd);
void removeClient(int fd); void addClient(short unsigned fd);
void updateServer(int fd); void removeClient(short unsigned fd);
void pollLoop(int server_fd); void updateServer(short unsigned fd);
void pollLoop(int server_fd, std::vector<int> &newClients, std::vector<int> &disconnected, std::vector<std::pair<int, std::string> > &readyClients);
private: private:
std::vector<struct pollfd> _fds; std::vector<struct pollfd> _fds;
std::map<int, std::string> _buffers;
}; };

View file

@ -1,14 +1,14 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* core.hpp :+: :+: :+: */ /* core.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: sben-tay <sben-tay@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/12 14:16:03 by rparodi #+# #+# */ /* Created: 2025/05/12 14:16:03 by rparodi #+# #+# */
/* Updated: 2025/05/20 17:23:41 by sben-tay ### ########.fr */ /* Updated: 2025/05/21 21:18:22 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#pragma once #pragma once
@ -30,6 +30,11 @@ enum e_state {
}; };
// INCLUDES (not to repeat)
#include <stdexcept>
#include <iostream>
#include <string>
#include "user.hpp"
#include "PollManager.hpp" #include "PollManager.hpp"
#include "color.hpp" #include "color.hpp"
#include "server.hpp" #include "server.hpp"

View file

@ -1,35 +1,36 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* server.hpp :+: :+: :+: */ /* server.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: rparodi <rparodi@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/20 21:50:32 by rparodi #+# #+# */ /* Created: 2025/05/20 21:50:32 by rparodi #+# #+# */
/* Updated: 2025/05/24 16:48:04 by rparodi ### ########.fr */ /* Updated: 2025/05/24 16:48:04 by rparodi ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#pragma once #pragma once
#include <string> #include "core.hpp"
#include "PollManager.hpp"
#include <list>
#include "user.hpp"
#include "channel.hpp"
class Server { class User;
private: class Server
int _port; {
int _serverFd; private:
std::string _password; int _port;
PollManager _pollManager; int _serverFd;
public: std::string _password;
Server(int port, const std::string &password); PollManager _pollManager;
~Server(); std::map<int, User *> _users;
void start();
std::list<User *> getUsersList() const; public:
std::list<Channel *> getChannelsList() const; Server(int port, const std::string &password);
unsigned short int getPort() const; ~Server();
void showInfo() const; void start();
unsigned short int getPort() const;
std::list<User *> getUsersList() const;
std::list<Channel *> getChannelsList() const;
void showInfo() const;
void printUsers() const;
}; };

View file

@ -1,36 +1,42 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* user.hpp :+: :+: :+: */ /* user.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: rparodi <rparodi@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/20 21:57:49 by rparodi #+# #+# */ /* Created: 2025/05/20 21:57:49 by rparodi #+# #+# */
/* Updated: 2025/05/20 22:13:41 by rparodi ### ########.fr */ /* Updated: 2025/05/21 21:16:56 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#pragma once #pragma once
#include <string> #include "core.hpp"
#include <iostream>
#include "color.hpp"
class User { class User
private: {
int _fd; private:
bool _registered; short unsigned int _fd;
std::string _nickname; bool _registered;
std::string _hostname; std::string _nickname;
std::string _read_buffer; std::string _hostname;
std::string _write_buffer; std::string _read_buffer;
public: std::string _write_buffer;
int getFd() const; std::string _username;
bool isReadyToSend() const; bool _hasNick;
bool isRegistered() const; bool _hasUser;
std::string getName() const;
std::string extractFullCommand(); public:
void appendToReadBuffer(const std::string &data); User(short unsigned fd);
void appendToWriteBuffer(const std::string &data); short unsigned int getFd() const;
void setNickname(const std::string &nickname); bool isReadyToSend() const;
bool isRegistered() const;
std::string getName() const;
std::string extractFullCommand();
void appendToReadBuffer(const std::string &data);
void appendToWriteBuffer(const std::string &data);
void setNickname(const std::string &nickname);
void setUsername(const std::string &username);
void checkRegistration();
}; };

View file

@ -10,72 +10,86 @@
PollManager::PollManager() {} PollManager::PollManager() {}
PollManager::~PollManager() { PollManager::~PollManager()
for (size_t i = 0; i < _fds.size(); ++i) { {
close(_fds[i].fd); for (size_t i = 0; i < _fds.size(); ++i)
} {
} close(_fds[i].fd);
void PollManager::pollLoop(int server_fd) {
struct pollfd server_pollfd;
server_pollfd.fd = server_fd;
server_pollfd.events = POLLIN;
_fds.push_back(server_pollfd);
std::cout << "Serveur prêt à accepter des connexions..." << std::endl;
while (true) {
int poll_count = poll(&_fds[0], _fds.size(), -1);
if (poll_count == -1) {
std::cerr << "poll error\n" << std::endl;
continue;
}
for (size_t i = 0; i < _fds.size(); ++i) {
int fd = _fds[i].fd;
if ((fd == server_fd) && (_fds[i].revents & POLLIN)) {
int client_fd = accept(server_fd, NULL, NULL);
if (client_fd == -1) {
std::cerr << "Error accept()" << std::endl;
continue;
}
addClient(client_fd);
}
else if (_fds[i].revents & POLLIN) {
char buffer[1024];
ssize_t bytes = recv(fd, buffer, sizeof(buffer) -1, 0);
if (bytes > 0) {
buffer[bytes] = '\0';
_buffers[fd] += buffer;
std::cout << "Client " << fd << " send : " << buffer;
} else {
std::cout << "Client " << fd << " disconected." << std::endl;
// removeClient(fd);
--i;
}
}
}
} }
} }
void PollManager::addClient(int fd) { void PollManager::pollLoop(int server_fd, std::vector<int> &newClients, std::vector<int> &disconnected, std::vector<std::pair<int, std::string> > &readyClients)
{
int poll_count = poll(&_fds[0], _fds.size(), -1);
if (poll_count == -1)
{
std::cerr << "poll error\n"
<< std::endl;
return;
}
for (size_t i = 0; i < _fds.size(); ++i)
{
short unsigned fd = _fds[i].fd;
if ((fd == server_fd) && (_fds[i].revents & POLLIN))
{
int client_fd = accept(server_fd, NULL, NULL);
if (client_fd == -1)
{
std::cerr << "Error accept()" << std::endl;
continue;
}
addClient(client_fd);
newClients.push_back(client_fd);
}
else if (_fds[i].revents & POLLIN)
{
char buffer[1024];
ssize_t bytes = recv(fd, buffer, sizeof(buffer) - 1, 0);
if (bytes > 0)
{
buffer[bytes] = '\0';
readyClients.push_back(std::make_pair(fd, std::string(buffer)));
std::cout << "Received data from fd " << fd << ": " << buffer << std::endl;
std::cout << std::flush;
}
else
{
removeClient(fd);
disconnected.push_back(fd);
--i;
}
}
}
// }
}
void PollManager::addClient(short unsigned fd)
{
struct pollfd pfd; struct pollfd pfd;
pfd.fd = fd; pfd.fd = fd;
pfd.events = POLLIN; pfd.events = POLLIN;
_fds.push_back(pfd); _fds.push_back(pfd);
_buffers[fd] = ""; std::cout << "Client connected (fd " << fd << ")" << std::endl;
std::cout << "Client connecté (fd " << fd << ")" << std::endl;
} }
void PollManager::removeClient(int fd) { void PollManager::removeClient(short unsigned fd)
for (size_t i = 0; i < _fds.size(); ++i) { {
if (_fds[i].fd == fd) { for (size_t i = 0; i < _fds.size(); ++i)
{
if (_fds[i].fd == fd)
{
_fds.erase(_fds.begin() + i); _fds.erase(_fds.begin() + i);
break; break;
} }
} }
_buffers.erase(fd);
close(fd); close(fd);
std::cout << "Client retiré (fd " << fd << ")" << std::endl; std::cout << "Client disconnected (fd " << fd << ")" << std::endl;
}
void PollManager::setServerFd(int fd)
{
struct pollfd pfd;
pfd.fd = fd;
pfd.events = POLLIN;
_fds.push_back(pfd);
} }

View file

@ -1,18 +1,19 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* Server.cpp :+: :+: :+: */ /* Server.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: sben-tay <sben-tay@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/13 11:11:07 by rparodi #+# #+# */ /* Created: 2025/05/13 11:11:07 by rparodi #+# #+# */
/* Updated: 2025/05/21 13:03:51 by rparodi ### ########.fr */ /* Updated: 2025/05/22 18:45:41 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#include "color.hpp" #include "color.hpp"
#include "server.hpp" #include "server.hpp"
#include "core.hpp" #include "core.hpp"
#include "PollManager.hpp"
#include <iostream> #include <iostream>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -28,21 +29,28 @@
* *
* @note Thanks to check the port / password before give them to the constructor. * @note Thanks to check the port / password before give them to the constructor.
*/ */
Server::Server(int port, const std::string &password) : _port(port), _password(password) { Server::Server(int port, const std::string &password) : _port(port), _password(password)
std::cout << CLR_GREY << "Info: Server constructor called" << CLR_RESET << std::endl; {
std::cout << CLR_GREY << "Info: Server constructor called" << CLR_RESET << std::endl;
} }
/** /**
* @brief The default destructor of the Server class. * @brief The default destructor of the Server class.
*/ */
Server::~Server() { Server::~Server()
std::cout << CLR_GREY << "Info: Server destructor called" << CLR_RESET << std::endl; {
std::cout << CLR_GREY << "Info: Server destructor called" << CLR_RESET << std::endl;
if (_serverFd != -1)
{
close(_serverFd);
}
} }
void Server::start()
void Server::start() { {
_serverFd = socket(AF_INET, SOCK_STREAM, 0); _serverFd = socket(AF_INET, SOCK_STREAM, 0);
if (_serverFd == -1) { if (_serverFd == -1)
{
std::cerr << "Erreur socket" << std::endl; std::cerr << "Erreur socket" << std::endl;
return; return;
} }
@ -53,16 +61,57 @@ void Server::start() {
addr.sin_addr.s_addr = INADDR_ANY; addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(_port); addr.sin_port = htons(_port);
if (bind(_serverFd, (sockaddr*)&addr, sizeof(addr)) == -1 || if (bind(_serverFd, (sockaddr *)&addr, sizeof(addr)) == -1 ||
listen(_serverFd, 10) == -1) { listen(_serverFd, 10) == -1)
{
std::cerr << "Erreur bind/listen" << std::endl; std::cerr << "Erreur bind/listen" << std::endl;
close(_serverFd); close(_serverFd);
return; return;
} }
std::cout << "Serveur lancé sur le port " << _port << std::endl; std::cout << "Serveur lancé sur le port " << _port << std::endl;
_pollManager.setServerFd(_serverFd);
std::vector<int> newClients;
std::vector<int> disconnected;
std::vector<std::pair<int, std::string> > readyClients;
while (true)
{
printUsers();
newClients.clear();
disconnected.clear();
readyClients.clear();
_pollManager.pollLoop(_serverFd, newClients, disconnected,
readyClients);
std::cout << "Poll loop finished" << std::endl;
std::cout << "New clients: " << newClients.size() << std::endl;
for (size_t i = 0; i < newClients.size(); ++i)
{
_users[newClients[i]] = new User(newClients[i]);
}
// Handle disconnected clients
for (size_t i = 0; i < disconnected.size(); ++i)
{
delete _users[disconnected[i]];
_users.erase(disconnected[i]);
}
for (size_t i = 0; i < readyClients.size(); ++i)
{
int fd = readyClients[i].first;
const std::string &data = readyClients[i].second;
if (_users.count(fd))
{
_users[fd]->appendToReadBuffer(data);
std::string cmd;
while (!(cmd = _users[fd]->extractFullCommand()).empty())
{
// This prints every command/message received from any client
std::cout << "Client " << fd << " says: " << cmd << std::endl;
}
}
}
_pollManager.pollLoop(_serverFd); // Optionally: handle server shutdown, signals, etc.
}
close(_serverFd); close(_serverFd);
} }
@ -73,12 +122,12 @@ void Server::start() {
* It is used for debug purpose. * It is used for debug purpose.
*/ */
void Server::showInfo() const { void Server::showInfo() const
std::cout << std::endl; {
std::cout << CLR_BLUE << "IRCSettings:" << CLR_RESET << std::endl; std::cout << std::endl;
std::cout << CLR_BLUE << "\t- Port:\t\t" << CLR_GOLD << this->_port << CLR_RESET << std::endl; std::cout << CLR_BLUE << "IRCSettings:" << CLR_RESET << std::endl;
std::cout << CLR_BLUE << "\t- Password:\t" << CLR_GOLD << this->_password << CLR_RESET << std::endl; std::cout << CLR_BLUE << "\t- Port:\t\t" << CLR_GOLD << this->_port << CLR_RESET << std::endl;
std::cout << CLR_BLUE << "\t- Password:\t" << CLR_GOLD << this->_password << CLR_RESET << std::endl;
} }
/** /**
@ -86,7 +135,14 @@ void Server::showInfo() const {
* *
* @return the port of the server * @return the port of the server
*/ */
unsigned short int Server::getPort() const { unsigned short int Server::getPort() const { return this->_port; }
return this->_port;
}
void Server::printUsers() const
{
std::cout << "Connected users:" << std::endl;
for (std::map<int, User *>::const_iterator it = _users.begin(); it != _users.end(); ++it)
{
std::cout << "User fd: " << it->first << std::endl;
std::cout << "Nickname: " << it->second->getNickname() << std::endl;
}
}

View file

@ -1,24 +1,24 @@
/* ************************************************************************** */ /******************************************************************************/
/* */ /* */
/* ::: :::::::: */ /* ::: :::::::: */
/* user.cpp :+: :+: :+: */ /* user.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */ /* +:+ +:+ +:+ */
/* By: rparodi <rparodi@student.42.fr> +#+ +:+ +#+ */ /* By: omoudni <omoudni@student.42paris.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/05/20 22:03:36 by rparodi #+# #+# */ /* Created: 2025/05/21 20:37:12 by omoudni #+# #+# */
/* Updated: 2025/05/20 22:15:14 by rparodi ### ########.fr */ /* Updated: 2025/05/22 17:13:35 by omoudni ### ########.fr */
/* */ /* */
/* ************************************************************************** */ /******************************************************************************/
#include "user.hpp" #include "core.hpp"
/** // Constructor
* @brief Getter for the fd of the user User::User(short unsigned int fd) : _fd(fd), _registered(false), _hasNick(false), _hasUser(false) {}
*
* @return the actual fd of the user // Getter for fd
*/ short unsigned int User::getFd() const
int User::getFd() const { {
return this->_fd; return _fd;
} }
/** /**
@ -30,47 +30,85 @@ std::string User::getName() const {
return this->_nickname; return this->_nickname;
} }
/** void User::setUsername(const std::string &username)
* @brief Setter for the nickname of the user (also checker) {
* _username = username;
* @param nickname the nickname given to set to the user _hasUser = true;
*/ checkRegistration();
void User::setNickname(const std::string &nickname) {
if (nickname.empty()) {
throw std::invalid_argument("Nickname cannot be empty");
} else if (nickname == "anonymous") {
throw std::invalid_argument("Nickname cannot be 'anonymous'");
} else if (nickname.length() > 9) {
throw std::length_error("Nickname is too long");
} else if (nickname == this->_nickname) {
throw std::invalid_argument("The nickname is the same");
} else {
this->_nickname = nickname;
}
} }
/** // Setter for nickname (with basic checks)
* @brief Getter for the state of the user void User::setNickname(const std::string &nickname)
* {
* @return true if the user is registered, false otherwise if (nickname.empty())
*/ {
bool User::isRegistered() const { throw std::invalid_argument("Nickname cannot be empty");
return this->_registered; }
else if (nickname == "anonymous")
{
throw std::invalid_argument("Nickname cannot be 'anonymous'");
}
else if (nickname.length() > 9)
{
throw std::length_error("Nickname is too long");
}
else if (nickname == _nickname)
{
throw std::invalid_argument("The nickname is the same");
}
else
{
_nickname = nickname;
_hasNick = true;
checkRegistration();
}
} }
void User::appendToReadBuffer(const std::string &data) {
std::cerr << CLR_RED << "Error: Method not found (" << __FILE_NAME__ ":" << __LINE__ << ")" << CLR_RESET << std::endl; // Registration state
(void)data; bool User::isRegistered() const
{
return _registered;
} }
void User::appendToWriteBuffer(const std::string &data) {
std::cerr << CLR_RED << "Error: Method not found (" << __FILE_NAME__ ":" << __LINE__ << ")" << CLR_RESET << std::endl; // Append to read buffer
(void)data; void User::appendToReadBuffer(const std::string &data)
{
_read_buffer += data;
} }
// Append to write buffer
void User::appendToWriteBuffer(const std::string &data)
{
_write_buffer += data;
}
// Check registration
void User::checkRegistration()
{
if (!_registered && _hasNick && _hasUser)
{
_registered = true;
}
}
// Check if the user is ready to send
bool User::isReadyToSend() const
{
return !_write_buffer.empty();
}
// Extract full command from read buffer
std::string User::extractFullCommand() { std::string User::extractFullCommand() {
std::cerr << CLR_RED << "Error: Method not found (" << __FILE_NAME__ ":" << __LINE__ << ")" << CLR_RESET << std::endl; std::string command;
return nullptr; size_t pos = _read_buffer.find("\r\n");
} if (pos == std::string::npos)
pos = _read_buffer.find("\n"); // fallback
bool User::isReadyToSend() const { if (pos != std::string::npos) {
std::cerr << CLR_RED << "Error: Method not found (" << __FILE_NAME__ ":" << __LINE__ << ")" << CLR_RESET << std::endl; command = _read_buffer.substr(0, pos);
return (false); _read_buffer.erase(0, pos + 1);
if (_read_buffer[pos] == '\r') // clean up stray \r
_read_buffer.erase(0, 1);
}
return command;
} }