diff --git a/includes/Channel.hpp b/includes/Channel.hpp index 4de5625..3c47600 100644 --- a/includes/Channel.hpp +++ b/includes/Channel.hpp @@ -18,10 +18,12 @@ class User; */ class Channel { private: - std::string _name; - std::string _password; - std::vector _users; - std::vector _operators; + std::string _name; + std::string _password; + std::vector _users; + std::vector _operators; + std::vector _inviteList; + std::vector _banList; std::string _topic; std::string _modes; int _limit; @@ -30,6 +32,7 @@ class Channel { bool checkChannelName(std::string name) const; std::vector::iterator findUser(std::string nickname); std::vector::iterator findOper(std::string nickname); + bool isModesSet(std::string modesToCheck) const; public: //Constructors and destructor @@ -38,6 +41,7 @@ class Channel { //Getters std::string getName() const; + std::string getPassword() const; std::vector getUsers() const; std::vector getOperators() const; std::vector getAllUsers() const; @@ -53,6 +57,11 @@ class Channel { //Operations bool checkPassword(std::string password) const; + bool isInviteOnly() const; + bool isUserInvited(std::string nickname) const; + bool isUserBanned(std::string nickname, std::string username, std::string hostname) const; + bool hasLimit() const; + bool isFull() const; void addUser(User user); void removeUser(std::string nickname); void removeOper(std::string nickname); diff --git a/includes/Server.hpp b/includes/Server.hpp index d519b19..11e2e91 100644 --- a/includes/Server.hpp +++ b/includes/Server.hpp @@ -61,6 +61,7 @@ class Server { void attemptUserRegistration(int clientFd); void addChannel(Channel channel); + std::vector getChannels() const; void removeChannel(std::string channelName); }; diff --git a/includes/commands/JoinCommand.hpp b/includes/commands/JoinCommand.hpp new file mode 100644 index 0000000..1bfa2e8 --- /dev/null +++ b/includes/commands/JoinCommand.hpp @@ -0,0 +1,28 @@ +#ifndef JOIN_COMMAND_HPP +# define JOIN_COMMAND_HPP + +# include + +# include "ICommand.hpp" + +# include "Server.hpp" + +# include "libsUtils.hpp" +# include "utils.hpp" + +/** + * An ICommand implementation that is responsible for the binding and creation of a channel. + * + */ +class JoinCommand : public ICommand { + private: + std::map _channels; //key: channelName, value: channelPassword + + public: + JoinCommand(std::string channels, std::string keys); + ~JoinCommand(); + + void execute(Server &server, int fd); +}; + +#endif \ No newline at end of file diff --git a/includes/exceptions/exceptions.hpp b/includes/exceptions/exceptions.hpp index 42efda2..d2365b4 100644 --- a/includes/exceptions/exceptions.hpp +++ b/includes/exceptions/exceptions.hpp @@ -121,4 +121,44 @@ class PasswordMismatchException : public IRCException { PasswordMismatchException() : IRCException("464", ":Password incorrect") {} }; +/** + * This exception is thrown when an user attemps to join a channel that is invite-only and it was not invited. + */ +class InviteOnlyChanException : public IRCException { + public: + InviteOnlyChanException(std::string channelName) : IRCException("473", channelName + " :Cannot join channel (+i)") {} +}; + +/** + * This exception is thrown when an user attemps to join a channel where it was banned previously. + */ +class BannedFromChanException : public IRCException { + public: + BannedFromChanException(std::string channelName) : IRCException("474", channelName + " :Cannot join channel (+b)") {} +}; + +/** + * This exception is thrown when a channel password is incorrect. + */ +class BadChannelKeyException : public IRCException { + public: + BadChannelKeyException(std::string channelName) : IRCException("475", channelName + " :Cannot join channel (+k)") {} +}; + +/** + * This exception is thrown when the channel is full + */ +class ChannelIsFullException : public IRCException { + public: + ChannelIsFullException(std::string channelName) : IRCException("471", channelName + " :Cannot join channel (+l)") {} +}; + +/** + * This exception is thrown when a user joins too many channels. + */ +class TooManyChannelsException : public IRCException { + public: + TooManyChannelsException(std::string channelName) : IRCException("405", channelName + " :You have joined too many channels") {} +}; + #endif \ No newline at end of file diff --git a/includes/parser/CommandParser.hpp b/includes/parser/CommandParser.hpp index 6c121af..09fcdb0 100644 --- a/includes/parser/CommandParser.hpp +++ b/includes/parser/CommandParser.hpp @@ -9,6 +9,7 @@ # include "PassParser.hpp" # include "NickParser.hpp" # include "QuitParser.hpp" +# include "JoinParser.hpp" # include "libsUtils.hpp" @@ -26,7 +27,8 @@ enum Commands { TOPIC, MODE, KICK, - INVITE + INVITE, + OPER }; /** diff --git a/includes/parser/JoinParser.hpp b/includes/parser/JoinParser.hpp new file mode 100644 index 0000000..cf8f4a5 --- /dev/null +++ b/includes/parser/JoinParser.hpp @@ -0,0 +1,18 @@ +#ifndef JOIN_PARSER_HPP +# define JOIN_PARSER_HPP + +# include "IParser.hpp" + +# include "JoinCommand.hpp" + +# include "libsUtils.hpp" + +/** + * An IParser implementation that is responsible for parsing the JOIN command. + */ +class JoinParser : public IParser { + public: + ICommand *parse(const std::vector& tokens); +}; + +#endif \ No newline at end of file diff --git a/includes/utils.hpp b/includes/utils.hpp index 62f391a..0e7558d 100644 --- a/includes/utils.hpp +++ b/includes/utils.hpp @@ -6,7 +6,9 @@ */ # include +# include std::string trim(const std::string& str); +std::vector split(const std::string &s, char delim); #endif \ No newline at end of file diff --git a/src/Channel.cpp b/src/Channel.cpp index 786d8f7..1c7d699 100644 --- a/src/Channel.cpp +++ b/src/Channel.cpp @@ -68,6 +68,19 @@ std::vector::iterator Channel::findOper(std::string nickname) { return this->_operators.end(); } +/** + * This function aims to checks if the modes of the param matchs the ones of the channel. + * + * @param modesToCheck The modes to check. + */ +bool Channel::isModesSet(std::string modesToCheck) const { + for (size_t i = 0; i < modesToCheck.size(); i++) { + if (this->_modes.find(modesToCheck[i]) == std::string::npos) + return false; + } + return true; +} + /** * This function aims to get the name of the channel. * @@ -77,6 +90,15 @@ std::string Channel::getName() const{ return this->_name; } +/** + * This function aims to get the password of the channel. + * + * @return The password of the channel. + */ +std::string Channel::getPassword() const{ + return this->_password; +} + /** * This function aims to get the users of the channel. * @@ -173,6 +195,61 @@ bool Channel::checkPassword(std::string password) const { return this->_password == password; } +/** + * This function aims to check if the channel is invite only. + * + * @return `true` if the channel is invite only, `false` otherwise. + */ +bool Channel::isInviteOnly() const { + return isModesSet("i"); +} + +/** + * This function aims to check if the user is invited to the channel. + * + * @param nickname The nickname of the user. + * + * @return `true` if the user is invited, `false` otherwise. + */ +bool Channel::isUserInvited(std::string nickname) const { + return std::find(this->_inviteList.begin(), this->_inviteList.end(), nickname) != this->_inviteList.end(); +} + +/** + * This function aims to check if the user is banned from the channel. + * + * @param nickname The nickname of the user. + * @param username The username of the user. + * @param hostname The hostname of the user. + * + * @return `true` if the user is banned, `false` otherwise. + */ +bool Channel::isUserBanned(std::string nickname, std::string username, std::string hostname) const { + return std::find(this->_banList.begin(), this->_banList.end(), nickname) != this->_banList.end() + || std::find(this->_banList.begin(), this->_banList.end(), username) != this->_banList.end() + || std::find(this->_banList.begin(), this->_banList.end(), hostname) != this->_banList.end(); +} + +/** + * This function aims to check if the channel has a limit of users. + * + * @return `true` if the channel has a limit of users, `false` otherwise. + */ +bool Channel::hasLimit() const { + return this->_limit != NO_LIMIT; +} + +/** + * This function aims to check if the channel is full. + * + * @return `true` if the channel is full, `false` otherwise. + */ +bool Channel::isFull() const { + return this->_users.size() + this->_operators.size() == this->_limit; +} + + + /** * This function aims to add a user to the channel. * @@ -205,7 +282,7 @@ void Channel::removeUser(std::string nickname) { /** * This function aims to add an operator to the channel. * - * @param user The operator to add. + * @param nickname The operator to add. * * @throw `ChannelException` If the operator is already in the channel. */ diff --git a/src/Server.cpp b/src/Server.cpp index 69d3244..14eac9b 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -322,6 +322,15 @@ void Server::addChannel(Channel channel) { this->_channels.push_back(channel); } +/** + * This function aims to get the channels of the server. + * + * @return The channels of the server. + */ +std::vector Server::getChannels() const { + return this->_channels; +} + /** * This function aims to remove a channel from the server. * diff --git a/src/commands/JoinCommand.cpp b/src/commands/JoinCommand.cpp new file mode 100644 index 0000000..d7fc7d9 --- /dev/null +++ b/src/commands/JoinCommand.cpp @@ -0,0 +1,113 @@ +#include "JoinCommand.hpp" + +/** + * Constructs a new JoinCommand. + * + * @param channels The channels to join. + * @param keys The passwords to join the channels. + */ +JoinCommand::JoinCommand(std::string channels, std::string keys) { + std::vector channelsVec = split(channels, ','); + std::vector keysVec = split(keys, ','); + + for (size_t i = 0; i < channelsVec.size(); i++) { + if (channelsVec[i] == '' && keysVec[i] == '') + continue; + if (channelsVec[i] == '') {} + // throw IRCException(); + if (i < keysVec.size() && keysVec[i] != "") + this->_channels[channelsVec[i]] = keysVec[i]; + else + this->_channels[channelsVec[i]] = ""; + } + channelsVec.clear(); + keysVec.clear(); +} + +/** + * Destroys the JoinCommand. + */ +JoinCommand::~JoinCommand() { + this->_channels.clear(); +} + +/** ----------------TESTING------------- + * JOIN (#/&)channel password -> joins channel with password if it's correct + * JOIN #c1,#c2 password -> password for c1, none for c2 + * JOIN #c1,#c2 password, -> password for c1, none for c2 ??? + * JOIN #c1,#c2 password1,password2 -> password1 for c1, password2 for c2 + * JOIN #c1,#c2 ,password -> none for c1, password for c2?? + * JOIN #c1,,#c2 password1,,password2 -> ignored "channel" between commas + * JOIN #c1,,#c2 password1,password2 -> idk what happens here ¿¿¿??? + */ + +/** + * Executes the command JOIN. + * + * @param server The server where the command will be executed + * @param clientFd The socket file descriptor of the client + * + */ +void JoinCommand::execute(Server &server, int clientFd) { + User *user = server.getUserByFd(clientFd); + std::vector serverChannels = server.getChannels(); + + bool isOperator; + std::string nickname = user->getNickname(); + std::string username = user->getUsername(); + std::string hostname = user->getHostname(); + + std::string channelName; + std::string channelKey; + + for (std::vector::iterator it = this->_channels.begin(); it != this->_channels.end(); it++) { + channelName = it->first; + channelKey = it->second; + isOperator = false; + + //0. If channel[i] does not exist, create it + if (server.findChannel(channelName) == serverChannels.end()) { + Channel newChannel(channelName, *user); + server.addChannel(newChannel); + if (channelKey != "") + server.findChannel(channelName)->setPassword(channelKey); + isOperator = true; + } + + Channel *channel = server.findChannel(channelName); + + //1. Check if channel[i] is invite-only channel and if user is invited -> ERR_INVITEONLYCHAN + /*if (channel->isInviteOnly() && !channel->isUserInvited(nickname)) { + throw InviteOnlyChanException(channel->getName()); + }*/ + + //2. Check if user's nick/username/hostname is banned from channel[i] -> ERR_BANNEDFROMCHAN + /*if (channel->isUserBanned(nickname, username, hostname)) { + throw BannedFromChanException(channel->getName()); + }*/ + + //3. Check if password is correct if channel[i] is password-protected + if (channel->isPasswordSet() && channel->getPassword() != channelKey) { + throw BadChannelKeyException(channel->getName()); + } + + //4. Check if channel[i] has limit and if its full + if (channel->hasLimit() && channel->isFull()) { + throw ChannelIsFullException(channel->getName()); + } + + //5. Check if user has joined max channels + if (user->isUserInMaxChannels()) { + throw TooManyChannelsException(channel->getName()); + } + + isOperator ? channel->addOper(user) + : channel->addUser(user); + + user->addChannel(channel); + + //6. Send JOIN message to all users in channel[i] ¿? + //server.sendMessage(clientFd, RPL_TOPIC(channel->getName(), channel->getTopic())); + //server.sendMessage(clientFd, RPL_NAMREPLY(channel->getName(), channel->getAllUsers())); + } +} \ No newline at end of file diff --git a/src/parser/CommandParser.cpp b/src/parser/CommandParser.cpp index 891318e..79cf351 100644 --- a/src/parser/CommandParser.cpp +++ b/src/parser/CommandParser.cpp @@ -37,6 +37,8 @@ IParser* CommandParser::getParser(std::string command) { return new UserParser(); if (command == "NICK") return new NickParser(); + if (command == "JOIN") + return new JoinParser(); throw CommandNotFoundException(); } diff --git a/src/parser/JoinParser.cpp b/src/parser/JoinParser.cpp new file mode 100644 index 0000000..5552c3f --- /dev/null +++ b/src/parser/JoinParser.cpp @@ -0,0 +1,21 @@ +#include "JoinParser.hpp" + +/** + * Parses the JOIN command. + * + * The format of the JOIN command is as follows: + * + * Command: JOIN + * Parameters: {,} [{,}] + * + * @param tokens The parameters of the command. + * + * @throws `NeedMoreParamsException` if the number of arguments is less than the expected. + * @return The parsed command. + */ +ICommand *JoinParser::parse(const std::vector& tokens) { + + if (tokens.size() < 2) + throw NeedMoreParamsException("JOIN"); + return new JoinCommand(tokens[1], tokens[2]); +} \ No newline at end of file diff --git a/src/utils.cpp b/src/utils.cpp index a68ac57..f25cc8e 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -20,3 +20,21 @@ std::string trim(const std::string& str) { return std::string(it, rit.base()); } + +/** + * Splits the string by the delimiter. + * + * @param s The string to be split. + * @param delim The delimiter. + * + * @return The vector of strings. + */ +std::vector split(const std::string &s, char delim) { + std::vector elems; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +}