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

18
ft_irc3/.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "linux-gcc-x64",
"includePath": [
"${workspaceFolder}/**"
],
"compilerPath": "/usr/bin/gcc",
"cStandard": "${default}",
"cppStandard": "${default}",
"intelliSenseMode": "linux-gcc-x64",
"compilerArgs": [
""
]
}
],
"version": 4
}

24
ft_irc3/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": false,
"cwd": "/home/fgras-ca/Bureau/ft_irc3/src",
"program": "/home/fgras-ca/Bureau/ft_irc3/src/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

120
ft_irc3/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,120 @@
{
"files.associations": {
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"map": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"typeinfo": "cpp",
"list": "cpp",
"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++",
"C_Cpp_Runner.debuggerPath": "gdb",
"C_Cpp_Runner.cStandard": "",
"C_Cpp_Runner.cppStandard": "",
"C_Cpp_Runner.msvcBatchPath": "",
"C_Cpp_Runner.useMsvc": false,
"C_Cpp_Runner.warnings": [
"-Wall",
"-Wextra",
"-Wpedantic",
"-Wshadow",
"-Wformat=2",
"-Wcast-align",
"-Wconversion",
"-Wsign-conversion",
"-Wnull-dereference"
],
"C_Cpp_Runner.msvcWarnings": [
"/W4",
"/permissive-",
"/w14242",
"/w14287",
"/w14296",
"/w14311",
"/w14826",
"/w44062",
"/w44242",
"/w14905",
"/w14906",
"/w14263",
"/w44265",
"/w14928"
],
"C_Cpp_Runner.enableWarnings": true,
"C_Cpp_Runner.warningsAsError": false,
"C_Cpp_Runner.compilerArgs": [],
"C_Cpp_Runner.linkerArgs": [],
"C_Cpp_Runner.includePaths": [],
"C_Cpp_Runner.includeSearch": [
"*",
"**/*"
],
"C_Cpp_Runner.excludeSearch": [
"**/build",
"**/build/**",
"**/.*",
"**/.*/**",
"**/.vscode",
"**/.vscode/**"
],
"C_Cpp_Runner.useAddressSanitizer": false,
"C_Cpp_Runner.useUndefinedSanitizer": false,
"C_Cpp_Runner.useLeakSanitizer": false,
"C_Cpp_Runner.showCompilationTime": false,
"C_Cpp_Runner.useLinkTimeOptimization": false,
"C_Cpp_Runner.msvcSecureNoWarnings": false
}

46
ft_irc3/Makefile Normal file
View File

@@ -0,0 +1,46 @@
# **************************************************************************** #
# #
# ::: :::::::: #
# Makefile :+: :+: :+: #
# +:+ +:+ +:+ #
# By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ #
# +#+#+#+#+#+ +#+ #
# Created: 2024/05/15 12:13:50 by fgras-ca #+# #+# #
# Updated: 2024/05/15 12:47:49 by fgras-ca ### ########.fr #
# #
# **************************************************************************** #
CXX = g++
CXXFLAGS = -Wall -Wextra -Werror -std=c++98
LDFLAGS = -pthread
SRC_DIR = src
INC_DIR = includes
OBJ_DIR = obj
LOG_DIR = logs
SRCS = $(wildcard $(SRC_DIR)/*.cpp)
OBJS = $(SRCS:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o)
NAME = ircserv
all: $(NAME)
$(NAME): $(OBJS)
$(CXX) $(CXXFLAGS) -I$(INC_DIR) -o $@ $^ $(LDFLAGS)
mkdir -p $(LOG_DIR)
touch $(LOG_DIR)/irc_server.log
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
mkdir -p $(OBJ_DIR)
$(CXX) $(CXXFLAGS) -I$(INC_DIR) -c $< -o $@
clean:
rm -rf $(OBJ_DIR) $(NAME)
fclean: clean
rm -rf $(LOG_DIR)
re: fclean all
.PHONY: all clean fclean re

50
ft_irc3/badwords.txt Normal file
View File

@@ -0,0 +1,50 @@
fuck
Fuck
fucking
fucking
Shit
shit
Shitty
shitty
Bullshit
bullshit
shithead
Shithead
Scumbag
scumbag
asshole
Asshole
Cocksucker
cocksucker
Dickhead
dickhead
Suck my dick
suck my dick!
Ass
ass
Bastard
Bastard
connard
Connard
Va te faire foutre
va te faire foutre
ferme ta gueule
Ferme ta gueule
Pute
pute
Salope
salope
trou du cul
Trou du cul
sac à merde
Sac à merde
casse-couilles
Casse-couilles
tête de bite
Tête de bite
bite
bite
pétasse
Pétasse
Fini à la pisse
fini à la pisse

0
ft_irc3/cmd Normal file
View File

View File

@@ -0,0 +1,48 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* AdditionalCommands.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/21 18:09:05 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 18:48:32 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef ADDITIONALCOMMANDS_HPP
#define ADDITIONALCOMMANDS_HPP
#include "Client.hpp"
#include "Server.hpp"
#include "Channel.hpp"
#include "CommandHandler.hpp"
#include "Utils.hpp"
#include "RPL.hpp"
#include "Who.hpp"
#include "InviteHandler.hpp"
#include "TopicHandler.hpp"
#include "KickHandler.hpp"
#include <sstream>
#include <string>
#include <fstream>
class Server;
class Client;
class Channel;
class AdditionalCommands
{
private:
Server *_server;
public:
AdditionalCommands(Server *server);
void processCommand(Client *client, const std::string &command);
void broadcastChannelList(Client *client, Server *server);
void handlePartCommand(Server *server, Client *client, const std::string &command);
void handleNickCommand(Server *server, Client *client, const std::string &command);
void handlePrivmsgCommand(Server *server, Client *client, const std::string &command);
};
#endif

View File

@@ -0,0 +1,45 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* BotFilter.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/06/06 11:44:44 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 13:41:41 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef BOTFILTER_HPP
#define BOTFILTER_HPP
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cctype>
#include "Server.hpp"
#include "Client.hpp"
#include "Channel.hpp"
class BotFilter
{
public:
BotFilter(Server *server);
void loadBadWords(const std::string &fileName);
bool checkMessage(Client *client, Channel *channel, const std::string &message);
private:
Server *_server;
std::vector<std::string> _badWords;
std::map<int, int> _warnings;
bool containsBadWords(const std::string &message);
void warnClient(Client *client, Channel *channel);
void kickClient(Client *client, Channel *channel);
};
#endif

View File

@@ -0,0 +1,81 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Channel.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:41:35 by fgras-ca #+# #+# */
/* Updated: 2024/06/04 16:12:23 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CHANNEL_HPP
#define CHANNEL_HPP
#include <string>
#include <vector>
#include <algorithm>
#include <set>
#include <ctime>
#include "RPL.hpp"
#include "Client.hpp"
#include "Server.hpp"
class Server;
class Client;
class Channel
{
public:
Channel(const std::string &name);
~Channel();
const std::string &getName() const;
void addClient(Client *client);
void removeClient(Client *client);
bool isEmpty() const;
const std::vector<Client *> &getClients() const;
void addOperator(Client *client);
bool isOperator(Client *client) const;
void removeOperator(Client *client); // Ajouté
bool hasClient(Client *client) const;
void broadcast(const std::string &message, Client *_client, Server *_server);
bool isBanned(Client *client) const;
bool isFull() const;
bool isInviteOnly() const;
bool isInvited(Client *client) const;
bool checkKey(const std::string &key) const;
const std::string &getTopic() const;
const std::string &getTopicSetter() const;
time_t getTopicTime() const;
void setTopic(const std::string &topic, const std::string &setter);
std::string getKey();
void setClientLimit(size_t limit);
size_t getClientLimit() const;
void setInviteOnly(bool inviteOnly);
void setKey(const std::string &key);
void setTopicProtection(bool protection);
std::string getModes() const;
bool getTopicProtection() const;
void addInvitedClient(Client* client);
private:
std::string _name;
std::vector<Client *> _clients;
std::vector<Client *> _operators;
std::set<Client *> _bannedClients;
std::set<Client *> _invitedClients;
std::string _key;
std::string _topic;
std::string _topicSetter;
time_t _topicTime;
size_t _clientLimit;
bool _inviteOnly;
bool _topicProtection;
};
#endif

View File

@@ -0,0 +1,68 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Client.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:15:42 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 13:23:23 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CLIENT_HPP
#define CLIENT_HPP
#include <string>
#include <set>
class Client
{
public:
Client(int fd, const std::string &nickname, const std::string &user, const std::string &host, const std::string &password, const std::string &realname);
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 &getHost() const;
void setHost(const std::string &host);
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);
// Ajout des méthodes pour la gestion du statut "away"
bool isAway() const;
const std::string &getAwayMessage() const;
void setAwayMessage(const std::string &message);
void setAway(bool away);
std::string getKey() const;
void setkey(const std::string &key);
char buffer[1024];
char buffer2[1024];
void joinChannel(const std::string &channel);
void leaveChannel(const std::string &channel);
const std::set<std::string> &getChannels() const;
private:
int _fd;
std::string _nickname;
std::string _user;
std::string _host;
std::string _password;
std::string _realname;
bool _authenticated;
bool _operator;
bool _away;
std::string _awayMessage;
std::string _key;
std::set<std::string> _channels;
};
#endif

View File

@@ -0,0 +1,56 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ClientManager.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 18:30:07 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 13:01:05 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef CLIENTMANAGER_HPP
#define CLIENTMANAGER_HPP
#include "Client.hpp"
#include "Channel.hpp"
#include "Server.hpp"
#include "CommandHandler.hpp"
#include "RPL.hpp"
#include "Utils.hpp"
#include "BotFilter.hpp"
#include <poll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <sstream>
#include <algorithm>
#include <fcntl.h>
#include <iostream>
#include <netdb.h>
#include <arpa/inet.h>
#include <fstream>
#include <map>
#include <vector>
class Server;
class Client;
class BotFilter;
class ClientManager
{
public:
ClientManager(Server *server);
void acceptClient();
void handleClientNext(int client_fd, char * buffer, int bytes_received);
void handleClient(int client_fd);
void removeClient(int client_fd);
private:
Server *_server;
BotFilter *_botFilter;
};
#endif

View File

@@ -0,0 +1,62 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* CommandHandler.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 18:14:12 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 18:47:51 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef COMMANDHANDLER_HPP
#define COMMANDHANDLER_HPP
#include "Utils.hpp"
#include "Server.hpp"
#include "Client.hpp"
#include "Channel.hpp"
#include "Who.hpp"
#include "AdditionalCommands.hpp"
#include "RPL.hpp"
#include "Join.hpp"
#include "Welcome.hpp"
#include "ModeHandler.hpp"
#include <string>
#include <sstream>
#include <algorithm>
class Server;
class Client;
class Channel;
class WhoHandler;
class AdditionalCommands;
class ModeHandler;
class CommandHandler
{
private:
Server *_server;
AdditionalCommands *_additionalCommands;
ModeHandler *_modeHandler;
public:
CommandHandler(Server *server);
bool isValidNickname(const std::string& nickname);
bool isNicknameInUse(const std::string& nickname);
void handleNick(Client* client, const std::vector<std::string>& tokens);
void handleUser(Client* client, const std::vector<std::string>& tokens);
void handlePingCommand(Client* client, const std::vector<std::string>& tokens);
void handleCommand(Client *client, const std::string &command);
void handleCapCommand(Client* client, const std::vector<std::string>& tokens);
void handlePassCommand(Client* client, const std::vector<std::string>& tokens);
void handleQuitCommand(Client* client, const std::vector<std::string>& tokens);
void handleErrorCommand(Client* client, const std::string &message);
};
#endif

View File

@@ -0,0 +1,40 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* InviteHandler.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/30 13:01:50 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 18:51:00 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef INVITEHANDLER_HPP
#define INVITEHANDLER_HPP
#include <string>
#include <vector>
#include <sstream>
#include "Server.hpp"
#include "Client.hpp"
#include "Channel.hpp"
#include "RPL.hpp"
#include "Utils.hpp"
class Server;
class Client;
class Channel;
class InviteHandler
{
public:
InviteHandler(Server* server);
void handleInviteCommand(Client* client, const std::string& command);
private:
Server* _server;
};
#endif

40
ft_irc3/includes/Join.hpp Normal file
View File

@@ -0,0 +1,40 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Join.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/21 19:51:08 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 18:51:10 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef JOIN_HPP
#define JOIN_HPP
#include <string>
#include "Client.hpp"
#include "Server.hpp"
#include "Channel.hpp"
#include "RPL.hpp"
#include "Utils.hpp"
#include "Server.hpp"
#include "Channel.hpp"
#include "Client.hpp"
#include <map>
#include <vector>
#include <iostream>
#include <sstream>
class JoinHandler
{
public:
void handleJoinCommand(Client *client, const std::string &channelName, Server *server);
void sendJoinSuccess(Client *client, Channel *channel, Server *server);
std::string getUsersList(Channel *channel);
};
#endif

View File

@@ -0,0 +1,35 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* KickHandler.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/06/01 16:59:42 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 18:51:26 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef KICKHANDLER_HPP
#define KICKHANDLER_HPP
#include "Server.hpp"
#include "Client.hpp"
#include "Channel.hpp"
#include "Utils.hpp"
#include <string>
#include <vector>
#include <sstream>
class KickHandler
{
public:
KickHandler(Server* server);
void handleKickCommand(Client* client, const std::string& command);
private:
Server* _server;
};
#endif

View File

@@ -0,0 +1,52 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ModeHandler.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/30 11:12:57 by fgras-ca #+# #+# */
/* Updated: 2024/06/04 14:00:47 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef MODEHANDLER_HPP
#define MODEHANDLER_HPP
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <algorithm>
#include "Client.hpp"
#include "Channel.hpp"
#include "Server.hpp"
#include "RPL.hpp"
#include "Utils.hpp"
class Server;
class Client;
class Channel;
class ModeHandler
{
public:
ModeHandler(Server* server);
void handleModeCommand(Client* client, const std::string& command);
private:
Server* _server;
void handleUserMode(Client* client, const std::vector<std::string>& tokens);
void handleChannelMode(Client* client, const std::vector<std::string>& tokens);
void setChannelMode(Client *client, Channel* channel, const std::string& mode, bool adding, const std::string& argument);
void applyModeL(Client *client, Channel* channel, bool adding, const std::string& argument);
void applyModeI(Client *client, Channel* channel, bool adding);
void applyModeK(Client *client, Channel* channel, bool adding, const std::string& argument);
void applyModeT(Channel* channel, bool adding);
void applyModeO(Client *client, Channel* channel, bool adding, const std::string& argument);
};
#endif

552
ft_irc3/includes/RPL.hpp Normal file
View File

@@ -0,0 +1,552 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* RPL.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/19 15:12:47 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 12:28:50 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef RPL_HPP
#define RPL_HPP
#include <string>
#include <sstream>
#include "Client.hpp"
#include "Utils.hpp"
#include "Channel.hpp"
#define SERVER_NAME "IRC_Server"
#define SERVER_VERSION "1.0"
#define USER_MODES "None"
#define CHANNEL_MODES "l"
#define CHANNEL_MODES_WITH_PARAMS "l"
#define CLIENT_FD(client) (client->getFd())
#define CLIENT_NICK(client) ((client)->getNickname())
#define CLIENT_USER(client) ((client)->getUser())
#define CLIENT_HOST(client) ((client)->getHost())
#define CLIENT_REALNAME(client) ((client)->getRealName())
inline std::string RPL_WELCOME(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 001 " << CLIENT_NICK(client)
<< " :Welcome to the Internet Relay Network " << CLIENT_NICK(client)
<< "!" << CLIENT_USER(client) << "@" << CLIENT_HOST(client) << "\r\n";
return oss.str();
}
inline std::string RPL_YOURHOST(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 002 " << CLIENT_NICK(client)
<< " :Your host is " << SERVER_NAME << ", running version " << SERVER_VERSION << "\r\n";
return oss.str();
}
inline std::string RPL_CREATED(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 003 " << CLIENT_NICK(client)
<< " :This server was created " << __DATE__ << "\r\n";
return oss.str();
}
inline std::string RPL_MYINFO(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 004 " << CLIENT_NICK(client) << " "
<< SERVER_NAME << " " << SERVER_VERSION << " "
<< USER_MODES << " " << CHANNEL_MODES << " "
<< CHANNEL_MODES_WITH_PARAMS << "\r\n";
return oss.str();
}
inline std::string RPL_ISUPPORT(Client* client, const std::string& tokens)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 005 " << CLIENT_NICK(client)
<< " " << tokens << " :are supported by this server\r\n";
return oss.str();
}
inline std::string RPL_UMODEIS(Client* client, const std::string& modes)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 221 " << CLIENT_NICK(client) << " :" << modes << "\r\n";
return oss.str();
}
inline std::string RPL_AWAY(Client *client, const std::string& target, const std::string& message)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 301 " << CLIENT_NICK(client) << " " << target << " :" << message << "\r\n";
return oss.str();
}
inline std::string RPL_WHOISUSER(Client *client, Client* target)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 311 " << CLIENT_NICK(client) << " " << CLIENT_NICK(target) << " "
<< CLIENT_USER(target) << " " << CLIENT_HOST(target) << " * :" << CLIENT_REALNAME(target) << "\r\n";
return oss.str();
}
inline std::string RPL_WHOISSERVER(Client *client, const std::string& targetNick, const std::string& serverInfo)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 312 " << CLIENT_NICK(client) << " " << targetNick << " " << SERVER_NAME
<< " :" << serverInfo << "\r\n";
return oss.str();
}
inline std::string RPL_ENDOFWHO(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 315 " << CLIENT_NICK(client) << " " << channel << " :End of /WHO list.\r\n";
return oss.str();
}
inline std::string RPL_ENDOFWHOIS(Client *client, const std::string& targetNick)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 318 " << CLIENT_NICK(client) << " " << targetNick << " :End of /WHOIS list.\r\n";
return oss.str();
}
inline std::string RPL_LIST(Client *client, const std::string& channel, int numVisible, const std::string& topic)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 322 " << CLIENT_NICK(client) << " " << channel << " " << numVisible << " :" << topic << "\r\n";
return oss.str();
}
inline std::string RPL_LISTEND(Client *client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 323 " << CLIENT_NICK(client) << " :End of /LIST\r\n";
return oss.str();
}
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_CREATIONTIME(Client *client, const std::string& channel, time_t creationTime)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 329 " << CLIENT_NICK(client) << " " << channel << " " << creationTime << "\r\n";
return oss.str();
}
inline std::string RPL_NOTOPIC(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 331 " << CLIENT_NICK(client) << " " << channel << " :No topic is set\r\n";
return oss.str();
}
inline std::string RPL_TOPIC(Client* client, const std::string& channel, const std::string& topic)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 332 " << CLIENT_NICK(client) << " " << channel << " :" << topic << "\r\n";
return oss.str();
}
inline std::string RPL_TOPICWHOTIME(Client* client, const std::string& channel, const std::string& setter, time_t setTime)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 333 " << CLIENT_NICK(client) << " " << channel << " " << setter << " " << setTime << "\r\n";
return oss.str();
}
inline std::string RPL_INVITELIST(Client *client, const std::string& channel, const std::string& inviteMask)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 336 " << CLIENT_NICK(client) << " " << channel << " " << inviteMask << "\r\n";
return oss.str();
}
inline std::string RPL_ENDOFINVITELIST(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 337 " << CLIENT_NICK(client) << " " << channel << " :End of channel invite list\r\n";
return oss.str();
}
inline std::string RPL_INVITING(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 341 " << CLIENT_NICK(client) << " " << CLIENT_NICK(client) << " " << channel << "\r\n";
return oss.str();
}
inline std::string RPL_EXCEPTLIST(Client *client, const std::string& channel, const std::string& exceptionMask)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 348 " << CLIENT_NICK(client) << " " << channel << " " << exceptionMask << "\r\n";
return oss.str();
}
inline std::string RPL_ENDOFEXCEPTLIST(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 349 " << CLIENT_NICK(client) << " " << channel << " :End of channel exception list\r\n";
return oss.str();
}
inline std::string RPL_NAMREPLY(Client* client, const std::string& channel, const std::string& users)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 353 " << CLIENT_NICK(client) << " = " << channel << " :" << users << "\r\n";
return oss.str();
}
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_ENDOFNAMES(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 366 " << CLIENT_NICK(client) << " " << channel << " :End of /NAMES list\r\n";
return oss.str();
}
inline std::string RPL_BANLIST(Client *client, const std::string& channel, const std::string& banMask)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 367 " << CLIENT_NICK(client) << " " << channel << " " << banMask << "\r\n";
return oss.str();
}
inline std::string RPL_ENDOFBANLIST(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 368 " << CLIENT_NICK(client) << " " << channel << " :End of channel ban list\r\n";
return oss.str();
}
inline std::string RPL_MOTD(Client* client, const std::string& line)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 372 " << CLIENT_NICK(client)
<< " :- " << line << "\r\n";
return oss.str();
}
inline std::string RPL_MOTDSTART(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 375 " << CLIENT_NICK(client)
<< " :- " << SERVER_NAME << " Message of the day - \r\n";
return oss.str();
}
inline std::string RPL_ENDOFMOTD(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 376 " << CLIENT_NICK(client)
<< " :End of /MOTD command.\r\n";
return oss.str();
}
inline std::string ERR_NOSUCHNICK(Client *client, const std::string& nick)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 401 " << CLIENT_NICK(client) << " " << nick << " :No such nick/channel\r\n";
return oss.str();
}
inline std::string ERR_NOSUCHCHANNEL(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 403 " << CLIENT_NICK(client) << " " << channel << " :No such channel\r\n";
return oss.str();
}
inline std::string ERR_CANNOTSENDTOCHAN(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 404 " << CLIENT_NICK(client) << " " << channel << " :Cannot send to channel\r\n";
return oss.str();
}
inline std::string ERR_TOOMANYCHANNELS(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 405 " << CLIENT_NICK(client) << " " << channel << " :You have joined too many channels\r\n";
return oss.str();
}
// RPL Error Messages
inline std::string ERR_NOORIGIN(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 409 " << CLIENT_NICK(client) << " :No origin specified\r\n";
return oss.str();
}
inline std::string ERR_NORECIPIENT(Client *client, const std::string& command)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 411 " << CLIENT_NICK(client) << " :No recipient given (" << command << ")\r\n";
return oss.str();
}
inline std::string ERR_NOTEXTTOSEND(Client *client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 412 " << CLIENT_NICK(client) << " :No text to send\r\n";
return oss.str();
}
inline std::string ERR_UNKNOWNCOMMAND(Client* client, const std::string& command)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 421 " << CLIENT_NICK(client) << " " << command << " :Unknown command\r\n";
return oss.str();
}
inline std::string ERR_NOMOTD(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 422 " << CLIENT_NICK(client)
<< " :MOTD File is missing\r\n";
return oss.str();
}
inline std::string ERR_NONICKNAMEGIVEN(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 431 " << CLIENT_NICK(client) << " :No nickname given\r\n";
return oss.str();
}
inline std::string ERR_ERRONEUSNICKNAME(Client* client, const std::string& nickname)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 432 " << CLIENT_NICK(client) << " " << nickname << " :Erroneous nickname\r\n";
return oss.str();
}
inline std::string ERR_NICKNAMEINUSE(Client* client, const std::string& nickname)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 433 " << CLIENT_NICK(client) << " " << nickname << " :Nickname is already in use\r\n";
return oss.str();
}
inline std::string ERR_USERNOTINCHANNEL(Client *client, const std::string& nick, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 441 " << CLIENT_NICK(client) << " " << nick << " " << channel
<< " :They aren't on that channel\r\n";
return oss.str();
}
inline std::string ERR_NOTONCHANNEL(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 442 " << CLIENT_NICK(client) << " " << channel << " :You're not on that channel\r\n";
return oss.str();
}
// Ajoutez cette fonction pour ERR_USERONCHANNEL
inline std::string ERR_USERONCHANNEL(Client* client, const std::string& nick, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 443 " << CLIENT_NICK(client) << " " << nick << " " << channel
<< " :is already on channel\r\n";
return oss.str();
}
inline std::string ERR_NOTREGISTERED(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 451 " << CLIENT_NICK(client) << " :You have not registered\r\n";
return oss.str();
}
//RPL Password
// RPL Error Messages
inline std::string ERR_NEEDMOREPARAMS(Client* client, const std::string& command)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 461 " << CLIENT_NICK(client) << " " << command << " :Not enough parameters\r\n";
return oss.str();
}
inline std::string ERR_ALREADYREGISTERED(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 462 " << CLIENT_NICK(client) << " :You may not reregister\r\n";
return oss.str();
}
inline std::string ERR_PASSWDMISMATCH(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 464 " << CLIENT_NICK(client) << " :Password incorrect\r\n";
return oss.str();
}
inline std::string ERR_INVALIDKEY(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 467 " << CLIENT_NICK(client) << " " << channel << " :Invalid key\r\n";
return oss.str();
}
inline std::string ERR_KEYSET(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 467 " << CLIENT_NICK(client) << " " << channel
<< " :Channel key already set\r\n";
return oss.str();
}
inline std::string ERR_LINKSET(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 469 " << CLIENT_NICK(client) << " " << channel
<< " :No key set\r\n";
return oss.str();
}
inline std::string ERR_CHANNELISFULL(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 471 " << CLIENT_NICK(client) << " " << channel << " :Cannot join channel (channel is full)\r\n";
return oss.str();
}
inline std::string ERR_UNKNOWNMODE(Client* client, char mode, const std::string& channelName)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 472 " << CLIENT_NICK(client) << " " << mode << " :is unknown mode char to me for " << channelName << "\r\n";
return oss.str();
}
inline std::string ERR_INVITEONLYCHAN(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 473 " << CLIENT_NICK(client) << " " << channel << " :Cannot join channel (invite only)\r\n";
return oss.str();
}
inline std::string ERR_BANNEDFROMCHAN(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 474 " << CLIENT_NICK(client) << " " << channel << " :Cannot join channel (banned)\r\n";
return oss.str();
}
inline std::string ERR_BADCHANNELKEY(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 475 " << CLIENT_NICK(client) << " " << channel << " :Cannot join channel (incorrect key)\r\n";
return oss.str();
}
inline std::string ERR_BADCHANMASK(Client* client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 476 " << CLIENT_NICK(client) << " " << channel << " :Bad channel mask\r\n";
return oss.str();
}
inline std::string RPL_NOCHANMODES(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 477 " << CLIENT_NICK(client) << " " << channel << " :Channel doesn't support modes\r\n";
return oss.str();
}
inline std::string ERR_CHANOPRIVSNEEDED(Client *client, const std::string& channel)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 482 " << CLIENT_NICK(client) << " " << channel << " :You're not channel operator\r\n";
return oss.str();
}
inline std::string ERR_UMODEUNKNOWNFLAG(Client *client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 501 " << CLIENT_NICK(client) << " :Unknown MODE flag\r\n";
return oss.str();
}
inline std::string ERR_USERSDONTMATCH(Client *client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 502 " << CLIENT_NICK(client) << " :Cannot change mode for other users\r\n";
return oss.str();
}
inline std::string ERR_INVALIDMODEPARAM(Client* client, const std::string& param)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " 696 " << CLIENT_NICK(client) << " " << param << " :Invalid mode parameter\r\n";
return oss.str();
}
inline std::string RPL_PONG(const std::string& token)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " PONG " << SERVER_NAME << " " << token << "\r\n";
return oss.str();
}
inline std::string RPL_CAP(Client *client, const std::string& subcommand, const std::string& capabilities)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " CAP " << CLIENT_NICK(client) << " " << subcommand << " :" << capabilities << "\r\n";
return oss.str();
}
inline std::string RPL_PASSACCEPTED(Client* client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " NOTICE " << CLIENT_NICK(client)
<< " :Password accepted!\r\n";
return oss.str();
}
inline std::string RPL_CAPEND(Client *client)
{
std::ostringstream oss;
oss << ":" << SERVER_NAME << " CAP " << CLIENT_NICK(client) << " END\r\n";
return oss.str();
}
inline std::string MODEACCEPTMESSAGE(Client *client, std::string channel, const std::string& mode)
{
std::ostringstream oss;
oss << ":" << client->getNickname() << " MODE " << channel << " " << mode << " :" << "\r\n";
return oss.str();
}
inline std::string BOTMESSAGE(Client *client, std::string channel, const std::string& message)
{
std::ostringstream oss;
oss << ":" << client->getNickname() << " WARNING " << channel << " " << message << " :" << "\r\n";
return oss.str();
}
#endif

View File

@@ -0,0 +1,91 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Server.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:15:13 by fgras-ca #+# #+# */
/* Updated: 2024/06/06 12:04:58 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef SERVER_HPP
#define SERVER_HPP
#include "Utils.hpp"
#include "Client.hpp"
#include "Channel.hpp"
#include "ClientManager.hpp"
#include "CommandHandler.hpp"
#include "AdditionalCommands.hpp"
#include "RPL.hpp"
#include "ModeHandler.hpp"
#include "TopicHandler.hpp"
#include "BotFilter.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <unistd.h>
#include <string>
#include <vector>
#include <map>
#include <poll.h>
class Client;
class Channel;
class ClientManager;
class CommandHandler;
class AdditionalCommands;
class ModeHandler;
class TopicHandler;
class BotFilter;
class Server
{
public:
Server(int port, const std::string &password);
~Server();
void run();
void log(const std::string &message, const std::string &color = "\033[0m");
void sendToClient(int client_fd, const std::string &message);
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);
Channel* getChannelByName(const std::string &name);
void disconnectClient(int clientFd);
bool MatchFd(const pollfd& pfd, int clientFd);
void removePollFd(int clientFd);
protected:
int _server_fd;
int _port;
std::string _password;
std::map<int, Client *> _clients;
std::map<std::string, Channel *> _channels;
std::vector<struct pollfd> _poll_fds;
ClientManager *_clientManager;
CommandHandler *_commandHandler;
ModeHandler *_modeHandler;
TopicHandler *_topicHandler;
BotFilter *_botFilter;
friend class ClientManager;
friend class CommandHandler;
friend class ModeHandler;
friend class TopicHandler;
private:
void initServer();
void handleServerCommands();
};
#endif

View File

@@ -0,0 +1,38 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* TopicHandler.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/30 17:04:33 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 19:02:10 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef TOPIC_HANDLER_HPP
#define TOPIC_HANDLER_HPP
#include <string>
#include <vector>
#include <sstream>
#include "Server.hpp"
#include "Client.hpp"
#include "Channel.hpp"
#include "Utils.hpp"
class TopicHandler
{
public:
TopicHandler(Server* server);
void handleTopicCommand(Client* client, const std::string& command);
private:
void viewTopic(Client* client, Channel* channel);
void changeTopic(Client* client, Channel* channel, const std::string& topic);
Server* _server;
};
#endif

View File

@@ -0,0 +1,30 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Utils.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/15 12:16:02 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 15:13:40 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef UTILS_HPP
#define UTILS_HPP
#include <string>
#include <iostream>
#include <vector>
#define RESET "\033[0m"
#define RED "\033[1;31m"
#define GREEN "\033[1;32m"
#define YELLOW "\033[1;33m"
#define BLUE "\033[1;34m"
#define MAGENTA "\033[1;35m"
#define CYAN "\033[1;36m"
std::vector<std::string> split(const std::string &input, const std::string &delimiters);
#endif

View File

@@ -0,0 +1,29 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Welcome.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/21 19:53:17 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 19:02:40 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef WELCOME_HPP
#define WELCOME_HPP
#include "Client.hpp"
#include "Server.hpp"
#include "Utils.hpp"
#include <string>
class WelcomeHandler
{
public:
void sendWelcomeMessages(Client *client, Server *server);
void sendMotd(Client *client, Server *server);
};
#endif

38
ft_irc3/includes/Who.hpp Normal file
View File

@@ -0,0 +1,38 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* Who.hpp :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: fgras-ca <fgras-ca@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2024/05/17 16:08:48 by fgras-ca #+# #+# */
/* Updated: 2024/06/01 19:03:06 by fgras-ca ### ########.fr */
/* */
/* ************************************************************************** */
#ifndef WHO_HPP
#define WHO_HPP
#include "Server.hpp"
#include "Client.hpp"
#include "Channel.hpp"
#include "Utils.hpp"
#include "RPL.hpp"
#include <sstream>
#include <iostream>
class Server;
class WhoHandler
{
public:
WhoHandler(Server *server);
void handleWhoCommand(Client *client, const std::string &command);
void handleWhoisCommand(Client *client, const std::string &command);
private:
Server *_server;
};
#endif

9
ft_irc3/motd.txt Normal file
View File

@@ -0,0 +1,9 @@
░▒▓████████▓▒░▒▓████████▓▒░▒▓█▓▒░▒▓███████▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░
░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓███████▓▒░░▒▓█▓▒░ ░▒▓████████▓▒░░▒▓██████▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░ ░▒▓█▓▒░▒▓████████▓▒░

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;
}

0
ft_irc3/target Normal file
View File