diff --git a/ft_irc3/includes/ModeWhoHandler.hpp b/ft_irc3/includes/ModeWhoHandler.hpp index 711ccd9..80dd316 100644 --- a/ft_irc3/includes/ModeWhoHandler.hpp +++ b/ft_irc3/includes/ModeWhoHandler.hpp @@ -6,7 +6,7 @@ /* By: fgras-ca +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/05/17 16:08:48 by fgras-ca #+# #+# */ -/* Updated: 2024/05/19 15:15:39 by fgras-ca ### ########.fr */ +/* Updated: 2024/05/19 20:36:23 by fgras-ca ### ########.fr */ /* */ /* ************************************************************************** */ @@ -30,6 +30,7 @@ public: ModeWhoHandler(Server *server); void handleModeCommand(Client *client, const std::string &command); void handleWhoCommand(Client *client, const std::string &command); + void handleWhoisCommand(Client *client, const std::string &command); private: Server *_server; diff --git a/ft_irc3/includes/RPL.hpp b/ft_irc3/includes/RPL.hpp index 52990b2..bf60160 100644 --- a/ft_irc3/includes/RPL.hpp +++ b/ft_irc3/includes/RPL.hpp @@ -6,7 +6,7 @@ /* By: fgras-ca +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/05/19 15:12:47 by fgras-ca #+# #+# */ -/* Updated: 2024/05/19 19:07:25 by fgras-ca ### ########.fr */ +/* Updated: 2024/05/19 22:27:05 by fgras-ca ### ########.fr */ /* */ /* ************************************************************************** */ @@ -30,6 +30,7 @@ #define CLIENT_NICK(client) ((client)->getNickname()) #define CLIENT_USER(client) ((client)->getUser()) #define CLIENT_HOST(client) ((client)->getHost()) +#define CLIENT_REALNAME(client) ((client)->getRealName()) // Fonctions pour générer les réponses RPL inline std::string RPL_WELCOME(Client* client) { @@ -217,5 +218,120 @@ inline std::string ERR_UNKNOWNCOMMAND(int clientFd, const std::string& command) return oss.str(); } +// RPL Mode Messages +inline std::string RPL_CHANNELMODEIS(int clientFd, const std::string& channel, const std::string& mode) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 324 " << clientFd << " " << channel << " " << mode << "\r\n"; + return oss.str(); +} + +inline std::string RPL_NOCHANMODES(int clientFd, const std::string& channel) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 477 " << clientFd << " " << channel << " :Channel doesn't support modes\r\n"; + return oss.str(); +} + +inline std::string ERR_NOSUCHCHANNEL(int clientFd, const std::string& channel) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 403 " << clientFd << " " << channel << " :No such channel\r\n"; + return oss.str(); +} + +inline std::string ERR_CHANOPRIVSNEEDED(int clientFd, const std::string& channel) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 482 " << clientFd << " " << channel << " :You're not channel operator\r\n"; + return oss.str(); +} + +inline std::string ERR_NOSUCHNICK(int clientFd, const std::string& nick) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 401 " << clientFd << " " << nick << " :No such nick/channel\r\n"; + return oss.str(); +} + +// WHO Command RPLs +inline std::string RPL_WHOREPLY(const std::string& channel, Client* target) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 352 " << CLIENT_NICK(target) << " " << channel << " " + << CLIENT_USER(target) << " " << CLIENT_HOST(target) << " " << SERVER_NAME << " " + << CLIENT_NICK(target) << " H :0 " << CLIENT_REALNAME(target) << "\r\n"; + return oss.str(); +} + +inline std::string RPL_ENDOFWHO(int clientFd, const std::string& channel) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 315 " << clientFd << " " << channel << " :End of /WHO list.\r\n"; + return oss.str(); +} + +// WHOIS Command RPLs +inline std::string RPL_WHOISUSER(int clientFd, Client* target) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 311 " << clientFd << " " << CLIENT_NICK(target) << " " + << CLIENT_USER(target) << " " << CLIENT_HOST(target) << " * :" << CLIENT_REALNAME(target) << "\r\n"; + return oss.str(); +} + +inline std::string RPL_WHOISSERVER(int clientFd, const std::string& targetNick, const std::string& serverInfo) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 312 " << clientFd << " " << targetNick << " " << SERVER_NAME + << " :" << serverInfo << "\r\n"; + return oss.str(); +} + +inline std::string RPL_ENDOFWHOIS(int clientFd, const std::string& targetNick) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 318 " << clientFd << " " << targetNick << " :End of /WHOIS list.\r\n"; + return oss.str(); +} + +inline std::string ERR_NONICKNAMEGIVEN(int clientFd) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 431 " << clientFd << " :No nickname given\r\n"; + return oss.str(); +} + +// CAP Command RPLs +inline std::string RPL_CAP(int clientFd, const std::string& subcommand, const std::string& capabilities) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " CAP " << clientFd << " " << subcommand << " :" << capabilities << "\r\n"; + return oss.str(); +} + +// ERR_NOTREGISTERED +inline std::string ERR_NOTREGISTERED(int clientFd) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " 451 " << clientFd << " :You have not registered\r\n"; + return oss.str(); +} + +inline std::string RPL_PASSACCEPTED(Client* client) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " NOTICE " << CLIENT_FD(client) + << " :Password accepted!\r\n"; + return oss.str(); +} + +// Add this function to handle the CAP END response +inline std::string RPL_CAPEND(int clientFd) +{ + std::ostringstream oss; + oss << ":" << SERVER_NAME << " CAP " << clientFd << " END\r\n"; + return oss.str(); +} #endif // RPL_HPP diff --git a/ft_irc3/includes/Server.hpp b/ft_irc3/includes/Server.hpp index 0d071dc..a66c868 100644 --- a/ft_irc3/includes/Server.hpp +++ b/ft_irc3/includes/Server.hpp @@ -6,7 +6,7 @@ /* By: fgras-ca +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/05/15 12:15:13 by fgras-ca #+# #+# */ -/* Updated: 2024/05/19 16:30:56 by fgras-ca ### ########.fr */ +/* Updated: 2024/05/19 21:13:24 by fgras-ca ### ########.fr */ /* */ /* ************************************************************************** */ @@ -55,6 +55,7 @@ public: void broadcast(const std::string &message); Client* getClientByName(const std::string &name); // Ajoutez cette méthode void sendChannelListToClient(Client *client); + void disconnectClient(int clientFd); protected: diff --git a/ft_irc3/ircserv b/ft_irc3/ircserv index 7508ad6..64921c1 100755 Binary files a/ft_irc3/ircserv and b/ft_irc3/ircserv differ diff --git a/ft_irc3/obj/ClientManager.o b/ft_irc3/obj/ClientManager.o index d4b69bf..39eac96 100644 Binary files a/ft_irc3/obj/ClientManager.o and b/ft_irc3/obj/ClientManager.o differ diff --git a/ft_irc3/obj/CommandHandler.o b/ft_irc3/obj/CommandHandler.o index 3a5713e..545fe05 100644 Binary files a/ft_irc3/obj/CommandHandler.o and b/ft_irc3/obj/CommandHandler.o differ diff --git a/ft_irc3/obj/ModeWhoHandler.o b/ft_irc3/obj/ModeWhoHandler.o index 30531d0..5f6d31b 100644 Binary files a/ft_irc3/obj/ModeWhoHandler.o and b/ft_irc3/obj/ModeWhoHandler.o differ diff --git a/ft_irc3/obj/Server.o b/ft_irc3/obj/Server.o index b0c9ebd..bee98a5 100644 Binary files a/ft_irc3/obj/Server.o and b/ft_irc3/obj/Server.o differ diff --git a/ft_irc3/src/ClientManager.cpp b/ft_irc3/src/ClientManager.cpp index 426e767..b76fd33 100644 --- a/ft_irc3/src/ClientManager.cpp +++ b/ft_irc3/src/ClientManager.cpp @@ -6,7 +6,7 @@ /* By: fgras-ca +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/05/15 18:32:23 by fgras-ca #+# #+# */ -/* Updated: 2024/05/19 18:55:55 by fgras-ca ### ########.fr */ +/* Updated: 2024/05/19 22:09:39 by fgras-ca ### ########.fr */ /* */ /* ************************************************************************** */ @@ -34,10 +34,8 @@ void ClientManager::acceptClient() _server->_poll_fds.push_back(client_pollfd); std::stringstream ss; - ss << "Client connected: " << client_fd; - _server->log(ss.str(), GREEN); - - sendWelcomeMessages(newClient, _server); + ss << "Client attempting connection: " << client_fd; + _server->log(ss.str(), YELLOW); } void ClientManager::handleClient(int client_fd) diff --git a/ft_irc3/src/CommandHandler.cpp b/ft_irc3/src/CommandHandler.cpp index 480b1b8..cc14fde 100644 --- a/ft_irc3/src/CommandHandler.cpp +++ b/ft_irc3/src/CommandHandler.cpp @@ -6,7 +6,7 @@ /* By: fgras-ca +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/05/15 18:26:34 by fgras-ca #+# #+# */ -/* Updated: 2024/05/19 19:13:14 by fgras-ca ### ########.fr */ +/* Updated: 2024/05/19 23:47:26 by fgras-ca ### ########.fr */ /* */ /* ************************************************************************** */ @@ -54,33 +54,15 @@ void CommandHandler::handleCapCommand(Client* client, const std::vector capLines; - capLines.push_back("multi-prefix extended-join account-notify batch invite-notify tls"); - capLines.push_back("cap-notify server-time example.org/dummy-cap=dummyvalue example.org/second-dummy-cap"); - capLines.push_back("userhost-in-names sasl=EXTERNAL,DH-AES,DH-BLOWFISH,ECDSA-NIST256P-CHALLENGE,PLAIN"); - - for (size_t i = 0; i < capLines.size(); ++i) - { - std::ostringstream oss; - oss << ":irc.example.com CAP " << client->getNickname() << " LS"; - if (i != capLines.size() - 1) - oss << " * :"; - else - oss << " :"; - oss << capLines[i] << "\r\n"; - _server->sendToClient(client->getFd(), oss.str()); - } + _server->sendToClient(client->getFd(), RPL_CAP(client->getFd(), "LS", capabilities)); } else if (subcommand == "LIST") { - // Example for CAP LIST response - std::ostringstream oss; - oss << ":irc.example.com CAP " << client->getNickname() << " LIST :example.org/example-cap example.org/second-example-cap account-notify invite-notify batch example.org/third-example-cap\r\n"; - _server->sendToClient(client->getFd(), oss.str()); + _server->sendToClient(client->getFd(), RPL_CAP(client->getFd(), "LIST", capabilities)); } else if (subcommand == "REQ") { @@ -89,21 +71,16 @@ void CommandHandler::handleCapCommand(Client* client, const std::vectorsendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client->getFd(), "CAP")); return; } - - // Example for CAP REQ response - std::string requestedCaps = tokens[2]; - std::ostringstream ackResponse; - ackResponse << ":irc.example.com CAP " << client->getNickname() << " ACK :" << requestedCaps << "\r\n"; - _server->sendToClient(client->getFd(), ackResponse.str()); + std::string requestedCapabilities = tokens[2]; + // For simplicity, we assume all requested capabilities are accepted + _server->sendToClient(client->getFd(), RPL_CAP(client->getFd(), "ACK", requestedCapabilities)); } else if (subcommand == "END") { - // Example for CAP END response - _server->sendToClient(client->getFd(), ":irc.example.com CAP " + client->getNickname() + " END\r\n"); + _server->sendToClient(client->getFd(), RPL_CAPEND(client->getFd())); } else { - // Unknown CAP subcommand _server->sendToClient(client->getFd(), ERR_UNKNOWNCOMMAND(client->getFd(), "CAP")); } } @@ -168,40 +145,41 @@ void CommandHandler::handleNick(Client* client, const std::vector& _server->sendToClient(client->getFd(), ERR_NICKNAMEINUSE(client, newNick)); return; } - - std::string oldNick = client->getNickname(); client->setNickname(newNick); - if (!oldNick.empty()) { - _server->broadcast(":" + oldNick + " NICK " + newNick + "\r\n"); - } - _server->sendToClient(client->getFd(), ":" + newNick + " NICK " + newNick + "\r\n"); - _server->log("Client " + oldNick + " changed nickname to " + newNick, GREEN); + _server->log("Client NickName is " + newNick, GREEN); } void CommandHandler::handleUser(Client* client, const std::vector& tokens) { - if (tokens.size() < 5) { - _server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "USER")); - return; - } - - if (client->isAuthenticated()) { - _server->sendToClient(client->getFd(), ERR_ALREADYREGISTERED(client)); - return; - } - + // Set the user and realname fields client->setUser(tokens[1]); - client->setRealName(tokens[4].substr(1)); // remove leading ':' + std::string realname = tokens[4]; + if (realname[0] == ':') { + realname = realname.substr(1); // Remove leading ':' + } + client->setRealName(realname); + // Log the values for debugging + std::ostringstream logMsg; + logMsg << "Client " << client->getFd() << ": USER command set username to " << tokens[1] << " and real name to " << realname; + _server->log(logMsg.str(), BLUE); + + // Authenticate if password and nickname are already set if (client->getPassword() == _server->_password && !client->getNickname().empty()) { client->authenticate(); sendWelcomeMessages(client, _server); - _server->log("Client " + client->getNickname() + " authenticated.", GREEN); + _server->log("Client " + client->getNickname() + " authenticated successfully.", GREEN); + } else { + std::ostringstream authFailMsg; + authFailMsg << "Client " << client->getFd() << ": USER command failed - authentication conditions not met."; + _server->log(authFailMsg.str(), RED); } } + + void CommandHandler::processCommand(Client *client, const std::string &command) { if (command.find("JOIN") == 0) @@ -230,6 +208,11 @@ void CommandHandler::processCommand(Client *client, const std::string &command) { ModeWhoHandler whoHandler(_server); whoHandler.handleWhoCommand(client, command); + } + else if (command.find("WHOIS") == 0) + { + ModeWhoHandler whoHandler(_server); + whoHandler.handleWhoisCommand(client, command); } else if (command.find("PING") == 0) { diff --git a/ft_irc3/src/ModeWhoHandler.cpp b/ft_irc3/src/ModeWhoHandler.cpp index ef39134..fc9dce5 100644 --- a/ft_irc3/src/ModeWhoHandler.cpp +++ b/ft_irc3/src/ModeWhoHandler.cpp @@ -6,12 +6,14 @@ /* By: fgras-ca +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/05/17 16:09:20 by fgras-ca #+# #+# */ -/* Updated: 2024/05/18 16:41:10 by fgras-ca ### ########.fr */ +/* Updated: 2024/05/19 20:52:30 by fgras-ca ### ########.fr */ /* */ /* ************************************************************************** */ #include "ModeWhoHandler.hpp" #include "Channel.hpp" +#include "RPL.hpp" +#include ModeWhoHandler::ModeWhoHandler(Server *server) : _server(server) @@ -27,7 +29,7 @@ void ModeWhoHandler::handleModeCommand(Client *client, const std::string &comman std::map &channels = _server->getChannels(); if (channels.find(channelName) == channels.end()) { - _server->sendToClient(client->getFd(), ":" SERVER_NAME " 403 " + client->getNickname() + " " + channelName + " :No such channel\r\n"); + _server->sendToClient(client->getFd(), ERR_NOSUCHCHANNEL(client->getFd(), channelName)); return; } @@ -40,17 +42,17 @@ void ModeWhoHandler::handleModeCommand(Client *client, const std::string &comman if (targetClient) { channel->addOperator(targetClient); - _server->sendToClient(client->getFd(), ":" + client->getNickname() + " MODE " + channelName + " +o " + user + "\r\n"); - _server->log(GREEN "Client " + user + " is now an operator in channel " + channelName + RESET, GREEN); + _server->sendToClient(client->getFd(), RPL_CHANNELMODEIS(client->getFd(), channelName, "+o")); + _server->log("Client " + user + " is now an operator in channel " + channelName, GREEN); } else { - _server->sendToClient(client->getFd(), ":" SERVER_NAME " 401 " + client->getNickname() + " " + user + " :No such nick/channel\r\n"); + _server->sendToClient(client->getFd(), ERR_NOSUCHNICK(client->getFd(), user)); } } else { - _server->sendToClient(client->getFd(), ":" SERVER_NAME " 482 " + client->getNickname() + " " + channelName + " :You're not channel operator\r\n"); + _server->sendToClient(client->getFd(), ERR_CHANOPRIVSNEEDED(client->getFd(), channelName)); } } } @@ -58,28 +60,52 @@ void ModeWhoHandler::handleModeCommand(Client *client, const std::string &comman void ModeWhoHandler::handleWhoCommand(Client *client, const std::string &command) { std::istringstream iss(command); - std::string cmd, channelName; - iss >> cmd >> channelName; + std::string cmd, mask; + iss >> cmd >> mask; std::map &channels = _server->getChannels(); - if (channels.find(channelName) == channels.end()) + if (channels.find(mask) != channels.end()) { - _server->sendToClient(client->getFd(), ":" SERVER_NAME " 403 " + client->getNickname() + " " + channelName + " :No such channel\r\n"); + Channel *channel = channels[mask]; + std::vector channelClients = channel->getClients(); + + for (size_t i = 0; i < channelClients.size(); ++i) + { + _server->sendToClient(client->getFd(), RPL_WHOREPLY(mask, channelClients[i])); + } + } + else + { + Client *targetClient = _server->getClientByName(mask); + if (targetClient) + { + _server->sendToClient(client->getFd(), RPL_WHOREPLY("*", targetClient)); + } + } + + _server->sendToClient(client->getFd(), RPL_ENDOFWHO(client->getFd(), mask)); +} + +void ModeWhoHandler::handleWhoisCommand(Client *client, const std::string &command) +{ + std::istringstream iss(command); + std::string cmd, target; + iss >> cmd >> target; + + if (target.empty()) + { + _server->sendToClient(client->getFd(), ERR_NONICKNAMEGIVEN(client->getFd())); return; } - Channel *channel = channels[channelName]; - std::vector channelClients = channel->getClients(); - - for (size_t i = 0; i < channelClients.size(); ++i) + Client *targetClient = _server->getClientByName(target); + if (!targetClient) { - std::stringstream whoMsg; - whoMsg << ":" SERVER_NAME " 352 " << client->getNickname() << " " << channelName << " " << channelClients[i]->getNickname() << " " - << channelClients[i]->getUser() << " " << channelClients[i]->getHost() << " " << _server->getPassword() << " " << channelClients[i]->getRealName() << "\r\n"; - _server->sendToClient(client->getFd(), whoMsg.str()); + _server->sendToClient(client->getFd(), ERR_NOSUCHNICK(client->getFd(), target)); + return; } - std::stringstream endOfWhoMsg; - endOfWhoMsg << ":" SERVER_NAME " 315 " << client->getNickname() << " " << channelName << " :End of /WHO list.\r\n"; - _server->sendToClient(client->getFd(), endOfWhoMsg.str()); -} \ No newline at end of file + _server->sendToClient(client->getFd(), RPL_WHOISUSER(client->getFd(), targetClient)); + _server->sendToClient(client->getFd(), RPL_WHOISSERVER(client->getFd(), target, "IRC server info")); + _server->sendToClient(client->getFd(), RPL_ENDOFWHOIS(client->getFd(), target)); +} diff --git a/ft_irc3/src/Server.cpp b/ft_irc3/src/Server.cpp index d41bd4e..bd076c8 100644 --- a/ft_irc3/src/Server.cpp +++ b/ft_irc3/src/Server.cpp @@ -6,7 +6,7 @@ /* By: fgras-ca +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/05/15 12:17:12 by fgras-ca #+# #+# */ -/* Updated: 2024/05/19 19:21:54 by fgras-ca ### ########.fr */ +/* Updated: 2024/05/19 22:16:03 by fgras-ca ### ########.fr */ /* */ /* ************************************************************************** */ @@ -43,6 +43,14 @@ void Server::initServer() exit(EXIT_FAILURE); } + int opt = 1; + if (setsockopt(_server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) + { + log("Failed to set socket options.", RED); + close(_server_fd); + exit(EXIT_FAILURE); + } + struct sockaddr_in server_addr; std::memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; @@ -52,12 +60,14 @@ void Server::initServer() if (bind(_server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { log("Failed to bind socket.", RED); + close(_server_fd); // Close the socket if bind fails exit(EXIT_FAILURE); } if (listen(_server_fd, SOMAXCONN) == -1) { log("Failed to listen on socket.", RED); + close(_server_fd); // Close the socket if listen fails exit(EXIT_FAILURE); } @@ -109,6 +119,7 @@ void Server::run() } } + std::map &Server::getChannels() { return _channels; @@ -203,6 +214,26 @@ void Server::sendChannelListToClient(Client *client) sendToClient(client->getFd(), RPL_LISTEND(client->getFd())); } +void Server::disconnectClient(int clientFd) +{ + close(clientFd); + _clients.erase(clientFd); + + for (std::vector::iterator it = _poll_fds.begin(); it != _poll_fds.end(); ++it) + { + if (it->fd == clientFd) + { + _poll_fds.erase(it); + break; + } + } + + std::ostringstream oss; + oss << "Client disconnected: " << clientFd; + log(oss.str(), YELLOW); +} + + /* Explications des Fonctions Server::Server(int port, const std::string &password)