This commit is contained in:
2024-05-17 20:29:22 +02:00
parent aaa1457b61
commit 3660d41dea
45 changed files with 359 additions and 1091 deletions

View File

@@ -48,7 +48,17 @@
"stdexcept": "cpp",
"streambuf": "cpp",
"typeinfo": "cpp",
"list": "cpp"
"list": "cpp",
"chrono": "cpp",
"condition_variable": "cpp",
"ctime": "cpp",
"set": "cpp",
"ratio": "cpp",
"mutex": "cpp",
"semaphore": "cpp",
"stop_token": "cpp",
"thread": "cpp",
"cinttypes": "cpp"
},
"C_Cpp_Runner.cCompilerPath": "gcc",
"C_Cpp_Runner.cppCompilerPath": "g++",

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:41:35 by fgras-ca #+# #+# */
/* Updated: 2024/05/15 12:48:40 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 20:08:13 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -16,23 +16,27 @@
#include <string>
#include <vector>
#include <algorithm>
#include <set>
class Client;
class Channel {
public:
Channel(const std::string &name);
~Channel();
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<Client *> &getClients() const;
const std::string &getName() const;
void addClient(Client *client);
void removeClient(Client *client);
bool isEmpty() const;
const std::vector<Client *> &getClients() const;
void addOperator(Client *client);
bool isOperator(Client *client) const;
private:
std::string _name;
std::vector<Client *> _clients;
std::string _name;
std::vector<Client *> _clients;
std::vector<Client *> _operators;
};
#endif

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:15:42 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 18:18:02 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 17:21:39 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -18,32 +18,30 @@
class Client
{
public:
Client(int fd);
Client(int fd, const std::string &nickname, const std::string &user);
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;
int getFd() const;
const std::string &getNickname() const;
void setNickname(const std::string &nickname);
const std::string &getUser() const;
void setUser(const std::string &user);
const std::string &getPassword() const;
void setPassword(const std::string &password);
const std::string &getRealName() const;
void setRealName(const std::string &realname);
bool isAuthenticated() const;
void authenticate();
bool isOperator() const;
void setOperator(bool isOperator);
private:
int _fd;
std::string _password;
std::string _nickname;
std::string _user;
std::string _realname;
bool _authenticated;
int _fd;
std::string _nickname;
std::string _user;
std::string _password;
std::string _realname;
bool _authenticated;
bool _operator;
};
#endif

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 18:30:07 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 16:26:03 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 18:51:44 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -35,9 +35,11 @@ class ClientManager
public:
ClientManager(Server *server);
void acceptClient();
void broadcastChannelList(Client *client);
void handleClient(int client_fd);
void removeClient(int client_fd);
private:
Server *_server;
};

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 18:14:12 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 17:35:46 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 17:10:38 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -18,12 +18,15 @@
#include "Client.hpp"
#include "Channel.hpp"
#include "AdditionalCommands.hpp"
#include "ModeWhoHandler.hpp"
#include "CommandHandler.hpp"
#include <string>
#include <sstream>
#include <algorithm>
class Server;
class ModeWhoHandler;
class CommandHandler
{

View File

@@ -0,0 +1,34 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ModeWhoHandler.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/17 16:08:48 by fgras-ca #+# #+# */
/* Updated: 2024/05/17 18:08:06 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef MODEWHOHANDLER_HPP
#define MODEWHOHANDLER_HPP
#include "Server.hpp"
#include "Client.hpp"
#include "Channel.hpp"
#include "Utils.hpp"
class Server;
class ModeWhoHandler
{
public:
ModeWhoHandler(Server *server);
void handleModeCommand(Client *client, const std::string &command);
void handleWhoCommand(Client *client, const std::string &command);
private:
Server *_server;
};
#endif

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:15:13 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 17:40:34 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 20:03:06 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -51,6 +51,9 @@ public:
std::map<std::string, Channel *> &getChannels();
std::map<int, Client *> &getClients();
const std::string &getPassword() const;
void broadcast(const std::string &message);
Client* getClientByName(const std::string &name); // Ajoutez cette méthode
void sendChannelListToClient(Client *client);
protected:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/16 15:27:29 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 16:41:53 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 20:15:14 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -64,19 +64,21 @@ void handleNickCommand(Server *server, Client *client, const std::string &comman
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);
// Enlever le ':' initial dans le message si présent
if (!message.empty() && message[0] == ':')
message = message.substr(1);
// Récupérer les canaux du serveur
std::map<std::string, Channel *> &channels = server->getChannels();
std::map<int, Client *> &clients = server->getClients();
// Si la cible est un canal
if (channels.find(target) != channels.end())
{
Channel *channel = channels[target];
@@ -84,6 +86,7 @@ void handlePrivmsgCommand(Server *server, Client *client, const std::string &com
for (size_t i = 0; i < channelClients.size(); ++i)
{
// Envoyer le message à tous les clients du canal sauf l'émetteur
if (channelClients[i] != client)
{
std::stringstream privMsg;
@@ -92,17 +95,23 @@ void handlePrivmsgCommand(Server *server, Client *client, const std::string &com
}
}
}
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());
}
// Si la cible est un utilisateur
else
{
std::stringstream ss;
ss << ":server 401 " << client->getNickname() << " " << target << " :No such nick/channel\r\n";
server->sendToClient(client->getFd(), ss.str());
Client *targetClient = server->getClientByName(target); // Utiliser getClientByName pour trouver le client par nom
if (targetClient)
{
std::stringstream privMsg;
privMsg << ":" << client->getNickname() << " PRIVMSG " << target << " :" << message << "\r\n";
server->sendToClient(targetClient->getFd(), privMsg.str());
}
else
{
// Si la cible n'est ni un canal ni un utilisateur existant, envoyer un message d'erreur
std::stringstream errorMsg;
errorMsg << ":server 401 " << client->getNickname() << " " << target << " :No such nick/channel\r\n";
server->sendToClient(client->getFd(), errorMsg.str());
}
}
}

View File

@@ -6,16 +6,15 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:42:57 by fgras-ca #+# #+# */
/* Updated: 2024/05/15 12:54:40 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 20:08:28 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "Channel.hpp"
#include "Client.hpp"
#include <algorithm>
Channel::Channel(const std::string &name)
: _name(name)
: _name(name)
{
}
@@ -25,25 +24,38 @@ Channel::~Channel()
const std::string &Channel::getName() const
{
return _name;
return _name;
}
void Channel::addClient(Client *client)
{
_clients.push_back(client);
_clients.push_back(client);
}
void Channel::removeClient(Client *client)
{
_clients.erase(std::remove(_clients.begin(), _clients.end(), client), _clients.end());
_clients.erase(std::remove(_clients.begin(), _clients.end(), client), _clients.end());
}
bool Channel::isEmpty() const
{
return _clients.empty();
return _clients.empty();
}
const std::vector<Client *> &Channel::getClients() const
{
return _clients;
return _clients;
}
void Channel::addOperator(Client *client)
{
if (std::find(_operators.begin(), _operators.end(), client) == _operators.end())
{
_operators.push_back(client);
}
}
bool Channel::isOperator(Client *client) const
{
return std::find(_operators.begin(), _operators.end(), client) != _operators.end();
}

View File

@@ -6,72 +6,83 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:17:42 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 18:18:19 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 17:20:02 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "Client.hpp"
Client::Client(int fd)
: _fd(fd), _authenticated(false)
Client::Client(int fd, const std::string &nickname, const std::string &user)
: _fd(fd), _nickname(nickname), _user(user), _authenticated(false), _operator(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;
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::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;
}

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 18:32:23 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 16:25:50 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 18:53:09 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -22,11 +22,11 @@ void ClientManager::acceptClient()
int client_fd = accept(_server->_server_fd, NULL, NULL);
if (client_fd < 0)
{
_server->log("Accept failed.", RED);
_server->log("Failed to accept client.", RED);
return;
}
fcntl(client_fd, F_SETFL, O_NONBLOCK);
_server->_clients[client_fd] = new Client(client_fd);
_server->_clients[client_fd] = new Client(client_fd, "", ""); // Initialize with default values
struct pollfd client_pollfd;
client_pollfd.fd = client_fd;
client_pollfd.events = POLLIN;
@@ -35,7 +35,16 @@ void ClientManager::acceptClient()
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::broadcastChannelList(Client *client) {
std::map<std::string, Channel *> &channels = _server->getChannels();
for (std::map<std::string, Channel *>::iterator it = channels.begin(); it != channels.end(); ++it) {
std::stringstream channelMsg;
channelMsg << ":server 322 " << client->getNickname() << " " << it->first << " :Existing channel\r\n";
_server->sendToClient(client->getFd(), channelMsg.str());
}
_server->sendToClient(client->getFd(), ":server 323 " + client->getNickname() + " :End of /LIST\r\n");
}
void ClientManager::handleClient(int client_fd)
@@ -65,6 +74,7 @@ void ClientManager::handleClient(int client_fd)
line.erase(std::remove(line.begin(), line.end(), '\n'), line.end());
_server->_commandHandler->handleCommand(client, line);
broadcastChannelList(_server->_clients[client_fd]);
}
}

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 18:26:34 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 19:34:36 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 20:05:16 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -27,14 +27,16 @@ void CommandHandler::handleCommand(Client *client, const std::string &command)
}
std::string commandType = tokens[0];
if (commandType == "PASS")
if (commandType == "CAP")
{}
else 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->sendToClient(client->getFd(), ":server 001 " + client->getNickname() + " :Password accepted!\r\n");
_server->log("Client " + client->getNickname() + " provided correct password.", GREEN);
_server->sendChannelListToClient(client);
}
else
{
@@ -62,8 +64,12 @@ void CommandHandler::handleCommand(Client *client, const std::string &command)
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");
std::stringstream welcomeMsg;
welcomeMsg << ":server 001 " << client->getNickname() << " :Welcome to the IRC server!\r\n";
_server->sendToClient(client->getFd(), welcomeMsg.str());
_server->log("Client " + client->getNickname() + " authenticated.", GREEN);
_server->sendChannelListToClient(client);
}
}
}
@@ -101,6 +107,16 @@ void CommandHandler::processCommand(Client *client, const std::string &command)
{
handlePrivmsgCommand(_server, client, command);
}
else if (command.find("MODE") == 0)
{
ModeWhoHandler modeHandler(_server);
modeHandler.handleModeCommand(client, command);
}
else if (command.find("WHO") == 0)
{
ModeWhoHandler whoHandler(_server);
whoHandler.handleWhoCommand(client, command);
}
else
{
std::stringstream ss;
@@ -116,6 +132,9 @@ void CommandHandler::handleJoinCommand(Client *client, const std::string &channe
{
_server->getChannels()[channelName] = new Channel(channelName);
_server->log("Channel created: " + channelName, GREEN);
// Notifier tous les clients de la création du canal
_server->broadcast(":server NOTICE * :New channel created: " + channelName + "\r\n");
}
Channel *channel = _server->getChannels()[channelName];
channel->addClient(client);
@@ -136,6 +155,9 @@ void CommandHandler::handleJoinCommand(Client *client, const std::string &channe
_server->log(ss.str(), MAGENTA);
}
std::string CommandHandler::getUsersList(Channel *channel)
{
std::vector<Client *> clients = channel->getClients();

View File

@@ -0,0 +1,95 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ModeWhoHandler.cpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/17 16:09:20 by fgras-ca #+# #+# */
/* Updated: 2024/05/17 18:24:34 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#include "ModeWhoHandler.hpp"
#include "Channel.hpp"
#include <sstream>
#include <iostream>
// Couleurs ANSI pour les logs
#define RESET "\033[0m"
#define GREEN "\033[32m"
#define YELLOW "\033[33m"
#define RED "\033[31m"
#define BLUE "\033[34m"
#define MAGENTA "\033[35m"
ModeWhoHandler::ModeWhoHandler(Server *server)
: _server(server)
{
}
void ModeWhoHandler::handleModeCommand(Client *client, const std::string &command)
{
std::istringstream iss(command);
std::string cmd, channelName, mode, user;
iss >> cmd >> channelName >> mode >> user;
std::map<std::string, Channel *> &channels = _server->getChannels();
if (channels.find(channelName) == channels.end())
{
_server->sendToClient(client->getFd(), ":server 403 " + client->getNickname() + " " + channelName + " :No such channel\r\n");
return;
}
Channel *channel = channels[channelName];
if (mode == "+o")
{
if (client->isOperator())
{
Client *targetClient = _server->getClientByName(user);
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);
}
else
{
_server->sendToClient(client->getFd(), ":server 401 " + client->getNickname() + " " + user + " :No such nick/channel\r\n");
}
}
else
{
_server->sendToClient(client->getFd(), ":server 482 " + client->getNickname() + " " + channelName + " :You're not channel operator\r\n");
}
}
}
void ModeWhoHandler::handleWhoCommand(Client *client, const std::string &command)
{
std::istringstream iss(command);
std::string cmd, channelName;
iss >> cmd >> channelName;
std::map<std::string, Channel *> &channels = _server->getChannels();
if (channels.find(channelName) == channels.end())
{
_server->sendToClient(client->getFd(), ":server 403 " + client->getNickname() + " " + channelName + " :No such channel\r\n");
return;
}
Channel *channel = channels[channelName];
std::vector<Client *> channelClients = channel->getClients();
for (size_t i = 0; i < channelClients.size(); ++i)
{
std::stringstream whoMsg;
whoMsg << ":server 352 " << client->getNickname() << " " << channelName << " " << channelClients[i]->getNickname() << " "
<< channelClients[i]->getUser() << " " << _server->getPassword() << " " << channelClients[i]->getRealName() << "\r\n";
_server->sendToClient(client->getFd(), whoMsg.str());
}
std::stringstream endOfWhoMsg;
endOfWhoMsg << ":server 315 " << client->getNickname() << " " << channelName << " :End of /WHO list.\r\n";
_server->sendToClient(client->getFd(), endOfWhoMsg.str());
}

View File

@@ -6,7 +6,7 @@
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:17:12 by fgras-ca #+# #+# */
/* Updated: 2024/05/16 18:52:19 by fgras-ca ### ########.fr */
/* Updated: 2024/05/17 20:03:44 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
@@ -144,9 +144,10 @@ void Server::handleServerCommands()
void Server::log(const std::string &message, const std::string &color)
{
std::cout << color << message << RESET << std::endl;
std::cout << color << message << "\033[0m" << std::endl;
}
void Server::sendToClient(int client_fd, const std::string &message)
{
int result = send(client_fd, message.c_str(), message.length(), 0);
@@ -164,10 +165,17 @@ void Server::sendToClient(int client_fd, const std::string &message)
}
}
std::map<int, Client *> &Server::getClients()
const std::string &Server::getPassword() const
{
return _clients;
return _password;
}
void Server::broadcast(const std::string &message)
{
for (std::map<int, Client *>::iterator it = _clients.begin(); it != _clients.end(); ++it)
{
sendToClient(it->first, message);
}
}
std::map<std::string, Channel *> &Server::getChannels()
@@ -175,11 +183,38 @@ std::map<std::string, Channel *> &Server::getChannels()
return _channels;
}
const std::string &Server::getPassword() const
std::map<int, Client *> &Server::getClients()
{
return _password;
return _clients;
}
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; // Remplacez nullptr par NULL
}
void Server::sendChannelListToClient(Client *client)
{
std::map<std::string, Channel *> &channels = getChannels();
for (std::map<std::string, Channel *>::iterator it = channels.begin(); it != channels.end(); ++it)
{
std::stringstream channelMsg;
channelMsg << ":server 322 " << client->getNickname() << " " << it->first << " :" << it->second->getClients().size() << "\r\n";
sendToClient(client->getFd(), channelMsg.str());
}
std::stringstream endOfListMsg;
endOfListMsg << ":server 323 " << client->getNickname() << " :End of /LIST\r\n";
sendToClient(client->getFd(), endOfListMsg.str());
}
/* Explications des Fonctions
Server::Server(int port, const std::string &password)

0
ft_irc3/target Normal file
View File