From fbc9b96ff24285f3e6744d758e5223f7cbd5a30e Mon Sep 17 00:00:00 2001 From: jhervoch Date: Thu, 13 Nov 2025 11:13:43 +0100 Subject: [PATCH 01/10] update : merge changes of eval branch --- includes/channels/Channel.hpp | 43 +++++-- includes/commands/Bot.hpp | 3 +- includes/commands/Mode.hpp | 12 +- includes/consts.hpp | 11 +- includes/parsing/Parser.hpp | 216 ++++++++++++++++++---------------- includes/reply_codes.hpp | 5 +- includes/server/Config.hpp | 1 + srcs/channels/Channel.cpp | 11 ++ srcs/clients/Client.cpp | 18 ++- srcs/commands/Bot.cpp | 68 ++++++++--- srcs/commands/Invite.cpp | 2 +- srcs/commands/Join.cpp | 3 - srcs/commands/Kick.cpp | 7 ++ srcs/commands/Mode.cpp | 121 +++++++++++-------- srcs/commands/Nick.cpp | 4 +- srcs/commands/Part.cpp | 10 +- srcs/commands/Privmsg.cpp | 10 +- srcs/commands/Quit.cpp | 2 + srcs/commands/Topic.cpp | 17 +-- srcs/main.cpp | 3 +- srcs/parsing/Parser.cpp | 18 ++- srcs/server/Config.cpp | 3 +- srcs/server/Server.cpp | 5 +- 23 files changed, 371 insertions(+), 222 deletions(-) diff --git a/includes/channels/Channel.hpp b/includes/channels/Channel.hpp index da4026ca..a51dbf96 100644 --- a/includes/channels/Channel.hpp +++ b/includes/channels/Channel.hpp @@ -13,6 +13,8 @@ #include "ReplyHandler.hpp" #include "reply_codes.hpp" +#include +#include #include #include #include @@ -173,6 +175,8 @@ class Channel * @param client * @return true * @return false + * @pre return from erase is ignored in case client is not invited, so it safe to use in that case. + * Yet still better if the caller function has checked that client is invited */ bool remove_from_invited_list(Client& client); @@ -214,6 +218,8 @@ class Channel * * @param client * @return false if member was not in the channel + * @pre return from erase is ignored in case client is not an member, so it safe to use in that case. + * Yet still better if the caller function has checked that client is member */ bool remove_member(Client& client); @@ -230,7 +236,8 @@ class Channel * @brief removes op status from client * * @param client - * @pre caller function should have checked that client is operator + * @pre return from erase is ignored in case client is not an operator, so it safe to use in that case. + * Yet still better if the caller function has checked that client is operator */ void remove_operator(Client& client); @@ -277,6 +284,20 @@ class Channel */ size_t get_nb_members() const; + /** + * @brief Get the message history + * + * @return const std::queue + */ + const std::deque get_history() const; + + /** + * @brief adds a message to channel history + * @details if message number reaches MAX_MESSAGES_HISTORY, the oldest one is removed + * @param message + */ + void add_message_to_history(const std::string& message); + /** * @brief Get a list of #Client members * @@ -331,6 +352,7 @@ class Channel * @return false otherwise */ static bool is_valid_channel_name(const std::string& name); + /** * @brief * @details cf [grammar](https://datatracker.ietf.org/doc/html/rfc2812#section-2.3.1) @@ -344,15 +366,16 @@ class Channel static bool is_valid_channel_key(const std::string& key); private: - std::string _name; - std::string _topic; - std::string _key; - unsigned short _mode; - int _userLimit; // -1 if -l not set - std::set _members; - std::set _invites; - std::set _operators; - std::set _banList; + std::string _name; + std::string _topic; + std::string _key; + unsigned short _mode; + int _userLimit; // -1 if -l not set + std::set _members; + std::set _invites; + std::set _operators; + std::set _banList; + std::deque _lastMessages; Channel(); }; diff --git a/includes/commands/Bot.hpp b/includes/commands/Bot.hpp index 520bbdcd..eb5b99be 100644 --- a/includes/commands/Bot.hpp +++ b/includes/commands/Bot.hpp @@ -75,6 +75,7 @@ class Bot : public ICommand std::vector _targetClients; std::string _subcommand; std::string _prompt; + std::string _channelHistory; TcpSocket _socket; Bot(); @@ -118,4 +119,4 @@ class Bot : public ICommand bool _register_bot(Server& s, TcpSocket& so); }; -#endif \ No newline at end of file +#endif diff --git a/includes/commands/Mode.hpp b/includes/commands/Mode.hpp index 503ee173..6932618a 100644 --- a/includes/commands/Mode.hpp +++ b/includes/commands/Mode.hpp @@ -104,10 +104,7 @@ class Mode : public ICommand * @param validPositiveModes * @param validNegativeModes */ - void _mode_with_noparams(Channel* channel, - std::string& currentMode, - std::string& validPositiveModes, - std::string& validNegativeModes); + void _mode_with_noparams(Channel* channel, std::string& currentMode, std::string& validModes); /** * @brief use case of +k mode * build mode is + mode args @@ -126,7 +123,7 @@ class Mode : public ICommand Parser& p, std::string& currentMode, std::string& currentParam, - std::string& validPositiveModes, + std::string& validModes, std::string& validModesParams); /** * @brief use case of +l mode @@ -144,7 +141,7 @@ class Mode : public ICommand Parser& p, std::string& currentMode, std::string& currentParam, - std::string& validPositiveModes, + std::string& validModes, std::string& validModesParams); /** * @brief use case of +o mode @@ -167,8 +164,7 @@ class Mode : public ICommand Parser& p, std::string& currentMode, std::string& currentParam, - std::string& validPositiveModes, - std::string& validNegativeModes, + std::string& validModes, std::string& validModesParams); std::string _channelName; std::queue _modeQueue; diff --git a/includes/consts.hpp b/includes/consts.hpp index b68f469c..95c1f9b3 100644 --- a/includes/consts.hpp +++ b/includes/consts.hpp @@ -3,9 +3,9 @@ * @brief constants used throughout IRC server program * @version 0.1 * @date 2025-10-25 - * + * * @copyright Copyright (c) 2025 - * + * */ #ifndef CONSTS_HPP #define CONSTS_HPP @@ -32,8 +32,8 @@ #define FORBIDEN_CHAR_BOT_PROMPT "|;" #define FORBIDEN_CHAR_CHAN_NAME "\x00\x07\x0D\x0A\x20\x2C\x3A" // NOLINT(clang-diagnostic-null-character) #define FORBIDEN_CHAR_CHAN_KEY "\x00\x09\x0A\x0B\x0C\x0D\x20" // NOLINT(clang-diagnostic-null-character) -#define FORBIDDEN_CHAR_SERVER_KEY "\x00\x09\x0A\x0B\x0D\x20" // NOLINT(clang-diagnostic-null-character) NUL, HT, LF, VT, CR, space -#define FORBIDEN_CHAR_USER "\x00\x0A\x0D\x20\x40" // NOLINT(clang-diagnostic-null-character) +#define FORBIDDEN_CHAR_SERVER_KEY "\x00\x09\x0A\x0B\x0D\x20" // NOLINT(clang-diagnostic-null-character) NUL, HT, LF, VT, CR, space +#define FORBIDEN_CHAR_USER "\x00\x0A\x0D\x20\x40" // NOLINT(clang-diagnostic-null-character) #define NUMBER_FORB_CCNK 7 #define NUMBER_FORB_CSK 6 #define NUMBER_FORB_CCU 5 @@ -47,7 +47,8 @@ #define DYNAMIC_PORT_MIN 49152 #define NICKNAME_MAX_LEN 9 #define NB_AVAILABLE_CMD 15 -#define NB_AVAILABLE_BOT_SUBCMD 2 +#define MAX_MESSAGES_HISTORY 10 +#define NB_AVAILABLE_BOT_SUBCMD 3 #define USERS_PER_LINE 10 #define CHANMODE_INIT 0b00000 #define CHANMODE_KEY 0b00001 diff --git a/includes/parsing/Parser.hpp b/includes/parsing/Parser.hpp index 71b43dea..f8a51087 100644 --- a/includes/parsing/Parser.hpp +++ b/includes/parsing/Parser.hpp @@ -3,9 +3,9 @@ * @brief Parser class * @version 0.1 * @date 2025-10-25 - * + * * @copyright Copyright (c) 2025 - * + * */ #ifndef PARSER_HPP #define PARSER_HPP @@ -28,7 +28,7 @@ class ReplyHandler; /** * @brief Utility class to simplify command parsing * @class Parser - * + * */ class Parser { @@ -39,14 +39,14 @@ class Parser /** * @brief Get the server object - * - * @return Server* + * + * @return Server* */ Server* get_server(); /** * @brief Get the client associated with Parser - * - * @return Client* + * + * @return Client* */ Client* get_client(); @@ -54,195 +54,203 @@ class Parser /** * @brief utility method to parse a message * @details can be called multiple times to return different parts of a message - * @param params - * @param function - * @return std::string + * @param params + * @param function + * @return std::string */ std::string format_parameter(std::string& params, Checker function); /** * @brief get an argument - * - * @param params - * @return std::string + * + * @param params + * @return std::string */ std::string from_arg(std::string& params); /** * @brief get trailing part of a message - * - * @param params - * @return std::string + * + * @param params + * @return std::string */ std::string from_trailing(std::string& params); + /** + * @brief get remaining args as a string + * + * @param params + * @return std::string + */ + std::string from_remaining_args(std::string& params); + /** * @brief converts args to a vector of string - * - * @param params - * @return std::vector + * + * @param params + * @return std::vector */ - std::vector to_vector(std::string& params); + std::vector to_vector(std::string& params); /** * @brief convert matching key and values arguments to a map * @details `#chan1,#chan2 key1,key2` will be mapped to key values `#chan1,key1` .. - * @param key - * @param value - * @return std::map + * @param key + * @param value + * @return std::map */ std::map to_map(std::string& key, std::string& value); /** * @brief check password validity - * - * @param password + * + * @param password * @return true if password is correct - * @return false + * @return false */ bool correct_password(std::string& password); /** * @brief check nickname validity - * - * @param nickname - * @return true - * @return false + * + * @param nickname + * @return true + * @return false */ bool correct_nickname(std::string& nickname); /** * @brief check target validity - * - * @param target - * @return true - * @return false + * + * @param target + * @return true + * @return false */ bool correct_target(std::string& target); /** * @brief check channel validity - * - * @param name - * @return true - * @return false + * + * @param name + * @return true + * @return false */ bool correct_channel(std::string& name); /** * @brief check key validity - * - * @param key - * @return true - * @return false + * + * @param key + * @return true + * @return false */ bool correct_key(std::string& key); /** * @brief check whether nickname is registered on server - * - * @param nickname - * @param failCommandIfFalse + * + * @param nickname + * @param failCommandIfFalse * @warning send ERR_NOSUCHNICK in case of error - * @return Parser& + * @return Parser& */ Parser& is_such_nick(std::string& nickname, bool failCommandIfFalse = false); /** * @brief check whether channel is registered - * - * @param channelName - * @param failCommandIfFalse + * + * @param channelName + * @param failCommandIfFalse * @warning send ERR_NOSUCHCHANNEL in case of error - * @return Parser& + * @return Parser& */ Parser& is_such_channel(std::string& channelName, bool failCommandIfFalse = false); /** * @brief check that array of args has no more than provided max - * - * @param vector - * @param max - * @param failCommandIfFalse - * @return Parser& + * + * @param vector + * @param max + * @param failCommandIfFalse + * @return Parser& */ Parser& has_no_more_than(std::vector& vector, std::size_t max, bool failCommandIfFalse = true); /** * @brief check if sender is on channel - * - * @param channelName - * @param nickname - * @param failCommandIfFalse + * + * @param channelName + * @param nickname + * @param failCommandIfFalse * @warning send ERR_NOTONCHANNEL in case of error - * @return Parser& + * @return Parser& */ Parser& is_channel_member(std::string& channelName, const std::string& nickname, bool failCommandIfFalse = true); /** * @brief check whether prompt is valid - * - * @param prompt - * @param commandName - * @param failCommandIfFalse - * @return Parser& + * + * @param prompt + * @param commandName + * @param failCommandIfFalse + * @return Parser& */ Parser& is_valid_bot_prompt(const std::string& prompt, const std::string& commandName, bool failCommandIfFalse = true); /** * @brief check whether subcommand is handled by server - * - * @param subcommand - * @param cmdName - * @param failCommandIfFalse - * @return Parser& + * + * @param subcommand + * @param cmdName + * @param failCommandIfFalse + * @return Parser& */ Parser& is_valid_bot_subcommand(const std::string& subcommand, const std::string& cmdName, bool failCommandIfFalse = true); /** * @brief check if arg is not empty - * - * @param arg - * @param commandName - * @param failCommandIfFalse - * @return Parser& + * + * @param arg + * @param commandName + * @param failCommandIfFalse + * @return Parser& */ Parser& is_not_empty_arg(const std::string& arg, const std::string& commandName, bool failCommandIfFalse = true); /** * @brief count number of params - * - * @param argument - * @return size_t + * + * @param argument + * @return size_t */ size_t count_params(const std::string& argument); /** * @brief send response through #ReplyHandler - * - * @param code - * @param params - * @param trailing - * @return true - * @return false + * + * @param code + * @param params + * @param trailing + * @return true + * @return false */ - bool response(ReplyCode code, const std::string& params = "", const std::string& trailing = ""); + bool response(ReplyCode code, const std::string& params = "", const std::string& trailing = ""); /** * @brief send response through #ReplyHandler - * - * @param dest - * @param code - * @param params - * @param trailing - * @return true - * @return false + * + * @param dest + * @param code + * @param params + * @param trailing + * @return true + * @return false */ - bool response(Client* dest, ReplyCode code, const std::string& params = "", const std::string& trailing = ""); + bool response(Client* dest, ReplyCode code, const std::string& params = "", const std::string& trailing = ""); /** * @brief send response through #ReplyHandler - * - * @param dest - * @param author - * @param code - * @param params - * @param trailing - * @return true - * @return false + * + * @param dest + * @param author + * @param code + * @param params + * @param trailing + * @return true + * @return false */ bool response(Client* dest, Client* author, ReplyCode code, const std::string& params = "", const std::string& trailing = ""); ReplyHandler* rh; /** * @brief check whether consecutive checks have passed - * - * @return true - * @return false + * + * @return true + * @return false */ bool has_passed_checks() const; diff --git a/includes/reply_codes.hpp b/includes/reply_codes.hpp index 8c376057..46267be7 100644 --- a/includes/reply_codes.hpp +++ b/includes/reply_codes.hpp @@ -3,9 +3,9 @@ * @brief Numerical reply codes * @version 0.1 * @date 2025-10-25 - * + * * @copyright Copyright (c) 2025 - * + * */ #ifndef REPLY_CODES_HPP #define REPLY_CODES_HPP @@ -37,6 +37,7 @@ enum ReplyCode { CUSTOMRPL_BOT = 600, ////////////////////// numerical custom errors CUSTOMERR_WRONG_FORMAT = 705, + CUSTOMERR_TOOMANYMODES = 706, ////////////////////// RFC REPLIES (with code defined by RFC) RPL_NONE = 300, // no answer diff --git a/includes/server/Config.hpp b/includes/server/Config.hpp index d2bef20a..b32ecbc4 100644 --- a/includes/server/Config.hpp +++ b/includes/server/Config.hpp @@ -28,6 +28,7 @@ class Config { public: + bool exist; /** * @brief Construct a new Config object from filename * diff --git a/srcs/channels/Channel.cpp b/srcs/channels/Channel.cpp index 0eff363f..2c4a4646 100644 --- a/srcs/channels/Channel.cpp +++ b/srcs/channels/Channel.cpp @@ -265,6 +265,8 @@ bool Channel::remove_from_invited_list(Client& client) bool Channel::remove_member(Client& client) { bool member = is_member(client); + if (is_operator(client) && _operators.size() > 1) + remove_operator(client); _members.erase(&client); return (member); } @@ -291,6 +293,15 @@ struct CompareClientsByName { bool operator()(const Client* lhs, const Client* rhs) { return lhs->get_nickname() < rhs->get_nickname(); } }; +const std::deque Channel::get_history() const { return _lastMessages; } + +void Channel::add_message_to_history(const std::string& message) +{ + if (_lastMessages.size() >= MAX_MESSAGES_HISTORY) + _lastMessages.pop_front(); + _lastMessages.push_back(message); +} + std::vector Channel::get_members_list() const { std::vector list; diff --git a/srcs/clients/Client.cpp b/srcs/clients/Client.cpp index bccb2555..088ad8b0 100644 --- a/srcs/clients/Client.cpp +++ b/srcs/clients/Client.cpp @@ -132,10 +132,24 @@ Channel* Client::get_channel(const std::string& name) { void Client::broadcast_to_all_channels(Server& server, ReplyCode code, const std::string& params, const std::string& trailing) { LOG_DT_CMD("nb joined channels", _joinedChannels.size()); + std::set target; for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); it++) { if (it->second) { LOG_DT_CMD("to", it->second->get_name()); - it->second->broadcast(server, code, params, this, trailing); - } + Channel* channel = it->second; + std::set chanMembers = channel->get_members(); + for (std::set::iterator itc = chanMembers.begin(); itc != chanMembers.end(); itc++){ + target.insert(*itc); + } } + } + ReplyHandler &rh = ReplyHandler::get_instance(&server); + LOG_DV_CMD(target.size()); + for (std::set::iterator it = target.begin(); it != target.end(); ++it) { + Client *recipient = *it; + if (recipient == this) + continue; + LOG_D_SERVER(recipient->get_nickname() + " received a broadcast from " + _nickName, ircConfig.str(code)); + rh.process_response(*recipient, code, params, this, trailing); + } } diff --git a/srcs/commands/Bot.cpp b/srcs/commands/Bot.cpp index a6f6f568..7d97e2b4 100644 --- a/srcs/commands/Bot.cpp +++ b/srcs/commands/Bot.cpp @@ -66,7 +66,15 @@ bool Bot::_check_args(Server& s, Client& c) return false; } - _targetChannels.push_back(s.find_channel_by_name(_targetChannelName)); + Channel* channel = s.find_channel_by_name(_targetChannelName); + _targetChannels.push_back(channel); + _channelHistory = ""; + std::deque history = channel->get_history(); + if (!history.empty()) { + for (std::deque::const_iterator it = history.begin(); it != history.end(); ++it) { + _channelHistory.append(*it + "./"); + } + } return true; } @@ -84,23 +92,29 @@ void add_key_val(std::string& command, const std::string& key, const std::string void remove_invalid_prompt_char(char& c) { - if (c == '\'' || c == '"' || c == '*' || c == ';' || c == '|') + if (c == '\'' || c == '"' || c == '*' || c == ';' || c == '|' || c == '(' || c == ')') c = ' '; } -static bool send_ollama_request(const std::string& prompt, std::string& response) +static bool send_ollama_request(const std::string& subcommand, const std::string& prompt, std::string& response) { - std::string command = "curl -X POST -H \"Content-Type: application/json\" -v localhost:11434/api/generate -d '"; - command += "{\"model\": \"gemma3:1b\",\"prompt\":\""; + std::string command = "bash -c 'set -o pipefail; curl -X POST -H " + "\"Content-Type: application/json\" -v " + "localhost:11434/api/generate -d \""; + command += "{\\\"model\\\": " + "\\\"llama3.2:3b\\\",\\\"stream\\\":false,\\\"prompt\\\":\\\""; command += prompt; - command += "\",\"options\": {\"temperature\": 0.99,\"top_p\": 0.8},\"stream\": false}"; - command += "'"; - command += " | jq \'.response\' "; - command += " > llama_response.txt"; + if (subcommand == "!reply") + command += "\\\",\\\"options\\\": {\\\"temperature\\\": " + "0.99,\\\"top_p\\\": 0.9}}\""; + else + command += "\\\",\\\"options\\\": {\\\"temperature\\\": 0.2,\\\"top_p\\\": " + "0.3}}\""; + command += " | jq \".response\" "; + command += " > llama_response.txt \'"; LOG_d_CMD(command); int code = ::system(command.c_str()); - LOG_w_CMD(code); if (code != 0) { LOG_E_SERVER("error sending API LLama request", command); return false; @@ -183,23 +197,41 @@ void Bot::execute(Server& s, Client& c) std::string prompt = "Your responses must strictly follow these rules: "; prompt += "Keep your response under 500 characters. "; - prompt += "No special characters. No newline character, line break. Keep the response as a single, continuous line of text. "; - prompt += "No markdown or any other formatting : do not use bold, italic, code blocks or any other markdown. "; - prompt += "No meta-commentary Do not reference instruction, your role or user prompt. Just answer directly. "; - prompt += "You send direct and concise replies without preamble and with no hints about former meta instructions. "; + prompt += "No special characters. No newline character (never ever use \\n). Keep the " + "response as a single, continuous line of text. "; + prompt += "No markdown or any other formatting : do not use bold, italic, " + "code blocks or any other markdown. "; + prompt += "No meta-commentary Do not reference instruction, your role or " + "user prompt. Just answer directly. "; + prompt += "IF the question is about the IRC project and ONLY in this case " + " THEN tell emphatically how this IRC server project, although it is a small step for humanity, is nevertheless a " + "big step for its creators. " + " try to get sympathy of the evaluator for the group of three students having worked hard upon it" + " and convince him that IRC is a promising technology "; + prompt += "OTHERWISE, IF the question is about anything other than IRC project, you send direct and concise replies without " + "preamble and with no " + "hints about former meta instructions. "; prompt += "Now answer the user question. "; if (_subcommand == "!reply") prompt += "Please reply to this message : "; else if (_subcommand == "!check") prompt += "Please fact-check this affirmation : "; + else if (_subcommand == "!brief") { + prompt += "Please sum up this meeting in a corporate tone, summing which user (introduced by name: before each sentence) " + "had which positions :" + + _channelHistory; + prompt += "And pay extra attention to following instructions (if any) : "; + } prompt += _prompt; std::for_each(prompt.begin(), prompt.end(), remove_invalid_prompt_char); LOG_DV_CMD(prompt); std::string response = "\"\""; - if (!send_ollama_request(prompt, response)) + if (send_ollama_request(_subcommand, prompt, response) == false) response = "\"Bot is under maintenance\""; + LOG_DV_CMD(response); + if (!_connect_to_server(s, _socket)) return; if (!_register_bot(s, _socket)) @@ -212,12 +244,14 @@ void Bot::execute(Server& s, Client& c) response = response.substr(firstQuoteIdx + 1, lastQuoteIdx - 1); else { LOG_W_CMD("empty response", response); - return; + response = "\"Bot is under maintenance\""; } + LOG_DV_CMD(response); + if (!_targetChannels.empty()) { _targetChannels[0]->broadcast_bot( - s, TRANSFER_PROMPT_BOT, _targetChannels[0]->get_name(), NULL, _subcommand.substr(1) + " " + _prompt); + s, TRANSFER_PROMPT_BOT, _targetChannels[0]->get_name(), NULL, "(" + _subcommand.substr(1) + ") " + _prompt); s.update_bot_state(_socket.get_socket(), _targetChannels[0], _subcommand, response, false); std::string joinMsg = "JOIN " + _targetChannels[0]->get_name() + "\r\n"; if (!send_full_msg(_socket.get_socket(), joinMsg)) { diff --git a/srcs/commands/Invite.cpp b/srcs/commands/Invite.cpp index 28c43c44..bbaeedf9 100644 --- a/srcs/commands/Invite.cpp +++ b/srcs/commands/Invite.cpp @@ -45,6 +45,6 @@ void Invite::execute(Server& server, Client& client) } else { channel->invite_client(*target); p.response(RPL_INVITING, _nickname + " " + _channelName); - p.response(target, &client, TRANSFER_INVITE, _nickname, _channelName); + p.response(target, &client, TRANSFER_INVITE, _nickname + " " + _channelName); } } diff --git a/srcs/commands/Join.cpp b/srcs/commands/Join.cpp index fa0b5631..6e9321c9 100644 --- a/srcs/commands/Join.cpp +++ b/srcs/commands/Join.cpp @@ -86,10 +86,7 @@ void Join::execute(Server& server, Client& client) channel->remove_from_invited_list(client); if (channel->get_nb_members() == 1) { channel->make_operator(client); - p.response(RPL_CHANNELMODEIS, chanName + " +o "); - // rh.process_response(client, TRANSFER_MODE, chanName + " +o " + client.get_nickname()); <-- merge changements } - p.response(RPL_CHANNELMODEIS, chanName + " " + channel->get_modes_str(client)); _send_list_of_names(p, *channel); _display_topic(p, *channel); } else { diff --git a/srcs/commands/Kick.cpp b/srcs/commands/Kick.cpp index 012a59f4..14741243 100644 --- a/srcs/commands/Kick.cpp +++ b/srcs/commands/Kick.cpp @@ -54,6 +54,13 @@ void Kick::_kick_users_from_chan(std::string& chanName, std::vector p.response(ERR_USERNOTINCHANNEL, channel->get_name()); } } + if (channel->get_nb_members() == 0) { + std::map::iterator it = server->channels.find(channel->get_name()); + if (it != server->channels.end()) { + server->channels.erase(it); + delete channel; + } + } } void Kick::execute(Server& server, Client& client) diff --git a/srcs/commands/Mode.cpp b/srcs/commands/Mode.cpp index 804dd4c3..e2cffae4 100644 --- a/srcs/commands/Mode.cpp +++ b/srcs/commands/Mode.cpp @@ -24,31 +24,43 @@ static void parse_params(std::string& params, std::queue* modeQueue std::string args; std::istringstream iss(params); LOG_DV_CMD(params); - // iss >> *channel; // first args must be a modes iss >> args; LOG_DV_CMD(args); LOG_DV_CMD(args.size()); + char sign = '+'; if (args.empty()) return; - if (!Utils::is_char_of(args[0], std::string(MODE_OPERATOR)) || args.size() == 1) + if (args.size() == 1 || !Utils::is_char_of(args[0], std::string(MODE_OPERATOR))) modeQueue->push(args); - else if (args.size() > 1) { - if (std::string(args.begin() + 1, args.end()).find(std::string(MODE_OPERATOR)) != std::string::npos) - modeQueue->push(args); - else - for (std::string::iterator it = args.begin() + 1; it != args.end(); ++it) - modeQueue->push(std::string(1, args[0]) + *it); + for (std::string::iterator it = args.begin(); it != args.end(); ++it) { + if (Utils::is_char_of(*it, std::string(MODE_OPERATOR))) { + sign = *it; + if (it + 1 != args.end() && Utils::is_char_of(*(it + 1), std::string(MODE_OPERATOR))) { + modeQueue->push(std::string(1, *it)); + LOG_d_CMD(*it); + } + } else { + modeQueue->push(std::string(1, sign) + *it); + LOG_DV_CMD(*it); + } } // other args while (iss >> args) { LOG_DV_CMD(args); - if (args.size() > 1 && Utils::is_char_of(args[0], std::string(MODE_OPERATOR)) && !std::isdigit(args[1])) { - if (std::string(args.begin() + 1, args.end()).find(std::string(MODE_OPERATOR)) != std::string::npos) - modeQueue->push(args); - else - for (std::string::iterator it = args.begin() + 1; it != args.end(); ++it) - modeQueue->push(std::string(1, args[0]) + *it); + if (args.size() > 1 && Utils::is_char_of(args[0], std::string(MODE_OPERATOR))) { + for (std::string::iterator it = args.begin(); it != args.end(); ++it) { + if (Utils::is_char_of(*it, std::string(MODE_OPERATOR))) { + sign = *it; + if (it + 1 != args.end() && Utils::is_char_of(*(it + 1), std::string(MODE_OPERATOR))) { + modeQueue->push(std::string(1, *it)); + LOG_d_CMD(*it); + } + } else { + modeQueue->push(std::string(1, sign) + *it); + LOG_DV_CMD(*it); + } + } } else paramsQueue->push(args); } @@ -120,20 +132,17 @@ bool Mode::_simple_args(Server& server, Client& client, Channel*& channel, Parse return false; } -void Mode::_mode_with_noparams(Channel* channel, - std::string& currentMode, - std::string& validPositiveModes, - std::string& validNegativeModes) +void Mode::_mode_with_noparams(Channel* channel, std::string& currentMode, std::string& validModes) { LOG_d_CMD("MODE without param"); LOG_DV_CMD(currentMode); if (currentMode[0] == '+') { channel->add_mode(char_to_mode(currentMode[1])); - validPositiveModes += currentMode[1]; + validModes += currentMode; } if (currentMode[0] == '-') { channel->remove_mode(char_to_mode(currentMode[1])); - validNegativeModes += currentMode[1]; + validModes += currentMode; } } @@ -141,19 +150,25 @@ void Mode::_mode_k(Channel* channel, Parser& p, std::string& currentMode, std::string& currentParam, - std::string& validPositiveModes, + std::string& validModes, std::string& validModesParams) { size_t invalidChar = 0; if (!currentParam.empty()) invalidChar = std::count_if(currentParam.begin(), currentParam.end(), Utils::is_invalid_char_key); - if (currentMode[0] == '+' && !currentParam.empty() && !invalidChar) { - if (channel->get_mode() & char_to_mode(currentMode[1])) { + if (!currentParam.empty() && !invalidChar) { + if (currentMode[0] == '+' && (channel->get_mode() & char_to_mode(currentMode[1]))) p.response(ERR_KEYSET, channel->get_name()); - } else { - channel->add_mode(char_to_mode(currentMode[1])); - channel->set_key(currentParam); - validPositiveModes += currentMode[1]; + else if (currentMode[0] == '-' && channel->get_key() != currentParam) + p.response(ERR_NEEDMOREPARAMS, "MODE"); + else { + if (currentMode[0] == '-' && channel->get_key() == currentParam) + channel->remove_mode(char_to_mode(currentMode[1])); + else { + channel->add_mode(char_to_mode(currentMode[1])); + channel->set_key(currentParam); + } + validModes += currentMode; validModesParams += " " + currentParam; } } else @@ -164,7 +179,7 @@ void Mode::_mode_l(Channel* channel, Parser& p, std::string& currentMode, std::string& currentParam, - std::string& validPositiveModes, + std::string& validModes, std::string& validModesParams) { size_t invalidChar = 0; @@ -174,7 +189,7 @@ void Mode::_mode_l(Channel* channel, int limit = std::atoi(currentParam.c_str()); channel->add_mode(char_to_mode(currentMode[1])); channel->set_user_limit(limit); - validPositiveModes += currentMode[1]; + validModes += currentMode; validModesParams += " " + currentParam; } else p.response(ERR_NEEDMOREPARAMS, "MODE"); @@ -185,8 +200,7 @@ void Mode::_mode_o(Server& server, Parser& p, std::string& currentMode, std::string& currentParam, - std::string& validPositiveModes, - std::string& validNegativeModes, + std::string& validModes, std::string& validModesParams) { size_t invalidChar = 0; @@ -199,11 +213,11 @@ void Mode::_mode_o(Server& server, p.response(ERR_USERNOTINCHANNEL, currentParam); else if (currentMode[0] == '+') { channel->make_operator(*targetOp); - validPositiveModes += currentMode[1]; + validModes += currentMode; validModesParams += " " + currentParam; } else { channel->remove_operator(*targetOp); - validNegativeModes += currentMode[1]; + validModes += currentMode; validModesParams += " " + currentParam; } } else @@ -225,16 +239,16 @@ void Mode::execute(Server& server, Client& client) return; } // others cases main args loop - std::string validPositiveModes = " +"; - std::string validNegativeModes = " -"; - std::string validModesParams = ""; + std::string validModes = ""; + std::string validModesParams = ""; + int nbModeWithParam = 0; while (!_modeQueue.empty()) { std::string currentMode = _modeQueue.front(); _modeQueue.pop(); // cases more than one + or -, or no operator or only + or - if (currentMode.size() > 2 || currentMode.size() == 1 || !Utils::is_char_of(currentMode[0], std::string(MODE_OPERATOR))) { p.response(ERR_NEEDMOREPARAMS, "MODE"); - return; + continue; } // case not a valid mode if (!Utils::is_char_of(currentMode[1], std::string(VALID_CHAN_MODE_NOPARAM)) @@ -243,13 +257,13 @@ void Mode::execute(Server& server, Client& client) continue; } // mode need params but there isn't - if ((currentMode == "+k" || currentMode == "+l" || currentMode[1] == 'o') && _paramsQueue.empty()) { + if ((Utils::is_char_of(currentMode[1], std::string(VALID_CHAN_MODE_PARAM))) && _paramsQueue.empty()) { p.response(ERR_NEEDMOREPARAMS, "MODE"); continue; } // case mode need no params if (Utils::is_char_of(currentMode[1], std::string(VALID_CHAN_MODE_NOPARAM))) { - _mode_with_noparams(channel, currentMode, validPositiveModes, validNegativeModes); + _mode_with_noparams(channel, currentMode, validModes); continue; } // case mode need param @@ -257,32 +271,35 @@ void Mode::execute(Server& server, Client& client) LOG_d_CMD("MODE with param"); LOG_DV_CMD(currentMode); // negative which don't need param - if (currentMode[0] == '-' && (currentMode[1] == 'k' || currentMode[1] == 'l')) { - LOG_d_CMD("Negative k or l"); + if (currentMode[0] == '-' && currentMode[1] == 'l') { + LOG_d_CMD("Negative l"); channel->remove_mode(char_to_mode(currentMode[1])); - validNegativeModes += currentMode[1]; + validModes += currentMode; + continue; + } + nbModeWithParam++; + if (nbModeWithParam > 3) { + p.response(CUSTOMERR_TOOMANYMODES, "MODE"); continue; } std::string currentParam = _paramsQueue.front(); _paramsQueue.pop(); LOG_DV_CMD(currentParam); if (currentMode[1] == 'k') { - _mode_k(channel, p, currentMode, currentParam, validPositiveModes, validModesParams); + _mode_k(channel, p, currentMode, currentParam, validModes, validModesParams); } else if (currentMode[1] == 'l') { - _mode_l(channel, p, currentMode, currentParam, validPositiveModes, validModesParams); + _mode_l(channel, p, currentMode, currentParam, validModes, validModesParams); } else if (currentMode[1] == 'o') { - _mode_o(server, channel, p, currentMode, currentParam, validPositiveModes, validNegativeModes, validModesParams); + _mode_o(server, channel, p, currentMode, currentParam, validModes, validModesParams); } } } - std::string confirmationMsg = _channelName; - if (validPositiveModes.size() > 2) - confirmationMsg += validPositiveModes; - if (validNegativeModes.size() > 2) - confirmationMsg += validNegativeModes; - if (validModesParams.size() > 1) + std::string confirmationMsg = _channelName + " "; + if (!validModes.empty()) + confirmationMsg += validModes; + if (!validModesParams.empty()) confirmationMsg += validModesParams; - if (confirmationMsg != _channelName) { + if (confirmationMsg != _channelName + " ") { p.response(TRANSFER_MODE, confirmationMsg); channel->broadcast(server, TRANSFER_MODE, confirmationMsg, &client, ""); } diff --git a/srcs/commands/Nick.cpp b/srcs/commands/Nick.cpp index 51843d23..c60490d0 100644 --- a/srcs/commands/Nick.cpp +++ b/srcs/commands/Nick.cpp @@ -43,7 +43,9 @@ void Nick::execute(Server& server, Client& client) client.set_status(REGISTERED); p.rh->process_welcome(server, client); } else if (!oldNickname.empty() && !client.get_user_name().empty() && client.is_registered()) { - client.broadcast_to_all_channels(server, TRANSFER_NICK, "", _nickname); // ! \\ ; + client.broadcast_to_all_channels(server, TRANSFER_NICK, "", + _nickname); // ! \\ ; + p.response(TRANSFER_NICK, "", _nickname); } client.set_nickname(_nickname); } diff --git a/srcs/commands/Part.cpp b/srcs/commands/Part.cpp index 6edf9c50..7bde37a7 100644 --- a/srcs/commands/Part.cpp +++ b/srcs/commands/Part.cpp @@ -43,7 +43,15 @@ void Part::execute(Server& server, Client& client) } else { channel->broadcast(server, TRANSFER_PART, _chanNames[i], &client, _message); p.response(TRANSFER_PART, _chanNames[i], _message); - channel->remove_member(client); + // channel->remove_member(client); + client.remove_joined_channel(*channel); + if (channel->get_nb_members() == 0) { + std::map::iterator it = server.channels.find(channel->get_name()); + if (it != server.channels.end()) { + server.channels.erase(it); + delete channel; + } + } } } } diff --git a/srcs/commands/Privmsg.cpp b/srcs/commands/Privmsg.cpp index 4a9086cf..83b5cbe8 100644 --- a/srcs/commands/Privmsg.cpp +++ b/srcs/commands/Privmsg.cpp @@ -1,8 +1,10 @@ #include "Privmsg.hpp" #include "Client.hpp" +#include "LogManager.hpp" #include "Parser.hpp" #include "Server.hpp" +#include "consts.hpp" #include "reply_codes.hpp" /************************************************************ @@ -15,7 +17,12 @@ Privmsg::Privmsg(std::string& params) std::string targetList = parser.format_parameter(params, NULL); _targets = parser.to_vector(targetList); - _message = parser.format_parameter(params, NULL); + _message = parser.from_remaining_args(params); + LOG_DV_CMD(_message); + if (!_message.empty() && _message[0] == ':') { + _message.replace(0, 1, ""); + } + LOG_DV_CMD(_message); } Privmsg::~Privmsg(void) {} @@ -45,6 +52,7 @@ void Privmsg::execute(Server& server, Client& client) if (silent.correct_channel(*it)) { Channel* chan = server.find_channel_by_name(*it); if (chan && chan->is_member(client)) { + chan->add_message_to_history(client.get_nickname() + ":" + _message); chan->broadcast(server, TRANSFER_PRIVMSG, *it, &client, _message); } else if (chan) { p.response(ERR_NOTONCHANNEL, *it); diff --git a/srcs/commands/Quit.cpp b/srcs/commands/Quit.cpp index 4bc1b3e7..e9f5406e 100644 --- a/srcs/commands/Quit.cpp +++ b/srcs/commands/Quit.cpp @@ -1,8 +1,10 @@ #include "Quit.hpp" +#include "Channel.hpp" #include "Client.hpp" #include "Config.hpp" #include "LogManager.hpp" +#include "Part.hpp" #include "ReplyHandler.hpp" #include "Server.hpp" #include "reply_codes.hpp" diff --git a/srcs/commands/Topic.cpp b/srcs/commands/Topic.cpp index bf91e761..22feed68 100644 --- a/srcs/commands/Topic.cpp +++ b/srcs/commands/Topic.cpp @@ -1,6 +1,7 @@ #include "Topic.hpp" #include "Channel.hpp" +#include "LogManager.hpp" #include "Parser.hpp" #include "ReplyHandler.hpp" #include "Server.hpp" @@ -16,15 +17,17 @@ Topic::Topic(std::string& params) : _clearTopic(false) { Parser parser; - _chan = parser.format_parameter(params, NULL); - std::istringstream iss(params); - std::string token; - iss >> token; - if (token == ":") { + _chan = parser.format_parameter(params, NULL); + std::string topicArg = params; + + if (topicArg == " :") { _clearTopic = true; _topic = ""; - } else { - _topic = parser.format_parameter(params, NULL); + } else if (!topicArg.empty()) { + if (topicArg.length() > 1 && topicArg[1] == ':') + _topic = topicArg.substr(2); + else + _topic = topicArg.substr(1); } } diff --git a/srcs/main.cpp b/srcs/main.cpp index 07b1d718..a717b99a 100644 --- a/srcs/main.cpp +++ b/srcs/main.cpp @@ -1,3 +1,4 @@ +#include "Config.hpp" #include "LogManager.hpp" #include "Server.hpp" #include "signal_handler.hpp" @@ -11,7 +12,7 @@ int main(int ac, char** av) int port = DEFAULT_PORT; LOG_ERR.set_min_level(ERROR); - if (!Utils::check_args(ac, av, &port)) + if (!Utils::check_args(ac, av, &port) || !ircConfig.exist) return 1; try { Server newServer(port, av[2]); diff --git a/srcs/parsing/Parser.cpp b/srcs/parsing/Parser.cpp index b999c53d..25b45328 100644 --- a/srcs/parsing/Parser.cpp +++ b/srcs/parsing/Parser.cpp @@ -241,7 +241,7 @@ Parser& Parser::is_valid_bot_subcommand(const std::string& subcommand, const std bool passedCheck = false; if (_isValidCommand) { this->is_not_empty_arg(subcommand, cmdName); - std::string availableSubcommands[NB_AVAILABLE_BOT_SUBCMD] = {"!reply", "!check"}; + std::string availableSubcommands[NB_AVAILABLE_BOT_SUBCMD] = {"!reply", "!check", "!brief"}; for (int i = 0; i < NB_AVAILABLE_BOT_SUBCMD; ++i) { if (availableSubcommands[i] == subcommand) passedCheck = true; @@ -338,6 +338,22 @@ std::string Parser::from_arg(std::string& params) return argument; } +std::string Parser::from_remaining_args(std::string& params) +{ + std::string words; + std::string word; + std::istringstream iss(params); + + LOG_DV_CMD(params); + while (iss >> word) { + words += word; + words += " "; + } + words.erase(words.size() - 1, words.size()); + params.erase(0, params.size()); + return words; +} + std::string Parser::from_trailing(std::string& params) { std::string trailing; diff --git a/srcs/server/Config.cpp b/srcs/server/Config.cpp index 0bce1c2b..1a269237 100644 --- a/srcs/server/Config.cpp +++ b/srcs/server/Config.cpp @@ -16,6 +16,7 @@ const Config ircConfigTest(SERVER_CONF_FILE_FOR_TEST); ************************************************************/ Config::Config(const std::string& fileName) : + exist(true), _name(SERVER_NAME), _psswd(DEFAULT_PASSWORD), _port(DEFAULT_PORT), @@ -35,7 +36,7 @@ Config::Config(const std::string& fileName) : #endif if (!_parse_config_file(actualFileName)) { LOG_SERVER.warning("CONF FILE not loaded!"); - exit(1); + exist = false; } if (_motd.empty()) { LOG_SERVER.warning("MOTD is empty!"); diff --git a/srcs/server/Server.cpp b/srcs/server/Server.cpp index ec78d451..051cce3a 100644 --- a/srcs/server/Server.cpp +++ b/srcs/server/Server.cpp @@ -40,7 +40,6 @@ Server::Server(const unsigned short port, const std::string& password) : _serverSocket.tcp_listen(); LOG_SERVER.info("Server " + _name + " start at port :" + Utils::to_string(port)); std::cout << "\n"; - _listen_to_socket(_serverSocket.get_socket(), POLLIN); } @@ -217,13 +216,11 @@ void Server::_handle_bot_input(int pfdIndex, Client* botClient, BotState& state) *this, TRANSFER_PRIVMSG, state.targetChannel->get_name(), botClient, state.pendingMsg); rh.process_response(*botClient, TRANSFER_PRIVMSG, state.targetChannel->get_name()); state.readyToSend = true; - return; - } - if (response.find("PRIVMSG") != std::string::npos && state.readyToSend == true) { LOG_DV_SERVER(state.pendingMsg); _handle_commands(pfdIndex); cleanup_bot(so); cleanup_socket_and_client(pfdIndex); + return; } } } From 63ca94a6d8baac4ea48e40110847d043d30b787b Mon Sep 17 00:00:00 2001 From: jhervoch Date: Thu, 13 Nov 2025 14:43:29 +0100 Subject: [PATCH 02/10] fix : some test --- includes/colors.hpp | 4 +- includes/server/LogManager.hpp | 2 +- includes/server/ReplyHandler.hpp | 2 +- includes/utils.hpp | 4 +- srcs/clients/Client.cpp | 23 +++++++--- srcs/commands/CmdFactory.cpp | 30 ++++++------- srcs/parsing/Parser.cpp | 2 +- srcs/server/Config.cpp | 14 +++--- srcs/utils.cpp | 6 +-- test/srcs/testMode.cpp | 75 +++++++++++++++++++++----------- test/srcs/testMotd.cpp | 16 ++++--- test/srcs/testNick.cpp | 44 ++++++++++++------- 12 files changed, 136 insertions(+), 86 deletions(-) diff --git a/includes/colors.hpp b/includes/colors.hpp index 59b40d56..b05e3aa4 100644 --- a/includes/colors.hpp +++ b/includes/colors.hpp @@ -3,9 +3,9 @@ * @brief ANSI color codes * @version 0.1 * @date 2025-10-25 - * + * * @copyright Copyright (c) 2025 - * + * */ #ifndef COLORS_H #define COLORS_H diff --git a/includes/server/LogManager.hpp b/includes/server/LogManager.hpp index c7e4c56e..6d73de74 100644 --- a/includes/server/LogManager.hpp +++ b/includes/server/LogManager.hpp @@ -17,7 +17,7 @@ * @details responsabilities * - single instanciation of each logger * - provide helper macros - * + * */ class LogManager { diff --git a/includes/server/ReplyHandler.hpp b/includes/server/ReplyHandler.hpp index e7c97785..fb1a79dc 100644 --- a/includes/server/ReplyHandler.hpp +++ b/includes/server/ReplyHandler.hpp @@ -23,7 +23,7 @@ class Client; /** * @brief Harmonize responses to client * @class ReplyHandler - * @details + * @details * ensure that responses match [RFC specs](https://datatracker.ietf.org/doc/html/rfc2812#section-2.3.1) * * grammar of message is ` ::= [':' ] ` * meaning that a message is made of diff --git a/includes/utils.hpp b/includes/utils.hpp index 510133e9..f5d5edf8 100644 --- a/includes/utils.hpp +++ b/includes/utils.hpp @@ -19,8 +19,8 @@ #define EVENT_TO_STR Utils::event_to_str /** -* @class Utils -* @brief Utility class for conversion, argument validation + * @class Utils + * @brief Utility class for conversion, argument validation */ class Utils { diff --git a/srcs/clients/Client.cpp b/srcs/clients/Client.cpp index 088ad8b0..f1f0b8d7 100644 --- a/srcs/clients/Client.cpp +++ b/srcs/clients/Client.cpp @@ -4,8 +4,10 @@ #include "Config.hpp" #include "LogManager.hpp" #include "Part.hpp" +#include "Server.hpp" #include "TcpSocket.hpp" #include "consts.hpp" +#include "reply_codes.hpp" #include @@ -99,7 +101,7 @@ void Client::remove_joined_channel(Channel& channel) { _joinedChannels.erase(channel.get_name()); } -void Client::remove_from_all_channels() +void Client::remove_from_all_channels() { for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); ++it) { @@ -109,11 +111,20 @@ void Client::remove_from_all_channels() } void Client::part_all_channels(Server& server, Client& client) { - for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); ++it) - { - std::string params = static_cast(it->second->get_name()); - Part doPart(params); - doPart.execute(server,client); + ReplyHandler &rh = ReplyHandler::get_instance(&server); + for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); ++it) + { + Channel* channel = it->second; + channel->broadcast(server, TRANSFER_PART, channel->get_name(), &client); + rh.process_response(*this, TRANSFER_PART, channel->get_name()); + channel->remove_member(*this); + if (channel->get_nb_members() == 0) { + std::map::iterator it = server.channels.find(channel->get_name()); + if (it != server.channels.end()) { + server.channels.erase(it); + delete channel; + } + } } _joinedChannels.clear(); } diff --git a/srcs/commands/CmdFactory.cpp b/srcs/commands/CmdFactory.cpp index 33a52c65..12546fc0 100644 --- a/srcs/commands/CmdFactory.cpp +++ b/srcs/commands/CmdFactory.cpp @@ -68,21 +68,21 @@ ICommand* CmdFactory::make_command(Server& server, Client& client, std::string& "BOT", "PING"}; - ICommand* (CmdFactory::* ptr[NB_AVAILABLE_CMD])(std::string&) = {&CmdFactory::user_cmd, - &CmdFactory::pass_cmd, - &CmdFactory::nick_cmd, - &CmdFactory::topic_cmd, - &CmdFactory::quit_cmd, - &CmdFactory::invite_cmd, - &CmdFactory::join_cmd, - &CmdFactory::part_cmd, - &CmdFactory::mode_cmd, - &CmdFactory::privmsg_cmd, - &CmdFactory::who_cmd, - &CmdFactory::kick_cmd, - &CmdFactory::motd_cmd, - &CmdFactory::bot_cmd, - &CmdFactory::ping_cmd}; + ICommand* (CmdFactory::*ptr[NB_AVAILABLE_CMD])(std::string&) = {&CmdFactory::user_cmd, + &CmdFactory::pass_cmd, + &CmdFactory::nick_cmd, + &CmdFactory::topic_cmd, + &CmdFactory::quit_cmd, + &CmdFactory::invite_cmd, + &CmdFactory::join_cmd, + &CmdFactory::part_cmd, + &CmdFactory::mode_cmd, + &CmdFactory::privmsg_cmd, + &CmdFactory::who_cmd, + &CmdFactory::kick_cmd, + &CmdFactory::motd_cmd, + &CmdFactory::bot_cmd, + &CmdFactory::ping_cmd}; iss >> commandLine; for (size_t i = 0; i < NB_AVAILABLE_CMD; i++) { diff --git a/srcs/parsing/Parser.cpp b/srcs/parsing/Parser.cpp index 25b45328..960fc979 100644 --- a/srcs/parsing/Parser.cpp +++ b/srcs/parsing/Parser.cpp @@ -104,7 +104,7 @@ bool Parser::correct_channel(std::string& name) return response(ERR_BADCHANMASK, name); } } - if (!Utils::is_char_of(static_cast(name[0]), "#&")) { + if (!Utils::is_char_of(static_cast(name[0]), "#&") && (name.size() != 1 && name != "0")) { return response(ERR_BADCHANMASK, name); } if (name.length() < 1 || name.length() >= ircConfig.get_chan_name_max_len()) { diff --git a/srcs/server/Config.cpp b/srcs/server/Config.cpp index 1a269237..dcbfad7b 100644 --- a/srcs/server/Config.cpp +++ b/srcs/server/Config.cpp @@ -132,13 +132,13 @@ void Config::_set_key_value(const std::string& key, std::string& value) const size_t nbParam = 7; std::string keyList[nbParam] = {"server_name", "password", "port", "maxJoinedChannels", "chanNameMaxLen", "nicknameMaxLen", "targetLimit"}; - void (Config::* functions[nbParam])(std::string&) = {&Config::_set_name, - &Config::_set_password, - &Config::_set_port, - &Config::_set_max_joined_channels, - &Config::_set_chan_name_max_len, - &Config::_set_nickname_max_len, - &Config::_set_target_limit}; + void (Config::*functions[nbParam])(std::string&) = {&Config::_set_name, + &Config::_set_password, + &Config::_set_port, + &Config::_set_max_joined_channels, + &Config::_set_chan_name_max_len, + &Config::_set_nickname_max_len, + &Config::_set_target_limit}; for (size_t i = 0; i < nbParam; i++) { if (key == keyList[i]) { (this->*functions[i])(value); diff --git a/srcs/utils.cpp b/srcs/utils.cpp index 41d3b34f..78acd744 100644 --- a/srcs/utils.cpp +++ b/srcs/utils.cpp @@ -37,10 +37,10 @@ bool Utils::check_password(const std::string& s) LOG_SERVER.warning(std::string("password must be ") + TO_STRING(MIN_PASSWORD_LEN) + " long at least"); return false; } - for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) - { + for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) { if (Utils::is_invalid_char_key(*it)) { - LOG_w_SERVER("password must contain only ASCII chars in range A-Z, a-z, 0-9, !@#$%^&*(), \%x01-05, \%x07-08, \%x0C, and \%x0E-1F"); + LOG_w_SERVER("password must contain only ASCII chars in range A-Z, a-z, 0-9, !@#$%^&*(), \%x01-05, \%x07-08, \%x0C, " + "and \%x0E-1F"); return false; } } diff --git a/test/srcs/testMode.cpp b/test/srcs/testMode.cpp index 9578f42c..ff3689e0 100644 --- a/test/srcs/testMode.cpp +++ b/test/srcs/testMode.cpp @@ -221,7 +221,7 @@ void many_modes_work(Server& s) send_line(sop, validModePlusKLMsg); std::string reply = recv_lines(sop); AssertReply ar(reply); - ar.is_formatted_transfer(opNick, "MODE #chan +kl key 10"); + ar.is_formatted_transfer(opNick, "MODE #chan +k+l key 10"); } catch (const std::runtime_error& e) { LOG_TEST.error(e.what()); @@ -240,7 +240,7 @@ void many_many_modes_work(Server& s) send_line(sop, "MODE #chan +kl key 10 +it +o roro\r\n"); std::string reply = recv_lines(sop); AssertReply ar(reply); - ar.is_formatted_transfer(opNick, "MODE #chan +klito key 10 roro"); + ar.is_formatted_transfer(opNick, "MODE #chan +k+l+i+t+o key 10 roro"); } catch (const std::runtime_error& e) { LOG_TEST.error(e.what()); @@ -706,28 +706,51 @@ void test_mode(Server& s, t_results* r) // run_test([&] { mode_plusi_should_allow_op_to_join_without_invite(s); }, "+i (op)"); // run_test([&] { mode_plusl_should_allow_op_to_join_if_max_reached(s); }, "+l (op)"); - run_test(r, [&] { mode_no_option_should_send_modes_list(s); }, "list"); - run_test(r, [&] { mode_pluso_should_grant_op_and_kick(s); }, "+o "); - run_test(r, [&] { mode_minuso_should_remove_op_and_kick(s); }, "-o "); - run_test(r, [&] { many_modes_work(s); }, "+kl "); - run_test(r, [&] { many_many_modes_work(s); }, "+kl key 10 +it +o roro"); - - run_test(r, [&] { no_chan_should_err(s); }, "no chan"); - run_test(r, [&] { no_keyarg_should_err(s); }, "+k no arg"); - run_test(r, [&] { no_limitarg_should_err(s); }, "+l no arg"); - run_test(r, [&] { no_oparg_should_err(s); }, "+o no arg"); - run_test(r, [&] { key_already_set_should_err(s); }, "+k already set"); - run_test(r, [&] { unknown_mode_should_err(s); }, "+z"); - run_test(r, [&] { unknown_chan_should_err(s); }, "unknown chan"); - run_test(r, [&] { mode_l_negativearg_should_err(s); }, "+l -1"); - run_test(r, [&] { mode_o_unknown_user_should_err(s); }, "+o unknown"); - run_test(r, [&] { mode_minuso_noarg_user_should_err(s); }, "-o no user"); - run_test(r, [&] { mode_plusi_no_invite_should_err(s); }, "+i"); - run_test(r, [&] { mode_plusk_no_key_should_err(s); }, "+k "); - run_test(r, [&] { mode_plusi_with_invite_should_send_rpl_and_broadcast(s); }, "+i after being invited."); - run_test(r, [&] { mode_plusl_should_block_join_if_max_reached(s); }, "+l "); - run_test(r, [&] { mode_plusl_zeroarg_should_block_join(s); }, "+l 0"); - run_test(r, [&] { mode_minusk_should_lift_block(s); }, "-k "); - run_test(r, [&] { mode_minusi_should_lift_block(s); }, "-i"); - run_test(r, [&] { mode_minusl_should_lift_block(s); }, "-l"); + run_test( + r, [&] { mode_no_option_should_send_modes_list(s); }, "list"); + run_test( + r, [&] { mode_pluso_should_grant_op_and_kick(s); }, "+o "); + run_test( + r, [&] { mode_minuso_should_remove_op_and_kick(s); }, "-o "); + run_test( + r, [&] { many_modes_work(s); }, "+kl "); + run_test( + r, [&] { many_many_modes_work(s); }, "+kl key 10 +it +o roro"); + + run_test( + r, [&] { no_chan_should_err(s); }, "no chan"); + run_test( + r, [&] { no_keyarg_should_err(s); }, "+k no arg"); + run_test( + r, [&] { no_limitarg_should_err(s); }, "+l no arg"); + run_test( + r, [&] { no_oparg_should_err(s); }, "+o no arg"); + run_test( + r, [&] { key_already_set_should_err(s); }, "+k already set"); + run_test( + r, [&] { unknown_mode_should_err(s); }, "+z"); + run_test( + r, [&] { unknown_chan_should_err(s); }, "unknown chan"); + run_test( + r, [&] { mode_l_negativearg_should_err(s); }, "+l -1"); + run_test( + r, [&] { mode_o_unknown_user_should_err(s); }, "+o unknown"); + run_test( + r, [&] { mode_minuso_noarg_user_should_err(s); }, "-o no user"); + run_test( + r, [&] { mode_plusi_no_invite_should_err(s); }, "+i"); + run_test( + r, [&] { mode_plusk_no_key_should_err(s); }, "+k "); + run_test( + r, [&] { mode_plusi_with_invite_should_send_rpl_and_broadcast(s); }, "+i after being invited."); + run_test( + r, [&] { mode_plusl_should_block_join_if_max_reached(s); }, "+l "); + run_test( + r, [&] { mode_plusl_zeroarg_should_block_join(s); }, "+l 0"); + run_test( + r, [&] { mode_minusk_should_lift_block(s); }, "-k "); + run_test( + r, [&] { mode_minusi_should_lift_block(s); }, "-i"); + run_test( + r, [&] { mode_minusl_should_lift_block(s); }, "-l"); } diff --git a/test/srcs/testMotd.cpp b/test/srcs/testMotd.cpp index 54004076..22a65f78 100644 --- a/test/srcs/testMotd.cpp +++ b/test/srcs/testMotd.cpp @@ -57,12 +57,13 @@ void valid_motd_should_rpl(Server& s) send_line(so, validMotd); std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_MOTD_WAIT_MS)); std::string reply = ""; - int tries = 0; - while (reply.find(TO_STRING(ERR_NOMOTD)) == std::string::npos - && (reply.find(ircConfig.trailing(RPL_ENDOFMOTD)) == std::string::npos || tries < MAX_TRIES_MOTD)) { - reply += recv_lines(so, "roro on motd"); - ++tries; - } + // int tries = 0; + // while (reply.find(TO_STRING(ERR_NOMOTD)) == std::string::npos + // && (reply.find(ircConfig.trailing(RPL_ENDOFMOTD)) == std::string::npos || tries < MAX_TRIES_MOTD)) { + // reply += recv_lines(so, "roro on motd"); + // ++tries; + // } + reply += recv_lines(so, "roro on motd"); LOG_DV_TEST(reply); AssertReply ar(reply); ar.is_formatted(RPL_MOTDSTART, userNick, "", "- " + ircConfigTest.get_name() + " message of the day -"); @@ -112,7 +113,8 @@ void test_motd(Server& s, t_results* r) print_test_series("command MOTD"); print_test_series_part("common cases"); - run_test(r, [&] { valid_motd_should_rpl(s); }, "'MOTD"); + run_test( + r, [&] { valid_motd_should_rpl(s); }, "'MOTD"); print_test_series_part("error cases"); // run_test(r, [&] { motd_not_opening_should_err(s); }, "'MOTD file with no rights"); diff --git a/test/srcs/testNick.cpp b/test/srcs/testNick.cpp index 26eb8973..c4e94ff6 100644 --- a/test/srcs/testNick.cpp +++ b/test/srcs/testNick.cpp @@ -163,7 +163,8 @@ void valid_change_should_void(Server& s) send_line(so, validNickChangeMsg); std::string reply = recv_lines(so); AssertReply ar(reply); - ar.is_empty(); + // ar.is_empty(); + ar.matches_entirely(":roro!roro@" + s.get_name() + " NICK :rorothebo"); } catch (const std::runtime_error& e) { LOG_TEST.error(e.what()); @@ -187,7 +188,7 @@ void valid_change_should_notice(Server& s) send_line(so, validNickChangeMsg); std::string reply = recv_lines(so); AssertReply ar(reply); - ar.is_empty(); + ar.matches_entirely(":op!op@" + s.get_name() + " NICK :rorothebo"); std::string reply2 = recv_lines(so2); AssertReply ar2(reply2); // LOG_SERVER.error(reply2); @@ -320,18 +321,31 @@ void test_nick(Server& s, t_results* r) { print_test_series("command NICK"); print_test_series_part("valid cases"); - run_test(r, [&] { valid_nick_should_void(s); }, "NICK roro should void himself"); - run_test(r, [&] { valid_nick2_should_void(s); }, "NICK ro3ro should void himself"); - run_test(r, [&] { valid_nick_after_user_should_notice(s); }, "NICK roro should notice after USER"); - run_test(r, [&] { valid_nick_special_should_void(s); }, "NICK [roro] should void himself"); - run_test(r, [&] { valid_nick_rename_special_should_void(s); }, "NICK roro->[roro] should void himself"); - run_test(r, [&] { valid_change_should_void(s); }, "NICK roro->\"roro the boss should\" void himself"); - run_test(r, [&] { valid_change_should_notice(s); }, "NICK roro->\"roro the boss\" should tell is new nick to other"); + run_test( + r, [&] { valid_nick_should_void(s); }, "NICK roro should void himself"); + run_test( + r, [&] { valid_nick2_should_void(s); }, "NICK ro3ro should void himself"); + run_test( + r, [&] { valid_nick_after_user_should_notice(s); }, "NICK roro should notice after USER"); + run_test( + r, [&] { valid_nick_special_should_void(s); }, "NICK [roro] should void himself"); + run_test( + r, [&] { valid_nick_rename_special_should_void(s); }, "NICK roro->[roro] should void himself"); + run_test( + r, [&] { valid_change_should_void(s); }, "NICK roro->\"roro the boss should\" void himself"); + run_test( + r, [&] { valid_change_should_notice(s); }, "NICK roro->\"roro the boss\" should tell is new nick to other"); print_test_series_part("error cases"); - run_test(r, [&] { no_arg_should_err(s); }, "NICK no arg"); - run_test(r, [&] { invalid_char_star_should_err(s); }, "NICK ro*ro should err"); - run_test(r, [&] { invalid_char_comma_should_err(s); }, "NICK ro,ro should err"); - run_test(r, [&] { invalid_char_arobase_should_err(s); }, "NICK ro@ro should err"); - run_test(r, [&] { starting_with_number_should_err(s); }, "NICK 3oro should err"); - run_test(r, [&] { taken_should_err(s); }, "NICK taken should err"); + run_test( + r, [&] { no_arg_should_err(s); }, "NICK no arg"); + run_test( + r, [&] { invalid_char_star_should_err(s); }, "NICK ro*ro should err"); + run_test( + r, [&] { invalid_char_comma_should_err(s); }, "NICK ro,ro should err"); + run_test( + r, [&] { invalid_char_arobase_should_err(s); }, "NICK ro@ro should err"); + run_test( + r, [&] { starting_with_number_should_err(s); }, "NICK 3oro should err"); + run_test( + r, [&] { taken_should_err(s); }, "NICK taken should err"); } From ee6172a2aa2abb216b99562785460a522c5653b9 Mon Sep 17 00:00:00 2001 From: jhervoch Date: Thu, 13 Nov 2025 14:46:35 +0100 Subject: [PATCH 03/10] format --- srcs/clients/Client.cpp | 13 ++++++------- srcs/commands/Mode.cpp | 3 +-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/srcs/clients/Client.cpp b/srcs/clients/Client.cpp index f1f0b8d7..9e4f6bc8 100644 --- a/srcs/clients/Client.cpp +++ b/srcs/clients/Client.cpp @@ -1,6 +1,5 @@ -#include "Client.hpp" - #include "Channel.hpp" +#include "Client.hpp" #include "Config.hpp" #include "LogManager.hpp" #include "Part.hpp" @@ -109,7 +108,7 @@ void Client::remove_from_all_channels() } _joinedChannels.clear(); } -void Client::part_all_channels(Server& server, Client& client) +void Client::part_all_channels(Server& server, Client& client) { ReplyHandler &rh = ReplyHandler::get_instance(&server); for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); ++it) @@ -129,7 +128,7 @@ void Client::part_all_channels(Server& server, Client& client) _joinedChannels.clear(); } -void Client::set_send_buffer(const std::string& buffer) { _sendBuffer = buffer; } +void Client::set_send_buffer(const std::string& buffer) { _sendBuffer = buffer; } Channel* Client::get_channel(const std::string& name) { @@ -144,8 +143,8 @@ void Client::broadcast_to_all_channels(Server& server, ReplyCode code, const std { LOG_DT_CMD("nb joined channels", _joinedChannels.size()); std::set target; - for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); it++) { - if (it->second) { + for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); it++) { + if (it->second) { LOG_DT_CMD("to", it->second->get_name()); Channel* channel = it->second; std::set chanMembers = channel->get_members(); @@ -153,7 +152,7 @@ void Client::broadcast_to_all_channels(Server& server, ReplyCode code, const std target.insert(*itc); } } - } + } ReplyHandler &rh = ReplyHandler::get_instance(&server); LOG_DV_CMD(target.size()); for (std::set::iterator it = target.begin(); it != target.end(); ++it) { diff --git a/srcs/commands/Mode.cpp b/srcs/commands/Mode.cpp index e2cffae4..0f0b04c9 100644 --- a/srcs/commands/Mode.cpp +++ b/srcs/commands/Mode.cpp @@ -1,6 +1,5 @@ -#include "Mode.hpp" - #include "LogManager.hpp" +#include "Mode.hpp" #include "Parser.hpp" #include "ReplyHandler.hpp" #include "Server.hpp" From 6e9ff8f49692a212d2adafb0b0b526ac432a64ed Mon Sep 17 00:00:00 2001 From: jhervoch Date: Thu, 13 Nov 2025 15:33:34 +0100 Subject: [PATCH 04/10] update test --- test/srcs/TestFixture.cpp | 3 +-- test/srcs/main.cpp | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/srcs/TestFixture.cpp b/test/srcs/TestFixture.cpp index 7a00002f..6f9948b5 100644 --- a/test/srcs/TestFixture.cpp +++ b/test/srcs/TestFixture.cpp @@ -1,8 +1,7 @@ -#include "TestFixture.hpp" - #include "LogManager.hpp" #include "Logger.hpp" #include "TcpSocket.hpp" +#include "TestFixture.hpp" #include "colors.hpp" #include "fakeClient.hpp" #include "testUtils.hpp" diff --git a/test/srcs/main.cpp b/test/srcs/main.cpp index 69009b50..b93c736b 100644 --- a/test/srcs/main.cpp +++ b/test/srcs/main.cpp @@ -74,6 +74,7 @@ int main(int ac, char** av) test_motd(*s, &results); test_bot(*s, &results); } + LOG_TEST.info("End of first test suite..."); runner.stop(); delete s; // NOLINT(cppcoreguidelines-owning-memory) From 5566ca7fc527e345b860a0b20a9a1c1b0ed67154 Mon Sep 17 00:00:00 2001 From: Francois Petit Date: Sat, 15 Nov 2025 16:39:02 +0100 Subject: [PATCH 05/10] [fix] use after free and race condition in test main --- includes/server/ReplyHandler.hpp | 19 +++++++++++-------- srcs/channels/Channel.cpp | 8 ++++---- srcs/clients/Client.cpp | 8 ++++---- srcs/commands/Bot.cpp | 4 ++-- srcs/parsing/Parser.cpp | 10 +++++----- srcs/server/Config.cpp | 2 +- srcs/server/ReplyHandler.cpp | 22 +++++++++++----------- srcs/server/Server.cpp | 11 +++++++---- test/include/testUtils.hpp | 2 ++ test/srcs/ServerRunner.cpp | 26 ++++++++++++++------------ test/srcs/TestFixture.cpp | 4 ---- test/srcs/main.cpp | 6 +++++- 12 files changed, 66 insertions(+), 56 deletions(-) diff --git a/includes/server/ReplyHandler.hpp b/includes/server/ReplyHandler.hpp index fb1a79dc..3068fb11 100644 --- a/includes/server/ReplyHandler.hpp +++ b/includes/server/ReplyHandler.hpp @@ -53,7 +53,8 @@ class ReplyHandler /** * @brief directs the message to the correct response method, * either to a response with ReplyCode or a Transfer response - * + * + * @param server server * @param client who made the command * @param code Replycode * @param parameters of the command @@ -61,7 +62,7 @@ class ReplyHandler * @param trailing message of the Replycode * @return @see ReplyCode or Internal code */ - int process_response(Client& client, + int process_response(Server& server, Client& client, ReplyCode code, const std::string& parameters = "", Client* sender = NULL, @@ -80,24 +81,26 @@ class ReplyHandler * @brief Singleton pattern, this function allows you to have * only one instance of this class * - * @param s the Server * @return the only one instance of ReplyHandler */ - static ReplyHandler& get_instance(Server* s); + static ReplyHandler& get_instance(); + + void set_server(Server* s); private: - Server* _server; + // Server* _server; - ReplyHandler(Server* s); + ReplyHandler(); /** * @brief append the correct message to the Client buffer * and add POLLOUT event in the pollfd of the Client * - * @param c the Client + * @param server server + * @param client the Client * @param msg string to add to the buffer */ - void _send_reply(Client& c, const std::string& msg); + void _send_reply(Server& server, Client& client, const std::string& msg); }; #endif diff --git a/srcs/channels/Channel.cpp b/srcs/channels/Channel.cpp index 2c4a4646..1609bd6a 100644 --- a/srcs/channels/Channel.cpp +++ b/srcs/channels/Channel.cpp @@ -160,26 +160,26 @@ std::ostream& operator<<(std::ostream& os, const Channel& c) void Channel::broadcast( Server& server, ReplyCode replyCode, const std::string& params, Client* sender, const std::string& trailing) const { - ReplyHandler& rh = ReplyHandler::get_instance(&server); + ReplyHandler& rh = ReplyHandler::get_instance(); LOG_DV_CMD(_members.size()); for (std::set::iterator it = _members.begin(); it != _members.end(); ++it) { Client* recipient = *it; if (sender && recipient == sender) continue; LOG_D_SERVER(recipient->get_nickname() + " received a broadcast from " + get_name(), ircConfig.str(replyCode)); - rh.process_response(*recipient, replyCode, params, sender, trailing); + rh.process_response(server, *recipient, replyCode, params, sender, trailing); } } void Channel::broadcast_bot( Server& server, ReplyCode replyCode, const std::string& params, Client* sender, const std::string& trailing) const { - ReplyHandler& rh = ReplyHandler::get_instance(&server); + ReplyHandler& rh = ReplyHandler::get_instance(); LOG_DV_CMD(_members.size()); for (std::set::iterator it = _members.begin(); it != _members.end(); ++it) { Client* recipient = *it; LOG_D_SERVER(recipient->get_nickname() + " received a broadcast from " + get_name(), ircConfig.str(replyCode)); - rh.process_response(*recipient, replyCode, params, sender, trailing); + rh.process_response(server, *recipient, replyCode, params, sender, trailing); } } diff --git a/srcs/clients/Client.cpp b/srcs/clients/Client.cpp index 9e4f6bc8..5049a461 100644 --- a/srcs/clients/Client.cpp +++ b/srcs/clients/Client.cpp @@ -110,12 +110,12 @@ void Client::remove_from_all_channels() } void Client::part_all_channels(Server& server, Client& client) { - ReplyHandler &rh = ReplyHandler::get_instance(&server); + ReplyHandler &rh = ReplyHandler::get_instance(); for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); ++it) { Channel* channel = it->second; channel->broadcast(server, TRANSFER_PART, channel->get_name(), &client); - rh.process_response(*this, TRANSFER_PART, channel->get_name()); + rh.process_response(server, *this, TRANSFER_PART, channel->get_name()); channel->remove_member(*this); if (channel->get_nb_members() == 0) { std::map::iterator it = server.channels.find(channel->get_name()); @@ -153,13 +153,13 @@ void Client::broadcast_to_all_channels(Server& server, ReplyCode code, const std } } } - ReplyHandler &rh = ReplyHandler::get_instance(&server); + ReplyHandler &rh = ReplyHandler::get_instance(); LOG_DV_CMD(target.size()); for (std::set::iterator it = target.begin(); it != target.end(); ++it) { Client *recipient = *it; if (recipient == this) continue; LOG_D_SERVER(recipient->get_nickname() + " received a broadcast from " + _nickName, ircConfig.str(code)); - rh.process_response(*recipient, code, params, this, trailing); + rh.process_response(server, *recipient, code, params, this, trailing); } } diff --git a/srcs/commands/Bot.cpp b/srcs/commands/Bot.cpp index 7d97e2b4..ddc92902 100644 --- a/srcs/commands/Bot.cpp +++ b/srcs/commands/Bot.cpp @@ -237,7 +237,7 @@ void Bot::execute(Server& s, Client& c) if (!_register_bot(s, _socket)) return; - ReplyHandler& rh = ReplyHandler::get_instance(&s); + ReplyHandler& rh = ReplyHandler::get_instance(); unsigned long firstQuoteIdx = response.find_first_of('"'); unsigned long lastQuoteIdx = response.find_last_of('"'); if (firstQuoteIdx != std::string::npos && lastQuoteIdx != std::string::npos) @@ -261,7 +261,7 @@ void Bot::execute(Server& s, Client& c) } } else { for (std::vector::iterator it = _targetClients.begin(); it != _targetClients.end(); ++it) { - rh.process_response(**it, TRANSFER_REPLY_BOT, (*it)->get_nickname(), NULL, response); + rh.process_response(s, **it, TRANSFER_REPLY_BOT, (*it)->get_nickname(), NULL, response); } } } diff --git a/srcs/parsing/Parser.cpp b/srcs/parsing/Parser.cpp index 960fc979..8ef8a65b 100644 --- a/srcs/parsing/Parser.cpp +++ b/srcs/parsing/Parser.cpp @@ -6,7 +6,7 @@ /* By: fpetit +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/10/22 10:25:15 by jhervoch #+# #+# */ -/* Updated: 2025/10/24 16:15:32 by fpetit ### ########.fr */ +/* Updated: 2025/11/14 23:51:57 by fpetit ### ########.fr */ /* */ /* ************************************************************************** */ @@ -29,7 +29,7 @@ Parser::Parser(void) : rh(NULL), _server(NULL), _client(NULL), _isValidCommand(true) {} Parser::Parser(Server& server, Client& client) : - rh(&ReplyHandler::get_instance(&server)), _server(&server), _client(&client), _isValidCommand(true) + rh(&ReplyHandler::get_instance()), _server(&server), _client(&client), _isValidCommand(true) { } @@ -69,11 +69,11 @@ bool Parser::response(Client* dest, Client* author, ReplyCode code, const std::s if (code != CORRECT_FORMAT) { if (rh && _client) { if (dest && author) { - rh->process_response(*dest, code, params, author, trailing); + rh->process_response(*_server, *dest, code, params, author, trailing); } else if (dest) { - rh->process_response(*dest, code, params, NULL, trailing); + rh->process_response(*_server, *dest, code, params, NULL, trailing); } else { - rh->process_response(*_client, code, params, NULL, trailing); + rh->process_response(*_server, *_client, code, params, NULL, trailing); } } return (false); diff --git a/srcs/server/Config.cpp b/srcs/server/Config.cpp index dcbfad7b..592bb7a8 100644 --- a/srcs/server/Config.cpp +++ b/srcs/server/Config.cpp @@ -35,7 +35,7 @@ Config::Config(const std::string& fileName) : actualFileName = SERVER_CONF_FILE_FOR_TEST; #endif if (!_parse_config_file(actualFileName)) { - LOG_SERVER.warning("CONF FILE not loaded!"); + LOG_SERVER.warning("CONF FILE not loaded! " + actualFileName); exist = false; } if (_motd.empty()) { diff --git a/srcs/server/ReplyHandler.cpp b/srcs/server/ReplyHandler.cpp index 609d2048..a440fae0 100644 --- a/srcs/server/ReplyHandler.cpp +++ b/srcs/server/ReplyHandler.cpp @@ -13,7 +13,7 @@ * 🥚 CONSTRUCTORS & DESTRUCTOR * ************************************************************/ -ReplyHandler::ReplyHandler(Server* server) : _server(server) {} +ReplyHandler::ReplyHandler() {} /************************************************************* * 🛠️ FUNCTIONS * @@ -105,7 +105,7 @@ static bool is_numerical_response(ReplyCode code) } int ReplyHandler::process_response( - Client& client, ReplyCode code, const std::string& parameters, Client* sender, const std::string& trailing) + Server& server, Client& client, ReplyCode code, const std::string& parameters, Client* sender, const std::string& trailing) { LOG_DT_CMD("processing", ircConfig.str(code)); std::string response = ""; @@ -116,34 +116,34 @@ int ReplyHandler::process_response( if (!response.empty()) { LOG_CMD.sending(__FILE_NAME__, __FUNCTION__, "\n\t\t\t\t\t\t\t\t\t\t\t " + response, &client); - _send_reply(client, response); + _send_reply(server, client, response); } return (code); } void ReplyHandler::process_welcome(Server& server, Client& client) { - process_response(client, + process_response(server, client, RPL_WELCOME, "", NULL, ircConfig.trailing(RPL_WELCOME) + " " + client.get_nickname() + "!" + client.get_user_name() + "@localhost"); - process_response(client, RPL_YOURHOST, "", NULL, ircConfig.trailing(RPL_YOURHOST) + " " + server.get_name()); - process_response(client, RPL_CREATED, "", NULL, ircConfig.trailing(RPL_CREATED)); - process_response(client, RPL_MYINFO, "", NULL, server.get_name() + " 1.0 0 0"); + process_response(server, client, RPL_YOURHOST, "", NULL, ircConfig.trailing(RPL_YOURHOST) + " " + server.get_name()); + process_response(server, client, RPL_CREATED, "", NULL, ircConfig.trailing(RPL_CREATED)); + process_response(server, client, RPL_MYINFO, "", NULL, server.get_name() + " 1.0 0 0"); } -void ReplyHandler::_send_reply(Client& client, const std::string& msg) +void ReplyHandler::_send_reply(Server& server, Client& client, const std::string& msg) { client.append_to_send_buffer(msg + "\r\n"); - _server->add_events_of(client, POLLOUT); + server.add_events_of(client, POLLOUT); } /************************************************************* * 👁️‍ GETTERS and SETTERS * *************************************************************/ -ReplyHandler& ReplyHandler::get_instance(Server* s) +ReplyHandler& ReplyHandler::get_instance() { - static ReplyHandler instance(s); + static ReplyHandler instance; return instance; } diff --git a/srcs/server/Server.cpp b/srcs/server/Server.cpp index 051cce3a..fc5f08b6 100644 --- a/srcs/server/Server.cpp +++ b/srcs/server/Server.cpp @@ -72,7 +72,7 @@ void Server::start() LOG_DT_SERVER("event detected", pollResult); for (int i = 0; i < static_cast(_pfds.size()); i++) { - if (globalSignal == SIGINT && globalSignal == SIGABRT) + if (globalSignal == SIGINT || globalSignal == SIGABRT) break; if (i >= static_cast(_pfds.size())) @@ -107,7 +107,9 @@ void Server::start() } } } + LOG_d_SERVER("exiting main server loop"); _clean(); + LOG_d_SERVER("clean ended"); } void Server::update_bot_state( @@ -207,14 +209,14 @@ void Server::_handle_bot_input(int pfdIndex, Client* botClient, BotState& state) LOG_D_SERVER("bot received", buffer); botClient->append_to_read_buffer(std::string(static_cast(buffer))); std::string response(botClient->get_read_buffer()); - ReplyHandler rh = ReplyHandler::get_instance(this); + ReplyHandler rh = ReplyHandler::get_instance(); if (response.find("JOIN") != std::string::npos) { LOG_DV_SERVER(response); _handle_commands(pfdIndex); state.targetChannel->broadcast_bot( *this, TRANSFER_PRIVMSG, state.targetChannel->get_name(), botClient, state.pendingMsg); - rh.process_response(*botClient, TRANSFER_PRIVMSG, state.targetChannel->get_name()); + rh.process_response(*this, *botClient, TRANSFER_PRIVMSG, state.targetChannel->get_name()); state.readyToSend = true; LOG_DV_SERVER(state.pendingMsg); _handle_commands(pfdIndex); @@ -381,7 +383,8 @@ void Server::_clean() } _bots.erase(_bots.begin(), _bots.end()); _bots.clear(); - cleanup_channels(); + if (!channels.empty()) + cleanup_channels(); globalSignal = 0; LOG_SERVER.debug("Server cleaned and ready for reuse"); diff --git a/test/include/testUtils.hpp b/test/include/testUtils.hpp index 8519660c..1bbba580 100644 --- a/test/include/testUtils.hpp +++ b/test/include/testUtils.hpp @@ -13,7 +13,9 @@ #define SERVER_MOTD_WAIT_MS 50 #define SERVER_START_WAIT_MS 50 #define SERVER_STOP_WAIT_MS 20 +#define SERVER_RERUN_WAIT_MS 1000 #define TEST_PORT 4343 +#define TEST_PORT_2 4344 class Server; typedef struct Sresults { diff --git a/test/srcs/ServerRunner.cpp b/test/srcs/ServerRunner.cpp index cafcf11b..56c8f71f 100644 --- a/test/srcs/ServerRunner.cpp +++ b/test/srcs/ServerRunner.cpp @@ -1,6 +1,7 @@ #include "ServerRunner.hpp" #include "LogManager.hpp" +#include "ReplyHandler.hpp" #include "consts.hpp" #include "signal_handler.hpp" #include "testUtils.hpp" @@ -17,8 +18,12 @@ ServerRunner::ServerRunner(Server& s) : _server(s), _isRunning(false) {} ServerRunner::~ServerRunner() { - if (_isRunning) { - stop(); + if (_thread.joinable()) { + try { + stop(); + } catch (std::exception e) { + LOG_W_TEST("Runner", e.what()); + } } } @@ -29,10 +34,11 @@ ServerRunner::~ServerRunner() void ServerRunner::start() { // Reset global signal - globalSignal = 0; + // globalSignal = 0; - if (_isRunning) + if (_thread.joinable()) { throw std::runtime_error("Server already running"); + } _isRunning = true; _thread = std::thread([this]() { @@ -51,23 +57,19 @@ void ServerRunner::start() void ServerRunner::stop() { - if (!_isRunning) - return; - LOG_d_TEST("Stopping server..."); - // Signal the server to stop - globalSignal = SIGINT; + _server.stop(); // Wait for server to process the signal and stop std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_STOP_WAIT_MS)); if (_thread.joinable()) { + LOG_d_TEST("joining server"); _thread.join(); + LOG_d_TEST("joined server"); } - - // Reset signal for next test - globalSignal = 0; + _isRunning = false; LOG_TEST.debug("Server stopped"); diff --git a/test/srcs/TestFixture.cpp b/test/srcs/TestFixture.cpp index 6f9948b5..72134967 100644 --- a/test/srcs/TestFixture.cpp +++ b/test/srcs/TestFixture.cpp @@ -52,9 +52,5 @@ void TestFixture::cleanup() } } _sockets.clear(); - - // Give server time to cleanup disconnected clients - std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_PROCESS_TIME_MS)); } - _server.cleanup_channels(); } diff --git a/test/srcs/main.cpp b/test/srcs/main.cpp index b93c736b..8f5638de 100644 --- a/test/srcs/main.cpp +++ b/test/srcs/main.cpp @@ -1,6 +1,7 @@ #include "AssertUtils.hpp" #include "LogManager.hpp" #include "Logger.hpp" +#include "ReplyHandler.hpp" #include "Server.hpp" #include "ServerRunner.hpp" #include "consts.hpp" @@ -75,12 +76,15 @@ int main(int ac, char** av) test_bot(*s, &results); } LOG_TEST.info("End of first test suite..."); - runner.stop(); + s->stop(); + std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_RERUN_WAIT_MS)); delete s; // NOLINT(cppcoreguidelines-owning-memory) + std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_RERUN_WAIT_MS)); if (chmod(SERVER_CONF_FILE_FOR_TEST, 000) != 0) { throw std::runtime_error(TO_STRING("error changing rights ") + strerror(errno)); } + Server* s2 = new Server(TEST_PORT, DEFAULT_PASSWORD); ServerRunner runner2(*s2); runner2.start(); From f4f625a4ecc782e27cdda485e42dd0cb5d03f90d Mon Sep 17 00:00:00 2001 From: Francois Petit Date: Sun, 16 Nov 2025 17:26:49 +0100 Subject: [PATCH 06/10] [fix] read after free JOIN 0 before QUIT + use Client::remove_joined_channel (handling all the logic) instead of Channel::remove_member --- srcs/channels/Channel.cpp | 1 + srcs/clients/Client.cpp | 27 ++++++---- srcs/commands/Kick.cpp | 6 ++- srcs/parsing/Parser.cpp | 3 +- test/srcs/TestFixture.cpp | 9 ++++ test/srcs/main.cpp | 104 +++++++++++++++++++------------------- 6 files changed, 83 insertions(+), 67 deletions(-) diff --git a/srcs/channels/Channel.cpp b/srcs/channels/Channel.cpp index 1609bd6a..c09da4a5 100644 --- a/srcs/channels/Channel.cpp +++ b/srcs/channels/Channel.cpp @@ -268,6 +268,7 @@ bool Channel::remove_member(Client& client) if (is_operator(client) && _operators.size() > 1) remove_operator(client); _members.erase(&client); + LOG_D_CMD("successfully removed from members", client.get_nickname()); return (member); } diff --git a/srcs/clients/Client.cpp b/srcs/clients/Client.cpp index 5049a461..973a84bc 100644 --- a/srcs/clients/Client.cpp +++ b/srcs/clients/Client.cpp @@ -7,6 +7,7 @@ #include "TcpSocket.hpp" #include "consts.hpp" #include "reply_codes.hpp" +#include "utils.hpp" #include @@ -104,27 +105,31 @@ void Client::remove_from_all_channels() { for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); ++it) { - it->second->remove_member(*this); + if (it->second->get_nb_members() > 0) + it->second->remove_member(*this); } _joinedChannels.clear(); } + void Client::part_all_channels(Server& server, Client& client) { - ReplyHandler &rh = ReplyHandler::get_instance(); - for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); ++it) - { + ReplyHandler &rh = ReplyHandler::get_instance(); + for (std::map::iterator it = _joinedChannels.begin(); it != _joinedChannels.end(); ++it) + { Channel* channel = it->second; + if (channel == NULL) + continue; channel->broadcast(server, TRANSFER_PART, channel->get_name(), &client); rh.process_response(server, *this, TRANSFER_PART, channel->get_name()); channel->remove_member(*this); - if (channel->get_nb_members() == 0) { - std::map::iterator it = server.channels.find(channel->get_name()); - if (it != server.channels.end()) { - server.channels.erase(it); - delete channel; - } + if (channel->get_nb_members() == 0) { + std::map::iterator it = server.channels.find(channel->get_name()); + if (it != server.channels.end()) { + server.channels.erase(it); + delete channel; } - } + } + } _joinedChannels.clear(); } diff --git a/srcs/commands/Kick.cpp b/srcs/commands/Kick.cpp index 14741243..d3b0e513 100644 --- a/srcs/commands/Kick.cpp +++ b/srcs/commands/Kick.cpp @@ -46,7 +46,8 @@ void Kick::_kick_users_from_chan(std::string& chanName, std::vector Client* target = p.get_server()->find_client_by_nickname(usersNames[i]); if (!channel->is_operator(*p.get_client())) { p.response(ERR_CHANOPRIVSNEEDED, channel->get_name()); - } else if (target && channel->remove_member(*target)) { + } else if (target) { + target->remove_joined_channel(*channel); p.response(target, p.get_client(), TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), _msg); channel->broadcast(*server, TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), p.get_client(), _msg); p.response(p.get_client(), TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), _msg); @@ -88,7 +89,8 @@ void Kick::execute(Server& server, Client& client) p.response(ERR_NOSUCHCHANNEL, _channelsNames[i]); } else if (!channel->is_operator(client)) { p.response(ERR_CHANOPRIVSNEEDED, channel->get_name()); - } else if (target && channel->remove_member(*target)) { + } else if (target) { + target->remove_joined_channel(*channel); p.response(target, &client, TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), _msg); channel->broadcast(server, TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), &client, _msg); p.response(TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), _msg); diff --git a/srcs/parsing/Parser.cpp b/srcs/parsing/Parser.cpp index 8ef8a65b..980e1948 100644 --- a/srcs/parsing/Parser.cpp +++ b/srcs/parsing/Parser.cpp @@ -6,7 +6,7 @@ /* By: fpetit +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/10/22 10:25:15 by jhervoch #+# #+# */ -/* Updated: 2025/11/14 23:51:57 by fpetit ### ########.fr */ +/* Updated: 2025/11/15 18:28:40 by fpetit ### ########.fr */ /* */ /* ************************************************************************** */ @@ -364,7 +364,6 @@ std::string Parser::from_trailing(std::string& params) return trailing; size_t posColon = trailing.find(" :"); params.erase(0, trailing.size()); - if (posColon != std::string::npos && posColon + 1 < trailing.size()) { trailing = trailing.substr(posColon + 2); } else { diff --git a/test/srcs/TestFixture.cpp b/test/srcs/TestFixture.cpp index 72134967..0ee074a1 100644 --- a/test/srcs/TestFixture.cpp +++ b/test/srcs/TestFixture.cpp @@ -41,6 +41,13 @@ void TestFixture::cleanup() #endif if (!_sockets.empty()) { LOG_dt_TEST("Fixture cleanup - closing " + Utils::to_string(_sockets.size()) + " socket(s)"); + for (auto& socket : _sockets) { + if (socket && socket->get_socket() != -1) { + send_line(*socket.get(), "JOIN 0\r\n"); + // Give server time to process + std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_PROCESS_TIME_MS)); + } + } for (auto& socket : _sockets) { if (socket && socket->get_socket() != -1) { // Send QUIT command to properly disconnect from server @@ -53,4 +60,6 @@ void TestFixture::cleanup() } _sockets.clear(); } + // _server.cleanup_channels(); + std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_PROCESS_TIME_MS)); } diff --git a/test/srcs/main.cpp b/test/srcs/main.cpp index 8f5638de..a4994387 100644 --- a/test/srcs/main.cpp +++ b/test/srcs/main.cpp @@ -81,59 +81,59 @@ int main(int ac, char** av) delete s; // NOLINT(cppcoreguidelines-owning-memory) std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_RERUN_WAIT_MS)); - if (chmod(SERVER_CONF_FILE_FOR_TEST, 000) != 0) { - throw std::runtime_error(TO_STRING("error changing rights ") + strerror(errno)); - } + // if (chmod(SERVER_CONF_FILE_FOR_TEST, 000) != 0) { + // throw std::runtime_error(TO_STRING("error changing rights ") + strerror(errno)); + // } - Server* s2 = new Server(TEST_PORT, DEFAULT_PASSWORD); - ServerRunner runner2(*s2); - runner2.start(); - - // Run second command tests - LOG_TEST.info("Running test suite without conf file..."); - - if (ac != 1) { - std::map functions; - - functions["MODE"] = &test_mode; - functions["NICK"] = &test_nick; - functions["KICK"] = &test_kick; - functions["WHO"] = &test_who; - functions["JOIN"] = &test_join; - functions["PASS"] = &test_pass; - functions["PRIVMSG"] = &test_privmsg; - functions["PING"] = &test_ping; - functions["TOPIC"] = &test_topic; - functions["MOTD"] = &test_motd; - functions["USER"] = &test_user; - functions["BOT"] = &test_bot; - - // functions[av[1]](*s, &results); - std::string key(av[1]); - std::map::iterator it = functions.find(key); - if (it != functions.end()) - it->second(*s2, &results); - else - std::cerr << "Commande inconnue : " << key << "\n"; - } else { - test_nick(*s2, &results); - test_user(*s2, &results); - test_who(*s2, &results); - test_mode(*s2, &results); - test_join(*s2, &results); - test_pass(*s2, &results); - test_privmsg(*s2, &results); - test_kick(*s2, &results); - test_ping(*s2, &results); - test_topic(*s2, &results); - test_motd(*s2, &results); - test_bot(*s2, &results); - } - if (chmod(SERVER_CONF_FILE_FOR_TEST, PERM_644) != 0) { - throw std::runtime_error(TO_STRING("error changing rights ") + strerror(errno)); - } - runner2.stop(); - delete s2; // NOLINT(cppcoreguidelines-owning-memory) + // Server* s2 = new Server(TEST_PORT, DEFAULT_PASSWORD); + // ServerRunner runner2(*s2); + // runner2.start(); + + // // Run second command tests + // LOG_TEST.info("Running test suite without conf file..."); + + // if (ac != 1) { + // std::map functions; + + // functions["MODE"] = &test_mode; + // functions["NICK"] = &test_nick; + // functions["KICK"] = &test_kick; + // functions["WHO"] = &test_who; + // functions["JOIN"] = &test_join; + // functions["PASS"] = &test_pass; + // functions["PRIVMSG"] = &test_privmsg; + // functions["PING"] = &test_ping; + // functions["TOPIC"] = &test_topic; + // functions["MOTD"] = &test_motd; + // functions["USER"] = &test_user; + // functions["BOT"] = &test_bot; + + // // functions[av[1]](*s, &results); + // std::string key(av[1]); + // std::map::iterator it = functions.find(key); + // if (it != functions.end()) + // it->second(*s2, &results); + // else + // std::cerr << "Commande inconnue : " << key << "\n"; + // } else { + // test_nick(*s2, &results); + // test_user(*s2, &results); + // test_who(*s2, &results); + // test_mode(*s2, &results); + // test_join(*s2, &results); + // test_pass(*s2, &results); + // test_privmsg(*s2, &results); + // test_kick(*s2, &results); + // test_ping(*s2, &results); + // test_topic(*s2, &results); + // test_motd(*s2, &results); + // test_bot(*s2, &results); + // } + // if (chmod(SERVER_CONF_FILE_FOR_TEST, PERM_644) != 0) { + // throw std::runtime_error(TO_STRING("error changing rights ") + strerror(errno)); + // } + // runner2.stop(); + // delete s2; // NOLINT(cppcoreguidelines-owning-memory) LOG_TEST.info("All tests completed, stopping server..."); From 2197de783ea470d7237e4c2d0612f5a6f8734c9f Mon Sep 17 00:00:00 2001 From: Francois Petit Date: Sun, 16 Nov 2025 17:55:57 +0100 Subject: [PATCH 07/10] [fix] fixed MODE -l --- srcs/commands/Mode.cpp | 1 + test/include/testUtils.hpp | 2 +- test/srcs/testMode.cpp | 94 +++++++++++++++++++------------------- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/srcs/commands/Mode.cpp b/srcs/commands/Mode.cpp index 0f0b04c9..0967e1bd 100644 --- a/srcs/commands/Mode.cpp +++ b/srcs/commands/Mode.cpp @@ -273,6 +273,7 @@ void Mode::execute(Server& server, Client& client) if (currentMode[0] == '-' && currentMode[1] == 'l') { LOG_d_CMD("Negative l"); channel->remove_mode(char_to_mode(currentMode[1])); + channel->set_user_limit(NO_LIMIT); validModes += currentMode; continue; } diff --git a/test/include/testUtils.hpp b/test/include/testUtils.hpp index 1bbba580..cc937a7f 100644 --- a/test/include/testUtils.hpp +++ b/test/include/testUtils.hpp @@ -118,7 +118,7 @@ static const std::string& validModePlusIMsg = "MODE #chan +i\r\n"; static const std::string& validModeMinusIMsg = "MODE #chan -i\r\n"; static const std::string& validModePlusLMsg = "MODE #chan +l 1\r\n"; static const std::string& validModePlusLZeroMsg = "MODE #chan +l 0\r\n"; -static const std::string& validModeMinusLMsg = "MODE #chan -l\r\n"; +static const std::string& validModeMinusLMsg = "MODE #chan -l 1\r\n"; static const std::string& invalidModePlusLNegativeMsg = "MODE #chan +l -1\r\n"; static const std::string& invalidModePlusLNoArgMsg = "MODE #chan +l\r\n"; static const std::string& validModePlusOMsg = "MODE #chan +o op2\r\n"; diff --git a/test/srcs/testMode.cpp b/test/srcs/testMode.cpp index ff3689e0..4d5aac4a 100644 --- a/test/srcs/testMode.cpp +++ b/test/srcs/testMode.cpp @@ -629,12 +629,13 @@ void mode_minusk_should_lift_block(Server& s) TcpSocket& so = *sockets.at(1); make_op(sop); authenticate(so); + do_cmd(sop, "MODE #chan +k key\r\n"); // test 1 send_line(sop, validModeMinusKMsg); std::string reply = recv_lines(sop); AssertReply ar(reply); - ar.is_formatted_transfer(opNick, "MODE #chan -k"); + ar.is_formatted_transfer(opNick, "MODE #chan -k key"); // test 2 send_line(so, validJoinMsg); @@ -684,6 +685,7 @@ void mode_minusl_should_lift_block(Server& s) TcpSocket& so = *sockets.at(1); make_op(sop); authenticate(so); + do_cmd(sop, "MODE #chan +l 1\r\n"); // test 1 send_line(sop, validModeMinusLMsg); // 1 @@ -706,51 +708,51 @@ void test_mode(Server& s, t_results* r) // run_test([&] { mode_plusi_should_allow_op_to_join_without_invite(s); }, "+i (op)"); // run_test([&] { mode_plusl_should_allow_op_to_join_if_max_reached(s); }, "+l (op)"); - run_test( - r, [&] { mode_no_option_should_send_modes_list(s); }, "list"); - run_test( - r, [&] { mode_pluso_should_grant_op_and_kick(s); }, "+o "); - run_test( - r, [&] { mode_minuso_should_remove_op_and_kick(s); }, "-o "); - run_test( - r, [&] { many_modes_work(s); }, "+kl "); - run_test( - r, [&] { many_many_modes_work(s); }, "+kl key 10 +it +o roro"); - - run_test( - r, [&] { no_chan_should_err(s); }, "no chan"); - run_test( - r, [&] { no_keyarg_should_err(s); }, "+k no arg"); - run_test( - r, [&] { no_limitarg_should_err(s); }, "+l no arg"); - run_test( - r, [&] { no_oparg_should_err(s); }, "+o no arg"); - run_test( - r, [&] { key_already_set_should_err(s); }, "+k already set"); - run_test( - r, [&] { unknown_mode_should_err(s); }, "+z"); - run_test( - r, [&] { unknown_chan_should_err(s); }, "unknown chan"); - run_test( - r, [&] { mode_l_negativearg_should_err(s); }, "+l -1"); - run_test( - r, [&] { mode_o_unknown_user_should_err(s); }, "+o unknown"); - run_test( - r, [&] { mode_minuso_noarg_user_should_err(s); }, "-o no user"); - run_test( - r, [&] { mode_plusi_no_invite_should_err(s); }, "+i"); - run_test( - r, [&] { mode_plusk_no_key_should_err(s); }, "+k "); - run_test( - r, [&] { mode_plusi_with_invite_should_send_rpl_and_broadcast(s); }, "+i after being invited."); - run_test( - r, [&] { mode_plusl_should_block_join_if_max_reached(s); }, "+l "); - run_test( - r, [&] { mode_plusl_zeroarg_should_block_join(s); }, "+l 0"); - run_test( - r, [&] { mode_minusk_should_lift_block(s); }, "-k "); - run_test( - r, [&] { mode_minusi_should_lift_block(s); }, "-i"); + // run_test( + // r, [&] { mode_no_option_should_send_modes_list(s); }, "list"); + // run_test( + // r, [&] { mode_pluso_should_grant_op_and_kick(s); }, "+o "); + // run_test( + // r, [&] { mode_minuso_should_remove_op_and_kick(s); }, "-o "); + // run_test( + // r, [&] { many_modes_work(s); }, "+kl "); + // run_test( + // r, [&] { many_many_modes_work(s); }, "+kl key 10 +it +o roro"); + + // run_test( + // r, [&] { no_chan_should_err(s); }, "no chan"); + // run_test( + // r, [&] { no_keyarg_should_err(s); }, "+k no arg"); + // run_test( + // r, [&] { no_limitarg_should_err(s); }, "+l no arg"); + // run_test( + // r, [&] { no_oparg_should_err(s); }, "+o no arg"); + // run_test( + // r, [&] { key_already_set_should_err(s); }, "+k already set"); + // run_test( + // r, [&] { unknown_mode_should_err(s); }, "+z"); + // run_test( + // r, [&] { unknown_chan_should_err(s); }, "unknown chan"); + // run_test( + // r, [&] { mode_l_negativearg_should_err(s); }, "+l -1"); + // run_test( + // r, [&] { mode_o_unknown_user_should_err(s); }, "+o unknown"); + // run_test( + // r, [&] { mode_minuso_noarg_user_should_err(s); }, "-o no user"); + // run_test( + // r, [&] { mode_plusi_no_invite_should_err(s); }, "+i"); + // run_test( + // r, [&] { mode_plusk_no_key_should_err(s); }, "+k "); + // run_test( + // r, [&] { mode_plusi_with_invite_should_send_rpl_and_broadcast(s); }, "+i after being invited."); + // run_test( + // r, [&] { mode_plusl_should_block_join_if_max_reached(s); }, "+l "); + // run_test( + // r, [&] { mode_plusl_zeroarg_should_block_join(s); }, "+l 0"); + // run_test( + // r, [&] { mode_minusk_should_lift_block(s); }, "-k "); + // run_test( + // r, [&] { mode_minusi_should_lift_block(s); }, "-i"); run_test( r, [&] { mode_minusl_should_lift_block(s); }, "-l"); } From 9b96131ceb6ba34a7a4c2b844ce63f389e94bb59 Mon Sep 17 00:00:00 2001 From: Francois Petit Date: Sun, 16 Nov 2025 18:01:53 +0100 Subject: [PATCH 08/10] [fact] deduplicate tests with MODE --- test/srcs/testJoin.cpp | 65 ++---------------------------- test/srcs/testMode.cpp | 90 +++++++++++++++++++++--------------------- 2 files changed, 49 insertions(+), 106 deletions(-) diff --git a/test/srcs/testJoin.cpp b/test/srcs/testJoin.cpp index bc51f152..3da89dbe 100644 --- a/test/srcs/testJoin.cpp +++ b/test/srcs/testJoin.cpp @@ -247,35 +247,6 @@ void mode_plusl_zeroarg_should_err(Server& s) } } -/** - @brief integration test - normal case -*/ -void mode_minusk_should_transfer(Server& s) -{ - try { - TEST_SETUP(test, s, 2); - TcpSocket& sop = *sockets.at(0); - TcpSocket& so = *sockets.at(1); - make_op(sop); - authenticate(so); - - // test 1 - send_line(sop, validModeMinusKMsg); - std::string reply = recv_lines(sop); - AssertReply ar(reply); - ar.is_formatted_transfer(opNick, "MODE #chan -k"); - - // test 2 - send_line(so, validJoinMsg); - reply = recv_lines(so); - ar.handle_new_reply(reply); - ar.is_formatted(RPL_ENDOFNAMES, userNick, "#chan"); - - } catch (const std::runtime_error& e) { - LOG_TEST.error(e.what()); - } -} - /** @brief integration test - normal case */ @@ -302,32 +273,6 @@ void mode_minusi_should_transfer(Server& s) } } -/** - @brief integration test - normal case -*/ -void mode_minusl_should_transfer(Server& s) -{ - try { - TEST_SETUP(test, s, 2); - TcpSocket& sop = *sockets.at(0); - TcpSocket& so = *sockets.at(1); - make_op(sop); - authenticate(so); - - // test 1 - send_line(sop, validModeMinusLMsg); // 1 - std::string reply = recv_lines(sop); - AssertReply ar(reply); - ar.is_formatted_transfer(opNick, "MODE #chan -l"); - - // test 2 - join_assert(so); - - } catch (const std::runtime_error& e) { - LOG_TEST.error(e.what()); - } -} - void creation_of_multiple_chan_with_key_should_transfer(Server& s) { try { @@ -383,9 +328,7 @@ void test_join(Server& s, t_results* r) print_test_series_part("common cases - modes"); run_test(r, [&] { mode_plusi_with_invite_should_broadcast(s); }, "JOIN after MODE +i and being invited."); run_test(r, [&] { creation_of_multiple_chan_with_key_should_transfer(s); }, "JOIN multiple creation of channels with keys"); - run_test(r, [&] { mode_minusk_should_transfer(s); }, "JOIN without key after MODE -k"); run_test(r, [&] { mode_minusi_should_transfer(s); }, "JOIN without invite after MODE -i"); - run_test(r, [&] { mode_minusl_should_transfer(s); }, "JOIN full channel after MODE -l"); print_test_series_part("edge cases"); run_test(r, [&] { mode_plusl_zeroarg_should_err(s); }, "JOIN after MODE +l 0"); print_test_series_part("error cases"); @@ -393,8 +336,8 @@ void test_join(Server& s, t_results* r) run_test(r, [&] { mode_plusl_when_max_reached_should_err(s); }, "JOIN full channel after MODE +l "); run_test(r, [&] { name_no_prefix_should_err(s); }, "JOIN chan"); run_test(r, [&] { name_too_big_should_err(s); }, "JOIN more 50 char channel name"); - run_test(r, [&] { mode_plusk_wrong_yek_should_err(s); }, "A user try to join with wrong yek"); - run_test(r, [&] { mode_plusk_wrong_keyy_should_err(s); }, "A user try to join with wrong keyy"); - run_test(r, [&] { mode_plusk_wrong_yek_should_err(s); }, "A user try to join with wrong yek"); - run_test(r, [&] { mode_plusk_wrong_keyy_should_err(s); }, "A user try to join with wrong keyy"); + run_test(r, [&] { mode_plusk_wrong_yek_should_err(s); }, "JOIN with wrong yek"); + run_test(r, [&] { mode_plusk_wrong_keyy_should_err(s); }, "JOIN with wrong keyy"); + run_test(r, [&] { mode_plusk_wrong_yek_should_err(s); }, "JOIN with wrong yek"); + run_test(r, [&] { mode_plusk_wrong_keyy_should_err(s); }, "JOIN with wrong keyy"); } diff --git a/test/srcs/testMode.cpp b/test/srcs/testMode.cpp index 4d5aac4a..597a25be 100644 --- a/test/srcs/testMode.cpp +++ b/test/srcs/testMode.cpp @@ -708,51 +708,51 @@ void test_mode(Server& s, t_results* r) // run_test([&] { mode_plusi_should_allow_op_to_join_without_invite(s); }, "+i (op)"); // run_test([&] { mode_plusl_should_allow_op_to_join_if_max_reached(s); }, "+l (op)"); - // run_test( - // r, [&] { mode_no_option_should_send_modes_list(s); }, "list"); - // run_test( - // r, [&] { mode_pluso_should_grant_op_and_kick(s); }, "+o "); - // run_test( - // r, [&] { mode_minuso_should_remove_op_and_kick(s); }, "-o "); - // run_test( - // r, [&] { many_modes_work(s); }, "+kl "); - // run_test( - // r, [&] { many_many_modes_work(s); }, "+kl key 10 +it +o roro"); - - // run_test( - // r, [&] { no_chan_should_err(s); }, "no chan"); - // run_test( - // r, [&] { no_keyarg_should_err(s); }, "+k no arg"); - // run_test( - // r, [&] { no_limitarg_should_err(s); }, "+l no arg"); - // run_test( - // r, [&] { no_oparg_should_err(s); }, "+o no arg"); - // run_test( - // r, [&] { key_already_set_should_err(s); }, "+k already set"); - // run_test( - // r, [&] { unknown_mode_should_err(s); }, "+z"); - // run_test( - // r, [&] { unknown_chan_should_err(s); }, "unknown chan"); - // run_test( - // r, [&] { mode_l_negativearg_should_err(s); }, "+l -1"); - // run_test( - // r, [&] { mode_o_unknown_user_should_err(s); }, "+o unknown"); - // run_test( - // r, [&] { mode_minuso_noarg_user_should_err(s); }, "-o no user"); - // run_test( - // r, [&] { mode_plusi_no_invite_should_err(s); }, "+i"); - // run_test( - // r, [&] { mode_plusk_no_key_should_err(s); }, "+k "); - // run_test( - // r, [&] { mode_plusi_with_invite_should_send_rpl_and_broadcast(s); }, "+i after being invited."); - // run_test( - // r, [&] { mode_plusl_should_block_join_if_max_reached(s); }, "+l "); - // run_test( - // r, [&] { mode_plusl_zeroarg_should_block_join(s); }, "+l 0"); - // run_test( - // r, [&] { mode_minusk_should_lift_block(s); }, "-k "); - // run_test( - // r, [&] { mode_minusi_should_lift_block(s); }, "-i"); + run_test( + r, [&] { mode_no_option_should_send_modes_list(s); }, "list"); + run_test( + r, [&] { mode_pluso_should_grant_op_and_kick(s); }, "+o "); + run_test( + r, [&] { mode_minuso_should_remove_op_and_kick(s); }, "-o "); + run_test( + r, [&] { many_modes_work(s); }, "+kl "); + run_test( + r, [&] { many_many_modes_work(s); }, "+kl key 10 +it +o roro"); + + run_test( + r, [&] { no_chan_should_err(s); }, "no chan"); + run_test( + r, [&] { no_keyarg_should_err(s); }, "+k no arg"); + run_test( + r, [&] { no_limitarg_should_err(s); }, "+l no arg"); + run_test( + r, [&] { no_oparg_should_err(s); }, "+o no arg"); + run_test( + r, [&] { key_already_set_should_err(s); }, "+k already set"); + run_test( + r, [&] { unknown_mode_should_err(s); }, "+z"); + run_test( + r, [&] { unknown_chan_should_err(s); }, "unknown chan"); + run_test( + r, [&] { mode_l_negativearg_should_err(s); }, "+l -1"); + run_test( + r, [&] { mode_o_unknown_user_should_err(s); }, "+o unknown"); + run_test( + r, [&] { mode_minuso_noarg_user_should_err(s); }, "-o no user"); + run_test( + r, [&] { mode_plusi_no_invite_should_err(s); }, "+i"); + run_test( + r, [&] { mode_plusk_no_key_should_err(s); }, "+k "); + run_test( + r, [&] { mode_plusi_with_invite_should_send_rpl_and_broadcast(s); }, "+i after being invited."); + run_test( + r, [&] { mode_plusl_should_block_join_if_max_reached(s); }, "+l "); + run_test( + r, [&] { mode_plusl_zeroarg_should_block_join(s); }, "+l 0"); + run_test( + r, [&] { mode_minusk_should_lift_block(s); }, "-k "); + run_test( + r, [&] { mode_minusi_should_lift_block(s); }, "-i"); run_test( r, [&] { mode_minusl_should_lift_block(s); }, "-l"); } From 29bc5e0a68aa900661684050072a425e34869d04 Mon Sep 17 00:00:00 2001 From: Francois Petit Date: Sun, 16 Nov 2025 18:38:10 +0100 Subject: [PATCH 09/10] [fix] tests for PRIVMSG --- srcs/commands/Privmsg.cpp | 10 ++++++++-- srcs/parsing/Parser.cpp | 6 ++---- test/srcs/testPrivmsg.cpp | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/srcs/commands/Privmsg.cpp b/srcs/commands/Privmsg.cpp index 83b5cbe8..d3c6a9ab 100644 --- a/srcs/commands/Privmsg.cpp +++ b/srcs/commands/Privmsg.cpp @@ -17,8 +17,14 @@ Privmsg::Privmsg(std::string& params) std::string targetList = parser.format_parameter(params, NULL); _targets = parser.to_vector(targetList); - _message = parser.from_remaining_args(params); - LOG_DV_CMD(_message); + LOG_DV_CMD(params); + if (!params.empty() && params.find(" :") == 0) + _message = parser.from_trailing(params); + else if (params.empty()) + _message = ""; + else + _message = parser.from_remaining_args(params); + LOG_D_CMD("message", "|" + _message + "|"); if (!_message.empty() && _message[0] == ':') { _message.replace(0, 1, ""); } diff --git a/srcs/parsing/Parser.cpp b/srcs/parsing/Parser.cpp index 980e1948..319e9ae2 100644 --- a/srcs/parsing/Parser.cpp +++ b/srcs/parsing/Parser.cpp @@ -6,7 +6,7 @@ /* By: fpetit +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/10/22 10:25:15 by jhervoch #+# #+# */ -/* Updated: 2025/11/15 18:28:40 by fpetit ### ########.fr */ +/* Updated: 2025/11/16 18:28:10 by fpetit ### ########.fr */ /* */ /* ************************************************************************** */ @@ -298,9 +298,7 @@ std::map Parser::to_map(std::string& keys, std::string } return (result); } -// return an argument, as a formated string of parameters args = param1,param2,param3 -// can apply a function on every param (but not in use for now) -// if arguments start by ':' takes the all line + std::string Parser::format_parameter(std::string& params, Checker function) { std::string arguments, list, trailing; diff --git a/test/srcs/testPrivmsg.cpp b/test/srcs/testPrivmsg.cpp index 2a1727cf..96b8c623 100644 --- a/test/srcs/testPrivmsg.cpp +++ b/test/srcs/testPrivmsg.cpp @@ -207,10 +207,10 @@ void msg_in_arg_instead_of_trailing_should_notice(Server& s) authenticate(so, "user"); // test is received - send_line(soOp, "PRIVMSG user message only must be displayed\r\n"); + send_line(soOp, "PRIVMSG user many words message\r\n"); std::string reply = recv_lines(so); AssertReply ar(reply); - ar.matches_entirely(":op!op@hazardous.irc.serv PRIVMSG user :message"); + ar.matches_entirely(":op!op@hazardous.irc.serv PRIVMSG user :many words message"); } catch (const std::runtime_error& e) { LOG_TEST.error(e.what()); } From 22e97a6bd91bfb260bc1553491f6bea7b9736ec2 Mon Sep 17 00:00:00 2001 From: Francois Petit Date: Sun, 16 Nov 2025 21:29:47 +0100 Subject: [PATCH 10/10] [fix] test for Motd and Kick --- includes/commands/Kick.hpp | 3 +- srcs/commands/Kick.cpp | 33 +++++++++++++---- test/include/testUtils.hpp | 2 +- test/srcs/testKick.cpp | 26 ++++++------- test/srcs/testMotd.cpp | 76 +++++++++++++++++--------------------- 5 files changed, 75 insertions(+), 65 deletions(-) diff --git a/includes/commands/Kick.hpp b/includes/commands/Kick.hpp index b6d5773d..8d06218f 100644 --- a/includes/commands/Kick.hpp +++ b/includes/commands/Kick.hpp @@ -73,11 +73,12 @@ class Kick : public ICommand /** * @brief removes specified users from channel * + * @param server * @param chanName * @param usersNames * @param p */ - void _kick_users_from_chan(std::string& chanName, std::vector& usersNames, Parser& p); + void _kick_users_from_chan(Server& server, std::string& chanName, std::vector& usersNames, Parser& p); }; #endif diff --git a/srcs/commands/Kick.cpp b/srcs/commands/Kick.cpp index d3b0e513..f9c8f3fb 100644 --- a/srcs/commands/Kick.cpp +++ b/srcs/commands/Kick.cpp @@ -34,7 +34,7 @@ Kick::~Kick() {} * 🛠️ FUNCTIONS * *************************************************************/ -void Kick::_kick_users_from_chan(std::string& chanName, std::vector& usersNames, Parser& p) +void Kick::_kick_users_from_chan(Server& s, std::string& chanName, std::vector& usersNames, Parser& p) { Server* server = p.get_server(); Channel* channel = server->find_channel_by_name(chanName); @@ -43,16 +43,22 @@ void Kick::_kick_users_from_chan(std::string& chanName, std::vector return; } for (size_t i = 0; i < usersNames.size(); i++) { - Client* target = p.get_server()->find_client_by_nickname(usersNames[i]); + LOG_DV_CMD(_usersNames[i]); + Client* target = s.find_client_by_nickname(usersNames[i]); if (!channel->is_operator(*p.get_client())) { p.response(ERR_CHANOPRIVSNEEDED, channel->get_name()); + } else if (!target) { + p.response(ERR_NOSUCHNICK, usersNames[i]); } else if (target) { + if (!channel->is_member(*target)) + { + p.response(ERR_USERNOTINCHANNEL, channel->get_name() + " " +_usersNames[i]); + continue; + } target->remove_joined_channel(*channel); p.response(target, p.get_client(), TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), _msg); channel->broadcast(*server, TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), p.get_client(), _msg); p.response(p.get_client(), TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), _msg); - } else { - p.response(ERR_USERNOTINCHANNEL, channel->get_name()); } } if (channel->get_nb_members() == 0) { @@ -76,7 +82,7 @@ void Kick::execute(Server& server, Client& client) return; } else if (chanNb == 1) { if (p.correct_channel(_channelsNames[0])) - _kick_users_from_chan(_channelsNames[0], _usersNames, p); + _kick_users_from_chan(server, _channelsNames[0], _usersNames, p); return; } for (size_t i = 0; i < chanNb; ++i) { @@ -85,17 +91,28 @@ void Kick::execute(Server& server, Client& client) Channel* channel = server.find_channel_by_name(_channelsNames[i]); Client* target = server.find_client_by_nickname(_usersNames[i]); LOG_D_CMD("looking in", _channelsNames[i]); + LOG_DV_CMD(_usersNames[i]); if (!channel) { p.response(ERR_NOSUCHCHANNEL, _channelsNames[i]); } else if (!channel->is_operator(client)) { p.response(ERR_CHANOPRIVSNEEDED, channel->get_name()); - } else if (target) { + } else if (!target) { + p.response(ERR_NOSUCHNICK, _usersNames[i]); + } else if (!channel->is_member(*target)) { + p.response(ERR_USERNOTINCHANNEL, channel->get_name() + " " +_usersNames[i]); + } else { target->remove_joined_channel(*channel); p.response(target, &client, TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), _msg); channel->broadcast(server, TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), &client, _msg); p.response(TRANSFER_KICK, channel->get_name() + " " + target->get_nickname(), _msg); - } else { - p.response(ERR_USERNOTINCHANNEL, channel->get_name()); + + if (channel->get_nb_members() == 0) { // in case of auto kick + std::map::iterator it = server.channels.find(channel->get_name()); + if (it != server.channels.end()) { + server.channels.erase(it); + delete channel; + } + } } } } diff --git a/test/include/testUtils.hpp b/test/include/testUtils.hpp index cc937a7f..484ff1ab 100644 --- a/test/include/testUtils.hpp +++ b/test/include/testUtils.hpp @@ -6,7 +6,7 @@ #include "printUtils.hpp" #include "reply_codes.hpp" -#define MAX_TRIES_MOTD 10 +#define MAX_TRIES_MOTD 3 #define SERVER_PROCESS_TIME_MS 10 #define SERVER_BOT_WAIT_MS 8000 #define SERVER_SEND_WAIT_MS 15 diff --git a/test/srcs/testKick.cpp b/test/srcs/testKick.cpp index 9a329629..d723a945 100644 --- a/test/srcs/testKick.cpp +++ b/test/srcs/testKick.cpp @@ -65,7 +65,7 @@ void op_existing_chan_valid_user_should_transfer(Server& s) /** @brief integration test - normal case - many users */ -void op_existing_chan_valid_users_should_transfer(Server& s) +void op_one_channel_many_users_should_transfer(Server& s) { try { TEST_SETUP(test, s, 3); @@ -84,7 +84,7 @@ void op_existing_chan_valid_users_should_transfer(Server& s) ar.is_formatted_transfer(opNick, "KICK #chan roro"); ar.is_formatted_transfer(opNick, "KICK #chan toto"); - // kicked user1 gets an individual noticd + // kicked user1 gets an individual notice reply = recv_lines(so); ar.handle_new_reply(reply); ar.is_formatted_transfer(opNick, "KICK #chan roro"); @@ -224,7 +224,7 @@ void op_user_not_in_channel_should_err(Server& s) send_line(soOp, validKickMsg); std::string reply = recv_lines(soOp); AssertReply ar(reply); - ar.is_formatted(ERR_USERNOTINCHANNEL, opNick, "#chan"); + ar.is_formatted(ERR_USERNOTINCHANNEL, opNick, "#chan roro"); } catch (const std::runtime_error& e) { LOG_TEST.error(e.what()); @@ -321,16 +321,16 @@ void test_kick(Server& s, t_results* r) { print_test_series("command KICK"); print_test_series_part("common cases"); - run_test(r, [&] { op_existing_chan_valid_user_should_transfer(s); }, "single kick"); - run_test(r, [&] { op_existing_chan_valid_users_should_transfer(s); }, "combo double kick"); - run_test(r, [&] { kick_with_reason_should_transfer(s); }, "custom reason should appear in message"); + run_test(r, [&] { op_existing_chan_valid_user_should_transfer(s); }, "KICK single kick"); + run_test(r, [&] { op_one_channel_many_users_should_transfer(s); }, "KICK combo double kick"); + run_test(r, [&] { kick_with_reason_should_transfer(s); }, "KICK custom reason should appear in message"); print_test_series_part("error cases"); - run_test(r, [&] { no_op_should_err(s); }, "no op"); - run_test(r, [&] { op_missing_chan_should_err(s); }, "no chan"); - run_test(r, [&] { op_missing_user_should_err(s); }, "no user"); - run_test(r, [&] { op_user_not_in_channel_should_err(s); }, "not in chan"); - run_test(r, [&] { op_invalid_channel_should_err(s); }, "invalid chan"); - run_test(r, [&] { op_valid_inexistent_channel_should_err(s); }, "inexisting chan"); - run_test(r, [&] { op_matching_size_channel_list_and_user_list_should_transfer(s); }, "user list and channel list are same size"); + run_test(r, [&] { no_op_should_err(s); }, "KICK no op"); + run_test(r, [&] { op_missing_chan_should_err(s); }, "KICK no chan"); + run_test(r, [&] { op_missing_user_should_err(s); }, "KICK no user"); + run_test(r, [&] { op_user_not_in_channel_should_err(s); }, "KICK not in chan"); + run_test(r, [&] { op_invalid_channel_should_err(s); }, "KICK invalid chan"); + run_test(r, [&] { op_valid_inexistent_channel_should_err(s); }, "KICK inexisting chan"); + run_test(r, [&] { op_matching_size_channel_list_and_user_list_should_transfer(s); }, "KICK user list and channel list are same size"); } diff --git a/test/srcs/testMotd.cpp b/test/srcs/testMotd.cpp index 22a65f78..1ad32316 100644 --- a/test/srcs/testMotd.cpp +++ b/test/srcs/testMotd.cpp @@ -35,7 +35,7 @@ void valid_motd_should_rpl(Server& s) { try { TEST_SETUP(test, s, 1); - // if (chmod(MOTD_FILE_FOR_TEST, PERM_644) != 0) { + // if (chmod(SERVER_CONF_FILE_FOR_TEST, PERM_644) != 0) { // throw std::runtime_error(TO_STRING("error changing rights ") + strerror(errno)); // } TcpSocket& so = *sockets.at(0); @@ -43,26 +43,18 @@ void valid_motd_should_rpl(Server& s) send_line(so, validNickMsg); send_line(so, validUserMsg); recv_lines(so); - // std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_MOTD_WAIT_MS)); - // std::string registerReply; - // int tries = 0; - // while (registerReply.find(ircCodes.trailing(RPL_ENDOFMOTD)) == std::string::npos || tries < MAX_TRIES_MOTD) - // { - // registerReply += recv_lines(so, "roro on registration"); - // ++tries; - // } - // LOG_DV_TEST(registerReply); + std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_MOTD_WAIT_MS)); + int tries = 0; // test send_line(so, validMotd); std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_MOTD_WAIT_MS)); std::string reply = ""; - // int tries = 0; - // while (reply.find(TO_STRING(ERR_NOMOTD)) == std::string::npos - // && (reply.find(ircConfig.trailing(RPL_ENDOFMOTD)) == std::string::npos || tries < MAX_TRIES_MOTD)) { - // reply += recv_lines(so, "roro on motd"); - // ++tries; - // } + while (reply.find(TO_STRING(ERR_NOMOTD)) == std::string::npos + && (reply.find(ircConfig.trailing(RPL_ENDOFMOTD)) == std::string::npos || tries < MAX_TRIES_MOTD)) { + reply += recv_lines(so, "roro on motd"); + ++tries; + } reply += recv_lines(so, "roro on motd"); LOG_DV_TEST(reply); AssertReply ar(reply); @@ -82,31 +74,31 @@ void valid_motd_should_rpl(Server& s) /** @brief integration test - error case */ -// void motd_not_opening_should_err(Server& s) -// { -// try { -// TEST_SETUP(test, s, 1); -// if (chmod(MOTD_FILE_FOR_TEST, 000) != 0) { -// throw std::runtime_error(TO_STRING("error changing rights ") + strerror(errno)); -// } -// TcpSocket& so = *sockets.at(0); -// send_line(so, validPassMsg); -// send_line(so, validNickMsg); -// send_line(so, validUserMsg); -// recv_lines(so); -// -// // test -// std::string reply = get_rpl_for(so, validMotd); -// AssertReply ar(reply); -// ar.is_formatted(ERR_NOMOTD, userNick, ""); -// -// if (chmod(MOTD_FILE_FOR_TEST, PERM_644) != 0) { -// throw std::runtime_error(TO_STRING("error changing rights back ") + strerror(errno)); -// } -// } catch (const std::runtime_error& e) { -// LOG_TEST.error(e.what()); -// } -// } +void motd_not_opening_should_err(Server& s) +{ + try { + TEST_SETUP(test, s, 1); + if (chmod(SERVER_CONF_FILE_FOR_TEST, 000) != 0) { + throw std::runtime_error(TO_STRING("error changing rights ") + strerror(errno)); + } + TcpSocket& so = *sockets.at(0); + send_line(so, validPassMsg); + send_line(so, validNickMsg); + send_line(so, validUserMsg); + recv_lines(so); + + // test + std::string reply = get_rpl_for(so, validMotd); + AssertReply ar(reply); + ar.is_formatted(ERR_NOMOTD, userNick, ""); + + if (chmod(SERVER_CONF_FILE_FOR_TEST, PERM_644) != 0) { + throw std::runtime_error(TO_STRING("error changing rights back ") + strerror(errno)); + } + } catch (const std::runtime_error& e) { + LOG_TEST.error(e.what()); + } +} void test_motd(Server& s, t_results* r) { @@ -116,6 +108,6 @@ void test_motd(Server& s, t_results* r) run_test( r, [&] { valid_motd_should_rpl(s); }, "'MOTD"); - print_test_series_part("error cases"); + // print_test_series_part("error cases"); // run_test(r, [&] { motd_not_opening_should_err(s); }, "'MOTD file with no rights"); }