diff --git a/.vscode/settings.json b/.vscode/settings.json index 09b9350..c33d1d3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,10 @@ { "files.associations": { + "iosfwd": "cpp", + "sstream": "cpp", + "vector": "cpp", + "algorithm": "cpp", + "string": "cpp", "__bit_reference": "cpp", "__config": "cpp", "__debug": "cpp", @@ -13,8 +18,8 @@ "__split_buffer": "cpp", "__string": "cpp", "__threading_support": "cpp", + "__tree": "cpp", "__tuple": "cpp", - "algorithm": "cpp", "array": "cpp", "atomic": "cpp", "bit": "cpp", @@ -36,22 +41,20 @@ "functional": "cpp", "initializer_list": "cpp", "ios": "cpp", - "iosfwd": "cpp", "iostream": "cpp", "istream": "cpp", "iterator": "cpp", "limits": "cpp", "locale": "cpp", + "map": "cpp", "memory": "cpp", "mutex": "cpp", "new": "cpp", "optional": "cpp", "ostream": "cpp", "ratio": "cpp", - "sstream": "cpp", "stdexcept": "cpp", "streambuf": "cpp", - "string": "cpp", "string_view": "cpp", "system_error": "cpp", "tuple": "cpp", @@ -59,6 +62,9 @@ "typeinfo": "cpp", "unordered_map": "cpp", "utility": "cpp", - "vector": "cpp" + "typescript.tsserver.maxTsServerIdleTime": "600000", + "editor.folding": "false", + "cpp.memoryLimit": "8192", + "editor.renderWhitespace": "all" } } \ No newline at end of file diff --git a/README.md b/README.md index 13247fa..be51105 100644 --- a/README.md +++ b/README.md @@ -24,5 +24,15 @@ | Notice | The NOTICE command is used to send notices between users, as well as to send notices to channels. The difference between NOTICE and PRIVMSG is that automatic replies must never be sent in response to a NOTICE message. | +// ./IRC 6667 123456789 + · i: Set/remove Invite-only channel + + · t: Set/remove the restrictions of the TOPIC command to channel operators + + · k: Set/remove the channel key (password) + + · o: Give/take channel operator privilege + + · l: Set/remove the user limit to channel diff --git a/tests/Channel.cpp b/tests/Channel.cpp index 9b2e676..124bc19 100644 --- a/tests/Channel.cpp +++ b/tests/Channel.cpp @@ -1,4 +1,7 @@ -#include "Channel.hpp" +# include "Channel.hpp" +# include + + /* ** ------------------------------- CONSTRUCTOR -------------------------------- @@ -52,6 +55,18 @@ std::ostream & operator<<( std::ostream & o, Channel const & i ) ** --------------------------------- METHODS ---------------------------------- */ + +void Channel::prodcastToChannel(std::string &message, User *client) +{ + for (std::vector::iterator it = _users.begin(); it != _users.end(); it++) + { + User *user = *it; + if (user != client) + send(user->getFd(), message.c_str(), message.size(), 0); + } + +} + void Channel::addUser( User *user) { _users.push_back(user); @@ -62,6 +77,29 @@ void Channel::removeUser(const User *user) _users.erase(std::remove(_users.begin(), _users.end(), user), _users.end()); } +std::string Channel::getName() const +{ + return _name; +} + +std::vector Channel::getUsers() const +{ + return _users; +} + + +int Channel::count_membres() { + return _users.size(); +} + +std::string Channel::get_topic() { + // Replace with your actual implementation + return _topic; // assuming topic is a string variable holding the topic of the channel +} + +void Channel::setTopic(const std::string& newTopic) { + _topic = newTopic; +} /* ** --------------------------------- ACCESSOR --------------------------------- diff --git a/tests/Channel.hpp b/tests/Channel.hpp index 4eaec08..bb9a28a 100644 --- a/tests/Channel.hpp +++ b/tests/Channel.hpp @@ -5,23 +5,99 @@ # include # include # include "User.hpp" +#include +# include // Add this line class Channel { - + int userLimit; + bool invite_only; + bool topic_restricted; + std::string key; + std::set voice_users; + std::set operators; + std::map modes; + public: + void setInviteOnly() { + invite_only = true; + } + + void removeInviteOnly() { + invite_only = false; + } + + bool isInviteOnly() const { + return invite_only; + } + + void restrictTopic() { + topic_restricted = true; + } + + void removeTopicRestriction() { + topic_restricted = false; + } + + bool isTopicRestricted() const { + return topic_restricted; + } + + void setKey(const std::string& newKey) { + key = newKey; + } + + void removeKey() { + key.clear(); + } + + const std::string& getKey() const { + return key; + } + + void addOperator(const std::string& user) { + operators.insert(user); + } + + void removeOperator(const std::string& user) { + operators.erase(user); + } + + bool isOperator(const std::string& user) const { + return operators.find(user) != operators.end(); + } + + void addMode(std::string mode) { modes[mode] = true; } // Add this method + void removeMode(std::string mode) { modes.erase(mode); } // Add this method + + void setUserLimit(int limit) { userLimit = limit; } // Add this method + void removeUserLimit() { userLimit = -1; } // Add this method + int getUserLimit() { return userLimit; } // Add this method + Channel(); Channel(const std::string &name); Channel( Channel const & src ); void addUser( User *user); void removeUser(const User *user); + + std::string getName() const; + std::vector getUsers() const; + ~Channel(); Channel & operator=( Channel const & rhs ); + void prodcastToChannel(std::string &message, User *client); + + void prodcastToChannel(std::string &message); + + int count_membres(); + std::string get_topic(); + void setTopic(const std::string& newTopic); private: std::string _name; + std::string _topic; // new member variable for the topic std::vector _users; diff --git a/tests/IRC b/tests/IRC deleted file mode 100755 index 819bd1e..0000000 Binary files a/tests/IRC and /dev/null differ diff --git a/tests/Server.cpp b/tests/Server.cpp index 238026b..d845962 100644 --- a/tests/Server.cpp +++ b/tests/Server.cpp @@ -46,10 +46,39 @@ std::ostream & operator<<( std::ostream & o, Server const & i ) ** --------------------------------- METHODS ---------------------------------- */ -void Server::start() +void Server::printChannels() const +{ + if (_channels.empty()) + { + std::cout << "No channels exist." << std::endl; + return; + } + + std::cout << "Existing channels:" << std::endl; + for (std::map::const_iterator it = _channels.begin(); it != _channels.end(); ++it) { + const std::string& channelName = it->first; + const Channel& channel = it->second; + + std::cout << "- " << channelName << " ("; + + const std::vector& users = _channels.find(channelName)->second.getUsers(); + std::cout << users.size() << " user"; + if (users.size() != 1) std::cout << "s"; + std::cout << "): "; + + for (std::vector::size_type i = 0; i < users.size(); ++i) { + if (i > 0) std::cout << ", "; + std::cout << users[i]->getNickName(); + } + std::cout << std::endl; + } +} + +void Server::start(string port, string password) { std::cout << "Server started" << std::endl; - _createServer(); + _password = password; + _createServer(port); } @@ -87,37 +116,380 @@ void Server::_hundleNewConnection(int socketFd) throw std::runtime_error("Accept failed"); return ; } - _setNonBlocking(clientfd); - FD_SET(clientfd, &_masterSet); - _clients.push_back(clientfd); - _authenticated[clientfd] = true; - if (clientfd > _maxFd) + try + { + User* newUser = new User(clientfd, "random" + std::to_string(clientfd), "random"); + _users[clientfd] = newUser; + _setNonBlocking(clientfd); + FD_SET(clientfd, &_masterSet); + _clients.push_back(clientfd); + _authenticated[clientfd] = true; + if (clientfd > _maxFd) _maxFd = clientfd; cout << "New connection for the socket :" << clientfd << endl; + } + catch(const std::exception& e) + { + std::cerr << e.what() << '\n'; + } + } +void Server::_brodcastMessage(string ChannelName, string message) +{ + map::iterator it = _channels.find(ChannelName); + + if (it != _channels.end()) + { + Channel &channel = it->second; + vector users = channel.getUsers(); + for (size_t i = 0; i < users.size(); i++) + { + User *user = users[i]; + send(user->getFd(), message.c_str(), message.size(), 0); + } + } + +} + +// void Server::_findChannel(map channels) +// { +// if (channels) +// } + void Server::_hundleMessage(string message, int clientFd) { std::istringstream iss(message); - string channelName; + string command; User *user; - user = _users[clientFd]; + user = _users[clientFd];; + while (iss >> command) + { + if (command == "PASS") + { + string password; + iss >> password; + if (password != _password) + { + string response = "The password is incorrect \n"; + send(clientFd, response.c_str(), response.size(), 0); + break ; + } + } + else if (command == "NICK") + { + string nickName; + iss >> nickName; + string msgNick = ":" + user->getNickName() + "!" + user->getUserName() + "@" + "0.0.0.0.IP NICK :" + nickName + "\r\n"; + user->setNickName(nickName); + send(clientFd, msgNick.c_str(), msgNick.size(), 0); + } + else if (command == "USER") + { + string userName, realName, ipAdresse; + iss >> userName; + user->setUserName(userName); + iss >> realName; + iss >> ipAdresse; + user->setIpAdresse(ipAdresse); + } + else if (command == "JOIN") + { + string channelName; + iss >> channelName; + _channels[channelName] = Channel(channelName); + bool op = (_channels.find(channelName) == _channels.end()); + Channel& channel = _channels[channelName]; + if (channel.isInviteOnly()) + { + string response = "invite only"; + send(clientFd, response.c_str(), response.size(), 0); + } + if (channel.getUserLimit()) + { + if (channel.count_membres() >= channel.getUserLimit()) + { + string response = "Channel is full"; + send(clientFd, response.c_str(), response.size(), 0); + } + } + // if (channel.getKey()) + // { + // string key; + // iss >> key; + // if (key != channel.getKey()) + // { + // string response = "Wrong key"; + // send(clientFd, response.c_str(), response.size(), 0); + // } + // } - string command; - iss >> command; - if (command == "join" || command == "JOIN") - { - _channels[channelName] = Channel(channelName); - Channel& channel = _channels[channelName]; + channel.addUser(user); + string joinMsg = ":" + user->getNickName() + "!" + user->getUserName() + "@" + user->getIpAdresse() + ".IP JOIN " + channelName + " * :realname\r\n"; + send(clientFd, joinMsg.c_str(), joinMsg.size(), 0); + } + else if (command == "PRIVMSG") + { + string recipent; + iss >> recipent; + if (recipent[0] == '#') + { + if (_channels.find(recipent) != _channels.end()) + { + string msg; + iss >> msg; + Channel &channel = _channels[recipent]; + vector users = channel.getUsers(); - // Add the user to the channel - channel.addUser(user); - // Send join confirmation to the user - std::string joinMsg = ":" "@localhost JOIN " + channelName + "\r\n"; - send(user->getFd(), joinMsg.c_str(), joinMsg.length(), 0); + for (vector::iterator it = users.begin(); it != users.end(); it++) + { + User *user = *it; + string message = ":" + user->getNickName() + "!" + user->getUserName() + "@" + user->getIpAdresse() + ".IP PRIVMSG " + recipent + " " + msg.substr(1) + "\r\n"; + send(user->getFd(), message.c_str(), message.size(), 0); + } + } + } + else + { + + map::iterator it = _users.begin(); + while (it != _users.end()) + { + User *use = it->second; + if(use->getNickName() == recipent) + { + string message; + iss >> message; + string prvMsg = ":" + user->getNickName() + "!" + user->getUserName() + "@" + user->getIpAdresse() + ".IP PRIVMSG " + recipent + " " + message + "\r\n"; + send(use->getFd(), prvMsg.c_str(), prvMsg.size(), 0); + } + ++it; + } + } + } + else if (command == "WHO") + { + string channelName; + iss >> channelName; + + if (_channels.find(channelName) != _channels.end()) + { + Channel &channel = _channels[channelName]; + vector users = channel.getUsers(); + string msg = ":127.0.0.1 353 " + user->getNickName() + " = " + channelName + " :"; + for(vector::iterator it = users.begin() ; it != users.end() ; ++it) + { + User *use = *it; + msg = msg + use->getNickName() + " "; + } + msg = msg + "\r\n"; + send(user->getFd(), msg.c_str(), msg.size(), 0); + for(vector::iterator it = users.begin() ; it != users.end() ; ++it) + { + User *use = *it; + string sendNickName = ":127.0.0.1 354 " + user->getNickName() + " 152 " + channelName + " " + use->getNickName() + " " + use->getIpAdresse() + ".IP * .127.0.0.1"; + + } + string endList; + endList = ":127.0.0.1 315 " + user->getNickName() + " " + channelName + " :End of /WHO list.\r\n"; + send(user->getFd(), endList.c_str(), endList.size(), 0); + } + } + else if (command == "KICK") + { + string channelName, nickName; + iss >> channelName; + iss >> nickName; + if (_channels.find(channelName) != _channels.end()) + { + Channel &channel = _channels[channelName]; + vector users = channel.getUsers(); + for(vector::iterator it = users.begin() ; it != users.end() ; ++it) + { + User *use = *it; + if (use->getNickName() == nickName) + { + string kickMsg = ":" + user->getNickName() + "!" + user->getUserName() + "@" + user->getIpAdresse() + ".IP KICK " + channelName + " " + nickName + " :Kicked by " + user->getNickName() + "\r\n"; + send(use->getFd(), kickMsg.c_str(), kickMsg.size(), 0); + channel.removeUser(use); + } + } + } + } + else if (command == "QUIT") + { + string msg; + iss >> msg; + string quitMsg = ":" + user->getNickName() + "!" + user->getUserName() + "@" + user->getIpAdresse() + ".IP QUIT :" + msg + "\r\n"; + send(user->getFd(), quitMsg.c_str(), quitMsg.size(), 0); + close(user->getFd()); + FD_CLR(user->getFd(), &_masterSet); + _clients.erase(std::remove(_clients.begin(),_clients.end(), user->getFd()), _clients.end()); + _authenticated.erase(user->getFd()); + } + else if (command == "PART") + { + string channelName; + iss >> channelName; + if (_channels.find(channelName) != _channels.end()) + { + Channel &channel = _channels[channelName]; + vector users = channel.getUsers(); + for(vector::iterator it = users.begin() ; it != users.end() ; ++it) + { + User *use = *it; + if (use->getNickName() == user->getNickName()) + { + string partMsg = ":" + user->getNickName() + "!" + user->getUserName() + "@" + user->getIpAdresse() + ".IP PART " + channelName + " :Leaving channel\r\n"; + } + } + } + } + else if (command == "OPER") + { + string userName, password; + iss >> userName; + iss >> password; + if (userName == "admin" && password == "admin") + { + user->setOperator(true); + string operMsg = ": " + user->getNickName() + "!" + user->getUserName() + "@" + + user->getIpAdresse() + ".IP MODE " + user->getNickName() + " +o\r\n"; + send(user->getFd(), operMsg.c_str(), operMsg.size(), 0); + } + } + else if (command == "LIST") + { + list(*user); + } + else if (command == "TOPIC") + { + string channelName; + iss >> channelName; + if (_channels.find(channelName) != _channels.end()) + { + Channel &channel = _channels[channelName]; + if (channel.isTopicRestricted() && !channel.isOperator(user->getNickName())) + { + string response = "403 " + user->getNickName() + " :You're not an operator of this channel.\r\n"; + send(user->getFd(), response.c_str(), response.size(), 0); + } + else { + string newTopic; + iss >> newTopic; + channel.setTopic(newTopic); + + string topicMsg = ":" + user->getNickName() + "!" + user->getUserName() + "@" + user->getIpAdresse() + ".IP TOPIC " + channelName + " :" + newTopic + "\r\n"; + } + } + } + else if (command == "MODE") + { + string target; + string mode; + iss >> target >> mode; + std::cout << mode << std::endl; + + if (_channels.find(target) != _channels.end()) // target is a channel + { + std::cout << "ffff" << target << std::endl; + + Channel &channel = _channels[target]; + if (mode[0] == '+') // adding a mode + { + if (mode[1] == 'l') // setting a user limit + { + int limit; + iss >> limit; + channel.setUserLimit(limit); + } + else if (mode[1] == 'i') // setting a channel to invite-only + { + channel.setInviteOnly(); + } + else if (mode[1] == 't') // restricting TOPIC command to operators + { + channel.restrictTopic(); + } + else if (mode[1] == 'k') // setting a channel key + { + std::string newKey; + iss >> newKey; + channel.setKey(newKey); + } + else if (mode[1] == 'o') // giving a user operator privileges + { + std::string user; + iss >> user; + channel.addOperator(user); + } + else + { + channel.addMode(mode.substr(1)); + } + } + else if (mode[0] == '-') // removing a mode + { + if (mode[1] == 'l') // removing a user limit + { + channel.removeUserLimit(); + } + else if (mode[1] == 'i') // removing invite-only status from a channel + { + channel.removeInviteOnly(); + } + else if (mode[1] == 't') // removing restriction on TOPIC command + { + channel.removeTopicRestriction(); + } + else if (mode[1] == 'k') // removing a channel key + { + channel.removeKey(); + } + else if (mode[1] == 'o') // taking away a user's operator privileges + { + std::string user; + iss >> user; + channel.removeOperator(user); + } + else + { + channel.removeMode(mode.substr(1)); + } + } + } + // ... existing code ... + } - } +} + } + + void Server::list(User& user) +{ + send_reply(321, user, "Channel", "Users Name"); + + for (map::iterator it = _channels.begin(); it != _channels.end(); it++) + { + std::stringstream ss; + ss << "#" << it->first << " " << it->second.count_membres(); + send_reply(322, user, ss.str(), it->second.get_topic()); + } + + send_reply(323, user, "", "End of /LIST"); +} + +void Server::send_reply(int code, User& client, const std::string& arg1, const std::string& arg2) +{ + std::stringstream ss; + ss << ":" << code << " " << arg1 << " " << arg2 << "\r\n"; + std::string msg = ss.str(); + + // Assuming Client has a method getFd() that returns a file descriptor + int fd = client.getFd(); + + // Send the message to the client + send(fd, msg.c_str(), msg.size(), 0); } void Server::_hundleClientMessage(int ClientFd) @@ -137,19 +509,13 @@ void Server::_hundleClientMessage(int ClientFd) } else { - str[bytesReceived] = '\0'; - // _authentification(ClientFd, ) - // std::cout << "Received: " << str << std::endl; - if (send(ClientFd, str, bytesReceived, 0) == -1) - { - std::cerr << "Failed to send message" << std::endl; - } + // cout MM / + _hundleMessage(str, ClientFd); } - // _authentification() } -void Server::_createServer() +void Server::_createServer(string port) { int socketFd; struct sockaddr_in serverAdresse; @@ -158,10 +524,10 @@ void Server::_createServer() if (socketFd < 0) { throw std::runtime_error("Socket creation failed"); } - int status=setsockopt(socketFd, SOL_SOCKET,SO_REUSEADDR , &op, sizeof(op)); + int status = setsockopt(socketFd, SOL_SOCKET,SO_REUSEADDR , &op, sizeof(op)); memset(&serverAdresse, 0, sizeof(serverAdresse)); serverAdresse.sin_family = AF_INET; - serverAdresse.sin_port = htons(6667); + serverAdresse.sin_port = htons(strtod(port.c_str(), NULL)); serverAdresse.sin_addr.s_addr = inet_addr("127.0.0.1"); bind(socketFd, (const struct sockaddr *)&serverAdresse, sizeof(serverAdresse)); listen(socketFd, 20); @@ -169,7 +535,6 @@ void Server::_createServer() FD_ZERO(&_masterSet); FD_SET(socketFd, &_masterSet); _maxFd = socketFd; - cout << "Server started" << endl; while (1) { _readSet = _masterSet; @@ -182,10 +547,9 @@ void Server::_createServer() { int clientFd = _clients[i]; if (FD_ISSET(clientFd, &_readSet)) - { _hundleClientMessage(clientFd); - } } + // printChannels(); } close(socketFd); } diff --git a/tests/Server.hpp b/tests/Server.hpp index c66a865..820801e 100644 --- a/tests/Server.hpp +++ b/tests/Server.hpp @@ -29,6 +29,7 @@ using std::map; using std::vector; + class Server { @@ -37,27 +38,33 @@ class Server Server(); Server( Server const & src ); ~Server(); - void start(); + void start(string port, string password); + // void send_err(int code, User &sender, std::string arg1, std::string msg); Server & operator=( Server const & rhs ); + void list(User& user); + void send_reply(int code, User& client, const std::string& arg1, const std::string& arg2); private: fd_set _masterSet; fd_set _readSet; map _users; map _authenticated; - // std::map _users; map _channels; vector _clients; int _maxFd; - - void _createServer(); + string _password; + + void _createServer(string port); void _setNonBlocking(int socketFd); void _hundleNewConnection(int socketFd); void _hundleClientMessage(int ClientFd); void _hundleMessage(string message, int clientFd); - // void _authentification(int socketFd, const string &nickName, const string &userName); + void _sendMessage(); + void _brodcastMessage(string ChannelName, string message); + void _findChannel(); + void printChannels() const ; }; std::ostream & operator<<( std::ostream & o, Server const & i ); diff --git a/tests/User.cpp b/tests/User.cpp index d046b7d..0075fa2 100644 --- a/tests/User.cpp +++ b/tests/User.cpp @@ -4,7 +4,7 @@ ** ------------------------------- CONSTRUCTOR -------------------------------- */ -User::User() +User::User() : _fd(0) , _userName("user"), _nickName("nick"), _ipAdresse("0.0.0.0"), _operators(false) { } @@ -12,11 +12,6 @@ User::User(int fd, const string &userName, const string &nickName) : _fd(fd), _u { } -User::User( const User & src ) -{ -} - - /* ** -------------------------------- DESTRUCTOR -------------------------------- */ @@ -37,6 +32,8 @@ User & User::operator=( User const & rhs ) this->_fd = rhs.getFd(); this->_nickName = rhs.getNickName(); this->_userName = rhs.getUserName(); + this->_userName = rhs.getIpAdresse(); + this->_operators = rhs.getOperator(); } return *this; } @@ -67,6 +64,17 @@ string User::getNickName() const return _nickName; } +string User::getIpAdresse() const +{ + return _ipAdresse; +} + +bool User::getOperator() const +{ + return _operators; +} + + void User::setFd(int &fd) { _fd = fd; @@ -82,6 +90,15 @@ void User::setNickName(const string &nickName) _nickName = nickName; } +void User::setIpAdresse(const string &ipAdresse) +{ + _ipAdresse = ipAdresse; +} + +void User::setOperator(bool op) +{ + _operators = op; +} /* ** --------------------------------- ACCESSOR --------------------------------- diff --git a/tests/User.hpp b/tests/User.hpp index d85d616..de1dcee 100644 --- a/tests/User.hpp +++ b/tests/User.hpp @@ -24,11 +24,18 @@ class User /// SETTERS AND GETTERS string getUserName() const; string getNickName() const; + string getIpAdresse() const; + bool getOperator() const; int getFd() const; + + int get_socket(); + // void setFd(int &fd); void setUserName(const string &userName); void setNickName(const string &nickName); + void setIpAdresse(const string &ipAdresse); + void setOperator(bool op); User & operator=( User const & rhs ); @@ -36,6 +43,8 @@ class User int _fd; string _userName; string _nickName; + std::string _ipAdresse; + bool _operators; }; diff --git a/tests/client.cpp b/tests/client.cpp index f93c889..5178675 100644 --- a/tests/client.cpp +++ b/tests/client.cpp @@ -37,7 +37,7 @@ void communicate(int client_fd) { std::string message; std::getline(std::cin, message); - if (send(client_fd, message.c_str(), message.length(), 0) == -1) { + if (send(client_fd, message.c_str(), message.size(), 0) == -1) { std::cerr << "Failed to send message" << std::endl; break; } diff --git a/tests/main.cpp b/tests/main.cpp index 79d3321..871c524 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,11 +1,18 @@ #include "Server.hpp" -int main() +int main(int ac, char **av) { + if (ac != 3) + { + cerr << "Must be run as follows: : ./ircserv " << endl; + return 1; + } try { + string port = av[1]; + string password = av[2]; Server srv; - srv.start(); + srv.start(av[1], av[2]); } catch(const std::exception& e) {