Initial commit

This commit is contained in:
Ladebeze66
2024-06-06 14:16:52 +02:00
commit d345c1127d
46 changed files with 4286 additions and 0 deletions

View File

@@ -0,0 +1,188 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* AdditionalCommands.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/16 15:27:29 by fgras-ca #+# #+# */
/* Updated: 2024/06/04 13:55:09 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "AdditionalCommands.hpp"
AdditionalCommands::AdditionalCommands(Server *server) : _server(server) {}
void AdditionalCommands::processCommand(Client *client, const std::string &command)
{
if (command.find("PART") == 0)
{
handlePartCommand(_server, client, command);
}
else if (command.find("PRIVMSG") == 0)
{
handlePrivmsgCommand(_server, client, command);
}
else if (command.find("WHO") == 0)
{
WhoHandler whoHandler(_server);
whoHandler.handleWhoCommand(client, command);
}
else if (command.find("WHOIS") == 0)
{
WhoHandler whoHandler(_server);
whoHandler.handleWhoisCommand(client, command);
}
else if (command.find("INVITE") == 0)
{
InviteHandler inviteHandler(_server);
inviteHandler.handleInviteCommand(client, command);
}
else if (command.find("TOPIC") == 0)
{
TopicHandler topichandler(_server);
topichandler.handleTopicCommand(client, command);
}
else if (command.find("LIST") == 0)
{
broadcastChannelList(client, _server);
}
else if (command.find("KICK") == 0)
{
KickHandler kickHandler(_server);
kickHandler.handleKickCommand(client, command);
}
else
{
_server->sendToClient(client->getFd(), ERR_UNKNOWNCOMMAND(client, command));
_server->log("Message from client " + client->getNickname() + ": " + command, MAGENTA);
}
}
void AdditionalCommands::broadcastChannelList(Client *client, Server *server)
{
std::map<std::string, Channel *> &channels = server->getChannels();
for (std::map<std::string, Channel *>::iterator it = channels.begin(); it != channels.end(); ++it)
{
server->sendToClient(client->getFd(), RPL_LIST(client, it->first, it->second->getClients().size(), it->second->getTopic()));
}
server->sendToClient(client->getFd(), RPL_LISTEND(client));
}
// Fonction pour gérer la commande PART
void AdditionalCommands::handlePartCommand(Server *server, Client *client, const std::string &command)
{
std::istringstream iss(command);
std::string cmd, channelNames;
iss >> cmd >> channelNames;
if (channelNames.empty())
{
server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "PART"));
return;
}
std::vector<std::string> channels = split(channelNames, ",");
std::map<std::string, Channel *> &channelMap = server->getChannels();
for (size_t i = 0; i < channels.size(); ++i)
{
std::string &channelName = channels[i];
if (channelMap.find(channelName) == channelMap.end())
{
server->sendToClient(client->getFd(), ERR_NOSUCHCHANNEL(client, channelName));
continue;
}
Channel *channel = channelMap[channelName];
if (!channel->hasClient(client)) {
server->sendToClient(client->getFd(), ERR_NOTONCHANNEL(client, channelName));
continue;
}
channel->removeClient(client);
std::ostringstream partMsg;
partMsg << ":" << client->getNickname() << " PART " << channelName << "\r\n";
server->sendToClient(client->getFd(), partMsg.str());
if (channel->isEmpty())
{
delete channel;
channelMap.erase(channelName);
}
server->log("Client " + client->getNickname() + " left channel " + channelName, MAGENTA);
}
}
void AdditionalCommands::handlePrivmsgCommand(Server *server, Client *client, const std::string &command)
{
std::istringstream iss(command);
std::string cmd, target, message;
iss >> cmd >> target;
getline(iss, message);
// Enlever le ':' initial dans le message si présent
if (!message.empty() && message[0] == ':')
message = message.substr(1);
if (target.empty())
{
server->sendToClient(client->getFd(), ERR_NORECIPIENT(client, "PRIVMSG"));
return;
}
if (message.empty())
{
server->sendToClient(client->getFd(), ERR_NOTEXTTOSEND(client));
return;
}
std::map<std::string, Channel *> &channels = server->getChannels();
if (channels.find(target) != channels.end())
{
Channel *channel = channels[target];
if (channel->isBanned(client))
{
server->sendToClient(client->getFd(), ERR_CANNOTSENDTOCHAN(client, target));
return;
}
std::vector<Client *> 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
{
Client *targetClient = server->getClientByName(target);
if (targetClient)
{
std::stringstream privMsg;
privMsg << ":" << client->getNickname() << " PRIVMSG " << target << message << "\r\n";
server->sendToClient(targetClient->getFd(), privMsg.str());
if (targetClient->isAway())
{
server->sendToClient(client->getFd(), RPL_AWAY(client, targetClient->getNickname(), targetClient->getAwayMessage()));
}
}
else
{
// Si la cible n'est ni un canal ni un utilisateur existant, envoyer un message d'erreur
server->sendToClient(client->getFd(), ERR_NOSUCHNICK(client, target));
}
}
}

70
ft_irc3/src/BotFilter.cpp Normal file
View File

@@ -0,0 +1,70 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* BotFilter.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/06/06 11:45:43 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 13:37:24 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "BotFilter.hpp"
BotFilter::BotFilter(Server *server) : _server(server) {}
void BotFilter::loadBadWords(const std::string &fileName)
{
std::ifstream file(fileName.c_str());
std::string line;
while (std::getline(file, line))
{
_badWords.push_back(line);
}
}
bool BotFilter::checkMessage(Client *client, Channel *channel, const std::string &message)
{
if (containsBadWords(message))
{
warnClient(client, channel);
return true;
}
return false;
}
bool BotFilter::containsBadWords(const std::string &message)
{
for (std::vector<std::string>::iterator it = _badWords.begin(); it != _badWords.end(); ++it)
{
if (message.find(*it) != std::string::npos)
{
return true;
}
}
return false;
}
void BotFilter::warnClient(Client* client, Channel *channel)
{
int fd = client->getFd();
_warnings[fd] += 1;
if (_warnings[fd] >= 3)
{
_server->sendToClient(fd, BOTMESSAGE(client, channel->getName(), "You have been kicked from the server for inappropriate language.\r\n"));
_server->disconnectClient(fd);
}
else
{
std::ostringstream oss;
oss << "Warning: Inappropriate language detected. You have " << (3 - _warnings[fd]) << " warnings left before you are kicked.\r\n";
_server->sendToClient(fd, BOTMESSAGE(client, channel->getName(), oss.str()));
}
}
void BotFilter::kickClient(Client *client, Channel *channel)
{
_server->sendToClient(client->getFd(), BOTMESSAGE(client, channel->getName(), "You have been kicked for inappropriate language.\r\n"));
_server->disconnectClient(client->getFd());
}

172
ft_irc3/src/Channel.cpp Normal file
View File

@@ -0,0 +1,172 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Channel.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:42:57 by fgras-ca #+# #+# */
/* Updated: 2024/06/04 16:12:22 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "Channel.hpp"
Channel::Channel(const std::string &name)
: _name(name), _clients(), _operators(), _bannedClients(), _invitedClients(), _key(""), _topic(""), _topicSetter(""), _topicTime(time(NULL)), _clientLimit(50), _inviteOnly(false), _topicProtection(false) {}
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<Client *> &Channel::getClients() const
{
return _clients;
}
void Channel::addOperator(Client *client)
{
_operators.push_back(client);
}
bool Channel::isOperator(Client *client) const
{
return std::find(_operators.begin(), _operators.end(), client) != _operators.end();
}
void Channel::removeOperator(Client *client)
{
_operators.erase(std::remove(_operators.begin(), _operators.end(), client), _operators.end());
}
bool Channel::hasClient(Client *client) const
{
return std::find(_clients.begin(), _clients.end(), client) != _clients.end();
}
void Channel::broadcast(const std::string &message, Client *_client, Server *_server)
{
for (std::vector<Client *>::iterator it = _clients.begin(); it != _clients.end(); ++it)
{
if (*it != _client)
{
// Send message to each client except the sender
_server->sendToClient((*it)->getFd(), message);
}
}
}
bool Channel::isBanned(Client *client) const
{
return _bannedClients.find(client) != _bannedClients.end();
}
bool Channel::isFull() const
{
return _clients.size() >= _clientLimit;
}
bool Channel::isInviteOnly() const
{
return _inviteOnly;
}
bool Channel::isInvited(Client *client) const
{
return _invitedClients.find(client) != _invitedClients.end();
}
bool Channel::checkKey(const std::string &key) const
{
return _key.empty() || _key == key;
}
const std::string &Channel::getTopic() const
{
return _topic;
}
const std::string &Channel::getTopicSetter() const
{
return _topicSetter;
}
time_t Channel::getTopicTime() const
{
return _topicTime;
}
void Channel::setTopic(const std::string &topic, const std::string &setter)
{
_topic = topic;
_topicSetter = setter;
_topicTime = std::time(NULL);
}
void Channel::setClientLimit(size_t limit)
{
_clientLimit = limit;
}
size_t Channel::getClientLimit() const
{
return _clientLimit;
}
void Channel::setInviteOnly(bool inviteOnly)
{
_inviteOnly = inviteOnly;
}
void Channel::setKey(const std::string &key)
{
_key = key;
}
std::string Channel::getKey()
{
return _key;
}
void Channel::setTopicProtection(bool protection)
{
_topicProtection = protection;
}
void Channel::addInvitedClient(Client* client)
{
_invitedClients.insert(client);
}
std::string Channel::getModes() const
{
std::string modes;
if (_inviteOnly) modes += 'i';
if (!_key.empty()) modes += 'k';
if (_clientLimit > 0) modes += 'l';
if (_topicProtection) modes += 't';
return modes;
}
bool Channel::getTopicProtection() const
{
return _topicProtection;
}

136
ft_irc3/src/Client.cpp Normal file
View File

@@ -0,0 +1,136 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Client.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:17:42 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 13:23:32 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "Client.hpp"
Client::Client(int fd, const std::string &nickname, const std::string &user, const std::string &host, const std::string &password, const std::string &realname)
: _fd(fd), _nickname(nickname), _user(user), _host(host), _password(password), _realname(realname), _authenticated(false), _operator(false), _away(false) {}
int Client::getFd() const
{
return _fd;
}
const std::string &Client::getNickname() const
{
return _nickname;
}
void Client::setNickname(const std::string &nickname)
{
_nickname = nickname;
}
const std::string &Client::getUser() const
{
return _user;
}
void Client::setUser(const std::string &user)
{
_user = user;
}
const std::string &Client::getHost() const
{
return _host;
}
void Client::setHost(const std::string &host)
{
_host = host;
}
const std::string &Client::getPassword() const
{
return _password;
}
void Client::setPassword(const std::string &password)
{
_password = password;
}
const std::string &Client::getRealName() const
{
return _realname;
}
void Client::setRealName(const std::string &realname)
{
_realname = realname;
}
bool Client::isAuthenticated() const
{
return _authenticated;
}
void Client::authenticate()
{
_authenticated = true;
}
bool Client::isOperator() const
{
return _operator;
}
void Client::setOperator(bool isOperator)
{
_operator = isOperator;
}
bool Client::isAway() const
{
return _away;
}
const std::string &Client::getAwayMessage() const
{
return _awayMessage;
}
void Client::setAwayMessage(const std::string &message)
{
_awayMessage = message;
}
void Client::setAway(bool away)
{
_away = away;
}
std::string Client::getKey() const
{
return _key;
}
void Client::setkey(const std::string &key)
{
_key = key;
}
void Client::joinChannel(const std::string &channel)
{
_channels.insert(channel);
}
void Client::leaveChannel(const std::string &channel)
{
_channels.erase(channel);
}
const std::set<std::string> &Client::getChannels() const
{
return _channels;
}

View File

@@ -0,0 +1,173 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ClientManager.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 18:32:23 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 14:09:52 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "ClientManager.hpp"
ClientManager::ClientManager(Server *server)
: _server(server)
{
_botFilter = new BotFilter(server);
_botFilter->loadBadWords("badwords.txt");
}
void ClientManager::acceptClient()
{
int client_fd = accept(_server->_server_fd, NULL, NULL);
if (client_fd < 0)
{
_server->log("Failed to accept client.", RED);
return;
}
Client *newClient = new Client(client_fd, "", "", "", "", "");
_server->_clients[client_fd] = newClient;
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 attempting connection: " << client_fd;
_server->log(ss.str(), YELLOW);
}
void ClientManager::handleClient(int client_fd)
{
Client* client = _server->getClients()[client_fd];
std::memset(client->buffer, 0, sizeof(client->buffer));
int bytes_received = recv(client_fd, client->buffer, sizeof(client->buffer), 0);
if (bytes_received <= 0)
{
std::ostringstream oss;
oss << "Client disconnected: " << client_fd;
_server->log(oss.str(), RED);
removeClient(client_fd);
return;
}
std::cout << "Received data (" << bytes_received << " bytes): " << client->buffer << std::endl;
if (std::string(client->buffer).find('\n') != std::string::npos)
{
strcat(client->buffer2, client->buffer);
std::string fullMessage(client->buffer2);
// Log the combined message
std::cout << "Complete message: " << fullMessage << std::endl;
bool messageAllowed = true;
if (!client->getChannels().empty())
{
for (std::set<std::string>::const_iterator it = client->getChannels().begin(); it != client->getChannels().end(); ++it)
{
const std::string& channelName = *it;
Channel* channel = _server->getChannelByName(channelName);
if (channel && !_botFilter->checkMessage(client, channel, fullMessage))
{
messageAllowed = false;
break;
}
}
}
if (messageAllowed)
{
handleClientNext(client_fd, client->buffer2, fullMessage.size());
}
std::memset(client->buffer2, 0, sizeof(client->buffer2));
}
else
{
strcat(client->buffer2, client->buffer);
}
}
void ClientManager::handleClientNext(int client_fd, char * buffer, int bytes_received)
{
std::string message(buffer, bytes_received);
std::ostringstream oss;
oss << "Received from client " << client_fd << ": " << message;
_server->log(oss.str(), BLUE);
Client* client = _server->getClients()[client_fd];
if (!client)
{
std::ostringstream oss;
oss << "Client not found for fd: " << client_fd;
_server->log(oss.str(), RED);
return;
}
std::istringstream message_stream(message);
std::string line;
while (std::getline(message_stream, line))
{
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
line.erase(std::remove(line.begin(), line.end(), '\n'), line.end());
if (!line.empty())
{
std::ostringstream oss;
oss << "Processing command from client " << client_fd << ": " << line;
_server->log(oss.str(), BLUE);
_server->_commandHandler->handleCommand(client, line);
}
}
}
void ClientManager::removeClient(int clientFd)
{
Client* client = _server->_clients[clientFd];
if (client)
{
_server->log("Removing client: " + client->getNickname(), YELLOW);
std::map<std::string, Channel*>::iterator it = _server->_channels.begin();
while (it != _server->_channels.end())
{
it->second->removeClient(client);
if (it->second->isEmpty())
{
delete it->second;
std::map<std::string, Channel*>::iterator it_to_delete = it++;
_server->_channels.erase(it_to_delete);
}
else
{
++it;
}
}
delete client;
_server->_clients.erase(clientFd);
}
std::vector<struct pollfd>::iterator it_poll = _server->_poll_fds.begin();
while (it_poll != _server->_poll_fds.end())
{
if (it_poll->fd == clientFd)
{
_server->_poll_fds.erase(it_poll);
break;
}
++it_poll;
}
std::stringstream ss;
ss << "Client disconnected: " << clientFd;
_server->log(ss.str(), YELLOW);
}

View File

@@ -0,0 +1,305 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* CommandHandler.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 18:26:34 by fgras-ca #+# #+# */
/* Updated: 2024/06/05 09:57:55 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "CommandHandler.hpp"
CommandHandler::CommandHandler(Server *server)
: _server(server), _additionalCommands(new AdditionalCommands(server)), _modeHandler(new ModeHandler(server))
{
// Ensure that _server is not null
if (!_server)
{
std::cerr << "Server pointer is null in CommandHandler constructor." << std::endl;
exit(EXIT_FAILURE);
}
}
void CommandHandler::handleCommand(Client* client, const std::string& command)
{
std::vector<std::string> tokens = split(command, " \n\r\t");
if (tokens.empty())
{
return;
}
std::string commandType = tokens[0];
if (commandType == "CAP")
{
handleCapCommand(client, tokens);
}
else if (commandType == "PASS")
{
handlePassCommand(client, tokens);
}
else if (commandType == "NICK")
{
handleNick(client, tokens);
}
else if (commandType == "USER")
{
handleUser(client, tokens);
}
else if (commandType == "QUIT")
{
handleQuitCommand(client, tokens);
}
else if (commandType == "PING")
{
handlePingCommand(client, tokens);
}
else if (commandType == "ERROR")
{
if (tokens.size() > 1)
{
handleErrorCommand(client, tokens[1]);
}
}
else if (commandType == "MODE")
{
_modeHandler->handleModeCommand(client, command);
}
else if (commandType == "JOIN")
{
std::string joinParams = command.substr(command.find(" ") + 1);
JoinHandler joinHandler;
joinHandler.handleJoinCommand(client, joinParams, _server);
}
else
{
if (!client->isAuthenticated())
{
_server->sendToClient(client->getFd(), ERR_NOTREGISTERED(client));
_server->log("Client " + client->getNickname() + " attempted to send a command before registering.", RED);
}
else
{
_additionalCommands->processCommand(client, command);
}
}
}
void CommandHandler::handleCapCommand(Client* client, const std::vector<std::string>& tokens)
{
if (tokens.size() < 2)
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "CAP"));
return;
}
std::string subcommand = tokens[1];
std::string capabilities = "multi-prefix extended-join account-notify batch invite-notify";
if (subcommand == "LS")
{
_server->sendToClient(client->getFd(), RPL_CAP(client, "LS", capabilities));
}
else if (subcommand == "LIST")
{
_server->sendToClient(client->getFd(), RPL_CAP(client, "LIST", capabilities));
}
else if (subcommand == "REQ")
{
if (tokens.size() < 3)
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "CAP"));
return;
}
std::string requestedCapabilities = tokens[2];
_server->sendToClient(client->getFd(), RPL_CAP(client, "ACK", requestedCapabilities));
}
else if (subcommand == "END")
{
_server->sendToClient(client->getFd(), RPL_CAPEND(client));
}
else
{
_server->sendToClient(client->getFd(), ERR_UNKNOWNCOMMAND(client, "CAP"));
}
}
void CommandHandler::handlePassCommand(Client* client, const std::vector<std::string>& tokens)
{
if (tokens.size() < 2)
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "PASS"));
return;
}
if (client->isAuthenticated())
{
_server->sendToClient(client->getFd(), ERR_ALREADYREGISTERED(client));
return;
}
if (tokens[1] == _server->_password)
{
client->setPassword(tokens[1]);
_server->sendToClient(client->getFd(), ":server NOTICE * :Password accepted\r\n");
_server->log("Client " + client->getNickname() + " provided correct password.", GREEN);
} else {
_server->sendToClient(client->getFd(), ERR_PASSWDMISMATCH(client));
_server->log("Client " + client->getNickname() + " failed authentication password.", RED);
}
}
bool CommandHandler::isValidNickname(const std::string& nickname)
{
if (nickname.empty() || nickname[0] == '#' || nickname[0] == ':' || nickname.find(' ') != std::string::npos)
{
return false;
}
return true;
}
bool CommandHandler::isNicknameInUse(const std::string& nickname)
{
for (std::map<int, Client*>::iterator it = _server->_clients.begin(); it != _server->_clients.end(); ++it)
{
if (it->second->getNickname() == nickname)
{
return true;
}
}
return false;
}
void CommandHandler::handleNick(Client* client, const std::vector<std::string>& tokens)
{
if (tokens.size() < 2)
{
_server->sendToClient(client->getFd(), ERR_NONICKNAMEGIVEN(client));
return;
}
std::string newNick = tokens[1];
if (!isValidNickname(newNick))
{
_server->sendToClient(client->getFd(), ERR_ERRONEUSNICKNAME(client, newNick));
return;
}
if (isNicknameInUse(newNick))
{
_server->sendToClient(client->getFd(), ERR_NICKNAMEINUSE(client, newNick));
return;
}
std::string oldNick = client->getNickname();
client->setNickname(newNick);
std::string nickMessage = ":" + oldNick + " NICK " + newNick + "\r\n";
for (std::map<int, Client*>::iterator it = _server->_clients.begin(); it != _server->_clients.end(); ++it)
{
if (it->second->isAuthenticated())
_server->sendToClient(it->second->getFd(), nickMessage);
}
std::ostringstream oss;
oss << "Client " << client->getFd() << " changed nickname from " << oldNick << " to " << newNick;
_server->log(oss.str(), GREEN);
}
void CommandHandler::handleUser(Client* client, const std::vector<std::string>& tokens)
{
if (tokens.size() < 5)
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "USER"));
std::ostringstream oss;
oss << "Client " << client->getFd() << ": USER command failed - not enough parameters.";
_server->log(oss.str(), RED);
return;
}
if (client->isAuthenticated())
{
_server->sendToClient(client->getFd(), ERR_ALREADYREGISTERED(client));
std::ostringstream oss;
oss << "Client " << client->getFd() << ": USER command failed - already registered.";
_server->log(oss.str(), RED);
return;
}
std::string username = tokens[1];
std::string realname = tokens[4].substr(1);
client->setUser(username);
client->setRealName(realname);
std::ostringstream oss;
oss << "Client " << client->getFd() << ": USER command set username to " << username << " and real name to " << realname;
_server->log(oss.str(), BLUE);
if (client->getPassword() == _server->_password && !client->getNickname().empty())
{
client->authenticate();
WelcomeHandler welcomeHandler;
welcomeHandler.sendWelcomeMessages(client, _server);
_server->log("Client " + client->getNickname() + " authenticated.", GREEN);
}
else
{
std::ostringstream oss;
oss << "Client " << client->getFd() << ": USER command failed - authentication conditions not met.";
_server->log(oss.str(), RED);
}
}
void CommandHandler::handlePingCommand(Client* client, const std::vector<std::string>& tokens)
{
if (tokens.size() < 2)
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "PING"));
return;
}
std::string serverName = tokens[1];
std::string pongMessage = ":" + serverName + " PONG " + serverName + "\r\n";
_server->sendToClient(client->getFd(), pongMessage);
}
void CommandHandler::handleQuitCommand(Client* client, const std::vector<std::string>& tokens)
{
std::string reason = "Quit: ";
if (tokens.size() > 1)
{
reason += tokens[1];
}
std::string quitMessage = ":" + client->getNickname() + " QUIT :" + reason + "\r\n";
std::map<std::string, Channel*>::iterator it;
for (it = _server->getChannels().begin(); it != _server->getChannels().end(); ++it)
{
if (it->second->hasClient(client))
{
it->second->broadcast(quitMessage, client, _server);
it->second->removeClient(client);
}
}
_server->sendToClient(client->getFd(), "ERROR :" + reason + "\r\n");
_server->disconnectClient(client->getFd());
}
void CommandHandler::handleErrorCommand(Client* client, const std::string &message)
{
_server->sendToClient(client->getFd(), "ERROR :" + message + "\r\n");
_server->disconnectClient(client->getFd());
}

View File

@@ -0,0 +1,67 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* InviteHandler.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/30 13:02:09 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 19:11:22 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "InviteHandler.hpp"
InviteHandler::InviteHandler(Server* server) : _server(server) {}
void InviteHandler::handleInviteCommand(Client* client, const std::string& command)
{
std::istringstream iss(command);
std::string cmd, nickname, channelName;
iss >> cmd >> nickname >> channelName;
if (nickname.empty() || channelName.empty())
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "INVITE"));
return;
}
Channel* channel = _server->getChannelByName(channelName);
if (!channel)
{
_server->sendToClient(client->getFd(), ERR_NOSUCHCHANNEL(client, channelName));
return;
}
if (!channel->hasClient(client))
{
_server->sendToClient(client->getFd(), ERR_NOTONCHANNEL(client, channelName));
return;
}
if (!channel->isOperator(client) && channel->isInviteOnly())
{
_server->sendToClient(client->getFd(), ERR_CHANOPRIVSNEEDED(client, channelName));
return;
}
Client* targetClient = _server->getClientByName(nickname);
if (!targetClient)
{
_server->sendToClient(client->getFd(), ERR_NOSUCHNICK(client, nickname));
return;
}
if (channel->hasClient(targetClient))
{
_server->sendToClient(client->getFd(), ERR_USERONCHANNEL(client, nickname, channelName));
return;
}
channel->addInvitedClient(targetClient);
_server->sendToClient(client->getFd(), RPL_INVITING(client, channel->getName()));
std::ostringstream inviteMsg;
inviteMsg << ":" << client->getNickname() << " INVITE " << nickname << " " << channelName << "\r\n";
_server->sendToClient(targetClient->getFd(), inviteMsg.str());
}

167
ft_irc3/src/Join.cpp Normal file
View File

@@ -0,0 +1,167 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Join.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/21 19:51:31 by fgras-ca #+# #+# */
/* Updated: 2024/05/21 20:13:30 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "Join.hpp"
void JoinHandler::handleJoinCommand(Client* client, const std::string& params, Server* server)
{
std::map<std::string, Channel*>& channels = server->getChannels();
Channel* channel;
server->log("Received JOIN command with params: " + params, RED);
// Split params into channel names and keys
std::vector<std::string> parts = split(params, " ");
server->log("Split parts: ", RED);
for (size_t i = 0; i < parts.size(); ++i)
{
std::ostringstream oss;
oss << "Part " << i << ": " << parts[i];
server->log(oss.str(), RED);
}
if (parts.empty())
{
server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "JOIN"));
return;
}
std::string channelNames = parts[0];
std::string keys = (parts.size() > 1) ? parts[1] : "";
server->log("Split params: channelNames = " + channelNames + ", keys = " + keys, RED);
std::vector<std::string> channelList = split(channelNames, ",");
std::vector<std::string> keyList = split(keys, ",");
server->log("channelList: ", RED);
for (size_t i = 0; i < channelList.size(); ++i)
{
server->log(channelList[i], RED);
}
server->log("keyList: ", RED);
for (size_t i = 0; i < keyList.size(); ++i)
{
server->log(keyList[i], RED);
}
for (size_t i = 0; i < channelList.size(); ++i)
{
const std::string& channelName = channelList[i];
server->log("Processing channel: " + channelName, RED);
if (channelName.empty())
{
server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "JOIN"));
return;
}
if (channelName[0] == '0' && channelName.size() == 1)
{
// Leave all channels
return;
}
if (channelName[0] != '#' && channelName[0] != '&')
{
server->sendToClient(client->getFd(), ERR_NOSUCHCHANNEL(client, channelName));
return;
}
if (channels.find(channelName) == channels.end())
{
channel = new Channel(channelName);
channels[channelName] = channel;
channel->addOperator(client); // Set client as operator for new channel
}
else
{
channel = channels[channelName];
}
if (channel->isBanned(client))
{
server->sendToClient(client->getFd(), ERR_BANNEDFROMCHAN(client, channelName));
return;
}
if (channel->isFull())
{
server->sendToClient(client->getFd(), ERR_CHANNELISFULL(client, channelName));
return;
}
if (channel->isInviteOnly() && !channel->isInvited(client))
{
server->sendToClient(client->getFd(), ERR_INVITEONLYCHAN(client, channelName));
return;
}
// Extract the key for this channel, if provided
std::string key = (i < keyList.size()) ? keyList[i] : "";
server->log("Using key: " + key + " for channel: " + channelName, RED);
if (!channel->checkKey(key))
{
server->sendToClient(client->getFd(), ERR_BADCHANNELKEY(client, channelName));
return;
}
channel->addClient(client);
sendJoinSuccess(client, channel, server);
}
}
void JoinHandler::sendJoinSuccess(Client* client, Channel* channel, Server* server)
{
std::vector<Client*> clients = channel->getClients();
for (std::vector<Client*>::iterator it = clients.begin(); it != clients.end(); ++it)
{
std::string joinMsg = ":" + client->getNickname() + " JOIN " + channel->getName() + "\r\n";
server->sendToClient((*it)->getFd(), joinMsg);
}
if (!channel->getTopic().empty())
{
server->sendToClient(client->getFd(), RPL_TOPIC(client, channel->getName(), channel->getTopic()));
server->sendToClient(client->getFd(), RPL_TOPICWHOTIME(client, channel->getName(), channel->getTopicSetter(), channel->getTopicTime()));
}
else
{
server->sendToClient(client->getFd(), RPL_NOTOPIC(client, channel->getName()));
}
std::ostringstream oss;
oss << RPL_CHANNELMODEIS(client->getFd(), channel->getName(), channel->getModes());
oss << RPL_CREATIONTIME(client, channel->getName(), channel->getTopicTime());
server->sendToClient(client->getFd(), oss.str());
std::string usersList = getUsersList(channel);
server->sendToClient(client->getFd(), usersList);
}
std::string JoinHandler::getUsersList(Channel* channel)
{
std::vector<Client*> clients = channel->getClients();
std::string users;
for (std::vector<Client*>::iterator it = clients.begin(); it != clients.end(); ++it)
{
if (channel->isOperator((*it)))
users += "@";
users += (*it)->getNickname() + " ";
}
std::ostringstream oss;
oss << RPL_NAMREPLY(clients[0], channel->getName(), users);
oss << RPL_ENDOFNAMES(clients[0], channel->getName());
return oss.str();
}

View File

@@ -0,0 +1,68 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* KickHandler.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/06/01 17:00:31 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 19:12:13 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "KickHandler.hpp"
KickHandler::KickHandler(Server* server) : _server(server) {}
void KickHandler::handleKickCommand(Client* client, const std::string& command)
{
std::vector<std::string> tokens = split(command, " ");
if (tokens.size() < 3)
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "KICK"));
return;
}
std::string channelName = tokens[1];
std::string targetNickname = tokens[2];
std::string comment = (tokens.size() > 3) ? tokens[3] : "Kicked by operator";
Channel* channel = _server->getChannelByName(channelName);
if (!channel)
{
_server->sendToClient(client->getFd(), ERR_NOSUCHCHANNEL(client, channelName));
return;
}
if (!channel->hasClient(client))
{
_server->sendToClient(client->getFd(), ERR_NOTONCHANNEL(client, channelName));
return;
}
if (!channel->isOperator(client))
{
_server->sendToClient(client->getFd(), ERR_CHANOPRIVSNEEDED(client, channelName));
return;
}
Client* targetClient = _server->getClientByName(targetNickname);
if (!targetClient)
{
_server->sendToClient(client->getFd(), ERR_NOSUCHNICK(client, targetNickname));
return;
}
if (!channel->hasClient(targetClient))
{
_server->sendToClient(client->getFd(), ERR_USERNOTINCHANNEL(client, targetNickname, channelName));
return;
}
channel->removeClient(targetClient);
std::string kickMessage = ":" + client->getNickname() + " KICK " + channelName + " " + targetNickname + " :" + comment + "\r\n";
channel->broadcast(kickMessage, NULL, _server);
_server->sendToClient(targetClient->getFd(), kickMessage);
}

277
ft_irc3/src/ModeHandler.cpp Normal file
View File

@@ -0,0 +1,277 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ModeHandler.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/30 11:13:08 by fgras-ca #+# #+# */
/* Updated: 2024/06/04 16:12:50 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "ModeHandler.hpp"
ModeHandler::ModeHandler(Server* server) : _server(server) {}
void ModeHandler::handleModeCommand(Client* client, const std::string& command)
{
_server->log("Received MODE command: " + command, BLUE);
std::vector<std::string> tokens = split(command, " \n\r\t");
if (tokens.size() < 2)
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "MODE"));
_server->log("MODE command error: Need more parameters", RED);
return;
}
std::string target = tokens[1];
if (target[0] == '#' || target[0] == '&')
{
_server->log("Handling channel mode for target: " + target, GREEN);
handleChannelMode(client, tokens);
}
else
{
_server->log("Handling user mode for target: " + target, GREEN);
handleUserMode(client, tokens);
}
}
void ModeHandler::handleUserMode(Client* client, const std::vector<std::string>& tokens)
{
_server->log("Entered handleUserMode", MAGENTA);
std::string target = tokens[1];
_server->log("User mode target: " + target, CYAN);
if (target != client->getNickname())
{
_server->sendToClient(client->getFd(), ERR_USERSDONTMATCH(client));
_server->log("User mode error: Users don't match", RED);
return;
}
if (tokens.size() == 2)
{
_server->sendToClient(client->getFd(), RPL_UMODEIS(client, ""));
_server->log("Sent user modes to client", GREEN);
}
else
{
std::string modeString = tokens[2];
_server->log("Updating user modes: " + modeString, BLUE);
}
}
void ModeHandler::handleChannelMode(Client* client, const std::vector<std::string>& tokens)
{
_server->log("Entered handleChannelMode", MAGENTA);
std::string channelName = tokens[1];
_server->log("Channel mode target: " + channelName, CYAN);
Channel* channel = _server->getChannelByName(channelName);
if (!channel)
{
_server->sendToClient(client->getFd(), ERR_NOSUCHCHANNEL(client, channelName));
_server->log("Channel mode error: No such channel " + channelName, RED);
return;
}
if (tokens.size() == 2)
{
std::ostringstream oss;
oss << RPL_CHANNELMODEIS(client->getFd(), channel->getName(), channel->getModes());
oss << RPL_CREATIONTIME(client, channel->getName(), channel->getTopicTime());
_server->sendToClient(client->getFd(), oss.str());
_server->log("Sent channel modes to client", GREEN);
return;
}
if (!channel->isOperator(client))
{
_server->sendToClient(client->getFd(), ERR_CHANOPRIVSNEEDED(client, channelName));
_server->log("Channel mode error: Channel operator privileges needed for " + channelName, RED);
return;
}
std::string modeString = tokens[2];
bool adding = true;
size_t argIndex = 3;
for (size_t i = 0; i < modeString.length(); ++i)
{
char mode = modeString[i];
if (mode == '+')
{
adding = true;
_server->log("Adding mode: " + std::string(1, mode), GREEN);
} else if (mode == '-')
{
adding = false;
_server->log("Removing mode: " + std::string(1, mode), RED);
} else if (mode == 'b' || mode == 'l' || mode == 'i' || mode == 'k' || mode == 't' || mode == 'o')
{
std::string argument;
if (argIndex < tokens.size())
{
argument = tokens[argIndex++];
}
setChannelMode(client, channel, std::string(1, mode), adding, argument);
}
else
{
_server->sendToClient(client->getFd(), ERR_UNKNOWNMODE(client, mode, channelName));
_server->log("Unknown mode: " + std::string(1, mode), RED);
}
}
}
void ModeHandler::setChannelMode(Client *client, Channel* channel, const std::string& mode, bool adding, const std::string& argument)
{
_server->log("Setting channel mode: " + mode + " Adding: " + (adding ? "true" : "false") + " Argument: " + argument, GREEN);
if (mode == "l")
{
applyModeL(client, channel, adding, argument);
}
else if (mode == "i")
{
applyModeI(client, channel, adding);
}
else if (mode == "k")
{
applyModeK(client, channel, adding, argument);
}
else if (mode == "t")
{
applyModeT(channel, adding);
}
else if (mode == "o")
applyModeO(client, channel, adding, argument);
}
void ModeHandler::applyModeL(Client *client, Channel* channel, bool adding, const std::string& argument)
{
if (adding)
{
int limit = std::atoi(argument.c_str());
if (limit <= 0)
{
_server->sendToClient(client->getFd(), ERR_INVALIDMODEPARAM(client, argument));
_server->log("Invalid limit for mode +l: " + argument, RED);
return;
}
std::ostringstream oss;
oss << limit;
_server->log("Applying mode L: Setting limit to " + oss.str(), GREEN);
channel->setClientLimit(limit);
}
else
{
_server->log("Applying mode L: Removing limit", RED);
channel->setClientLimit(0);
}
}
void ModeHandler::applyModeI(Client *client, Channel *channel, bool adding)
{
std::string modeChange;
bool isAlreadySet;
modeChange = adding ? "+i" : "-i";
isAlreadySet = channel->isInviteOnly() == adding;
if (!isAlreadySet)
{
_server->sendToClient(client->getFd(), MODEACCEPTMESSAGE(client, channel->getName(), modeChange));
_server->log("Applying mode I: " + std::string(adding ? "Setting invite-only" : "Removing invite-only"), GREEN);
channel->setInviteOnly(adding);
}
}
void ModeHandler::applyModeK(Client *client, Channel *channel, bool adding, const std::string &argument)
{
bool isAlreadyProtected;
isAlreadyProtected = !channel->getKey().empty();
if (adding)
{
if (isAlreadyProtected)
{
_server->sendToClient(client->getFd(), ERR_KEYSET(client, channel->getName()));
_server->log("Mode +k error: Channel already has a key set", RED);
return;
}
if (argument.empty())
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "MODE +k"));
_server->log("Mode +k error: No key provided", RED);
return;
}
if (argument.find(' ') != std::string::npos)
{
_server->sendToClient(client->getFd(), ERR_INVALIDMODEPARAM(client, argument));
_server->log("Invalid key for mode +k: contains spaces", RED);
return;
}
_server->sendToClient(client->getFd(), MODEACCEPTMESSAGE(client, channel->getName(), "+k " + argument));
_server->log("Applying mode K: Setting key to " + argument, GREEN);
channel->setKey(argument);
}
else
{
if (!isAlreadyProtected)
{
_server->sendToClient(client->getFd(), ERR_LINKSET(client, channel->getName()));
_server->log("Mode -k error: No key to remove", RED);
return;
}
_server->sendToClient(client->getFd(), MODEACCEPTMESSAGE(client, channel->getName(), "-k"));
_server->log("Applying mode K: Removing key", RED);
channel->setKey("");
}
}
void ModeHandler::applyModeT(Channel* channel, bool adding)
{
std::string modeChange;
bool isAlreadySet;
modeChange = adding ? "+t" : "-t";
isAlreadySet = channel->isInviteOnly() == adding;
if (!isAlreadySet)
{
_server->log("Applying mode T: " + std::string(adding ? "Setting topic protection" : "Removing topic protection"), GREEN);
channel->setTopicProtection(adding);
}
}
void ModeHandler::applyModeO(Client* client, Channel* channel, bool adding, const std::string& argument)
{
Client* targetClient = _server->getClientByName(argument);
if (!targetClient)
{
_server->sendToClient(client->getFd(), ERR_NOSUCHNICK(client, argument));
_server->log("Mode o error: No such client " + argument, RED);
return;
}
if (!channel->hasClient(targetClient))
{
_server->sendToClient(client->getFd(), ERR_USERNOTINCHANNEL(client, argument, channel->getName()));
_server->log("Mode o error: Client " + argument + " not in channel " + channel->getName(), RED);
return;
}
if (adding)
{
_server->log("Applying mode o: Adding operator " + argument, GREEN);
channel->addOperator(targetClient);
std::string message = ":" + client->getNickname() + " MODE " + channel->getName() + " +o " + targetClient->getNickname() + "\r\n";
channel->broadcast(message, NULL, _server);
}
else
{
_server->log("Applying mode o: Removing operator " + argument, RED);
channel->removeOperator(targetClient);
std::string message = ":" + client->getNickname() + " MODE " + channel->getName() + " -o " + targetClient->getNickname() + "\r\n";
channel->broadcast(message, NULL, _server);
}
}

252
ft_irc3/src/Server.cpp Normal file
View File

@@ -0,0 +1,252 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Server.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:17:12 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 13:00:25 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)), _modeHandler(new ModeHandler(this)), _topicHandler(new TopicHandler(this))
{
initServer();
_botFilter = new BotFilter(this);
_botFilter->loadBadWords("badwords.txt");
}
Server::~Server()
{
delete _clientManager;
delete _commandHandler;
delete _topicHandler;
delete _botFilter;
for (std::map<int, Client*>::iterator it = _clients.begin(); it != _clients.end(); ++it)
{
delete it->second;
}
for (std::map<std::string, Channel*>::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);
}
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;
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);
close(_server_fd);
exit(EXIT_FAILURE);
}
if (listen(_server_fd, SOMAXCONN) == -1)
{
log("Failed to listen on socket.", RED);
close(_server_fd);
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);
struct pollfd stdin_pollfd;
stdin_pollfd.fd = STDIN_FILENO;
stdin_pollfd.events = POLLIN;
stdin_pollfd.revents = 0;
_poll_fds.push_back(stdin_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 if (_poll_fds[i].fd == STDIN_FILENO)
{
handleServerCommands();
}
else
{
_clientManager->handleClient(_poll_fds[i].fd);
}
}
}
}
}
std::map<std::string, Channel *> &Server::getChannels()
{
return _channels;
}
const std::string &Server::getPassword() const
{
return _password;
}
void Server::handleServerCommands()
{
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<std::string, Channel*>::iterator it = _channels.begin(); it != _channels.end(); ++it)
{
log(it->first, BLUE);
}
}
else if (command == "clients")
{
log("Listing all clients:", BLUE);
for (std::map<int, Client*>::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 << 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(), YELLOW);
}
}
std::map<int, Client *> &Server::getClients()
{
return _clients;
}
void Server::broadcast(const std::string &message)
{
for (std::map<int, Client *>::iterator it = _clients.begin(); it != _clients.end(); ++it)
{
sendToClient(it->first, message);
}
}
Client* Server::getClientByName(const std::string &name)
{
for (std::map<int, Client *>::iterator it = _clients.begin(); it != _clients.end(); ++it)
{
if (it->second->getNickname() == name)
{
return it->second;
}
}
return NULL;
}
Channel* Server::getChannelByName(const std::string &name)
{
std::map<std::string, Channel *>::iterator it = _channels.find(name);
if (it != _channels.end())
{
return it->second;
}
return NULL;
}
bool Server::MatchFd(const pollfd& pfd, int clientFd)
{
return pfd.fd == clientFd;
}
void Server::removePollFd(int clientFd)
{
for (std::vector<struct pollfd>::iterator it = _poll_fds.begin(); it != _poll_fds.end(); ++it)
{
if (MatchFd(*it, clientFd))
{
_poll_fds.erase(it);
break;
}
}
}
void Server::disconnectClient(int clientFd)
{
close(clientFd);
_clients.erase(clientFd);
removePollFd(clientFd);
std::ostringstream oss;
oss << "Client disconnected: " << clientFd;
log(oss.str(), YELLOW);
}

View File

@@ -0,0 +1,96 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* TopicHandler.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/30 17:04:58 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 19:17:25 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "TopicHandler.hpp"
TopicHandler::TopicHandler(Server* server) : _server(server) {}
void TopicHandler::handleTopicCommand(Client* client, const std::string& command)
{
_server->log("Received TOPIC command: " + command, "\033[1;34m");
std::vector<std::string> tokens = split(command, " \n\r\t");
if (tokens.size() < 2)
{
_server->sendToClient(client->getFd(), ERR_NEEDMOREPARAMS(client, "TOPIC"));
_server->log("TOPIC command error: Need more parameters", RED);
return;
}
std::string channelName = tokens[1];
Channel* channel = _server->getChannelByName(channelName);
if (!channel)
{
_server->sendToClient(client->getFd(), ERR_NOSUCHCHANNEL(client, channelName));
_server->log("TOPIC command error: No such channel " + channelName, RED);
return;
}
if (!channel->hasClient(client))
{
_server->sendToClient(client->getFd(), ERR_NOTONCHANNEL(client, channelName));
_server->log("TOPIC command error: Client not on channel " + channelName, RED);
return;
}
if (tokens.size() == 2)
{
viewTopic(client, channel);
}
else
{
size_t topicStart = command.find(" :");
std::string topic = (topicStart != std::string::npos) ? command.substr(topicStart + 2) : "";
changeTopic(client, channel, topic);
}
}
void TopicHandler::viewTopic(Client* client, Channel* channel)
{
if (channel->getTopic().empty())
{
_server->sendToClient(client->getFd(), RPL_NOTOPIC(client, channel->getName()));
_server->log("No topic set for channel " + channel->getName(), GREEN);
}
else
{
_server->sendToClient(client->getFd(), RPL_TOPIC(client, channel->getName(), channel->getTopic()));
_server->sendToClient(client->getFd(), RPL_TOPICWHOTIME(client, channel->getName(), channel->getTopicSetter(), channel->getTopicTime()));
_server->log("Sent topic info for channel " + channel->getName(), GREEN);
}
}
void TopicHandler::changeTopic(Client* client, Channel* channel, const std::string& topic)
{
if (channel->getTopicProtection() && !channel->isOperator(client))
{
_server->sendToClient(client->getFd(), ERR_CHANOPRIVSNEEDED(client, channel->getName()));
_server->log("TOPIC command error: Channel operator privileges needed for " + channel->getName(), RED);
return;
}
if (topic == channel->getTopic())
{
_server->sendToClient(client->getFd(), RPL_TOPIC(client, channel->getName(), channel->getTopic()));
_server->log("TOPIC unchanged for channel " + channel->getName(), GREEN);
return;
}
channel->setTopic(topic, client->getNickname());
std::vector<Client*> clients = channel->getClients();
for (std::vector<Client*>::iterator it = clients.begin(); it != clients.end(); ++it)
{
_server->sendToClient((*it)->getFd(), ":" + client->getNickname() + " TOPIC " + channel->getName() + " :" + topic + "\r\n");
}
_server->log("Changed topic for channel " + channel->getName() + " to: " + topic, GREEN);
}

37
ft_irc3/src/Utils.cpp Normal file
View File

@@ -0,0 +1,37 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Utils.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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<std::string> split(const std::string &input, const std::string &delimiters)
{
std::vector<std::string> 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;
}

44
ft_irc3/src/Welcome.cpp Normal file
View File

@@ -0,0 +1,44 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Welcome.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/21 17:53:52 by fgras-ca #+# #+# */
/* Updated: 2024/05/30 16:43:14 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "Welcome.hpp"
void WelcomeHandler::sendWelcomeMessages(Client *client, Server *server)
{
server->sendToClient(client->getFd(), RPL_WELCOME(client));
server->sendToClient(client->getFd(), RPL_YOURHOST(client));
server->sendToClient(client->getFd(), RPL_CREATED(client));
server->sendToClient(client->getFd(), RPL_MYINFO(client));
server->sendToClient(client->getFd(), RPL_ISUPPORT(client, "i/t/k/o/l"));
sendMotd(client, server);
}
void WelcomeHandler::sendMotd(Client *client, Server *server)
{
std::ifstream motdFile("motd.txt");
if (motdFile.is_open())
{
std::string line;
server->sendToClient(client->getFd(), RPL_MOTDSTART(client));
while (std::getline(motdFile, line))
{
server->sendToClient(client->getFd(), RPL_MOTD(client, line));
}
server->sendToClient(client->getFd(), RPL_ENDOFMOTD(client));
motdFile.close();
}
else
{
server->sendToClient(client->getFd(), ERR_NOMOTD(client));
}
}

71
ft_irc3/src/Who.cpp Normal file
View File

@@ -0,0 +1,71 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Who.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/17 16:09:20 by fgras-ca #+# #+# */
/* Updated: 2024/05/30 16:56:03 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "Who.hpp"
WhoHandler::WhoHandler(Server *server)
: _server(server)
{
}
void WhoHandler::handleWhoCommand(Client *client, const std::string &command)
{
std::istringstream iss(command);
std::string cmd, mask;
iss >> cmd >> mask;
std::map<std::string, Channel *> &channels = _server->getChannels();
if (channels.find(mask) != channels.end())
{
Channel *channel = channels[mask];
std::vector<Client *> 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, mask));
}
void WhoHandler::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));
return;
}
Client *targetClient = _server->getClientByName(target);
if (!targetClient)
{
_server->sendToClient(client->getFd(), ERR_NOSUCHNICK(client, target));
return;
}
_server->sendToClient(client->getFd(), RPL_WHOISUSER(client, targetClient));
_server->sendToClient(client->getFd(), RPL_WHOISSERVER(client, target, "IRC server info"));
_server->sendToClient(client->getFd(), RPL_ENDOFWHOIS(client, target));
}

33
ft_irc3/src/main.cpp Normal file
View File

@@ -0,0 +1,33 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* main.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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 <iostream>
#include <cstdlib>
int main(int argc, char *argv[])
{
if (argc != 3)
{
std::cerr << RED << "Usage: " << argv[0] << " <port> <password>" << 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;
}