diff --git a/incs/ircserv.hpp b/incs/ircserv.hpp index 8729b07..382f0fd 100644 --- a/incs/ircserv.hpp +++ b/incs/ircserv.hpp @@ -27,6 +27,8 @@ #define GRE "\e[1;32m" #define YEL "\e[1;33m" +std::string sanitizeString(const std::string& input); + class ArgumentValidator { public: static void validate(int count, char** args); diff --git a/srcs/commands/NickCommand.cpp b/srcs/commands/NickCommand.cpp index cca05c2..2987fd8 100644 --- a/srcs/commands/NickCommand.cpp +++ b/srcs/commands/NickCommand.cpp @@ -1,4 +1,39 @@ #include "../../incs/ircserv.hpp" +#include + +static const size_t MAX_NICKNAME_LENGTH = 9; + + +static bool isValidNicknameStart(char c); +static bool isValidNicknameChar(char c); +static bool isControlChar(char c); + +static bool validateNickname(Server *server, const std::string &nickname, int clientFd) { + if (nickname.length() > MAX_NICKNAME_LENGTH) { + std::ostringstream oss; + oss << "432 * " << nickname << " :Nickname too long (max " << MAX_NICKNAME_LENGTH << " chars)\r\n"; + server->SendToClient(clientFd, oss.str()); + return false; + } + + if (!isValidNicknameStart(nickname[0])) { + server->SendToClient(clientFd, "432 * " + nickname + " :Nickname must start with letter or special char\r\n"); + return false; + } + + for (size_t i = 0; i < nickname.length(); i++) { + if (!isValidNicknameChar(nickname[i])) { + if (isControlChar(nickname[i])) { + server->SendToClient(clientFd, "432 * " + nickname + " :Nickname contains invalid characters\r\n"); + } else { + server->SendToClient(clientFd, "432 * " + nickname + " :Erroneous nickname\r\n"); + } + return false; + } + } + + return true; +} void Parser::handleNick(Server *server, const std::string &nickname, int clientFd) { Client* client = server->FindClientByFd(clientFd); @@ -9,21 +44,39 @@ void Parser::handleNick(Server *server, const std::string &nickname, int clientF return; } - for (size_t i = 0; i < nickname.length(); i++) { - char c = nickname[i]; - if (!isalnum(c) && c != '-' && c != '_' && c != '[' && c != ']' && c != '{' && c != '}' && c != '\\' && c != '|' && c != '`' && c != '^') { - server->SendToClient(clientFd, "432 * " + nickname + " :Erroneous nickname\r\n"); - return; - } + std::string sanitizedNickname = sanitizeString(nickname); + + if (!validateNickname(server, sanitizedNickname, clientFd)) { + return; } - if (server->IsNicknameInUse(nickname)) { - server->SendToClient(clientFd, "433 * " + nickname + " :Nickname is already in use\r\n"); + if (server->IsNicknameInUse(sanitizedNickname)) { + server->SendToClient(clientFd, "433 * " + sanitizedNickname + " :Nickname is already in use\r\n"); return; } - client->SetNickname(nickname); - server->SendToClient(clientFd, "Nickname set to " + nickname + "\r\n"); + std::string oldNickname = client->getNickname(); + client->SetNickname(sanitizedNickname); + + if (oldNickname.empty()) { + server->SendToClient(clientFd, "001 " + sanitizedNickname + " :Welcome to the IRC server\r\n"); + } else { + server->SendToClient(clientFd, ":" + oldNickname + " NICK " + sanitizedNickname + "\r\n"); + } server->CheckClientAuthentication(client); -} \ No newline at end of file +} + +static bool isValidNicknameStart(char c) { + return isalpha(c) || c == '_' || c == '[' || c == '{' || + c == '\\' || c == '|' || c == '`' || c == '^'; +} + +static bool isValidNicknameChar(char c) { + return isalnum(c) || c == '-' || c == '_' || c == '[' || c == ']' || + c == '{' || c == '}' || c == '\\' || c == '|' || c == '`' || c == '^'; +} + +static bool isControlChar(char c) { + return c < 32 || c == 127; +} diff --git a/srcs/commands/PrivmsgCommand.cpp b/srcs/commands/PrivmsgCommand.cpp index 454f790..a9ff470 100644 --- a/srcs/commands/PrivmsgCommand.cpp +++ b/srcs/commands/PrivmsgCommand.cpp @@ -27,11 +27,12 @@ void Parser::handlePrivmsg(Server *server, const std::string &target, const std: return; } - std::string formattedMessage = ":" + sender->getNickname() + " PRIVMSG " + target + " :" + message + "\r\n"; + std::string sanitizedMessage = sanitizeString(message); + std::string formattedMessage = ":" + sender->getNickname() + " PRIVMSG " + target + " :" + sanitizedMessage + "\r\n"; channel->broadcastMessage(formattedMessage, sender, server); // Bot stores last 100 messages if (channel->isBotActive()) { - channel->storeMessage("[" + sender->getNickname() + "] " + message); + channel->storeMessage("[" + sender->getNickname() + "] " + sanitizedMessage); // Bot responds to !help if (message == "!help") { std::string helpMsg = ":BOT!bot@server PRIVMSG " + target + " :\nAvailable bot commands:\n!help\n!datenow\n!weather \n!history\r\n"; @@ -88,7 +89,8 @@ void Parser::handlePrivmsg(Server *server, const std::string &target, const std: return; } - std::string formattedMessage = ":" + sender->getNickname() + " PRIVMSG " + target + " :" + message + "\r\n"; + std::string sanitizedMessage = sanitizeString(message); + std::string formattedMessage = ":" + sender->getNickname() + " PRIVMSG " + target + " :" + sanitizedMessage + "\r\n"; server->SendToClient(recipient->GetFd(), formattedMessage); } } @@ -109,6 +111,12 @@ bool isValidOperation(Server *server, Client *sender, const std::string &target, server->SendToClient(clientFd, "412 :No text to send\r\n"); return false; } + + if (message.length() > 512) { + server->SendToClient(clientFd, "412 :Message too long (max 512 chars)\r\n"); + return false; + } + return true; } diff --git a/srcs/commands/UserCommand.cpp b/srcs/commands/UserCommand.cpp index 627bb33..faff141 100644 --- a/srcs/commands/UserCommand.cpp +++ b/srcs/commands/UserCommand.cpp @@ -14,9 +14,17 @@ void Parser::handleUser(Server *server, const std::string &username, const std:: return; } - client->SetUsername(username); - client->SetRealname(realname); - server->SendToClient(clientFd, "Username set to " + username + "\r\n"); + std::string sanitizedUsername = sanitizeString(username); + std::string sanitizedRealname = sanitizeString(realname); + + if (sanitizedUsername.length() > 10) { + server->SendToClient(clientFd, "461 * USER :Username too long (max 10 chars)\r\n"); + return; + } + + client->SetUsername(sanitizedUsername); + client->SetRealname(sanitizedRealname); + server->SendToClient(clientFd, "Username set to " + sanitizedUsername + "\r\n"); server->CheckClientAuthentication(client); } \ No newline at end of file diff --git a/srcs/ircserv.cpp b/srcs/ircserv.cpp index 6c8c8c5..35fb405 100644 --- a/srcs/ircserv.cpp +++ b/srcs/ircserv.cpp @@ -1,6 +1,8 @@ #include "../incs/ircserv.hpp" #include #include +#include +#include int main(int argc, char **argv) { ServerController controller; @@ -19,9 +21,17 @@ void ServerController::execute(int argc, char** argv) { void ServerController::prepareExecution(int argc, char** argv) { ArgumentValidator::validate(argc, argv); + + char* endptr; + long portValue = std::strtol(argv[1], &endptr, 10); + if (*endptr != '\0' || endptr == argv[1]) { + throw std::runtime_error("Porta deve ser um número válido."); + } + + this->validatePort(static_cast(portValue)); SignalConfigurator::configure(); introduce(); - this->server.ServerInit(std::atoi(argv[1]), argv[2]); + this->server.ServerInit(static_cast(portValue), argv[2]); } void ServerController::validatePort(int portValue) { @@ -34,6 +44,11 @@ void ArgumentValidator::validate(int count, char** args) { if (count != 3) { throw std::runtime_error("Uso: " + std::string(args[0]) + " "); } + + std::string password(args[2]); + if (password.empty() || password.find_first_not_of(" \t\r\n") == std::string::npos) { + throw std::runtime_error("Senha não pode estar vazia."); + } } void SignalConfigurator::configure() { @@ -48,4 +63,20 @@ void ServerController::handleError(const std::exception& e) { void ServerController::announceShutdown() { std::cout << "O Servidor Foi Fechado!" << std::endl; +} + +bool isControlChar(char c) { + return c < 32 || c == 127; +} + +std::string sanitizeString(const std::string& input) { + std::string result = input; + + result.erase(std::remove_if(result.begin(), result.end(), isControlChar), result.end()); + + if (result.length() > 512) { + result = result.substr(0, 512); + } + + return (result); } \ No newline at end of file