diff --git a/ft_irc3/.vscode/c_cpp_properties.json b/ft_irc3/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..c2098a2 --- /dev/null +++ b/ft_irc3/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "linux-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "linux-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/ft_irc3/.vscode/launch.json b/ft_irc3/.vscode/launch.json new file mode 100644 index 0000000..670f779 --- /dev/null +++ b/ft_irc3/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "C/C++ Runner: Debug Session", + "type": "cppdbg", + "request": "launch", + "args": [], + "stopAtEntry": false, + "externalConsole": false, + "cwd": "/home/fgras-ca/Bureau/ft_irc3/src", + "program": "/home/fgras-ca/Bureau/ft_irc3/src/build/Debug/outDebug", + "MIMode": "gdb", + "miDebuggerPath": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/ft_irc3/.vscode/settings.json b/ft_irc3/.vscode/settings.json new file mode 100644 index 0000000..137f792 --- /dev/null +++ b/ft_irc3/.vscode/settings.json @@ -0,0 +1,110 @@ +{ + "files.associations": { + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "map": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "typeinfo": "cpp", + "list": "cpp" +}, + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "*", + "**/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false +} \ No newline at end of file diff --git a/ft_irc3/Makefile b/ft_irc3/Makefile new file mode 100644 index 0000000..8f65f1c --- /dev/null +++ b/ft_irc3/Makefile @@ -0,0 +1,46 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# Makefile :+: :+: :+: # +# +:+ +:+ +:+ # +# By: fgras-ca +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2024/05/15 12:13:50 by fgras-ca #+# #+# # +# Updated: 2024/05/15 12:47:49 by fgras-ca ### ########.fr # +# # +# **************************************************************************** # + +CXX = g++ +CXXFLAGS = -Wall -Wextra -Werror -std=c++98 +LDFLAGS = -pthread + +SRC_DIR = src +INC_DIR = includes +OBJ_DIR = obj +LOG_DIR = logs + +SRCS = $(wildcard $(SRC_DIR)/*.cpp) +OBJS = $(SRCS:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o) + +NAME = ircserv + +all: $(NAME) + +$(NAME): $(OBJS) + $(CXX) $(CXXFLAGS) -I$(INC_DIR) -o $@ $^ $(LDFLAGS) + mkdir -p $(LOG_DIR) + touch $(LOG_DIR)/irc_server.log + +$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp + mkdir -p $(OBJ_DIR) + $(CXX) $(CXXFLAGS) -I$(INC_DIR) -c $< -o $@ + +clean: + rm -rf $(OBJ_DIR) $(NAME) + +fclean: clean + rm -rf $(LOG_DIR) + +re: fclean all + +.PHONY: all clean fclean re diff --git a/ft_irc3/includes/AdditionalCommands.hpp b/ft_irc3/includes/AdditionalCommands.hpp new file mode 100644 index 0000000..e5ecb10 --- /dev/null +++ b/ft_irc3/includes/AdditionalCommands.hpp @@ -0,0 +1,32 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* AdditionalCommands.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/16 15:23:58 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 16:03:22 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef ADDITIONALCOMMANDS_HPP +#define ADDITIONALCOMMANDS_HPP + +#include "Client.hpp" +#include "Server.hpp" +#include "Channel.hpp" +#include "Utils.hpp" + +#include +#include + +class Server; +class Client; +class Channel; + +void handlePartCommand(Server *server, Client *client, const std::string &command); +void handleNickCommand(Server *server, Client *client, const std::string &command); +void handlePrivmsgCommand(Server *server, Client *client, const std::string &command); + +#endif diff --git a/ft_irc3/includes/Channel.hpp b/ft_irc3/includes/Channel.hpp new file mode 100644 index 0000000..07693f1 --- /dev/null +++ b/ft_irc3/includes/Channel.hpp @@ -0,0 +1,38 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Channel.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:41:35 by fgras-ca #+# #+# */ +/* Updated: 2024/05/15 12:48:40 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CHANNEL_HPP +#define CHANNEL_HPP + +#include +#include +#include + +class Client; + +class Channel { +public: + Channel(const std::string &name); + ~Channel(); + + const std::string &getName() const; + void addClient(Client *client); + void removeClient(Client *client); + bool isEmpty() const; + const std::vector &getClients() const; + +private: + std::string _name; + std::vector _clients; +}; + +#endif diff --git a/ft_irc3/includes/Client.hpp b/ft_irc3/includes/Client.hpp new file mode 100644 index 0000000..26fc493 --- /dev/null +++ b/ft_irc3/includes/Client.hpp @@ -0,0 +1,49 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Client.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:15:42 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 18:18:02 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CLIENT_HPP +#define CLIENT_HPP + +#include + +class Client +{ +public: + Client(int fd); + + void setPassword(const std::string &password); + const std::string &getPassword() const; + + void setNickname(const std::string &nickname); + const std::string &getNickname() const; + + void setUser(const std::string &user); + const std::string &getUser() const; + + void setRealName(const std::string &realname); + const std::string &getRealName() const; + + bool isAuthenticated() const; + void authenticate(); + + int getFd() const; + +private: + int _fd; + std::string _password; + std::string _nickname; + std::string _user; + std::string _realname; + bool _authenticated; +}; + +#endif diff --git a/ft_irc3/includes/ClientManager.hpp b/ft_irc3/includes/ClientManager.hpp new file mode 100644 index 0000000..43e637b --- /dev/null +++ b/ft_irc3/includes/ClientManager.hpp @@ -0,0 +1,45 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ClientManager.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 18:30:07 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 16:26:03 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef CLIENTMANAGER_HPP +#define CLIENTMANAGER_HPP + +#include +#include +#include "Client.hpp" +#include "Channel.hpp" +#include "Server.hpp" +#include +#include "CommandHandler.hpp" +#include "Utils.hpp" +#include +#include +#include +#include +#include +#include + +class Server; + +class ClientManager +{ +public: + ClientManager(Server *server); + void acceptClient(); + void handleClient(int client_fd); + void removeClient(int client_fd); + +private: + Server *_server; +}; + +#endif diff --git a/ft_irc3/includes/CommandHandler.hpp b/ft_irc3/includes/CommandHandler.hpp new file mode 100644 index 0000000..d727419 --- /dev/null +++ b/ft_irc3/includes/CommandHandler.hpp @@ -0,0 +1,41 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* CommandHandler.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 18:14:12 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 17:35:46 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef COMMANDHANDLER_HPP +#define COMMANDHANDLER_HPP + +#include "Utils.hpp" +#include "Server.hpp" +#include "Client.hpp" +#include "Channel.hpp" +#include "AdditionalCommands.hpp" + +#include +#include +#include + +class Server; + +class CommandHandler +{ +private: + Server *_server; + +public: + CommandHandler(Server *server); + void handleCommand(Client *client, const std::string &command); + void processCommand(Client *client, const std::string &command); + void handleJoinCommand(Client *client, const std::string &channelName); + std::string getUsersList(Channel *channel); +}; + +#endif diff --git a/ft_irc3/includes/Server.hpp b/ft_irc3/includes/Server.hpp new file mode 100644 index 0000000..c4d0dda --- /dev/null +++ b/ft_irc3/includes/Server.hpp @@ -0,0 +1,76 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Server.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:15:13 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 17:40:34 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef SERVER_HPP +#define SERVER_HPP + +#include "Utils.hpp" +#include "Client.hpp" +#include "Channel.hpp" +#include "ClientManager.hpp" +#include "CommandHandler.hpp" +#include "AdditionalCommands.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Client; +class Channel; +class ClientManager; +class CommandHandler; + +class Server +{ +public: + Server(int port, const std::string &password); + ~Server(); + void run(); + void log(const std::string &message, const std::string &color = "\033[0m"); + void sendToClient(int client_fd, const std::string &message); + + // Méthodes d'accès pour les canaux et les clients + std::map &getChannels(); + std::map &getClients(); + const std::string &getPassword() const; + + +protected: + int _server_fd; + int _port; + std::string _password; + std::map _clients; + std::map _channels; + std::vector _poll_fds; + ClientManager *_clientManager; + CommandHandler *_commandHandler; + + friend class ClientManager; + friend class CommandHandler; + +private: + void initServer(); + void handleServerCommands(); + void acceptClient(); + void removeClient(int client_fd); +}; + +#endif diff --git a/ft_irc3/includes/Utils.hpp b/ft_irc3/includes/Utils.hpp new file mode 100644 index 0000000..c5108b5 --- /dev/null +++ b/ft_irc3/includes/Utils.hpp @@ -0,0 +1,30 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Utils.hpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:16:02 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 18:14:58 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef UTILS_HPP +#define UTILS_HPP + +#include +#include +#include + +#define RESET "\033[0m" +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" +#define MAGENTA "\033[35m" +#define CYAN "\033[36m" + +std::vector split(const std::string &input, const std::string &delimiters); + +#endif diff --git a/ft_irc3/ircserv b/ft_irc3/ircserv new file mode 100755 index 0000000..ac98a32 Binary files /dev/null and b/ft_irc3/ircserv differ diff --git a/ft_irc3/logs/irc_server.log b/ft_irc3/logs/irc_server.log new file mode 100644 index 0000000..e69de29 diff --git a/ft_irc3/obj/AdditionalCommands.o b/ft_irc3/obj/AdditionalCommands.o new file mode 100644 index 0000000..75cfa4b Binary files /dev/null and b/ft_irc3/obj/AdditionalCommands.o differ diff --git a/ft_irc3/obj/Channel.o b/ft_irc3/obj/Channel.o new file mode 100644 index 0000000..6fa4de2 Binary files /dev/null and b/ft_irc3/obj/Channel.o differ diff --git a/ft_irc3/obj/Client.o b/ft_irc3/obj/Client.o new file mode 100644 index 0000000..f5bdb3d Binary files /dev/null and b/ft_irc3/obj/Client.o differ diff --git a/ft_irc3/obj/ClientManager.o b/ft_irc3/obj/ClientManager.o new file mode 100644 index 0000000..2ff931a Binary files /dev/null and b/ft_irc3/obj/ClientManager.o differ diff --git a/ft_irc3/obj/CommandHandler.o b/ft_irc3/obj/CommandHandler.o new file mode 100644 index 0000000..b954b0e Binary files /dev/null and b/ft_irc3/obj/CommandHandler.o differ diff --git a/ft_irc3/obj/Server.o b/ft_irc3/obj/Server.o new file mode 100644 index 0000000..58a0b4d Binary files /dev/null and b/ft_irc3/obj/Server.o differ diff --git a/ft_irc3/obj/Utils.o b/ft_irc3/obj/Utils.o new file mode 100644 index 0000000..4853158 Binary files /dev/null and b/ft_irc3/obj/Utils.o differ diff --git a/ft_irc3/obj/main.o b/ft_irc3/obj/main.o new file mode 100644 index 0000000..3beabb4 Binary files /dev/null and b/ft_irc3/obj/main.o differ diff --git a/ft_irc3/src/AdditionalCommands.cpp b/ft_irc3/src/AdditionalCommands.cpp new file mode 100644 index 0000000..148f0cb --- /dev/null +++ b/ft_irc3/src/AdditionalCommands.cpp @@ -0,0 +1,108 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* AdditionalCommands.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/16 15:27:29 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 16:41:53 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "AdditionalCommands.hpp" + +// Fonction pour gérer la commande PART +void handlePartCommand(Server *server, Client *client, const std::string &command) +{ + std::istringstream iss(command); + std::string cmd, channelName; + iss >> cmd >> channelName; + + std::map &channels = server->getChannels(); + + if (channels.find(channelName) != channels.end()) + { + Channel *channel = channels[channelName]; + channel->removeClient(client); + + std::stringstream partMsg; + partMsg << ":" << client->getNickname() << " PART " << channelName << "\r\n"; + server->sendToClient(client->getFd(), partMsg.str()); + + if (channel->isEmpty()) + { + delete channel; + channels.erase(channelName); + } + + server->log("Client " + client->getNickname() + " left channel " + channelName, MAGENTA); + } + else + { + std::stringstream ss; + ss << ":server 403 " << client->getNickname() << " " << channelName << " :No such channel\r\n"; + server->sendToClient(client->getFd(), ss.str()); + } +} + +// Fonction pour gérer la commande NICK +void handleNickCommand(Server *server, Client *client, const std::string &command) +{ + std::istringstream iss(command); + std::string cmd, newNick; + iss >> cmd >> newNick; + + std::stringstream nickMsg; + nickMsg << ":" << client->getNickname() << " NICK " << newNick << "\r\n"; + server->sendToClient(client->getFd(), nickMsg.str()); + + client->setNickname(newNick); + + std::stringstream ss; + ss << "Client " << client->getFd() << " changed nickname to " << newNick; + server->log(ss.str(), GREEN); +} + +// Fonction pour gérer la commande PRIVMSG +void handlePrivmsgCommand(Server *server, Client *client, const std::string &command) +{ + std::istringstream iss(command); + std::string cmd, target, message; + iss >> cmd >> target; + getline(iss, message); + if (!message.empty() && message[0] == ':') + message = message.substr(1); + + std::map &channels = server->getChannels(); + std::map &clients = server->getClients(); + + if (channels.find(target) != channels.end()) + { + Channel *channel = channels[target]; + std::vector channelClients = channel->getClients(); + + for (size_t i = 0; i < channelClients.size(); ++i) + { + if (channelClients[i] != client) + { + std::stringstream privMsg; + privMsg << ":" << client->getNickname() << " PRIVMSG " << target << " :" << message << "\r\n"; + server->sendToClient(channelClients[i]->getFd(), privMsg.str()); + } + } + } + else if (clients.find(atoi(target.c_str())) != clients.end()) + { + Client *targetClient = clients[atoi(target.c_str())]; + std::stringstream privMsg; + privMsg << ":" << client->getNickname() << " PRIVMSG " << target << " :" << message << "\r\n"; + server->sendToClient(targetClient->getFd(), privMsg.str()); + } + else + { + std::stringstream ss; + ss << ":server 401 " << client->getNickname() << " " << target << " :No such nick/channel\r\n"; + server->sendToClient(client->getFd(), ss.str()); + } +} diff --git a/ft_irc3/src/Channel.cpp b/ft_irc3/src/Channel.cpp new file mode 100644 index 0000000..9a2bf89 --- /dev/null +++ b/ft_irc3/src/Channel.cpp @@ -0,0 +1,49 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Channel.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:42:57 by fgras-ca #+# #+# */ +/* Updated: 2024/05/15 12:54:40 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "Channel.hpp" +#include "Client.hpp" +#include + +Channel::Channel(const std::string &name) + : _name(name) +{ +} + +Channel::~Channel() +{ +} + +const std::string &Channel::getName() const +{ + return _name; +} + +void Channel::addClient(Client *client) +{ + _clients.push_back(client); +} + +void Channel::removeClient(Client *client) +{ + _clients.erase(std::remove(_clients.begin(), _clients.end(), client), _clients.end()); +} + +bool Channel::isEmpty() const +{ + return _clients.empty(); +} + +const std::vector &Channel::getClients() const +{ + return _clients; +} diff --git a/ft_irc3/src/Client.cpp b/ft_irc3/src/Client.cpp new file mode 100644 index 0000000..f071dbd --- /dev/null +++ b/ft_irc3/src/Client.cpp @@ -0,0 +1,104 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Client.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:17:42 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 18:18:19 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "Client.hpp" + +Client::Client(int fd) + : _fd(fd), _authenticated(false) +{ +} + +void Client::setPassword(const std::string &password) +{ + _password = password; +} + +const std::string &Client::getPassword() const +{ + return _password; +} + +void Client::setNickname(const std::string &nickname) +{ + _nickname = nickname; +} + +const std::string &Client::getNickname() const +{ + return _nickname; +} + +void Client::setUser(const std::string &user) +{ + _user = user; +} + +const std::string &Client::getUser() const +{ + return _user; +} + +void Client::setRealName(const std::string &realname) +{ + _realname = realname; +} + +const std::string &Client::getRealName() const +{ + return _realname; +} + +bool Client::isAuthenticated() const +{ + return _authenticated; +} + +void Client::authenticate() +{ + _authenticated = true; +} + +int Client::getFd() const +{ + return _fd; +} + + + + +/*Client::Client(int fd) + +Description: Constructeur de la classe Client. Initialise le client avec le descripteur de fichier fourni. +Paramètres: +int fd: Le descripteur de fichier du client. +Client::~Client() + +Description: Destructeur de la classe Client. Gère la destruction de l'objet. +int Client::getFd() const + +Description: Renvoie le descripteur de fichier du client. +Retourne: Le descripteur de fichier du client. +void Client::setPassword(const std::string &password) + +Description: Définit le mot de passe du client et le marque comme authentifié. +Paramètres: +const std::string &password: Le mot de passe à définir. +bool Client::isAuthenticated() const + +Description: Vérifie si le client est authentifié. +Retourne: true si le client est authentifié, false sinon. +void logMessage(const std::string &message, const std::string &color) + +Description: Enregistre un message dans les logs et l'affiche avec des couleurs dans la console. +Paramètres: +const std::string &message: Le message à enregistrer. +const std::string &color: La couleur du message.*/ \ No newline at end of file diff --git a/ft_irc3/src/ClientManager.cpp b/ft_irc3/src/ClientManager.cpp new file mode 100644 index 0000000..451d870 --- /dev/null +++ b/ft_irc3/src/ClientManager.cpp @@ -0,0 +1,110 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* ClientManager.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 18:32:23 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 16:25:50 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "ClientManager.hpp" + +ClientManager::ClientManager(Server *server) + : _server(server) +{ +} + +void ClientManager::acceptClient() +{ + int client_fd = accept(_server->_server_fd, NULL, NULL); + if (client_fd < 0) + { + _server->log("Accept failed.", RED); + return; + } + fcntl(client_fd, F_SETFL, O_NONBLOCK); + _server->_clients[client_fd] = new Client(client_fd); + struct pollfd client_pollfd; + client_pollfd.fd = client_fd; + client_pollfd.events = POLLIN; + _server->_poll_fds.push_back(client_pollfd); + + std::stringstream ss; + ss << "Client connected: " << client_fd; + _server->log(ss.str(), GREEN); + _server->sendToClient(client_fd, ":server NOTICE * :Connected to the server\r\n"); // Debug message +} + +void ClientManager::handleClient(int client_fd) +{ + char buffer[1024]; + std::memset(buffer, 0, sizeof(buffer)); + int bytes_received = recv(client_fd, buffer, sizeof(buffer), 0); + if (bytes_received <= 0) + { + removeClient(client_fd); + return; + } + + std::string message(buffer); + std::stringstream ss; + ss << "Received from client " << client_fd << ": " << message; + _server->log(ss.str(), BLUE); + + Client *client = _server->_clients[client_fd]; + std::istringstream message_stream(message); + std::string line; + + while (std::getline(message_stream, line)) + { + // Suppression des caractères de fin de ligne + line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); + line.erase(std::remove(line.begin(), line.end(), '\n'), line.end()); + + _server->_commandHandler->handleCommand(client, line); + } +} + +void ClientManager::removeClient(int client_fd) +{ + Client *client = _server->_clients[client_fd]; + if (client) + { + // Retirer le client de tous les canaux auxquels il appartient + std::map::iterator it = _server->_channels.begin(); + while (it != _server->_channels.end()) + { + it->second->removeClient(client); + if (it->second->isEmpty()) + { + delete it->second; + std::map::iterator it_to_delete = it++; + _server->_channels.erase(it_to_delete); + } + else + { + ++it; + } + } + delete client; + _server->_clients.erase(client_fd); + } + + std::vector::iterator it_poll = _server->_poll_fds.begin(); + while (it_poll != _server->_poll_fds.end()) + { + if (it_poll->fd == client_fd) + { + _server->_poll_fds.erase(it_poll); + break; + } + ++it_poll; + } + + std::stringstream ss; + ss << "Client disconnected: " << client_fd; + _server->log(ss.str(), YELLOW); +} diff --git a/ft_irc3/src/CommandHandler.cpp b/ft_irc3/src/CommandHandler.cpp new file mode 100644 index 0000000..fecc912 --- /dev/null +++ b/ft_irc3/src/CommandHandler.cpp @@ -0,0 +1,152 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* CommandHandler.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 18:26:34 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 19:34:36 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "CommandHandler.hpp" + +CommandHandler::CommandHandler(Server *server) + : _server(server) +{ +} + +void CommandHandler::handleCommand(Client *client, const std::string &command) +{ + std::vector tokens = split(command, " \n\r\t"); + + if (tokens.empty()) + { + return; + } + + std::string commandType = tokens[0]; + + if (commandType == "PASS") + { + if (tokens.size() > 1 && tokens[1] == _server->_password) + { + client->setPassword(tokens[1]); + _server->sendToClient(client->getFd(), ":server 001 " + client->getNickname() + " :Password accepted\r\n"); + _server->log("Client " + client->getNickname() + " provided correct password.", GREEN); + } + else + { + _server->sendToClient(client->getFd(), ":server 464 " + client->getNickname() + " :Invalid password\r\n"); + _server->log("Client " + client->getNickname() + " failed authentication password.", RED); + } + } + else if (commandType == "NICK") + { + if (tokens.size() > 1) + { + client->setNickname(tokens[1]); + std::stringstream ss; + ss << "Client " << client->getFd() << " Nickname: " << tokens[1]; + _server->log(ss.str(), GREEN); + _server->sendToClient(client->getFd(), ":" + tokens[1] + " NICK " + tokens[1] + "\r\n"); + } + } + else if (commandType == "USER") + { + if (tokens.size() > 4) + { + client->setUser(tokens[1]); + client->setRealName(tokens[4].substr(1)); // remove leading ':' + if (client->getPassword() == _server->_password && !client->getNickname().empty()) + { + client->authenticate(); + _server->sendToClient(client->getFd(), ":server 001 " + client->getNickname() + " :Welcome to the IRC server\r\n"); + _server->log("Client " + client->getNickname() + " authenticated.", GREEN); + } + } + } + else + { + if (!client->isAuthenticated()) + { + _server->sendToClient(client->getFd(), ":server 451 " + client->getNickname() + " :Please provide the password first\r\n"); + _server->log("Client " + client->getNickname() + " failed authentication.", RED); + } + else + { + processCommand(client, command); + } + } + std::cout << "Client " << client->getFd() << " " << client->getNickname() << " " << client->getUser() << " " << client->getPassword() << " " << client->getRealName() << std::endl; +} + +void CommandHandler::processCommand(Client *client, const std::string &command) +{ + if (command.find("JOIN") == 0) + { + std::string channelName = command.substr(5); + handleJoinCommand(client, channelName); + } + else if (command.find("PART") == 0) + { + handlePartCommand(_server, client, command); + } + else if (command.find("NICK") == 0) + { + handleNickCommand(_server, client, command); + } + else if (command.find("PRIVMSG") == 0) + { + handlePrivmsgCommand(_server, client, command); + } + else + { + std::stringstream ss; + ss << ":server 421 " << client->getNickname() << " " << command << " :Unknown command\r\n"; + _server->sendToClient(client->getFd(), ss.str()); + _server->log("Message from client " + client->getNickname() + ": " + command, MAGENTA); + } +} + +void CommandHandler::handleJoinCommand(Client *client, const std::string &channelName) +{ + if (_server->getChannels().find(channelName) == _server->getChannels().end()) + { + _server->getChannels()[channelName] = new Channel(channelName); + _server->log("Channel created: " + channelName, GREEN); + } + Channel *channel = _server->getChannels()[channelName]; + channel->addClient(client); + + std::stringstream joinMsg; + joinMsg << ":" << client->getNickname() << " JOIN :" << channelName << "\r\n"; + _server->sendToClient(client->getFd(), joinMsg.str()); + + std::string usersList = getUsersList(channel); + _server->sendToClient(client->getFd(), usersList); + + std::stringstream endOfNamesMsg; + endOfNamesMsg << ":server 366 " << client->getNickname() << " " << channelName << " :End of /NAMES list.\r\n"; + _server->sendToClient(client->getFd(), endOfNamesMsg.str()); + + std::stringstream ss; + ss << "Client " << client->getNickname() << " joined channel " << channelName; + _server->log(ss.str(), MAGENTA); +} + +std::string CommandHandler::getUsersList(Channel *channel) +{ + std::vector clients = channel->getClients(); + std::stringstream ss; + ss << ":server 353 " << clients[0]->getNickname() << " = " << channel->getName() << " :"; + for (size_t i = 0; i < clients.size(); ++i) + { + if (i > 0) + ss << " "; + ss << clients[i]->getNickname(); + } + ss << "\r\n"; + return ss.str(); +} diff --git a/ft_irc3/src/Server.cpp b/ft_irc3/src/Server.cpp new file mode 100644 index 0000000..c40eb54 --- /dev/null +++ b/ft_irc3/src/Server.cpp @@ -0,0 +1,273 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Server.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:17:12 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 18:52:19 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "Server.hpp" + +Server::Server(int port, const std::string &password) + : _port(port), _password(password), _clientManager(new ClientManager(this)), _commandHandler(new CommandHandler(this)) +{ + initServer(); +} + +Server::~Server() +{ + delete _clientManager; + delete _commandHandler; + + for (std::map::iterator it = _clients.begin(); it != _clients.end(); ++it) + { + delete it->second; + } + for (std::map::iterator it = _channels.begin(); it != _channels.end(); ++it) + { + delete it->second; + } + close(_server_fd); +} + +void Server::initServer() +{ + _server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (_server_fd == -1) + { + log("Failed to create socket.", RED); + exit(EXIT_FAILURE); + } + + struct sockaddr_in server_addr; + std::memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(_port); + + if (bind(_server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) + { + log("Failed to bind socket.", RED); + exit(EXIT_FAILURE); + } + + if (listen(_server_fd, SOMAXCONN) == -1) + { + log("Failed to listen on socket.", RED); + exit(EXIT_FAILURE); + } + + log("Server initialized.", GREEN); +} + +void Server::run() +{ + struct pollfd server_pollfd; + server_pollfd.fd = _server_fd; + server_pollfd.events = POLLIN; + server_pollfd.revents = 0; + _poll_fds.push_back(server_pollfd); + + while (true) + { + int poll_count = poll(&_poll_fds[0], _poll_fds.size(), -1); + if (poll_count == -1) + { + log("Poll error.", RED); + exit(EXIT_FAILURE); + } + + for (size_t i = 0; i < _poll_fds.size(); ++i) + { + if (_poll_fds[i].revents & POLLIN) + { + if (_poll_fds[i].fd == _server_fd) + { + _clientManager->acceptClient(); + } + else + { + _clientManager->handleClient(_poll_fds[i].fd); + } + } + } + + handleServerCommands(); + } +} + +void Server::handleServerCommands() +{ + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + struct timeval tv = {0, 0}; // non-blocking + + int activity = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv); + + if (activity > 0 && FD_ISSET(STDIN_FILENO, &readfds)) + { + std::string command; + std::getline(std::cin, command); + + if (command == "quit") + { + log("Server shutting down.", YELLOW); + exit(EXIT_SUCCESS); + } + else if (command == "channels") + { + log("Listing all channels:", BLUE); + for (std::map::iterator it = _channels.begin(); it != _channels.end(); ++it) + { + log(it->first, BLUE); + } + } + else if (command == "clients") + { + log("Listing all clients:", BLUE); + for (std::map::iterator it = _clients.begin(); it != _clients.end(); ++it) + { + log(it->second->getNickname(), BLUE); + } + } + else + { + log("Unknown server command.", RED); + } + } +} + +void Server::log(const std::string &message, const std::string &color) +{ + std::cout << color << message << RESET << std::endl; +} + +void Server::sendToClient(int client_fd, const std::string &message) +{ + int result = send(client_fd, message.c_str(), message.length(), 0); + if (result < 0) + { + std::stringstream ss; + ss << "Failed to send message to client " << client_fd; + log(ss.str(), RED); + } + else + { + std::stringstream ss; + ss << "Sent message to client " << client_fd << ": " << message; + log(ss.str(), BLUE); + } +} + + +std::map &Server::getClients() +{ + return _clients; +} + +std::map &Server::getChannels() +{ + return _channels; +} + +const std::string &Server::getPassword() const +{ + return _password; +} + + +/* Explications des Fonctions +Server::Server(int port, const std::string &password) + +Description: Constructeur de la classe Server. Initialise le serveur avec le port et le mot de passe fournis. +Paramètres: +int port: Le port sur lequel le serveur écoute. +const std::string &password: Le mot de passe requis pour se connecter au serveur. +Détails: Appelle initServer() pour initialiser les paramètres du socket et commencer l'écoute des connexions. +Server::~Server() + +Description: Destructeur de la classe Server. Ferme le socket du serveur et libère la mémoire des clients. +Détails: Parcourt la map _clients et delete chaque objet Client. +void Server::initServer() + +Description: Initialise le socket du serveur et configure le binding et l'écoute. +Détails: Crée un socket avec socket(), lie le socket à une adresse avec bind(), et met le socket en mode écoute avec listen(). +void Server::run() + +Description: Boucle principale du serveur qui gère les connexions entrantes et les communications avec les clients. +Détails: Utilise poll() pour surveiller les événements sur les sockets. Accepte les nouvelles connexions avec acceptClient() et gère les communications avec handleClient(). +void Server::acceptClient() + +Description: Accepte une nouvelle connexion client et l'ajoute à la liste des clients. +Détails: Crée un nouvel objet Client et l'ajoute à la map _clients. Ajoute le descripteur de fichier du client à _poll_fds. +void Server::handleClient(int client_fd) + +Description: Gère les messages entrants des clients. +Paramètres: +int client_fd: Le descripteur de fichier du client. +Détails: Reçoit les messages du client et vérifie l'authentification. Si le client n'est pas authentifié, vérifie le mot de passe. Si le client est authentifié, répond au message. +void Server::removeClient(int client_fd) + +Description: Supprime un client de la liste et ferme la connexion. +Paramètres: +int client_fd: Le descripteur de fichier du client. +Détails: Ferme le socket du client et le supprime de la map _clients et du vecteur _poll_fds. +void Server::log(const std::string &message, const std::string &color) + +Description: Enregistre un message dans les logs et l'affiche avec des couleurs dans la console. +Paramètres: +const std::string &message: Le message à enregistrer. +const std::string &color: La couleur du message. +Détails: Utilise logMessage() pour enregistrer et afficher le message. +void Server::sendToClient(int client_fd, const std::string &message) + +Description: Envoie un message à un client. +Paramètres: +int client_fd: Le descripteur de fichier du client. +const std::string &message: Le message à envoyer. +Détails: Utilise send() pour envoyer le message au client. + +Lecture Ligne par Ligne : Utilisez std::istringstream pour lire le message ligne par ligne et traiter chaque commande séparément. +Suppression des Caractères de Fin de Ligne : Utilisez erase et std::remove pour supprimer les caractères \r et \n de chaque ligne avant de traiter la commande. +Vérification du Mot de Passe : Si le client n'est pas authentifié, vérifiez s'il envoie la commande PASS et validez le mot de passe. Sinon, répondez avec un message demandant le mot de passe. +Explications des Modifications +Notification de Jonction : Lorsque le client rejoint un canal, le serveur envoie une notification de jonction avec la commande JOIN. + + +std::stringstream joinMsg; +joinMsg << ":" << client->getFd() << " JOIN " << channelName << "\r\n"; +sendToClient(client->getFd(), joinMsg.str()); +Liste des Utilisateurs : Le serveur envoie la liste des utilisateurs dans le canal en utilisant le code de réponse 353. + + +std::string usersList = getUsersList(channel); +sendToClient(client->getFd(), usersList); +Fin de la Liste des Utilisateurs : Le serveur envoie un message de fin de liste des utilisateurs avec le code de réponse 366. + +std::stringstream endOfNamesMsg; +endOfNamesMsg << ":server 366 " << client->getFd() << " " << channelName << " :End of /NAMES list.\r\n"; +sendToClient(client->getFd(), endOfNamesMsg.str()); +Fonction getUsersList : Cette fonction génère la liste des utilisateurs dans le canal en utilisant le format correct. + +std::string Server::getUsersList(Channel *channel) +{ + std::vector clients = channel->getClients(); + std::stringstream ss; + ss << ":server 353 " << clients[0]->getFd() << " = " << channel->getName() << " :"; + for (size_t i = 0; i < clients.size(); ++i) + { + if (i > 0) + ss << " "; + ss << clients[i]->getFd(); + } + ss << "\r\n"; + return ss.str(); +} +Avec ces modifications, votre serveur IRC devrait maintenant informer correctement le client HexChat des utilisateurs présents dans un canal, ce qui devrait faire apparaître les canaux et leurs utilisateurs correctement dans l'interface du client. + +*/ \ No newline at end of file diff --git a/ft_irc3/src/Utils.cpp b/ft_irc3/src/Utils.cpp new file mode 100644 index 0000000..133e6cd --- /dev/null +++ b/ft_irc3/src/Utils.cpp @@ -0,0 +1,37 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* Utils.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:17:59 by fgras-ca #+# #+# */ +/* Updated: 2024/05/16 18:17:21 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "Utils.hpp" + +std::vector split(const std::string &input, const std::string &delimiters) +{ + std::vector result; + size_t startPos = 0; + size_t foundPos = input.find_first_of(delimiters, startPos); + + while (foundPos != std::string::npos) + { + if (foundPos != startPos) + { + result.push_back(input.substr(startPos, foundPos - startPos)); + } + startPos = foundPos + 1; + foundPos = input.find_first_of(delimiters, startPos); + } + + if (startPos < input.length()) + { + result.push_back(input.substr(startPos)); + } + + return result; +} diff --git a/ft_irc3/src/main.cpp b/ft_irc3/src/main.cpp new file mode 100644 index 0000000..88862d5 --- /dev/null +++ b/ft_irc3/src/main.cpp @@ -0,0 +1,33 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* main.cpp :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: fgras-ca +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2024/05/15 12:16:41 by fgras-ca #+# #+# */ +/* Updated: 2024/05/15 12:49:07 by fgras-ca ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "Server.hpp" +#include "Utils.hpp" +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 3) + { + std::cerr << RED << "Usage: " << argv[0] << " " << RESET << std::endl; + return EXIT_FAILURE; + } + + int port = std::atoi(argv[1]); + std::string password = argv[2]; + + Server server(port, password); + server.run(); + + return EXIT_SUCCESS; +}