├── .gitignore ├── Makefile ├── README.md ├── authentication ├── nickname.cpp ├── parse_auth.cpp ├── password.cpp └── username.cpp ├── bot ├── DiceBot.cpp └── TimeBot.cpp ├── channels └── channel.cpp ├── clients └── client.cpp ├── commands ├── Invite.cpp ├── Join.cpp ├── Kick.cpp ├── Mode.cpp ├── Nick.cpp ├── ParseCommands.cpp ├── Part.cpp ├── Pong.cpp ├── PrivMsg.cpp ├── Quit.cpp ├── Topic.cpp └── notice.cpp ├── headers ├── Channel.hpp ├── Client.hpp ├── Commands.hpp ├── Logger.hpp ├── Replies.hpp ├── Server.hpp └── Utils.hpp ├── main.cpp ├── server ├── create_server.cpp ├── is_connection.cpp └── server.cpp └── utils └── utils.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .vscode/ 3 | a.out 4 | ircserv 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME = ircserv 2 | 3 | SRCS = ./authentication/nickname.cpp \ 4 | ./authentication/parse_auth.cpp \ 5 | ./authentication/password.cpp \ 6 | ./authentication/username.cpp \ 7 | ./channels/channel.cpp \ 8 | ./clients/client.cpp \ 9 | ./bot/TimeBot.cpp \ 10 | ./bot/DiceBot.cpp \ 11 | ./commands/Invite.cpp \ 12 | ./commands/Join.cpp \ 13 | ./commands/Kick.cpp \ 14 | ./commands/Mode.cpp \ 15 | ./commands/Nick.cpp \ 16 | ./commands/Notice.cpp \ 17 | ./commands/ParseCommands.cpp \ 18 | ./commands/Part.cpp \ 19 | ./commands/Pong.cpp \ 20 | ./commands/PrivMsg.cpp \ 21 | ./commands/Quit.cpp \ 22 | ./commands/Topic.cpp \ 23 | ./main.cpp \ 24 | ./server/create_server.cpp \ 25 | ./server/is_connection.cpp \ 26 | ./server/server.cpp \ 27 | ./utils/utils.cpp 28 | 29 | INC = ./headers/Channel.hpp \ 30 | ./headers/Client.hpp \ 31 | ./headers/Commands.hpp \ 32 | ./headers/Logger.hpp \ 33 | ./headers/Replies.hpp \ 34 | ./headers/Server.hpp \ 35 | ./headers/Utils.hpp 36 | 37 | OBJS = $(SRCS:.cpp=.o) 38 | 39 | RM = rm -f 40 | 41 | CPP = c++ 42 | CPPFLAGS = -Wall -Wextra -Werror -std=c++98 43 | 44 | all: $(NAME) 45 | 46 | $(NAME): $(OBJS) 47 | $(CPP) $(OBJS) $(CPPFLAGS) -o $(NAME) 48 | 49 | %.o:%.cpp $(INC) 50 | $(CPP) $(CPPFLAGS) -c $< -o $@ 51 | 52 | clean: 53 | $(RM) $(OBJS) 54 | 55 | fclean: clean 56 | $(RM) $(NAME) 57 | 58 | re: fclean all -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## IRC_SERVER 3 | This project is intended for a deeper study of the operation of sockets and their interactions on the example of an IRC chat/server. 4 | The project is built via Makefile and is run using the following command: 5 | 6 | ./ircserv [port] [pass] 7 | 8 | To connect clients to this IRC server, you can use Limechat client or nc (netcat) as follows: 9 | 10 | nc [hostname] [port] 11 | 12 | NB : you can use Limechat 13 | 14 | # IRC_Description 15 | IRC (Internet Relay Chat) - an application-level Protocol for exchanging messages in real-time. 16 | Designed primarily for group communication in channels, it also allows you to communicate via private messages and exchange data, including files. We specifically used direct client-to-client file transfer (DCC). 17 | IRC uses the TCP transport protocol. 18 | 19 | # IRC_Network 20 | According to the protocol specifications, an IRC network is a group of servers connected to each other. The simplest network is a single server. In this project, we used a server-to-multiple-clients connection. 21 | 22 | # RFC Documentation 23 | RFC 1459 24 | 25 | # Setting Up and Running the IRC Server 26 | 27 | - Prerequisites 28 | Make sure you have the following installed on your system: 29 | 30 | C++ compiler 31 | Make utility 32 | 33 | - Compilation 34 | The IRC server is built using a Makefile. 35 | Run the following command to compile the project: 36 | 37 | make 38 | 39 | - Running the Server 40 | Once compiled, you can run the IRC server with the following command: 41 | 42 | ./ircserv [port] [pass] 43 | 44 | [port]: The port number on which the server will listen for incoming connections. 45 | 46 | [pass]: An optional password for server authentication. 47 | 48 | # Server Implementation Details 49 | The server is implemented in C++ and consists of the following key steps: 50 | 51 | - Socket Creation: 52 | The server creates a socket using the socket system call, specifying the IPv4 address family and TCP socket type. 53 | 54 | - Socket Options: 55 | Socket options are set using setsockopt to enable reusing the address (SO_REUSEADDR) and set the socket to non-blocking mode. 56 | 57 | - Binding: 58 | The socket is bound to a specific IP address and port using the bind system call. 59 | 60 | - Listening: 61 | The server starts listening for incoming connections using the listen system call. 62 | 63 | - Polling for Connections: 64 | The server uses the poll system call to wait for incoming connections or data on existing connections. 65 | 66 | - Handling Connections: 67 | The server distinguishes between server and client connections and handles them accordingly. 68 | For a new connections, the is_server_connection function is called. 69 | For client already connexted, the is_client_connection function is called. 70 | 71 | # Commands supported 72 | 73 | ft_irc supports the following command handling functionalities: 74 | 75 | - **Join Channel**: Users can join a channel or create a none existing one by sending the `JOIN` command followed by the channel name and a key if existed. 76 | 77 | - **Private Message to Channels and Clients**: Users can send private messages to channels by using the `PRIVMSG` command followed by the channel name, or directly to clients by specifying their nickname and the message desired to send. 78 | 79 | - **Kick**: Operators can kick users from a channel using the `KICK` command followed by the nickname of the client to kick. 80 | 81 | - **Mode**: Users can set channel modes using the `MODE` command, supported modes: ±itkol: 82 | 83 | · i: Set/remove Invite-only channel 84 | 85 | · t: Set/remove the restrictions of the TOPIC command to channel operators 86 | 87 | · k: Set/remove the channel key (password) 88 | 89 | · o: Give/take channel operator privilege 90 | 91 | · l: Set/remove the user limit to channel 92 | 93 | - **Part**: Users can leave a channel using the `PART` command, followed by the channel name. 94 | 95 | - **Notice**: Users can send notices to channels or clients using the `NOTICE` command. 96 | 97 | - **Invite**: Users can invite other clients to join a channel using the `INVITE` command. 98 | 99 | - **Quit**: Users can quit the server gracefully using the `QUIT` command, which disconnects them from the server. 100 | Refer to the IRC protocol documentation for detailed information on each command's syntax and usage. 101 | 102 | ## Reference 103 | Refer to the [the Modern IRC client Protocol Documentation](https://modern.ircdocs.horse/) for detailed information on each command's syntax and usage. 104 | 105 | -------------------------------------------------------------------------------- /authentication/nickname.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Client.hpp" 2 | 3 | int Server::if_nick_exist(std::string value){ 4 | std::map::iterator it; 5 | for (it = users.begin();it != users.end();it++) 6 | { 7 | if (it->second.nickname == value) 8 | return (1); 9 | } 10 | for (it = connections.begin();it != connections.end();it++) 11 | { 12 | if (it->second.nickname == value) 13 | return (1); 14 | } 15 | return (0); 16 | } 17 | 18 | int Server::parse_nick(Client &client, std::string value){ 19 | std::string err; 20 | std::string nickname = value.substr(0, value.find(' ')); 21 | 22 | if (!client.nickname.empty()){ 23 | return 0; 24 | } 25 | if (!client.password.empty()){ 26 | std::vector ret = split_space(value); 27 | if (ret.size() == 0){ 28 | err = ERR_NONICKNAMEGIVEN(this->hostname); 29 | sendReply(err.c_str(), client.fds.fd); 30 | return (0); 31 | } 32 | if (ret.size() != 1) 33 | { 34 | err = ":* 461 * :Not enough parameters\n"; 35 | sendReply(err.c_str(), client.fds.fd); 36 | return (0); 37 | } 38 | if (!isValidNick(value)) 39 | { 40 | sendReply(ERR_ERRONEUSNICKNAME(this->hostname, value), client.fds.fd); 41 | return (0); 42 | } 43 | if (if_nick_exist(value)) { 44 | err = ERR_NICKNAMEINUSE(this->hostname, value); 45 | sendReply(err.c_str(), client.fds.fd); 46 | return (0); 47 | } 48 | client.nickname = nickname; 49 | } 50 | else 51 | { 52 | std::string message_error = ":* 667 * :Enter PASS , NICK , USER \n"; 53 | sendReply(message_error.c_str(), client.fds.fd); 54 | return (-1); 55 | } 56 | if (!client.username.empty()){ 57 | client.buffer = ""; 58 | users[client.fds.fd] = Client(client); 59 | WelcomeMessage(client); 60 | } 61 | return (0); 62 | } 63 | 64 | -------------------------------------------------------------------------------- /authentication/parse_auth.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Client.hpp" 2 | #include "../headers/Utils.hpp" 3 | 4 | int Server::parse_pair(Client &client, std::pair pair) 5 | { 6 | if (pair.first == "PASS") 7 | { 8 | if (parse_pass(client, pair.second) == -1) 9 | return (-1); 10 | } 11 | else if (pair.first == "USER") 12 | { 13 | if (parse_user(client, pair.second) == -1) 14 | return (-1); 15 | } 16 | else if (pair.first == "NICK") 17 | { 18 | if (parse_nick(client, pair.second) == -1) 19 | return (-1); 20 | } 21 | else 22 | { 23 | Logger::error("BAD COMMAND"); 24 | return (-1); 25 | } 26 | client.buffer = ""; 27 | return (0); 28 | } 29 | 30 | std::pair my_split_pair(const std::string& line, char delimiter) { 31 | std::pair pair; 32 | size_t found = line.find(delimiter); 33 | std::string cmnd = line.substr(0, found); 34 | if (found == std::string::npos) { 35 | pair = make_pair(cmnd, ""); 36 | return pair; 37 | } 38 | std::string val = line.substr(found+1); 39 | my_trim_(val, delimiter); 40 | my_trim_(val, '\r'); 41 | my_trim_(val, '\n'); 42 | my_trim_(cmnd, delimiter); 43 | pair = make_pair(cmnd, val); 44 | return pair; 45 | } 46 | 47 | void Server::my_split_buffer(Client &client, std::string delimiter) { 48 | std::pair pair; 49 | size_t found = client.buffer.find(delimiter); 50 | 51 | while (found != std::string::npos) 52 | { 53 | std::string rec = client.buffer.substr(0, found); 54 | my_trim_(rec, ' '); 55 | pair = my_split_pair(rec, ' '); 56 | if (parse_pair(client, pair) < 0) 57 | break; 58 | //Deliminer xxx xxx\r\n (if limechat) 59 | found = client.buffer.find(delimiter); 60 | } 61 | return ; 62 | } 63 | 64 | void Server::parse_buffer_nc(Client &client) 65 | { 66 | my_split_buffer(client, "\n"); 67 | } 68 | 69 | void Server::parse_buffer_limechat(Client &client) 70 | { 71 | my_split_buffer(client, "\r\n"); 72 | } 73 | -------------------------------------------------------------------------------- /authentication/password.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Client.hpp" 2 | #include "../headers/Replies.hpp" 3 | 4 | void Server::WelcomeMessage(Client &client) 5 | { 6 | std::string msg = ":" + hostname + " 001 " + client.nickname + " :Welcome to the Internet Relay Network " + client.nickname + "!~" + client.username + "@" + hostname +"\r\n"; 7 | msg += ":" + hostname + " 002 " + client.nickname + " :Your host is " + hostname + ", running version leet-irc 1.0.0\r\n"; 8 | msg += ":" + hostname + " 003 " + client.nickname + " :This server has been created Jan 2024\r\n"; 9 | msg += ":" + hostname + " 004 " + client.nickname + " " + hostname + " leet-irc 1.0.0 aioOrsw aovimntklbeI\r\n"; 10 | msg += ":" + hostname + " 375 " + client.nickname + " :- " + hostname + " Message of the day -\r\n"; 11 | msg += ":" + hostname + " 376 " + client.nickname + " :End of MOTD command\r\n"; 12 | sendReply(msg.c_str(), client.fds.fd); 13 | } 14 | 15 | int Server::parse_pass(Client &client, std::string value){ 16 | std::string error; 17 | 18 | if (client.password != ""){ 19 | error = ERR_PASSWDALREADYSET(this->hostname); 20 | sendReply(error.c_str(), client.fds.fd); 21 | return 0; 22 | } 23 | if (value != password) 24 | { 25 | error = ERR_PASSWDMISMATCH(this->hostname); 26 | sendReply(error.c_str(), client.fds.fd); 27 | return -1; 28 | } 29 | else{ 30 | if (client.password == ""){ 31 | client.password = password; 32 | } 33 | } 34 | return (0); 35 | } -------------------------------------------------------------------------------- /authentication/username.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Client.hpp" 2 | 3 | int Server::if_user_exist(std::string value){ 4 | std::map::iterator it; 5 | for (it = users.begin();it != users.end();it++) 6 | { 7 | if (it->second.username == value) 8 | return (1); 9 | } 10 | for (it = connections.begin();it != connections.end();it++) 11 | { 12 | if (it->second.username == value) 13 | return (1); 14 | } 15 | return (0); 16 | } 17 | 18 | int Server::parse_user(Client &client, std::string value){ 19 | std::string user = client.username; 20 | std::string message_error; 21 | 22 | if (!client.username.empty()){ 23 | return 0; 24 | } 25 | if (!client.password.empty()){ 26 | std::vector ret = split_space(value); 27 | if (ret.size() < 4){ 28 | message_error = ":* 461 * :Not enough parameters\n"; 29 | send(client.fds.fd, message_error.c_str(), message_error.size() + 1, MSG_OOB); 30 | return (0); 31 | } 32 | else if (if_user_exist(ret[0])) { 33 | message_error = ":* 462 * :You may not reregister\n"; 34 | send(client.fds.fd, message_error.c_str(), message_error.size() + 1, MSG_OOB); 35 | return (0); 36 | } 37 | else{ 38 | client.username = ret[0]; 39 | client.realname = ret[3]; 40 | } 41 | } 42 | else 43 | { 44 | std::string message_error = ":* 667 * :Enter PASS , NICK , USER \n"; 45 | sendReply(message_error.c_str(), client.fds.fd); 46 | return (-1); 47 | } 48 | if (!client.nickname.empty()) 49 | { 50 | client.buffer = ""; 51 | users[client.fds.fd] = Client(client); 52 | WelcomeMessage(client); 53 | } 54 | return (0); 55 | } 56 | -------------------------------------------------------------------------------- /bot/DiceBot.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | void ft_diceBot(commandInfo& cmd, Server& server, Client& client) { 4 | if (cmd.cmnd_args.size() != 1) { 5 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), client.nickname, "To roll the dice, add an int param between 1 and 6!"), client.fds.fd); 6 | return ; 7 | } 8 | if (cmd.cmnd_args[0].find_first_not_of("0123456789") != std::string::npos) { 9 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), client.nickname, "Only numbers please! : [1..6]"), client.fds.fd); 10 | return; 11 | } 12 | 13 | int guessedNumber = to_int(cmd.cmnd_args[0]); 14 | 15 | if (guessedNumber < 1 || guessedNumber > 6) { 16 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), client.nickname, "Guessed number is out of range, can only be [1...6]"), client.fds.fd); 17 | return; 18 | } 19 | 20 | int diceRoll = rand() % 6 + 1; // Rolling a dice 21 | 22 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), client.nickname, "----- Welcome to Guess&Dice -----"), client.fds.fd); 23 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), client.nickname, "🎲 Rolling Dice..."), client.fds.fd); 24 | if (guessedNumber == diceRoll) { 25 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), client.nickname, "🎉 Congratulations, You guessed it right!"), client.fds.fd); 26 | } else { 27 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), client.nickname, "😔 Wrong guess! The correct number was: " + to_string(diceRoll)), client.fds.fd); 28 | } 29 | } -------------------------------------------------------------------------------- /bot/TimeBot.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | void ft_timeBot(commandInfo& cmd, Server& server, Client& client) { 4 | if (cmd.cmnd_args.size() > 0) { 5 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, "TIME"), client.fds.fd); 6 | return ; 7 | } 8 | std::time_t now = std::time(NULL); 9 | char buffer[80]; 10 | std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", std::localtime(&now)); 11 | std::string current_time(buffer); 12 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), client.nickname,"Current time is: " + current_time), client.fds.fd); 13 | } -------------------------------------------------------------------------------- /channels/channel.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Channel.hpp" 2 | 3 | Channel::Channel(std::string const &name, std::string const &password) : _name(name), _password(password), _channel_limit(MAX_CLIENTS_PER_CHANNEL) 4 | { 5 | _modes.push_back(std::make_pair(CHANNEL_MODE_INVITE_ONLY, 0)); 6 | _modes.push_back(std::make_pair(CHANNEL_MODE_TOPIC_SETTABLE_BY_CHANNEL_OPERATOR_ONLY, 0)); 7 | _modes.push_back(std::make_pair(CHANNEL_MODE_KEY, 0)); 8 | _modes.push_back(std::make_pair(CHANNEL_MODE_OPERATOR, 0)); 9 | _modes.push_back(std::make_pair(CHANNEL_MODE_USER_LIMIT, 0)); 10 | updateStringModes(); 11 | } 12 | 13 | // Setters 14 | void Channel::setTopic(std::string const &newTopic) 15 | { 16 | _topic = newTopic; 17 | } 18 | 19 | void Channel::setPassword(std::string const &passwd) 20 | { 21 | _password = passwd; 22 | } 23 | 24 | void Channel::setChannel_limit(int limit) 25 | { 26 | _channel_limit = limit; 27 | } 28 | 29 | // Getter 30 | std::string Channel::getPassword(void) const 31 | { 32 | return (_password); 33 | } 34 | 35 | std::string Channel::getName(void) const 36 | { 37 | return (_name); 38 | } 39 | 40 | std::string Channel::getTopic(void) const 41 | { 42 | return (_topic); 43 | } 44 | 45 | int Channel::getChannelLimit(void) const 46 | { 47 | return (_channel_limit); 48 | } 49 | 50 | std::vector > Channel::getModes(void) const 51 | { 52 | return (_modes); 53 | } 54 | 55 | std::string Channel::getStringModes(void) const { 56 | return (_stringModes); 57 | } 58 | 59 | std::vector &Channel::getAllClientsList(void) 60 | { 61 | return (allClientsList); 62 | } 63 | 64 | std::vector &Channel::getOpeList(void) 65 | { 66 | return (opeList); 67 | } 68 | 69 | std::vector &Channel::getInviteList(void) 70 | { 71 | return (inviteList); 72 | } 73 | 74 | // -------------- 75 | // Client stuff 76 | // -------------- 77 | 78 | int Channel::addClient(Client const &client) 79 | { 80 | if (allClientsList.size() >= (size_t)_channel_limit) 81 | return (-2); 82 | if (!isJoined(client.nickname)) 83 | { 84 | allClientsList.push_back(client); 85 | return (1); 86 | } 87 | return 0; 88 | } 89 | std::string Channel::listClients() 90 | { 91 | std::string list_users; 92 | for (size_t i = 0; i < allClientsList.size(); i++) 93 | { 94 | if (isOpe(allClientsList[i].nickname)) 95 | list_users += "@"; 96 | list_users += allClientsList[i].nickname; 97 | list_users += " "; 98 | } 99 | return list_users; 100 | } 101 | 102 | bool Channel::isJoined(std::string const &nickname) 103 | { 104 | for (size_t i = 0; i < allClientsList.size(); i++) 105 | { 106 | if (allClientsList[i].nickname == nickname) 107 | return true; 108 | } 109 | return false; 110 | } 111 | 112 | bool Channel::isOpe(std::string const &nickname) 113 | { 114 | for (size_t i = 0; i < opeList.size(); i++) 115 | { 116 | if (opeList[i].nickname == nickname) 117 | return true; 118 | } 119 | return false; 120 | } 121 | 122 | bool Channel::isInvited(std::string const &nickname) 123 | { 124 | for (size_t i = 0; i < inviteList.size(); i++) 125 | { 126 | if (inviteList[i].nickname == nickname) 127 | return true; 128 | } 129 | return false; 130 | } 131 | 132 | void Channel::addOpe(std::string const &nickname) 133 | { 134 | if (isJoined(nickname) && isOpe(nickname) == false) { 135 | for (size_t i = 0; i < allClientsList.size(); i++) 136 | { 137 | if (allClientsList[i].nickname == nickname) 138 | { 139 | opeList.push_back(allClientsList[i]); 140 | return; 141 | } 142 | } 143 | } 144 | } 145 | 146 | void Channel::removeOpe(std::string const &nickname) 147 | { 148 | for (std::vector::iterator it = opeList.begin(); it != opeList.end(); it++) 149 | { 150 | if (it->nickname == nickname) 151 | { 152 | opeList.erase(it); 153 | return; 154 | } 155 | } 156 | } 157 | 158 | void Channel::removeClient(Client const &client) 159 | { 160 | for (std::vector::iterator it = allClientsList.begin(); it != allClientsList.end(); it++) 161 | { 162 | if (it->fds.fd == client.fds.fd) 163 | { 164 | allClientsList.erase(it); 165 | return; 166 | } 167 | } 168 | } 169 | 170 | void Channel::invite(Client const &client) 171 | { 172 | if (!isJoined(client.nickname)) 173 | this->inviteList.push_back(client); 174 | } 175 | 176 | void Channel::removeInvite(Client const &client) { 177 | for (std::vector:: iterator it = inviteList.begin(); it != inviteList.end(); it++) { 178 | if (it->nickname == client.nickname) { 179 | inviteList.erase(it); 180 | return ; 181 | } 182 | } 183 | } 184 | 185 | // --------------------- 186 | // Channel modes stuff 187 | // --------------------- 188 | 189 | char Channel::getModeIdentifier(ChannelMode _mode) const 190 | { 191 | char identifier = 0; 192 | switch (_mode) 193 | { 194 | case CHANNEL_MODE_INVITE_ONLY: 195 | identifier = 'i'; 196 | break; 197 | case CHANNEL_MODE_KEY: 198 | identifier = 'k'; 199 | break; 200 | case CHANNEL_MODE_OPERATOR: 201 | identifier = 'o'; 202 | break; 203 | case CHANNEL_MODE_TOPIC_SETTABLE_BY_CHANNEL_OPERATOR_ONLY: 204 | identifier = 't'; 205 | break; 206 | case CHANNEL_MODE_USER_LIMIT: 207 | identifier = 'l'; 208 | break; 209 | } 210 | return (identifier); 211 | } 212 | 213 | void Channel::updateStringModes(void) 214 | { 215 | _stringModes = ""; 216 | bool limitFlag = false; 217 | bool keyFlag = false; 218 | for (std::vector >::const_iterator it = _modes.begin(); it != _modes.end(); it++) 219 | { 220 | char identifier = getModeIdentifier(it->first); 221 | if (identifier && identifier != 'o' && it->second == 1) //'o' is a user mode, can't be displayed in channel modes set. 222 | { 223 | _stringModes += identifier; 224 | if (identifier == 'l') 225 | limitFlag = true; 226 | else if (identifier == 'k') 227 | keyFlag = true; 228 | } 229 | } 230 | if (_stringModes.size() > 0 && keyFlag) 231 | _stringModes += " " + _password; 232 | if (_stringModes.size() > 0 && limitFlag) 233 | _stringModes += " " + to_string(_channel_limit); 234 | else if (_stringModes.size() == 0) 235 | _stringModes = " no mode is set"; 236 | } 237 | 238 | bool Channel::isInviteOnly(void) 239 | { 240 | return (hasMode(CHANNEL_MODE_INVITE_ONLY)); 241 | } 242 | 243 | bool Channel::hasMode(ChannelMode mode) 244 | { 245 | for (std::vector >::iterator it = _modes.begin(); it != _modes.end(); it++) 246 | { 247 | if (it->first == mode) { 248 | if (it->second == 1) 249 | return true; 250 | else 251 | return false; 252 | } 253 | } 254 | return false; 255 | } 256 | 257 | void Channel::addMode(ChannelMode mode) 258 | { 259 | for (std::vector >::iterator it = _modes.begin(); it != _modes.end(); it++) 260 | { 261 | if (it->first == mode) { 262 | if (it->second == 0) 263 | it->second = 1; 264 | } 265 | } 266 | updateStringModes(); 267 | } 268 | 269 | void Channel::removeMode(ChannelMode mode) 270 | { 271 | for (std::vector >::iterator it = _modes.begin(); it != _modes.end(); it++) 272 | { 273 | if (it->first == mode) { 274 | if (it->second == 1) 275 | it->second = 0; 276 | } 277 | } 278 | updateStringModes(); 279 | } 280 | 281 | bool Channel::hasKey(void) 282 | { 283 | return (hasMode(CHANNEL_MODE_KEY)); 284 | } 285 | 286 | // -------------- 287 | // Utils 288 | // -------------- 289 | 290 | void Channel::broadcastMessage(Client *sender, std::string message, bool opeOnly) 291 | { 292 | std::vector clients; 293 | if (opeOnly) 294 | clients = this->getOpeList(); 295 | else 296 | clients = this->getAllClientsList(); 297 | for (size_t i = 0; i < clients.size(); i++) { 298 | if (sender && sender->fds.fd && clients[i].fds.fd == sender->fds.fd) 299 | continue ; 300 | send(clients[i].fds.fd , message.c_str(), message.size(), 0); 301 | } 302 | } 303 | void Channel::updateNick(std::string oldNick, std::string newNick, std::vector &clients) 304 | { 305 | for (size_t i = 0; i < clients.size(); i++) 306 | { 307 | if (clients[i].nickname == oldNick) 308 | clients[i].nickname = newNick; 309 | } 310 | } 311 | void Server::broadcastMessage(Client *sender, std::string message, std::vector chared_channels) 312 | { 313 | std::vector::iterator it; 314 | for (it = chared_channels.begin();it != chared_channels.end();it++) 315 | { 316 | if (sender && sender->fds.fd && it->fds.fd == sender->fds.fd) 317 | continue; 318 | send(it->fds.fd, message.c_str(), message.size(), 0); 319 | } 320 | } 321 | 322 | bool Channel::isValidChannelName(const std::string &name) 323 | { 324 | if (name[0] != '#') 325 | return false; 326 | for (size_t i = 1; i < name.size(); i++) 327 | { 328 | if (name[i] == ',' || name[i] == ' ') 329 | return false; 330 | } 331 | return true; 332 | } 333 | 334 | void Server::addToChannels(Channel *channel) 335 | { 336 | channels.push_back(channel); 337 | } 338 | 339 | void Server::removeFromChannels(Channel *channel) 340 | { 341 | for (std::vector::iterator it = channels.begin(); it != channels.end(); it++) 342 | { 343 | if ((*it)->getName() == channel->getName()) 344 | { 345 | Channel *tmp = *it; 346 | channels.erase(it); 347 | delete tmp; 348 | return; 349 | } 350 | } 351 | } 352 | 353 | std::vector::iterator Server::getChannelByName(const std::string &name) 354 | { 355 | for (std::vector::iterator it = channels.begin(); it != channels.end(); it++) { 356 | if ((*it)->getName() == name) 357 | return it; 358 | } 359 | return channels.end(); 360 | } 361 | 362 | Channel::~Channel() 363 | { 364 | allClientsList.clear(); 365 | opeList.clear(); 366 | inviteList.clear(); 367 | } 368 | -------------------------------------------------------------------------------- /clients/client.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Client.hpp" 2 | 3 | Client::Client(struct pollfd fds, std::string username, std::string nickname, std::string realname, std::string password, std::string buffer){ 4 | this->fds = fds; 5 | this->username = username; 6 | this->realname = realname; 7 | this->nickname = nickname; 8 | this->password = password; 9 | this->buffer = buffer; 10 | } 11 | 12 | Client::Client(){ 13 | } 14 | 15 | Client::~Client(){ 16 | } 17 | 18 | void Client::addChannel(Channel *channel) 19 | { 20 | channels_joined.push_back(channel); 21 | } 22 | 23 | void Client::removeChannel(Channel *channel) 24 | { 25 | for (std::vector::iterator it = channels_joined.begin(); it != channels_joined.end(); it++) 26 | { 27 | if ((*it)->getName() == channel->getName()) 28 | { 29 | channels_joined.erase(it); 30 | return; 31 | } 32 | } 33 | } 34 | 35 | void Client::quitAllChannels() 36 | { 37 | channels_joined.clear(); 38 | } 39 | 40 | std::map::iterator Server::getClientByNickname(const std::string &nickName) { 41 | for (std::map::iterator it = this->users.begin(); it != this->users.end(); it++) 42 | { 43 | if (it->second.nickname == nickName) 44 | return (it); 45 | } 46 | return (this->users.end()); 47 | } 48 | 49 | void Server::removeClientFromServer(Client &client) 50 | { 51 | for (std::vector::iterator it2 = this->fds.begin(); it2 != this->fds.end(); it2++) 52 | { 53 | if (it2->fd == client.fds.fd) 54 | { 55 | this->fds.erase(it2); 56 | break; 57 | } 58 | } 59 | close(client.fds.fd); 60 | std::map::iterator it1 = hostNames.find(client.fds.fd); 61 | if (it1 != hostNames.end()){ 62 | hostNames.erase(it1); 63 | } 64 | std::map::iterator it = connections.find(client.fds.fd); 65 | if (it != connections.end()){ 66 | connections.erase(it); 67 | } 68 | it = users.find(client.fds.fd); 69 | if (it != users.end()){ 70 | users.erase(it); 71 | } 72 | } 73 | 74 | void Client::setNickname(std::string const &nickname) 75 | { 76 | this->nickname = nickname; 77 | } 78 | 79 | Client &Channel::getClient(std::string const &nickname) { 80 | for (std::vector::iterator it = allClientsList.begin(); it != allClientsList.end(); it++) 81 | { 82 | if (it->nickname == nickname) 83 | return *it; 84 | } 85 | return *allClientsList.end(); 86 | } -------------------------------------------------------------------------------- /commands/Invite.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | //e.g.: INVITE NICK #CHANNEL => INVITE is cmd.cmnd_name ; cmd.cmnd_args[0] is NICK ; cmd.cmnd_args[1] is #CHANNEL 4 | void ft_invite(commandInfo& cmd, Server& server, Client& client) { 5 | if (cmd.cmnd_args.size() < 2) 6 | { 7 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 8 | return ; 9 | } 10 | std::vector::iterator channelIter = server.getChannelByName(cmd.cmnd_args[1]); 11 | if (channelIter == server.channels.end()) //channel doesn't exist on server 12 | { 13 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[1]), client.fds.fd); 14 | return ; 15 | } 16 | if (!(*channelIter)->isJoined(client.nickname)) //the client who sent the invite cmnd should be a channel member 17 | { 18 | server.sendReply(ERR_NOTONCHANNEL(server.hostname, client.nickname, (*channelIter)->getName()), client.fds.fd); 19 | return ; 20 | } 21 | if (!(*channelIter)->isOpe(client.nickname)) // the client who sent the invite cmnd should be an operator 22 | { 23 | server.sendReply(ERR_CHANOPRIVSNEEDED(server.hostname, client.nickname, (*channelIter)->getName()), client.fds.fd); 24 | return ; 25 | } 26 | std::map::iterator target = server.getClientByNickname(cmd.cmnd_args[0]); 27 | if (target == server.users.end()) //no such client nickname on server 28 | { 29 | server.sendReply(ERR_NOSUCHNICK(server.hostname, client.nickname), client.fds.fd); 30 | return ; 31 | } 32 | if ((*channelIter)->isJoined(target->second.nickname)) //invited client is already in channel 33 | { 34 | server.sendReply(ERR_USERONCHANNEL(server.hostname, target->second.nickname, target->second.realname, (*channelIter)->getName()), client.fds.fd); 35 | return ; 36 | } 37 | if ((*channelIter)->isInvited(target->second.nickname)) //invited client is already invited 38 | { 39 | // server.sendReply(ERR_USERONCHANNEL(server.hostname, target->second.nickname, target->second.realname, (*channelIter)->getName()), client.fds.fd); 40 | return ; 41 | } 42 | (*channelIter)->invite(target->second); 43 | target->second.channels_joined.push_back(*channelIter); 44 | Logger::info(" " + client.nickname + " invited " + target->second.nickname + " to " + (*channelIter)->getName()); 45 | server.sendReply(RPL_INVITING(server.hostname, client.nickname, cmd.cmnd_args[0], cmd.cmnd_args[1]), client.fds.fd); 46 | server.sendReply(RPL_CUSTOM_INVITE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), cmd.cmnd_args[0], (*channelIter)->getName()), target->second.fds.fd); 47 | } 48 | -------------------------------------------------------------------------------- /commands/Join.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | std::vector split_args(commandInfo &cmd) 4 | { 5 | std::vector channels = split(cmd.cmnd_args[0], ","); 6 | std::vector keys; 7 | if (cmd.cmnd_args.size() > 1) 8 | keys = split(cmd.cmnd_args[1], ","); 9 | if (channels.size() < keys.size()) 10 | keys.resize(channels.size()); 11 | if (channels.size() > keys.size()) 12 | keys.resize(channels.size(), ""); 13 | std::vector channelJoins; 14 | for (size_t i = 0; i < channels.size(); ++i) 15 | { 16 | ChannelJoin join; 17 | join.name = channels[i]; 18 | join.key = keys[i]; 19 | channelJoins.push_back(join); 20 | } 21 | return (channelJoins); 22 | } 23 | 24 | bool joinReply(Server &server, Client &client, Channel &channel, bool newCnx) 25 | { 26 | int is_joined = channel.addClient(client); 27 | if (!is_joined) 28 | return (false); 29 | else if (is_joined < 0) 30 | { 31 | server.sendReply( ERR_CHANNELISFULL(server.hostname, client.nickname, channel.getName()), client.fds.fd); 32 | return (false); 33 | } 34 | client.addChannel(&channel); 35 | if (newCnx) 36 | channel.addOpe(client.nickname); 37 | Logger::debug("User [" + client.nickname + "] is joining the channel [" + channel.getName() + "]."); 38 | server.sendReply(RPL_CUSTOM_JOIN(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), channel.getName()), client.fds.fd); 39 | if (!channel.getTopic().empty()) 40 | server.sendReply(RPL_TOPIC(std::string(server.hostname), client.nickname, channel.getName(), channel.getTopic()), client.fds.fd); 41 | server.sendReply(RPL_NAMREPLY(server.hostname, client.nickname, std::string("="), channel.getName(), channel.listClients()), client.fds.fd); 42 | server.sendReply(RPL_ENDOFNAMES(server.hostname, client.nickname, channel.getName()), client.fds.fd); 43 | channel.broadcastMessage(&client, RPL_CUSTOM_JOIN(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), channel.getName()), false); 44 | return (true); 45 | } 46 | 47 | void ft_join(commandInfo &cmd, Server &server, Client &client) 48 | { 49 | if (cmd.cmnd_args.size() < 1) 50 | { 51 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 52 | return; 53 | } 54 | std::vector channels = split_args(cmd); 55 | if (channels.size() == 1 && channels[0].name == "0") // case JOIN 0 56 | { 57 | ft_quit(cmd, server, client); 58 | return; 59 | } 60 | for (size_t i = 0; i < channels.size(); i++) 61 | { 62 | if (channels[i].name.empty()) 63 | continue; 64 | if (!Channel::isValidChannelName(channels[i].name)) 65 | { 66 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, channels[i].name), client.fds.fd); 67 | continue; 68 | } 69 | std::vector::iterator ex_channel = server.getChannelByName(channels[i].name); 70 | 71 | if (ex_channel == server.channels.end()) { // channel doesnt exit in server 72 | Logger::debug("User [" + client.nickname + "] is creating a new channel [" + channels[i].name + "] and joining it."); 73 | Channel *new_channel = new Channel(channels[i].name, ""); 74 | joinReply(server, client, *new_channel, true); 75 | server.channels.push_back(new_channel); 76 | // client.addChannel(new_channel); 77 | continue; 78 | } 79 | if((*ex_channel)->isJoined(client.nickname)) 80 | continue; 81 | // check if channel is invite only 82 | if ((*ex_channel)->isInviteOnly()) { 83 | // check if user is in the invite list 84 | if (!(*ex_channel)->isInvited(client.nickname)) 85 | { 86 | Logger::debug("User [" + client.nickname + "] is trying to join the invite only channel [" + channels[i].name + "]."); 87 | server.sendReply(ERR_INVITEONLYCHAN(server.hostname, client.nickname, (*ex_channel)->getName()), client.fds.fd); 88 | continue; 89 | } 90 | } 91 | // check if channel uses keys 92 | if ((*ex_channel)->hasKey() && ((*ex_channel)->getPassword() != channels[i].key)) { // wrong key reply 93 | Logger::debug("User [" + client.nickname + "] is trying to join the channel [" + channels[i].name + "] with the wrong key [" + channels[i].key + "]."); 94 | server.sendReply(ERR_BADCHANNELKEY(server.hostname, client.nickname, (*ex_channel)->getName()), client.fds.fd); 95 | continue; 96 | } 97 | if (joinReply(server, client, **ex_channel, false)) 98 | { 99 | // client.addChannel(*ex_channel); 100 | (*ex_channel)->removeInvite(client); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /commands/Kick.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | // e.g.: KICK [] - Remove a user from a channel 4 | void ft_kick(commandInfo& cmd, Server& server, Client& client) { 5 | if (cmd.cmnd_args.size() < 2) 6 | { 7 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 8 | return ; 9 | } 10 | if (!Channel::isValidChannelName(cmd.cmnd_args[0])) 11 | { 12 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 13 | return ; 14 | } 15 | std::vector::iterator channel = server.getChannelByName(cmd.cmnd_args[0]); 16 | if (channel == server.channels.end()) 17 | { 18 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 19 | return ; 20 | } 21 | if (!(*channel)->isJoined(client.nickname)) 22 | { 23 | server.sendReply(ERR_NOTONCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 24 | return ; 25 | } 26 | if (!(*channel)->isOpe(client.nickname)) 27 | { 28 | server.sendReply(ERR_CHANOPRIVSNEEDED(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 29 | return ; 30 | } 31 | std::map::iterator target = server.getClientByNickname(cmd.cmnd_args[1]); 32 | if (target == server.users.end()) 33 | { 34 | server.sendReply(ERR_NOSUCHNICK(server.hostname, cmd.cmnd_args[1]), client.fds.fd); 35 | return ; 36 | } 37 | if (!(*channel)->isJoined(target->second.nickname)) 38 | { 39 | server.sendReply(ERR_USERNOTINCHANNEL(client.nickname, cmd.cmnd_args[1], cmd.cmnd_args[0]), client.fds.fd); 40 | return ; 41 | } 42 | (*channel)->removeClient(target->second); 43 | target->second.removeChannel(*channel); 44 | if ((*channel)->isOpe(cmd.cmnd_args[1])) 45 | (*channel)->removeOpe(cmd.cmnd_args[1]); 46 | if ((*channel)->getAllClientsList().size() == 0) 47 | server.channels.erase(channel); 48 | else if ((*channel)->getOpeList().size() == 0) 49 | { 50 | (*channel)->addOpe((*channel)->getAllClientsList()[0].nickname); 51 | (*channel)->broadcastMessage(NULL, RPL_MODE(setPrefix(server.hostNames[client.fds.fd], (*channel)->getAllClientsList()[0].nickname, (*channel)->getAllClientsList()[0].realname), ((*channel)->getName() + " +o " + (*channel)->getAllClientsList()[0].nickname)), false); 52 | } 53 | (*channel)->broadcastMessage(NULL, RPL_KICK(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), cmd.cmnd_args[0], cmd.cmnd_args[1], ((cmd.cmnd_args.size() > 2) ? cmd.cmnd_args[2] : "")), false); 54 | server.sendReply(RPL_KICK(":" + client.nickname, cmd.cmnd_args[0], cmd.cmnd_args[1], ((cmd.cmnd_args.size() > 2) ? cmd.cmnd_args[2] : "")), target->second.fds.fd); 55 | } 56 | -------------------------------------------------------------------------------- /commands/Mode.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | void InviteMode(commandInfo& cmd, Channel &channel, Server &server, Client &client, bool addSign) 4 | { 5 | if ((addSign && !channel.hasMode(CHANNEL_MODE_INVITE_ONLY)) || (!addSign && channel.hasMode(CHANNEL_MODE_INVITE_ONLY))) 6 | channel.broadcastMessage(NULL, RPL_MODE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.username), (cmd.cmnd_args[0] + " " + (addSign ? "+" : "-") + 'i')), false); 7 | if (addSign) 8 | channel.addMode(CHANNEL_MODE_INVITE_ONLY); 9 | else 10 | channel.removeMode(CHANNEL_MODE_INVITE_ONLY); 11 | } 12 | 13 | void topicMode(commandInfo& cmd, Channel &channel, Server &server, Client &client, bool addSign) 14 | { 15 | if ((addSign && !channel.hasMode(CHANNEL_MODE_TOPIC_SETTABLE_BY_CHANNEL_OPERATOR_ONLY)) || (!addSign && channel.hasMode(CHANNEL_MODE_TOPIC_SETTABLE_BY_CHANNEL_OPERATOR_ONLY))) 16 | channel.broadcastMessage(NULL, RPL_MODE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.username), (cmd.cmnd_args[0] + " " + (addSign ? "+" : "-") + 't')), false); 17 | if (addSign) 18 | channel.addMode(CHANNEL_MODE_TOPIC_SETTABLE_BY_CHANNEL_OPERATOR_ONLY); 19 | else 20 | channel.removeMode(CHANNEL_MODE_TOPIC_SETTABLE_BY_CHANNEL_OPERATOR_ONLY); 21 | } 22 | 23 | void limitUersMode(commandInfo& cmd, Channel &channel, Server &server, Client &client, bool addSign, std::vector::iterator &flagArgIt) 24 | { 25 | if (addSign) 26 | { 27 | if (flagArgIt == cmd.cmnd_args.end()) // no limit number speficied 28 | { 29 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 30 | return ; 31 | } 32 | if (!isNumber(*flagArgIt)) 33 | { 34 | server.sendReply(ERR_INVALIDMODEPARAM(client.nickname, channel.getName(), 'l', *flagArgIt), client.fds.fd); 35 | return ; 36 | } 37 | if (std::atoi((*flagArgIt).c_str()) <= 0) 38 | { 39 | server.sendReply(ERR_INVALIDMODEPARAM(client.nickname, channel.getName(), 'l', *flagArgIt), client.fds.fd); 40 | return ; 41 | } 42 | channel.addMode(CHANNEL_MODE_USER_LIMIT); 43 | channel.setChannel_limit(std::atoi((*flagArgIt).c_str())); 44 | } 45 | else 46 | { 47 | channel.removeMode(CHANNEL_MODE_USER_LIMIT); 48 | channel.setChannel_limit(MAX_CLIENTS_PER_CHANNEL); //default limit 49 | } 50 | channel.broadcastMessage(NULL, RPL_MODE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.username), (cmd.cmnd_args[0] + " " + (addSign ? "+" : "-") + "l " + (addSign ? *flagArgIt : ""))), false); 51 | } 52 | 53 | void operatorMode(commandInfo& cmd, Channel &channel, Server &server, Client &client, bool addSign, std::vector::iterator &flagArgIt) 54 | { 55 | if (flagArgIt == cmd.cmnd_args.end()) // no nickname speficied for operator privileges 56 | { 57 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 58 | return ; 59 | } 60 | if (!channel.isJoined(*flagArgIt)) 61 | { 62 | server.sendReply(ERR_USERNOTINCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 63 | return ; 64 | } 65 | if (addSign) // "+o" operator mode 66 | { 67 | channel.addOpe(*flagArgIt); 68 | channel.addMode(CHANNEL_MODE_OPERATOR); 69 | } 70 | else 71 | { 72 | channel.removeOpe(*flagArgIt); 73 | channel.removeMode(CHANNEL_MODE_OPERATOR); 74 | } 75 | channel.broadcastMessage(NULL, RPL_MODE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.username), (cmd.cmnd_args[0] + " " + (addSign ? "+" : "-") + "o " + *flagArgIt)), false); 76 | } 77 | 78 | void keyMode(commandInfo& cmd, Channel &channel, Server &server, Client &client, bool addSign, std::vector::iterator &flagArgIt) 79 | { 80 | if (flagArgIt == cmd.cmnd_args.end()) // no key speficied 81 | { 82 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 83 | return ; 84 | } 85 | if (addSign) 86 | { 87 | if (((*flagArgIt).empty() || (*flagArgIt).find(" ",0) != std::string::npos)) // key contains a space or empty key 88 | { 89 | server.sendReply(ERR_INVALIDMODEPARAM(client.nickname, channel.getName(), 'k', *flagArgIt), client.fds.fd); 90 | return ; 91 | } 92 | channel.addMode(CHANNEL_MODE_KEY); 93 | channel.setPassword(*flagArgIt); 94 | } 95 | else 96 | { 97 | if (*flagArgIt != channel.getPassword()) // key doesn't match the channel's key 98 | { 99 | server.sendReply(ERR_INVALIDMODEPARAM(client.nickname, channel.getName(), 'k', *flagArgIt), client.fds.fd); 100 | return ; 101 | } 102 | channel.removeMode(CHANNEL_MODE_KEY); 103 | channel.setPassword(""); 104 | } 105 | channel.broadcastMessage(NULL, RPL_MODE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.username), (cmd.cmnd_args[0] + " " + (addSign ? "+" : "-") + "k " + (addSign ? *flagArgIt : ""))), false); 106 | } 107 | 108 | 109 | //e.g.: MODE #channel +tlk Max_limit key_param 110 | void ft_mode(commandInfo& cmd, Server &server, Client &client) { 111 | if (cmd.cmnd_args.size() < 1) 112 | { 113 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 114 | return ; 115 | } 116 | if (!Channel::isValidChannelName(cmd.cmnd_args[0])) 117 | { 118 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 119 | return ; 120 | } 121 | std::vector::iterator channel = server.getChannelByName(cmd.cmnd_args[0]); 122 | if (channel == server.channels.end()) 123 | { 124 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 125 | return ; 126 | } 127 | if (!(*channel)->isJoined(client.nickname)) 128 | { 129 | server.sendReply(ERR_NOTONCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 130 | return ; 131 | } 132 | if (cmd.cmnd_args.size() == 1) // MODE #channel 133 | { 134 | server.sendReply(RPL_CHANNELMODEIS(server.hostname, client.nickname, (*channel)->getName(), (*channel)->getStringModes()), client.fds.fd); 135 | return ; 136 | } 137 | if (!(*channel)->isOpe(client.nickname)) 138 | { 139 | server.sendReply(ERR_CHANOPRIVSNEEDED(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 140 | return ; 141 | } 142 | 143 | bool addSign = true; // "+" 144 | bool foundMode = false; 145 | std::string firstArg = cmd.cmnd_args[1]; //e.g. +tol 146 | std::vector::iterator flagArgIt = cmd.cmnd_args.begin() + 2; //e.g. nick_operator,limit_number 147 | 148 | //loop through the arguments and add or remove the modes while checking for errors 149 | for (size_t i = 0; i < firstArg.size(); i++) //e.g.: loop sizeof(+tol) times 150 | { 151 | if (firstArg[i] == '+') 152 | addSign = true; 153 | else if (firstArg[i] == '-') 154 | addSign = false; 155 | else 156 | { 157 | foundMode = true; 158 | if (std::string("itloksn").find(firstArg[i]) != std::string::npos) //if the mode is valid 159 | { 160 | if (firstArg[i] == 's') 161 | continue ; 162 | if (firstArg[i] == 'n') 163 | continue ; 164 | if (firstArg[i] == 'i') { 165 | InviteMode(cmd, **channel, server, client, addSign); 166 | } 167 | else if (firstArg[i] == 't') { 168 | topicMode(cmd, **channel, server, client, addSign); 169 | } 170 | else if (firstArg[i] == 'l') { 171 | limitUersMode(cmd, **channel, server, client, addSign, flagArgIt); 172 | if (flagArgIt != cmd.cmnd_args.end()) 173 | flagArgIt++; 174 | } 175 | else if (firstArg[i] == 'o') { 176 | operatorMode(cmd, **channel, server, client, addSign, flagArgIt); 177 | if (flagArgIt != cmd.cmnd_args.end()) 178 | flagArgIt++; 179 | } 180 | else if (firstArg[i] == 'k') { 181 | keyMode(cmd, **channel, server, client, addSign, flagArgIt); 182 | if (flagArgIt != cmd.cmnd_args.end()) 183 | flagArgIt++; 184 | } 185 | } 186 | else 187 | { 188 | server.sendReply(ERR_UNKNOWNMODE(server.hostname, client.nickname, firstArg[i]), client.fds.fd); 189 | continue ; 190 | } 191 | } 192 | } 193 | if (!foundMode) //if no mode was found after + or - 194 | { 195 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 196 | return ; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /commands/Nick.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | bool Server::isValidNickChar(char c) 4 | { 5 | return (!(c == '#' || c == ':' || c == ' ')); 6 | } 7 | 8 | bool Server::isValidNick(std::string &nickname) 9 | { 10 | if (nickname.empty()) 11 | return (false); 12 | for (size_t i = 0; i < nickname.size(); i++) 13 | { 14 | if (!isValidNickChar(nickname[i])) 15 | return (false); 16 | } 17 | return (true); 18 | } 19 | // e.g.: NICK NEW_NICK 20 | void ft_nick(commandInfo& cmd, Server& server, Client& client) 21 | { 22 | if (!cmd.cmnd_args.size()) 23 | { 24 | server.sendReply(ERR_NONICKNAMEGIVEN(server.hostname), client.fds.fd); 25 | return; 26 | } 27 | if (!server.isValidNick(cmd.cmnd_args[0])) 28 | { 29 | server.sendReply(ERR_ERRONEUSNICKNAME(server.hostname, cmd.cmnd_args[0]), client.fds.fd); 30 | return; 31 | } 32 | if (server.if_nick_exist(cmd.cmnd_args[0])) 33 | { 34 | server.sendReply(ERR_NICKNAMEINUSE(server.hostname, cmd.cmnd_args[0]), client.fds.fd); 35 | return; 36 | } 37 | std::string oldNick = client.nickname; 38 | client.setNickname(cmd.cmnd_args[0]); 39 | server.connections[client.fds.fd].setNickname(cmd.cmnd_args[0]); 40 | std::vector channels = server.channels; 41 | server.sendReply(RPL_NICKCHANGE(oldNick, client.nickname), client.fds.fd); 42 | for (size_t i = 0; i < channels.size(); i++) 43 | { 44 | if (channels[i]->isJoined(oldNick)) 45 | { 46 | channels[i]->broadcastMessage(&client, RPL_NICKCHANGE(oldNick, client.nickname), false); 47 | channels[i]->updateNick(oldNick, client.nickname, (channels[i])->getAllClientsList()); 48 | channels[i]->updateNick(oldNick, client.nickname, (channels[i])->getOpeList()); 49 | } 50 | if (channels[i]->isInvited(oldNick)) 51 | { 52 | channels[i]->updateNick(oldNick, client.nickname, (channels[i])->getInviteList()); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /commands/ParseCommands.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | typedef void (*CommandHandlerFunc)(commandInfo &, Server&, Client&); // function pointer typedef 4 | 5 | bool isValidCommand(std::map commandHandlerMap, const std::string &cmdName) 6 | { 7 | return commandHandlerMap.count(cmdName); 8 | } 9 | 10 | void Server::executeCommands(const std::vector cmndBuffer, int clientFd) { 11 | Client &client = users[clientFd]; 12 | 13 | std::map commandHandlerMap; 14 | commandHandlerMap["PONG"] = ft_pong; //to ignore 15 | commandHandlerMap["JOIN"] = ft_join; 16 | commandHandlerMap["MODE"] = ft_mode; 17 | commandHandlerMap["PART"] = ft_part; 18 | commandHandlerMap["PRIVMSG"] = ft_privMsg; 19 | commandHandlerMap["QUIT"] = ft_quit; //requires registration 20 | commandHandlerMap["KICK"] = ft_kick; 21 | commandHandlerMap["TOPIC"] = ft_topic; 22 | commandHandlerMap["INVITE"] = ft_invite; 23 | commandHandlerMap["NICK"] = ft_nick; 24 | commandHandlerMap["NOTICE"] = ft_notice; 25 | commandHandlerMap["TIME"] = ft_timeBot; 26 | commandHandlerMap["DICE"] = ft_diceBot; 27 | // loop through multiple commands sent by client in quick succession, which might be received and buffered by the server as a single string separated by '\n' 28 | for (size_t i = 0; i < cmndBuffer.size(); i++) 29 | { 30 | commandInfo cmdInfo = parseCmndBuffer(cmndBuffer[i]); 31 | toUpper(cmdInfo.cmnd_name); 32 | if (!isValidCommand(commandHandlerMap, cmdInfo.cmnd_name)) 33 | { 34 | Logger::warning("Invalid command: " + cmdInfo.cmnd_name); 35 | sendReply(ERR_UNKNOWNCOMMAND(cmdInfo.cmnd_name), clientFd); 36 | continue; 37 | } 38 | commandHandlerMap[cmdInfo.cmnd_name](cmdInfo, *this, client); // execute the CommandHandlerFunc corresponding to the command name 39 | } 40 | } 41 | 42 | commandInfo parseCmndBuffer(const std::string &commandMessage) { 43 | commandInfo command; 44 | 45 | if (commandMessage.empty()) 46 | return command; 47 | 48 | size_t lastPartStartPos = commandMessage.find(" :"); 49 | 50 | std::string middlePart = commandMessage.substr(0, lastPartStartPos); 51 | 52 | std::vector middleParams = split_space(middlePart); 53 | if (!middleParams.empty()) { 54 | command.cmnd_name = middleParams[0]; 55 | command.cmnd_args.insert(command.cmnd_args.end(), middleParams.begin() + 1, middleParams.end()); 56 | } 57 | if (lastPartStartPos != std::string::npos) { //if colon found 58 | std::string lastPart = commandMessage.substr(lastPartStartPos + 2); // +2 to skip the colon and the space before it 59 | command.cmnd_args.push_back(lastPart); 60 | } 61 | return command; 62 | } 63 | -------------------------------------------------------------------------------- /commands/Part.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | //e.g.: PART {,} [] 4 | void ft_part(commandInfo& cmd, Server& server, Client& client) { 5 | if (!cmd.cmnd_args.size()) 6 | { 7 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 8 | return ; 9 | } 10 | std::vector channelsToLeave = split(cmd.cmnd_args[0], ","); 11 | for (size_t i = 0; i < channelsToLeave.size(); i++) 12 | { 13 | if (channelsToLeave[i].empty()) 14 | continue ; 15 | if (!Channel::isValidChannelName(channelsToLeave[i])) 16 | { 17 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, channelsToLeave[i]), client.fds.fd); 18 | continue ; 19 | } 20 | std::vector::iterator channel = server.getChannelByName(channelsToLeave[i]); 21 | if (channel == server.channels.end()) //channel does't exist in server 22 | { 23 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, channelsToLeave[i]), client.fds.fd); 24 | continue ; 25 | } 26 | if (!(*channel)->isJoined(client.nickname)) 27 | { 28 | server.sendReply(ERR_NOTONCHANNEL(server.hostname, client.nickname, channelsToLeave[i]), client.fds.fd); 29 | continue ; 30 | } 31 | std::string channelName = (*channel)->getName(); 32 | if ((*channel)->getAllClientsList().size() > 1) 33 | { 34 | (*channel)->removeClient(client); 35 | if ((*channel)->isOpe(client.nickname) && (*channel)->getOpeList().size() == 1) 36 | { 37 | (*channel)->addOpe((*channel)->getAllClientsList()[0].nickname); 38 | (*channel)->broadcastMessage(NULL, RPL_MODE(setPrefix(server.hostNames[client.fds.fd], (*channel)->getAllClientsList()[0].nickname, (*channel)->getAllClientsList()[0].realname), ((*channel)->getName() + " " + "+" + "o " + ((*channel)->getAllClientsList()[0].nickname))), false); 39 | 40 | } 41 | (*channel)->removeOpe(client.nickname); 42 | client.removeChannel(*channel); 43 | server.sendReply(RPL_PART(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), channelName, (cmd.cmnd_args.size() > 1 ? cmd.cmnd_args[1] : "")), client.fds.fd); 44 | (*channel)->broadcastMessage(&client, RPL_PART(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), channelName, (cmd.cmnd_args.size() > 1 ? cmd.cmnd_args[1] : "")), false); 45 | } 46 | else // this client is the last member in the channel 47 | { 48 | (*channel)->removeClient(client); 49 | client.removeChannel(*channel); 50 | server.removeFromChannels(*channel); 51 | server.sendReply(RPL_PART(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), channelName, (cmd.cmnd_args.size() > 1 ? cmd.cmnd_args[1] : "")), client.fds.fd); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /commands/Pong.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | void ft_pong(commandInfo& cmd, Server& server, Client& client) { 4 | //to silence the worning 5 | (void) cmd; 6 | (void) server; 7 | (void) client; 8 | return ; 9 | } -------------------------------------------------------------------------------- /commands/PrivMsg.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | void ft_privMsg(commandInfo& cmd, Server& server, Client& client) { 4 | 5 | if (cmd.cmnd_args.size() < 2) 6 | { 7 | server.sendReply(ERR_NOTEXTTOSEND(server.hostname, client.nickname), client.fds.fd); 8 | return; 9 | } 10 | cmd.cmnd_args.resize(2); 11 | if(cmd.cmnd_args[1].length() > 512) 12 | { 13 | server.sendReply(ERR_INPUTTOOLONG(server.hostname, client.nickname), client.fds.fd); 14 | return; 15 | } 16 | 17 | std::vector targets = split(cmd.cmnd_args[0], ","); 18 | for (size_t i = 0; i < targets.size(); i++) 19 | { 20 | if((targets[i][0] == '%' && targets[i][1] == '#') || targets[i][0] == '#') 21 | { 22 | bool opeOnly = false; 23 | if(targets[i][0] == '%') 24 | { 25 | targets[i].erase(0, 1); 26 | opeOnly = true; 27 | } 28 | std::vector::iterator channel = server.getChannelByName(targets[i]); 29 | 30 | if (channel == server.channels.end()) 31 | { 32 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname,targets[i]), client.fds.fd); 33 | continue;; 34 | } 35 | std::string channelName = (*channel)->getName(); 36 | if (!(*channel)->isJoined(client.nickname)) 37 | { 38 | server.sendReply(ERR_NOTONCHANNEL(server.hostname, client.nickname, channelName), client.fds.fd); 39 | continue;; 40 | } 41 | (*channel)->broadcastMessage(&client, RPL_CUSTOM_PRIVMSG(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), channelName, cmd.cmnd_args[1]), opeOnly); 42 | } 43 | else 44 | { 45 | std::map::iterator Receiver = server.getClientByNickname(targets[i]); 46 | if (Receiver == server.users.end()) 47 | { 48 | server.sendReply(ERR_NOSUCHNICK(server.hostname, targets[i]), client.fds.fd); 49 | continue;; 50 | } 51 | server.sendReply(RPL_CUSTOM_PRIVMSG(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), Receiver->second.nickname, cmd.cmnd_args[1]), Receiver->second.fds.fd); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /commands/Quit.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | void ft_quit(commandInfo& cmd, Server& server, Client& client) 4 | { 5 | for (std::vector::iterator it = client.channels_joined.begin(); it != client.channels_joined.end(); it++) 6 | { 7 | if (!(*it)->isJoined(client.nickname)) 8 | { 9 | if ((*it)->isInvited(client.nickname)) 10 | (*it)->removeInvite(client); 11 | continue; 12 | } 13 | if ((*it)->getAllClientsList().size() > 1) 14 | { 15 | (*it)->removeClient(client); 16 | if ((*it)->isOpe(client.nickname) && (*it)->getOpeList().size() == 1) 17 | { 18 | (*it)->addOpe((*it)->getAllClientsList()[0].nickname); 19 | (*it)->broadcastMessage(NULL, RPL_MODE(setPrefix(server.hostNames[client.fds.fd], (*it)->getAllClientsList()[0].nickname, (*it)->getAllClientsList()[0].realname), ((*it)->getName() + " +o " + ((*it)->getAllClientsList()[0].nickname))), false); 20 | } 21 | (*it)->removeOpe(client.nickname); 22 | (*it)->broadcastMessage(&client, RPL_PART(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), (*it)->getName(), ""), false); 23 | } 24 | else 25 | { 26 | (*it)->removeClient(client); 27 | server.removeFromChannels(*it); 28 | } 29 | } 30 | client.quitAllChannels(); 31 | toUpper(cmd.cmnd_name); 32 | if (cmd.cmnd_name == "QUIT") 33 | { 34 | server.sendReply(RPL_CUSTOM_QUIT(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), (!cmd.cmnd_args.empty() ? (":Quit: " + cmd.cmnd_args[0]) : "Quit ")), client.fds.fd); 35 | server.removeClientFromServer(client); 36 | Logger::info(":Quit: " + (!cmd.cmnd_args.empty() ? (cmd.cmnd_args[0]) : "")); 37 | Logger::info("Connection closed"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /commands/Topic.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | // TOPIC [ ] 4 | void ft_topic(commandInfo& cmd, Server& server, Client& client) { 5 | if (!cmd.cmnd_args.size()) 6 | { 7 | server.sendReply(ERR_NEEDMOREPARAMS(server.hostname, client.nickname, cmd.cmnd_name), client.fds.fd); 8 | return ; 9 | } 10 | if(cmd.cmnd_args.size() > 1 && cmd.cmnd_args[1].length() > 512) 11 | { 12 | server.sendReply(ERR_INPUTTOOLONG(server.hostname, client.nickname), client.fds.fd); 13 | return; 14 | } 15 | std::vector::iterator channel = server.getChannelByName(cmd.cmnd_args[0]); 16 | if (channel == server.channels.end()) 17 | { 18 | server.sendReply(ERR_NOSUCHCHANNEL(server.hostname, client.nickname, cmd.cmnd_args[0]), client.fds.fd); 19 | return ; 20 | } 21 | if (!(*channel)->isJoined(client.nickname)) 22 | { 23 | server.sendReply(ERR_NOTONCHANNEL(server.hostname, client.nickname, (*channel)->getName()), client.fds.fd); 24 | return ; 25 | } 26 | if ((*channel)->hasMode(CHANNEL_MODE_TOPIC_SETTABLE_BY_CHANNEL_OPERATOR_ONLY) && !(*channel)->isOpe(client.nickname) && cmd.cmnd_args.size() > 1) 27 | { 28 | server.sendReply(ERR_CHANOPRIVSNEEDED(server.hostname, client.nickname, (*channel)->getName()), client.fds.fd); 29 | return ; 30 | } 31 | if (cmd.cmnd_args.size() > 1) // in case of "TOPIC :" or "TOPIC :" 32 | (*channel)->setTopic(cmd.cmnd_args[1]); 33 | else // in case of "TOPIC " display the channel topic 34 | { 35 | if ((*channel)->getTopic().empty()) // in case of "TOPIC " display empty topic 36 | server.sendReply(RPL_NOTOPIC(server.hostname, client.nickname, (*channel)->getName()), client.fds.fd); 37 | 38 | else 39 | server.sendReply(RPL_TOPIC(server.hostname, client.nickname, (*channel)->getName(), (*channel)->getTopic()), client.fds.fd); 40 | return ; 41 | } 42 | (*channel)->broadcastMessage(NULL, RPL_CUSTOM_TOPIC(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), (*channel)->getName(), (*channel)->getTopic()), false); 43 | } 44 | 45 | 46 | //add numeric 333 (time) 47 | //display topic : sendreply to me 332 333 48 | //no topic is set : sendreply : 331 49 | //delete topic with : braodcast 50 | // set topic : broadcast -------------------------------------------------------------------------------- /commands/notice.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | void ft_notice(commandInfo& cmd, Server& server, Client& client) { 4 | 5 | if (cmd.cmnd_args.size() < 1) 6 | { 7 | server.sendReply(ERR_NOTEXTTOSEND(server.hostname, client.nickname), client.fds.fd); 8 | return; 9 | } 10 | 11 | cmd.cmnd_args.resize(2); 12 | if(cmd.cmnd_args[1].length() > 512) 13 | { 14 | server.sendReply(ERR_INPUTTOOLONG(server.hostname, client.nickname), client.fds.fd); 15 | return; 16 | } 17 | if((cmd.cmnd_args[0][0] == '%' && cmd.cmnd_args[0][1] == '#') || cmd.cmnd_args[0][0] == '#') 18 | { 19 | bool opeOnly = false; 20 | if(cmd.cmnd_args[0][0] == '%') 21 | { 22 | cmd.cmnd_args[0].erase(0, 1); 23 | opeOnly = true; 24 | } 25 | std::vector::iterator channel = server.getChannelByName(cmd.cmnd_args[0]); 26 | 27 | if (channel == server.channels.end()) 28 | return; 29 | std::string channelName = (*channel)->getName(); 30 | if (!(*channel)->isJoined(client.nickname)) 31 | return; 32 | (*channel)->broadcastMessage(&client, RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), channelName, cmd.cmnd_args[1]), opeOnly); 33 | } 34 | else 35 | { 36 | std::map::iterator Receiver = server.getClientByNickname(cmd.cmnd_args[0]); 37 | if (Receiver == server.users.end()) 38 | return; 39 | server.sendReply(RPL_CUSTOM_NOTICE(setPrefix(server.hostNames[client.fds.fd], client.nickname, client.realname), Receiver->second.nickname, cmd.cmnd_args[1]), Receiver->second.fds.fd); 40 | } 41 | } -------------------------------------------------------------------------------- /headers/Channel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Client.hpp" 4 | 5 | class Client; 6 | 7 | enum ChannelMode { 8 | CHANNEL_MODE_INVITE_ONLY = 0, //+i //doesn't need a parameter 9 | CHANNEL_MODE_TOPIC_SETTABLE_BY_CHANNEL_OPERATOR_ONLY = 1, //+t //doesn't need a parameter 10 | CHANNEL_MODE_KEY = 2, //+k //'key PARAMETER' undisplayable in channel modes set 11 | CHANNEL_MODE_OPERATOR = 3, //+o //user mode undisplayable in channel modes set 12 | CHANNEL_MODE_USER_LIMIT = 4, //+l 13 | }; 14 | 15 | class Channel { 16 | private: 17 | std::string _name; // channel name 18 | std::string _password; //channel key 19 | int _channel_limit; // maximum number of clients that can join the channel 20 | std::string _topic; // channel topic 21 | std::string _stringModes; // string representation of the channel modes 22 | std::vector > _modes; //all known channel modes and their active status 23 | std::vector inviteList; //all invited members 24 | std::vector allClientsList; //all members of the channel 25 | std::vector opeList; //all channel operators 26 | 27 | public: 28 | Channel(std::string const &name, std::string const &password); 29 | ~Channel(); 30 | // Setters 31 | void setTopic(std::string const &newTopic); 32 | void setPassword(std::string const &passwd); 33 | void setChannel_limit(int limit); 34 | // Getters 35 | std::string getPassword(void) const; 36 | std::string getName(void) const; 37 | std::string getTopic(void) const; 38 | std::vector > getModes(void) const; 39 | std::string getStringModes(void) const; 40 | int getChannelLimit(void) const; 41 | std::vector &getAllClientsList(void); 42 | std::vector &getOpeList(void); 43 | std::vector &getInviteList(void); 44 | // Client stuff 45 | int addClient(Client const &client); 46 | void removeClient(Client const &client); 47 | bool isJoined(std::string const &nickname); 48 | std::string listClients(); 49 | bool isOpe(std::string const &nickname); 50 | bool isInvited(std::string const &nickname); 51 | void addOpe(std::string const &nickname); 52 | void removeOpe(std::string const &nickname); 53 | void invite(Client const &client); 54 | Client &getClient(std::string const &nickname); 55 | void removeInvite(Client const &client); 56 | // Channel modes stuff 57 | char getModeIdentifier(ChannelMode _mode) const; 58 | void updateStringModes(void); 59 | bool isInviteOnly(void); 60 | bool hasMode(ChannelMode mode); 61 | void addMode(ChannelMode mode); 62 | void removeMode(ChannelMode mode); 63 | bool hasKey(void); 64 | // Utils 65 | void broadcastMessage(Client *sender, std::string message, bool opeOnly); 66 | static bool isValidChannelName(const std::string &name); 67 | void updateNick(std::string oldNick, std::string newNick, std::vector &clients); 68 | }; -------------------------------------------------------------------------------- /headers/Client.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Server.hpp" 4 | #include "Channel.hpp" 5 | 6 | class Channel; 7 | 8 | class Client{ 9 | 10 | public : 11 | struct pollfd fds; //>>fds.fd 12 | std::string username; 13 | std::string realname; 14 | std::string nickname; 15 | std::string password; 16 | std::string buffer; 17 | std::vector channels_joined; 18 | 19 | Client(); 20 | Client(struct pollfd fds, std::string username, std::string realname, std::string nickname, std::string password, std::string buffer); 21 | ~Client(); 22 | 23 | void addChannel(Channel *channel); 24 | void removeChannel(Channel *channel); 25 | void quitAllChannels(); 26 | void setNickname(std::string const &nickname); 27 | }; 28 | -------------------------------------------------------------------------------- /headers/Commands.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Server.hpp" 4 | 5 | struct ChannelJoin { 6 | std::string name; 7 | std::string key; 8 | }; 9 | 10 | typedef struct 11 | { 12 | std::string cmnd_name; 13 | std::vector cmnd_args; 14 | } commandInfo; 15 | 16 | class Client; 17 | class Server; 18 | 19 | //commands 20 | commandInfo parseCmndBuffer(const std::string &cmndBuffer); 21 | void ft_pong(commandInfo& cmd, Server& server, Client& client); 22 | void ft_nick(commandInfo& cmd, Server& server, Client& client); 23 | void ft_join(commandInfo& cmd, Server& server, Client& client); 24 | void ft_part(commandInfo& cmd, Server& server, Client& client); 25 | void ft_privMsg(commandInfo& cmd, Server& server, Client& client); 26 | void ft_quit(commandInfo& cmd, Server& server, Client& client); 27 | void ft_topic(commandInfo& cmd, Server& server, Client& client); 28 | void ft_invite(commandInfo &cmd, Server& server, Client& client); 29 | void ft_kick(commandInfo& cmd, Server& server, Client& client); 30 | void ft_mode(commandInfo& cmd, Server& server, Client& client); 31 | void ft_nick(commandInfo& cmd, Server& server, Client& client); 32 | void ft_notice(commandInfo& cmd, Server& server, Client& client); 33 | //bot commands 34 | void ft_timeBot(commandInfo& cmd, Server& server, Client& client); 35 | void ft_diceBot(commandInfo& cmd, Server& server, Client& client); -------------------------------------------------------------------------------- /headers/Logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Server.hpp" 4 | 5 | #define RESET "\033[0m" 6 | #define RED "\033[31m" 7 | #define GREEN "\033[32m" 8 | #define YELLOW "\033[33m" 9 | #define CYAN "\033[36m" 10 | 11 | class Logger { 12 | public: 13 | static void error(std::string msg) { 14 | std::cout << RED << msg << RESET << std::endl; 15 | } 16 | 17 | static void warning(std::string msg) { 18 | std::cout << YELLOW << msg << RESET << std::endl; 19 | } 20 | 21 | static void info(std::string msg) { 22 | std::cout << GREEN << msg << RESET << std::endl; 23 | } 24 | 25 | static void debug(std::string msg) { 26 | std::cout << CYAN << msg << RESET << std::endl; 27 | } 28 | }; -------------------------------------------------------------------------------- /headers/Replies.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define RPL_WELCOME(servername, nick) \ 4 | ":" + servername + " 001 " + nick + " :Welcome to the Internet Relay Network " + nick + "\r\n" 5 | #define RPL_YOURHOST(servername, nick, version) \ 6 | ":" + servername + " 002 " + nick + " :Your host is " + servername + ", running version " + version + "\r\n" 7 | #define RPL_CREATED(servername, nick) \ 8 | ":" + servername + " 003 " + nick + " :This server was created January 1st, 1970\r\n" 9 | #define RPL_MYINFO(servername, nick, version, usermodes, chanmodes) \ 10 | ":" + servername + " 004 " + nick + " " + servername + " " + version + " " + usermodes + " " + chanmodes + " :" + servername + " server\r\n" 11 | #define RPL_WHOISUSER(servername, nick, user, host, realname) \ 12 | ":" + servername + " 311 " + nick + " " + user + " " + host + " * :" + realname + "\r\n" 13 | #define RPL_WHOISSERVER 312 14 | #define RPL_WHOISOPERATOR 313 15 | #define RPL_WHOWASUSER 314 16 | #define RPL_WHOISIDLE 317 17 | #define RPL_ENDOFWHOIS 318 18 | #define RPL_WHOISCHANNELS(servername, nick, mode, channel) \ 19 | ":" + servername + " 311 " + nick + " " + mode + " " + channel + "\r\n" 20 | #define RPL_WHOISSPECIAL 320 21 | #define RPL_LISTSTART(servername, nick) \ 22 | ":" + servername + " 321 " + nick + " Channel :Users Name\r\n" 23 | #define RPL_LIST(servername, nick, channel, users, topic) \ 24 | ":" + servername + " 322 " + nick + " " + channel + " " + users + " :" + topic + "\r\n" 25 | #define RPL_LISTEND(servername, nick) \ 26 | ":" + servername + " 323 " + nick + " :End of /LIST\r\n" 27 | #define RPL_CHANNELMODEIS(servername, nick, channel, modes) \ 28 | ":" + servername + " 324 " + nick + " " + channel + " +" + modes + "\r\n" 29 | #define RPL_CREATIONTIME 329 30 | #define RPL_WHOISACCOUNT 330 31 | #define RPL_NOTOPIC(servername, nick, channel) \ 32 | ":" + servername + " 331 " + nick + " " + channel + " :No topic is set\r\n" 33 | #define RPL_TOPIC(servername, nick, channel, topic) \ 34 | ":" + servername + " 332 " + nick + " " + channel + " :" + topic + "\r\n" 35 | #define RPL_TOPICWHOTIME(servername, nick, channel, user, time) \ 36 | ":" + servername + " 333 " + nick + " " + channel + " " + user + " " + time + "\r\n" 37 | #define RPL_WHOISACTUALLY 338 38 | #define RPL_INVITING(servername, nick, user, channel) \ 39 | ":" + servername + " 341 " + nick + " " + user + " " + channel + "\r\n" 40 | #define RPL_INVITELIST 346 41 | #define RPL_ENDOFINVITELIST 347 42 | #define RPL_EXCEPTLIST 348 43 | #define RPL_ENDOFEXCEPTLIST 349 44 | #define RPL_VERSION 351 45 | #define RPL_NAMREPLY(servername, nick, symbol, channel, users) \ 46 | ":" + servername + " 353 " + nick + " " + symbol + " " + channel + " :" + users + "\r\n" 47 | #define RPL_ENDOFNAMES(servername, nick, channel) \ 48 | ":" + servername + " 366 " + nick + " " + channel + " :End of /NAMES list\r\n" 49 | #define RPL_ENDOFWHOWAS 369 50 | #define RPL_INFO 371 51 | #define RPL_ENDOFINFO 374 52 | #define RPL_WHOISHOST 378 53 | #define RPL_WHOISMODES 379 54 | #define RPL_YOUREOPER 381 55 | #define RPL_REHASHING 382 56 | #define RPL_TIME 391 57 | #define ERR_UNKNOWNERROR 400 58 | #define RPL_MOTDSTART(servername, nick) \ 59 | ":" + servername + " 375 " + nick + " :- " + servername + " Message of the day - \r\n" 60 | #define RPL_MOTD(servername, nick, motd) \ 61 | ":" + servername + " 372 " + nick + " :- " + motd + "\r\n" 62 | #define RPL_ENDOFMOTD(servername, nick) \ 63 | ":" + servername + " 376 " + nick + " :End of /MOTD command\r\n" 64 | #define ERR_NOSUCHNICK(servername, nick) \ 65 | ":" + servername + " 401 " + nick + " :No such nick/channel\r\n" 66 | #define ERR_NOSUCHSERVER 402 67 | #define ERR_NOSUCHCHANNEL(servername, nick, channel) \ 68 | ":" + servername + " 403 " + nick + " " + channel + " :No such channel\r\n" 69 | #define ERR_CANNOTSENDTOCHAN(servername, nick, channel) \ 70 | ":" + servername + " 404 " + nick + " " + channel + " :Cannot send to channel\r\n" 71 | #define ERR_TOOMANYCHANNELS(servername, nick, channel) \ 72 | ":" + servername + " 405 " + nick + " " + channel + " :You have joined too many channels\r\n" 73 | #define ERR_WASNOSUCHNICK 406 74 | #define ERR_UNKNOWNCOMMAND(command) "421 " + command + " :Unknown command\r\n" 75 | #define ERR_NONICKNAMEGIVEN(servername) \ 76 | ":" + servername + " 431 " + servername + " :No nickname given\r\n" 77 | #define ERR_ERRONEUSNICKNAME(servername, nick) \ 78 | ":" + servername + " 432 * " + nick + " :Erroneous nickname\r\n" 79 | #define ERR_NICKNAMEINUSE(servername, nick) \ 80 | ":" + servername + " 433 * " + nick + " :Nickname is already in use\r\n" 81 | #define ERR_USERNOTINCHANNEL(servername, nick, channel) \ 82 | ":" + servername + " 441 * " + nick + " " + channel + " :They aren't on that channel\r\n" 83 | #define ERR_NOTONCHANNEL(servername, nick, channel) \ 84 | ":" + servername + " 442 * " + nick + " " + channel + " :You're not on that channel\r\n" 85 | #define ERR_USERONCHANNEL(servername, nick, username, channel) \ 86 | ":" + servername + " 443 * " + nick + " " + username + " " + channel + " :is already on channel\r\n" 87 | #define ERR_NOTREGISTERED(source) "451 " + source + " :You have not registered\r\n" 88 | #define ERR_NEEDMOREPARAMS(servername, nick, command) \ 89 | ":" + servername + " 461 " + nick + " " + command + " :Wrong num parameters\r\n" 90 | #define ERR_ALREADYREGISTRED(servername, nick) \ 91 | ":" + servername + " 462 " + nick + " :You may not reregister\r\n" 92 | #define ERR_PASSWDALREADYSET(source) \ 93 | ":" + source + " 464 " + source + " :Password already set\r\n" 94 | #define ERR_PASSWDMISMATCH(source) \ 95 | ":" + source + " 464 " + source + " :Password incorrect\r\n" 96 | #define ERR_KEYSET(servername, nick, channel) \ 97 | ":" + servername + " 467 " + nick + " " + channel + " :Channel key already set\r\n" 98 | #define ERR_CHANNELISFULL(servername, nick, channel) \ 99 | ":" + servername + " 471 " + nick + " " + channel + " :Cannot join channel , channel is full(+l)\r\n" 100 | #define ERR_UNKNOWNMODE(servername, nick, mode) \ 101 | ":" + servername + " 472 " + nick + " " + mode + " :is unknown mode char to me for that channel\r\n" 102 | #define ERR_INVITEONLYCHAN(servername, nick, channel) \ 103 | ":" + servername + " 473 " + nick + " " + channel + " :Cannot join channel (+i)\r\n" 104 | #define ERR_BADCHANNELKEY(servername, nick, channel) \ 105 | ":" + servername + " 475 " + nick + " " + channel + " :Bad channel key\r\n" 106 | #define ERR_NOPRIVILEGES 481 107 | #define ERR_CHANOPRIVSNEEDED(servername, nick, channel) \ 108 | ":" + servername + " 482 " + nick + " " + channel + " :You're not channel operator\r\n" 109 | #define ERR_CANTKILLSERVER 483 110 | #define ERR_NOOPERHOST 491 111 | #define ERR_UMODEUNKNOWNFLAG 501 112 | #define ERR_USERSDONTMATCH(servername, nick) \ 113 | ":" + servername + " 502 " + nick + " :Cant change mode for other users\r\n" 114 | #define ERR_INVALIDKEY 525 115 | #define ERR_INVALIDMODEPARAM(nickname, channel, mode, param) "696 " + nickname + " " + channel + " " + mode + \ 116 | " * :Invalid mode " + mode + " parameter: "+ param + "\r\n" 117 | #define ERR_NOMOTD 422 118 | #define ERR_WRONGPASS(servername, nick) \ 119 | ":" + servername + " " + nick + " :Wrong pass\r\n" 120 | #define RPL_CUSTOM_JOIN(source, channel) source + " JOIN " + channel + "\r\n" 121 | #define RPL_CUSTOM_KICK(user, channel, nick, reason) \ 122 | ":" + user + " KICK " + channel + " " + nick + " :" + reason + "\r\n" 123 | #define RPL_CUSTOM_MODE(user, channel, mode) \ 124 | ":" + user + " MODE " + channel + " " + mode + "\r\n" 125 | #define RPL_CUSTOM_INVITE(source, nick, channel) \ 126 | source + " INVITE " + nick + " " + channel + "\r\n" 127 | #define RPL_MODE(source, modestring) source + " MODE " + modestring + "\r\n" 128 | #define RPL_PART(source, channel, reason) source + " PART " + channel + " :" + reason + "\r\n" 129 | #define RPL_KICK(source, channel, target, reason) source + " KICK " + channel + " " + target + " :" + reason + "\r\n" 130 | #define ERR_NOTEXTTOSEND(servername, nick)\ 131 | ":" + servername + " 412 " + nick + " :No text to send\r\n" 132 | #define ERR_INPUTTOOLONG(servername, nick) \ 133 | ":" + servername + " 417 " + nick + " :Input line too long\r\n" 134 | #define RPL_CUSTOM_PRIVMSG(source, channelORclient, message) \ 135 | source + " PRIVMSG " + channelORclient + " :" + message + "\r\n" 136 | #define RPL_CUSTOM_NOTICE(source, channelORclient, message) \ 137 | source + " NOTICE " + channelORclient + " :" + message + "\r\n" 138 | #define RPL_CUSTOM_TOPIC(source, channel, message) \ 139 | source + " TOPIC " + channel + " :" + message + "\r\n" 140 | #define RPL_CUSTOM_QUIT(source, reason) \ 141 | source + " QUIT " + reason + "\r\n" 142 | #define RPL_NICKCHANGE(oldNick, newNick) ":" + oldNick + " NICK " + newNick + "\r\n" 143 | 144 | -------------------------------------------------------------------------------- /headers/Server.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "Client.hpp" 24 | #include "Channel.hpp" 25 | #include "Replies.hpp" 26 | #include "Commands.hpp" 27 | #include "Utils.hpp" 28 | #include "Logger.hpp" 29 | #include 30 | 31 | #define BACKLOG 10 32 | #define MAX_CLIENTS_PER_CHANNEL 30 33 | #define MAX_BUFFER_SIZE 512 34 | #define MAX_CONNECTIONS 50 35 | #define MAX_CHANNELS 5 36 | 37 | class Client; 38 | class Channel; 39 | 40 | class Server{ 41 | public: 42 | int socket_fd; 43 | struct sockaddr_in ip4addr; 44 | unsigned int port; 45 | std::string password; 46 | std::vector fds; 47 | int current_size; 48 | std::string hostname ; 49 | std::map hostNames; 50 | std::vector channels; 51 | std::map connections; // clients connected but not authenticated 52 | std::map users; // clients authenticated 53 | 54 | Server(unsigned int port, std::string password); 55 | ~Server(); 56 | void create_server(); 57 | void waiting_for_connections(); 58 | int is_server_connection(); 59 | int is_client_connection(struct pollfd fd_struct, int index); 60 | void parse_buffer_nc(Client &client); 61 | void parse_buffer_limechat(Client &client); 62 | void my_split_buffer(Client &client, std::string delimiter); 63 | int if_nick_exist(std::string value); 64 | int if_user_exist(std::string value); 65 | int parse_pass(Client &client, std::string value); 66 | int parse_nick(Client &client, std::string value); 67 | bool isValidNickChar(char c); 68 | bool isValidNick(std::string &nickname); 69 | int parse_user(Client &client, std::string value); 70 | int parse_pair(Client &client, std::pair pair); 71 | void WelcomeMessage(Client &client); 72 | void addToChannels(Channel *channel); 73 | void removeFromChannels(Channel *channel); 74 | std::vector::iterator getChannelByName(const std::string &name); 75 | std::map::iterator getClientByNickname(const std::string &nickName); 76 | void removeClientFromServer(Client &client); 77 | void sendReply(const std::string &message, int clientFd); 78 | void executeCommands(const std::vector cmnds, int clientFd); 79 | void broadcastMessage(Client *sender, std::string message, std::vector chared_channels); 80 | }; -------------------------------------------------------------------------------- /headers/Utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Server.hpp" 4 | #include "Client.hpp" 5 | 6 | void my_trim_(std::string& s, char delimiter); 7 | std::vector split(const std::string input, const std::string &separator); 8 | std::vector split_space(const std::string &input); 9 | std::string setPrefix(std::string hostname, std::string nickname, std::string realname); 10 | bool isNumber(const std::string &s); 11 | std::string to_string(int i); 12 | int to_int(std::string i); 13 | void toUpper(std::string &str); -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/Server.hpp" 2 | 3 | int main(int ac, char **av) 4 | { 5 | signal(SIGPIPE, SIG_IGN); 6 | if (ac != 3) 7 | { 8 | Logger::error("ERROR : ./exec port password"); 9 | return (1); 10 | } 11 | if (atoi(av[1]) > 65535|| atoi(av[1]) < 1024){ 12 | Logger::error("ERROR : invalid port value"); 13 | return (1); 14 | } 15 | std::string port = av[1]; 16 | std::string pass = av[2]; 17 | int i = 0; 18 | if (!port.empty() && !pass.empty()) 19 | { 20 | while (port[i]) { 21 | if (!isdigit(port[i])) 22 | { 23 | Logger::error("ERROR : port must be a number"); 24 | return (1); 25 | } 26 | i++; 27 | } 28 | Server server(std::atoi(port.c_str()), pass); 29 | server.create_server(); 30 | server.waiting_for_connections(); 31 | return (0); 32 | } 33 | Logger::error("ERROR: empty parameter"); 34 | return (1); 35 | } 36 | -------------------------------------------------------------------------------- /server/create_server.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | void Server::create_server() 4 | { 5 | 6 | ip4addr.sin_port = htons(port); 7 | ip4addr.sin_family = AF_INET; 8 | ip4addr.sin_addr.s_addr = INADDR_ANY; 9 | // create socket 10 | socket_fd = socket(AF_INET, SOCK_STREAM, 0); 11 | if (socket_fd == -1) 12 | { 13 | std::cerr << "socket failed" << std::endl; 14 | if (socket_fd) 15 | close(socket_fd); 16 | exit(-1); 17 | } 18 | // set socket options 19 | int on = 1; 20 | int checker = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); 21 | if (checker < 0) 22 | { 23 | perror("setsockopt() failed"); 24 | if (socket_fd) 25 | close(socket_fd); 26 | exit(-6); 27 | } 28 | // Set the socket to non-blocking mode 29 | if (fcntl(socket_fd, F_SETFL, O_NONBLOCK) == -1) 30 | { 31 | std::cerr << "fcntl failed" << std::endl; 32 | if (socket_fd) 33 | close(socket_fd); 34 | exit(-2); 35 | } 36 | // define this socket with ip and port 37 | checker = bind(socket_fd, (struct sockaddr *)&ip4addr, sizeof ip4addr); 38 | if (checker == -1) 39 | { 40 | std::cerr << "bind failed" << std::endl; 41 | perror(NULL); 42 | if (socket_fd) 43 | close(socket_fd); 44 | exit(-3); 45 | } 46 | //listen for connections 47 | checker = listen(socket_fd, 1024); 48 | if (checker == -1) 49 | { 50 | std::cerr << "listen failed" << std::endl; 51 | if (socket_fd) 52 | close(socket_fd); 53 | exit(-4); 54 | } 55 | //add server fd to pollfds 56 | struct pollfd k; 57 | k.fd = socket_fd; 58 | k.events = POLLIN; 59 | k.revents = 0; 60 | fds.push_back(k); 61 | } 62 | 63 | void Server::waiting_for_connections(){ 64 | 65 | int timeout = 0; 66 | int checker; 67 | 68 | while (true) 69 | { 70 | checker = poll(&fds[0], fds.size(), timeout); 71 | if (checker < 0) 72 | { 73 | Logger::error("poll() failed"); 74 | break; 75 | } 76 | else 77 | { 78 | current_size = fds.size(); 79 | for (int i = 0; i < current_size; i++) 80 | { 81 | if (fds[i].revents == 0) 82 | { 83 | continue; 84 | } 85 | if (!(fds[i].revents & POLLIN)) 86 | { 87 | continue; 88 | } 89 | if (fds[i].fd == socket_fd) 90 | { 91 | if(is_server_connection() == -1) 92 | break; 93 | } 94 | else 95 | { 96 | if (is_client_connection(fds[i], i) == -1) 97 | break; 98 | } 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /server/is_connection.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | int Server::is_server_connection(){ 4 | //accept connection from server and add it to fds 5 | socklen_t ip4addrSize = sizeof(ip4addr); 6 | int new_sd = accept(socket_fd, (struct sockaddr*)&ip4addr, &ip4addrSize); 7 | if (new_sd < 0) 8 | { 9 | if (errno != EWOULDBLOCK) 10 | { 11 | Logger::error("accept() failed"); 12 | } 13 | return(-1); 14 | } 15 | hostNames[new_sd] = inet_ntoa(ip4addr.sin_addr); 16 | Logger::info("New incoming connection " + to_string(new_sd)); 17 | struct pollfd k; 18 | k.fd = new_sd; 19 | k.events = POLLIN; 20 | k.revents = 0; 21 | fds.push_back(k); 22 | 23 | return 0; 24 | } 25 | 26 | int Server::is_client_connection(struct pollfd fd_struct, int i){ 27 | 28 | char buffer[1024] = {0}; 29 | 30 | //read the buffer from client (user || new connection) 31 | int checker = recv(fd_struct.fd, buffer, sizeof(buffer), 0); 32 | if (checker <= 0) 33 | { 34 | //handle cntrl+C 35 | Logger::info("Connection closed"); 36 | std::map::iterator it = connections.find(fd_struct.fd); 37 | if (it != connections.end()){ 38 | connections.erase(it); 39 | } 40 | it = users.find(fd_struct.fd); 41 | if (it != users.end()) 42 | { 43 | commandInfo cmd = {"SIG", std::vector()}; 44 | ft_quit(cmd, *this, users[fd_struct.fd]); 45 | users.erase(it); 46 | } 47 | fds.erase(fds.begin() + i); 48 | close(fd_struct.fd); 49 | return -1; 50 | } 51 | 52 | //handle cntrl+D 53 | std::string content = buffer; 54 | if (content.find('\n') == std::string::npos) 55 | { 56 | if (users.find(fd_struct.fd) != users.end()) 57 | { 58 | users[fd_struct.fd].buffer += content; 59 | return 0; 60 | } 61 | else if (connections.find(fd_struct.fd) != connections.end()) 62 | { 63 | connections[fd_struct.fd].buffer += content; 64 | return 0; 65 | } 66 | else 67 | { 68 | connections.insert(std::pair(fd_struct.fd, Client(fd_struct, "", "", "", "", content))); 69 | return 0; 70 | } 71 | } 72 | 73 | //client authenticated , exist in users 74 | if (users.find(fd_struct.fd) != users.end()) 75 | { 76 | // parsing and executing cmnds 77 | std::vector cmndBuffer; 78 | // to avoid in case the client closes the connection while processing the request 79 | int clientFd = users[fd_struct.fd].fds.fd; 80 | // if there is a buffer from previous recv stopped by ^D 81 | content = users[fd_struct.fd].buffer + content; 82 | users[fd_struct.fd].buffer = ""; 83 | //split by \r\n (from limechat) in case of multiple commands sent by client in quick succession 84 | if (content.find('\r') != std::string::npos) 85 | cmndBuffer = split(content, "\r\n"); 86 | //split by \n (from nc) 87 | else if (content.find('\n') != std::string::npos) 88 | cmndBuffer = split(content, "\n"); 89 | executeCommands(cmndBuffer, clientFd); 90 | } 91 | else 92 | { 93 | //new client 94 | if (connections.find(fd_struct.fd) == connections.end()) 95 | connections.insert(std::pair(fd_struct.fd, Client(fd_struct, "", "", "", "", content))); 96 | else 97 | connections[fd_struct.fd].buffer += content; 98 | if (connections[fd_struct.fd].buffer.find('\r') != std::string::npos) 99 | parse_buffer_limechat(connections[fd_struct.fd]); //parse buffer with back slach r 100 | else 101 | parse_buffer_nc(connections[fd_struct.fd]); //parse buffer without backslash r 102 | connections[fd_struct.fd].buffer = ""; 103 | } 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /server/server.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Server.hpp" 2 | 3 | Server::Server(unsigned int port, std::string password) { 4 | this->port = port; 5 | this->password = password; 6 | this->hostname = "IRCServer.1337.ma"; 7 | Logger::info("-------Server : Welcome to " + this->hostname + "-------"); 8 | } 9 | 10 | Server::~Server(){ 11 | for(size_t i = 0; i < fds.size(); i++){ 12 | close (fds[i].fd); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /utils/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "../headers/Utils.hpp" 2 | 3 | void my_trim_(std::string& s, char delimiter) { 4 | size_t p = s.find_first_not_of(delimiter); 5 | s.erase(0, p); 6 | p = s.find_last_not_of(delimiter); 7 | if (std::string::npos != p) 8 | s.erase(p + 1); 9 | } 10 | 11 | std::vector split_space(const std::string &input) { 12 | std::vector result; 13 | std::size_t start = 0; 14 | std::size_t found = input.find(' '); 15 | 16 | while (found != std::string::npos) { 17 | if (found != start) 18 | result.push_back(input.substr(start, found - start)); 19 | start = found + 1; 20 | found = input.find(' ', start); 21 | } 22 | // Push the last part of the string if it's not empty 23 | std::string lastPart = input.substr(start); 24 | if (!lastPart.empty()) 25 | result.push_back(lastPart); 26 | for (std::vector::iterator it = result.begin(); it != result.end(); it++) 27 | my_trim_(*it, ' '); 28 | return result; 29 | } 30 | 31 | std::vector split(const std::string input, const std::string &separator) 32 | { 33 | std::vector result; 34 | std::size_t start = 0; 35 | std::size_t found = input.find(separator); 36 | 37 | while (found != std::string::npos) 38 | { 39 | // Only push non-empty substrings 40 | if (found != start) 41 | result.push_back(input.substr(start, found - start)); 42 | else 43 | result.push_back(""); 44 | start = found + separator.size(); // Move past the separator 45 | found = input.find(separator, start); 46 | } 47 | // Push the last part of the string if it's not empty 48 | std::string lastPart = input.substr(start); 49 | if (!lastPart.empty()) 50 | result.push_back(lastPart); 51 | for (std::vector::iterator it = result.begin(); it != result.end(); it++) 52 | my_trim_(*it, ' '); 53 | return result; 54 | } 55 | 56 | std::string setPrefix(std::string hostname, std::string nickname, std::string realname) 57 | { 58 | return (":" + nickname + "!" + realname + "@" + hostname); 59 | } 60 | 61 | bool isNumber(const std::string &s) 62 | { 63 | for (std::string::const_iterator it = s.begin(); it != s.end(); it++) 64 | { 65 | if (!std::isdigit(*it)) 66 | return false; 67 | } 68 | return true; 69 | } 70 | 71 | std::string to_string(int i) { 72 | std::stringstream ss; 73 | ss << i; 74 | return ss.str(); 75 | } 76 | 77 | void toUpper(std::string &str) 78 | { 79 | for (size_t i = 0; i < str.length(); i++) 80 | str[i] = toupper(str[i]); 81 | } 82 | 83 | void Server::sendReply(const std::string &message, int clientFd) 84 | { 85 | if (send(clientFd, message.c_str(), message.size(), 0) == -1) 86 | perror("send sys call failed: "); 87 | } 88 | 89 | int to_int(std::string i) { 90 | std::stringstream ss(i); 91 | int x; 92 | ss >> x; 93 | return x; 94 | } --------------------------------------------------------------------------------