From 7080abda0362793e14aa9cadd67290aa0908f836 Mon Sep 17 00:00:00 2001 From: jtuomi Date: Tue, 23 Sep 2025 20:59:41 +0300 Subject: [PATCH 1/7] Removes _addOwnSocket private method from Server and adds Server socket into epoll in the constructor. Also poll now checks whether event fd matches socket and just runs Handler::acceptClient --- inc/Server.hpp | 1 - src/Server.cpp | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/inc/Server.hpp b/inc/Server.hpp index 5fe9b03..61a078a 100644 --- a/inc/Server.hpp +++ b/inc/Server.hpp @@ -34,7 +34,6 @@ class Server { const int _max_events = 100; std::string _password; void _reloadHandler(Client &client) const; - void _addOwnSocket(int sockfd); public: Server(std::string port = "6667", std::string passwd = ""); virtual ~Server(); diff --git a/src/Server.cpp b/src/Server.cpp index 7e39dc7..54469a9 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -31,8 +31,12 @@ _password(passwd) throw std::runtime_error("Server::Server: ERROR - Failed listen on port " + port); } - _events.resize(_max_events); - _addOwnSocket(_sock); + struct epoll_event ev{}; + ev.data.fd = _sock; + ev.events = EPOLLIN; + epoll_ctl(this->_fd, EPOLL_CTL_ADD, _sock, &ev); + + _events.reserve(_max_events * sizeof(epoll_event)); } Server::~Server() { @@ -119,7 +123,10 @@ void Server::poll(int tout) { for (int idx = 0; idx < nbrEvents; idx++) { uint32_t event = _events[idx].events; int fd = _events[idx].data.fd; - + if (fd == _sock) { + Handler::acceptClient(_sock); + continue; + } for (uint32_t type : eventTypes) { if (_clients.count(fd) == 0) return ; @@ -150,11 +157,6 @@ void Server::registerHandler(const int fd, uint32_t eventType, std::function Date: Tue, 23 Sep 2025 20:23:23 +0300 Subject: [PATCH 2/7] Major refactoring to make User in Client non pointer and Client in Server map a shared_ptr. So syntax changes mostly, except in Client and Server. --- Makefile | 4 +-- inc/Client.hpp | 13 ++++--- inc/Server.hpp | 9 +++-- inc/User.hpp | 3 +- inc/macro.h | 20 +++++++---- src/Channel.cpp | 24 ++++++------- src/Client.cpp | 27 +++----------- src/Command.cpp | 74 ++++++++++++++++++--------------------- src/CommandDispatcher.cpp | 18 +++++----- src/Handler.cpp | 2 +- src/Server.cpp | 22 +++++------- src/main.cpp | 8 +++++ 12 files changed, 108 insertions(+), 116 deletions(-) diff --git a/Makefile b/Makefile index 785581c..052bb15 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,15 @@ NAME := ircserv BOT_NAME := ircbot CXX := c++ -CXXFLAGS:= -Wall -Wextra -Werror -std=c++20 -Iinc +CXXFLAGS:= -Wall -Wextra -Werror -std=c++20 -Iinc SRC := \ + Client.cpp \ main.cpp \ Server.cpp \ Channel.cpp \ User.cpp \ RecvParser.cpp \ - Client.cpp \ Command.cpp \ CommandDispatcher.cpp \ Handler.cpp diff --git a/inc/Client.hpp b/inc/Client.hpp index a8bb0ec..e9df9da 100644 --- a/inc/Client.hpp +++ b/inc/Client.hpp @@ -5,9 +5,8 @@ #include #include #include -#include "CommandDispatcher.hpp" #include "User.hpp" -class User; + class CommandDispatcher; /* * @class Client @@ -24,24 +23,24 @@ class Client { std::function _IN = nullptr; std::function _RDHUP = nullptr; std::function _HUP = nullptr; - User* _self; + User _self; bool _authenticated = false; bool _registered = false; - CommandDispatcher* _test; + CommandDispatcher* _dispatch; public: explicit Client(int fd); ~Client(); - Client(const Client&); - Client& operator=(const Client&); const int _fd; bool _initialized = false; bool handler(uint32_t eventType) const; void setHandler(uint32_t eventType, std::function handler); std::function& getHandler(uint32_t eventType); - User* getUser(void); + User& getUser(void); CommandDispatcher* getDispatch(void); void authenticate(void); bool isAuthenticated(void) const; bool& accessRegistered(void); }; + +#include "CommandDispatcher.hpp" diff --git a/inc/Server.hpp b/inc/Server.hpp index 61a078a..a699091 100644 --- a/inc/Server.hpp +++ b/inc/Server.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -21,10 +22,12 @@ constexpr static const std::array eventTypes{EPOLLIN, EPOLLHUP, EPOLLRDHUP}; +class Client; + class Server { private: std::map _channels; - std::unordered_map _clients; + std::unordered_map> _clients; std::vector _events; std::time_t _startTime; const int _fd; @@ -62,8 +65,8 @@ class Server { */ bool checkPassword(std::string password = "") const; void poll(int tout = -1); - const std::unordered_map& getClients() const; - Client& getClient(int fd); + const std::unordered_map>& getClients() const; + Client* getClient(int fd); int getServerFd() const; }; diff --git a/inc/User.hpp b/inc/User.hpp index dd21ecb..0c59972 100644 --- a/inc/User.hpp +++ b/inc/User.hpp @@ -1,6 +1,5 @@ #pragma once -#include "Channel.hpp" #include #include @@ -28,3 +27,5 @@ class User { Channel* getChannel(string needle); void exitChannel(string needle); }; + +#include "Channel.hpp" diff --git a/inc/macro.h b/inc/macro.h index 4f0938f..2f99f02 100644 --- a/inc/macro.h +++ b/inc/macro.h @@ -1,18 +1,25 @@ #pragma once #define HOST ":localhost " -#define NICK irc->getClient(fd).getUser()->getNick() -#define USER(X) irc->getClient(X).getUser() -#define PREFIX irc->getClient(fd).getUser()->createPrefix() -#define MSG ":" + USER(user)->getNick() + " " + type + " " + _name + " :" + msg + "\r\n" +#define NICK irc->getClient(fd)->getUser().getNick() +#define USER(X) irc->getClient(X)->getUser() +#define PREFIX irc->getClient(fd)->getUser().createPrefix() +#define MSG ":" + USER(user).getNick() + " " + type + " " + _name + " :" + msg + "\r\n" #define PARAM msg.params[0] #define PARAM1 msg.params[1] #define PARAM2 msg.params[2] +#define PRIVMSG ":" + NICK + " PRIVMSG " + PARAM + " :" + PARAM1 +#define CAP ":localhost CAP * LS :" +#define CAP410 "410 CAP :Unsupported subcommand" +#define CAP461 "461 CAP :Not enough parameters" +#define R315 "315 " + nick + " :End of /WHO list" +#define R318 "318 " + NICK + " :End of WHOIS list" #define R324 ":localhost 324 " + NICK + " " + PARAM + " " + ch->modes() #define R329 ":localhost 329 " + NICK + " " + PARAM + " " + ch->getTime() #define R331 "331 " + NICK + " " + PARAM + " :No topic is set" #define R332 "332 " + NICK + " " + PARAM + " :" + topic -#define R341 "341 :Invitation send" +#define R341 "341 " + NICK + " " + PARAM + " " + PARAM1 + " :Invitation send " +#define R352 "352 " + PARAM + " " + user.getUser() + " " + user.getHost() + " localhost " + user.getNick() + " H" #define R353 "353 " + NICK + " @ " + PARAM + " :" + names #define R366 "366 " + NICK + " " + PARAM + " :End of NAMES list" #define E401 "401 :No such nick" @@ -26,11 +33,12 @@ #define E431 "431 :No nickname given" #define E432 "432 " + oldNick + " " + newNick + " :Erroneous nickname" #define E433 "433 * " + newNick + " :Nickname is already in use" -#define E441 "441 :They aren't on that channel" +#define E441 "441 " + NICK + " " + PARAM + " :They aren't on that channel" #define E442 "442 :You're not on that channel" #define E443 "443 :User already on channel" #define E461 "461 :Missing parameters" #define E462 "462 " + NICK + " : You may not reregister" +#define E464 "464 " + NICK + " :Incorrect password" #define E471 "471 " + nick + " " + _name + " :Cannot join channel (+l)" #define E472 "472 :Unknown mode" #define E473 "473 " + nick + " " + _name + " :Cannot join channel (+i)" diff --git a/src/Channel.cpp b/src/Channel.cpp index 9ce266e..1914f2a 100644 --- a/src/Channel.cpp +++ b/src/Channel.cpp @@ -69,7 +69,7 @@ bool Channel::setTopic(int user, string topic) { return false; } _topic = topic; - string response = ":" + USER(user)->getNick() + " TOPIC " + _name + " :" + topic; + string response = ":" + USER(user).getNick() + " TOPIC " + _name + " :" + topic; message(-1, response); return true; } @@ -82,7 +82,7 @@ bool Channel::checkUser(int user) { const string Channel::addUser(int user, string passwd) { string ret; - const string nick = USER(user)->getNick(); + const string nick = USER(user).getNick(); if (not checkUser(user)) ret = E443; else if (_mode.contains('l') && _users.size() + _oper.size() >= _limit) @@ -101,21 +101,21 @@ const string Channel::addUser(int user, string passwd) { ret = E475; else if (isEmpty()) { _oper.emplace(user); - USER(user)->join(this); + USER(user).join(this); } else { _users.emplace(user); - USER(user)->join(this); + USER(user).join(this); } return ret; } void Channel::removeUser(int fd, string msg, string cmd) { if (_users.contains(fd)) { - USER(fd)->exitChannel(_name); + USER(fd).exitChannel(_name); _users.erase(fd); message(fd, msg, cmd); } else if (_oper.contains(fd)) { - USER(fd)->exitChannel(_name); + USER(fd).exitChannel(_name); _oper.erase(fd); if (_oper.empty()) { if (_users.empty()) { @@ -141,7 +141,7 @@ bool Channel::joinWithPassword(int fd, string passwd) { _oper.emplace(fd); else _users.emplace(fd); - USER(fd)->join(this); + USER(fd).join(this); return true; } else { return false; @@ -158,7 +158,7 @@ bool Channel::joinWithInvite(int fd, string passwd) { _invite.erase(fd); _users.emplace(fd); } - USER(fd)->join(this); + USER(fd).join(this); return true; } else { if (joinWithPassword(fd, passwd)) @@ -175,12 +175,12 @@ string Channel::userList(void) const { string ret; for (auto users : _users) { - ret += USER(users)->getNick(); + ret += USER(users).getNick(); ret += " "; } for (auto users : _oper) { ret += '@'; - ret += USER(users)->getNick(); + ret += USER(users).getNick(); ret += " "; } ret.erase(ret.end() - 1); @@ -202,7 +202,7 @@ bool Channel::makeOperator(int fd, string uname) { int newOp = 0; for (auto user : _users) { - if (USER(user)->getNick() == uname) { + if (USER(user).getNick() == uname) { newOp = user; break ; } @@ -221,7 +221,7 @@ void Channel::invite(int fd) { bool Channel::kick(int op, int user) { if (_users.contains(user) && _oper.contains(op)) { _users.erase(user); - USER(user)->exitChannel(_name); + USER(user).exitChannel(_name); return true; } else { return false; diff --git a/src/Client.cpp b/src/Client.cpp index 9566863..6ba1f46 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -1,36 +1,17 @@ #include "Client.hpp" - -Client::Client(int fd) : _self(new User()), _test(new CommandDispatcher()), _fd(fd) {} - -Client::Client(const Client& other) : -_IN(other._IN), -_RDHUP(other._RDHUP), -_HUP(other._HUP), -_self(new User(*other._self)), -_test(other._test), -_fd(other._fd), -_initialized(other._initialized){} - -Client& Client::operator=(const Client& other) { - if (this != &other) { - delete _self; - _self = new User(*other._self); - _test = other._test; - } - return *this; -} +Client::Client(int fd) : _self(User()), _dispatch(new CommandDispatcher()), _fd(fd) {} Client::~Client() { - delete _self; + delete _dispatch; } -User* Client::getUser(void) { +User& Client::getUser(void) { return _self; } CommandDispatcher* Client::getDispatch(void) { - return _test; + return _dispatch; } void Client::setHandler(uint32_t eventType, std::function handler) { diff --git a/src/Command.cpp b/src/Command.cpp index e4bd271..1ca24e4 100644 --- a/src/Command.cpp +++ b/src/Command.cpp @@ -27,15 +27,15 @@ void NickCommand::execute(const Message &msg, int fd) return sendResponse(E431, fd); const std::string &oldNick = NICK; std::string newNick = PARAM; - User* usr = USER(fd); + User& usr = USER(fd); std::regex nickname_regex("^[A-Za-z][A-Za-z0-9-_]*"); if (newNick.size() < 1 || newNick.size() > 9 || not std::regex_match(newNick, nickname_regex)) return sendResponse(E432, fd); for (auto client : irc->getClients()) - if (client.second.getUser()->getNick() == newNick) + if (client.second->getUser().getNick() == newNick) return sendResponse(E433, fd); sendResponse(PREFIX + " NICK :" + newNick, fd); - usr->setNick(fd, newNick); + usr.setNick(fd, newNick); } @@ -43,10 +43,10 @@ void UserCommand::execute(const Message &msg, int fd) { if (msg.params.size() < 4) return sendResponse(E461, fd); - if (not USER(fd)->getUser().empty()) + if (not USER(fd).getUser().empty()) return sendResponse(E462, fd); - USER(fd)->setUser(PARAM); - USER(fd)->setHost(PARAM1); + USER(fd).setUser(PARAM); + USER(fd).setHost(PARAM1); } void JoinCommand::execute(const Message &msg, int fd) @@ -85,18 +85,18 @@ void PartCommand::execute(const Message &msg, int fd) { if (msg.params.size() < 1) return sendResponse(E461, fd); - Client &client = irc->getClient(fd); + Client *client = irc->getClient(fd); std::regex channel_regex("^[#][A-Za-z0-9-_]{1,50}*"); if (!std::regex_match(PARAM, channel_regex)) return sendResponse(E403REV2, fd); - Channel *ch = client.getUser()->getChannel(PARAM); + Channel *ch = client->getUser().getChannel(PARAM); if (!ch) return sendResponse(E422, fd); std::string response = PARAM; if (msg.params.size() > 1) response.append(" :" + PARAM1); ch->removeUser(fd, response, "PART"); - sendResponse(client.getUser()->createPrefix() + " PART " + response, fd); + sendResponse(PREFIX + " PART " + response, fd); } void PrivmsgCommand::execute(const Message &msg, int fd) @@ -107,7 +107,7 @@ void PrivmsgCommand::execute(const Message &msg, int fd) return sendResponse(E412, fd); if (PARAM[0] == '#') { - Channel *ch = USER(fd)->getChannel(PARAM); + Channel *ch = USER(fd).getChannel(PARAM); if (not ch) return sendResponse(E442, fd); if (not ch->message(fd, PARAM1, "PRIVMSG")) @@ -116,8 +116,8 @@ void PrivmsgCommand::execute(const Message &msg, int fd) else { for (auto client : irc->getClients()) - if (client.second.getUser()->getNick() == PARAM) - return sendResponse(":" + NICK + " PRIVMSG " + PARAM + " :" + PARAM1, client.first); + if (client.second->getUser().getNick() == PARAM) + return sendResponse(PRIVMSG, client.first); sendResponse(E401, fd); } } @@ -130,19 +130,19 @@ void KickCommand::execute(const Message &msg, int fd) return sendResponse(E461, fd); if (not irc->channelExists(PARAM)) return sendResponse(E403, fd); - Channel *ch = irc->getClient(fd).getUser()->getChannel(PARAM); + Channel *ch = irc->getClient(fd)->getUser().getChannel(PARAM); if (not ch) return sendResponse(E442, fd); if (not ch->getOperators().contains(fd)) return sendResponse(E482, fd); for (auto client : ch->getOperators()) - if (USER(client)->getNick() == PARAM1) + if (USER(client).getNick() == PARAM1) return sendResponse(E481, fd); Client *target = nullptr; for (auto client : ch->getUsers()) - if (USER(client)->getNick() == PARAM1) + if (USER(client).getNick() == PARAM1) { - target = &irc->getClient(client); + target = irc->getClient(client); break ; } if (not target) @@ -164,9 +164,9 @@ void InviteCommand::execute(const Message &msg, int fd) return sendResponse(E461, fd); Client *target = nullptr; for (auto client : irc->getClients()) - if (client.second.getUser()->getNick() == PARAM) + if (client.second->getUser().getNick() == PARAM) { - target = &(client.second); + target = client.second.get(); break ; } if (not target) @@ -197,7 +197,7 @@ void TopicCommand::execute(const Message &msg, int fd) return sendResponse(E461, fd); if (not irc->channelExists(PARAM)) return sendResponse(E403, fd); - Channel *ch = irc->getClient(fd).getUser()->getChannel(PARAM); + Channel *ch = irc->getClient(fd)->getUser().getChannel(PARAM); if (not ch) return sendResponse(E442, fd); if (msg.params.size() < 2) @@ -217,7 +217,7 @@ constexpr std::string supported = "itkol", required = "klo"; void ModeCommand::execute(const Message &msg, int fd) { auto channel = [&]() { - Channel *ch = irc->getClient(fd).getUser()->getChannel(PARAM); + Channel *ch = irc->getClient(fd)->getUser().getChannel(PARAM); if (!ch) return sendResponse(E442, fd); if (msg.params.size() < 2) { @@ -259,7 +259,7 @@ void ModeCommand::execute(const Message &msg, int fd) *ch = backup; return ; } else if (c == 'o' && not ch->makeOperator(fd, msg.params[index++])) { - sendResponse("441 " + NICK + " " + PARAM + " :They aren't on that channel", fd); + sendResponse(E441, fd); *ch = backup; return ; } @@ -284,52 +284,48 @@ void ModeCommand::execute(const Message &msg, int fd) void QuitCommand::execute(const Message &msg, int fd) { if (!msg.params.empty()) - USER(fd)->quit(fd, PARAM); + USER(fd).quit(fd, PARAM); else - USER(fd)->quit(fd, "Client quit"); + USER(fd).quit(fd, "Client quit"); } void CapCommand::execute(const Message &msg, int fd) { if (msg.params.empty()) - return sendResponse("461 CAP :Not enough parameters", fd); + return sendResponse(CAP461, fd); if (PARAM == "LS") - return sendResponse(":localhost CAP * LS :", fd); + return sendResponse(CAP, fd); else if (PARAM == "END") return ; else - return sendResponse("410 CAP :Unsupported subcommand", fd); + return sendResponse(CAP410, fd); } void WhoisCommand::execute(const Message &msg, int fd) { (void)msg; - std::string nick = NICK; - sendResponse("318 " + nick + " :End of WHOIS list", fd); + sendResponse(R318, fd); } void WhoCommand::execute(const Message &msg, int fd) { if (msg.params.empty()) sendResponse(E461, fd); - Channel *ch = irc->getClient(fd).getUser()->getChannel(PARAM); + Channel *ch = irc->getClient(fd)->getUser().getChannel(PARAM); if (not ch) return sendResponse(E442, fd); const std::string &nick = NICK; for (auto id : ch->getUsers()) { - const User *user = USER(id); - sendResponse("352 " + PARAM + " " + user->getUser() + " " + - user->getHost() + " localhost " + user->getNick() + " H", fd); + const User& user = USER(id); + sendResponse(R352, fd); } for (auto id : ch->getOperators()) { - const User *user = USER(id); - sendResponse("352 " + nick + " " + PARAM + " " - + user->getUser() + " " + user->getHost() + - " localhost " + user->getNick() + " H @", fd); + const User& user = USER(id); + sendResponse(R352 + " @", fd); } - sendResponse("315 " + nick + " :End of /WHO list", fd); + sendResponse(R315, fd); } void PingCommand::execute(const Message &msg, int fd) @@ -345,8 +341,8 @@ void PassCommand::execute(const Message &msg, int fd) if (irc->checkPassword()) return ; else if (!msg.params.empty() && irc->checkPassword(PARAM)) - return irc->getClient(fd).authenticate(); - sendResponse("464 " + NICK + " :Incorrect password", fd); + return irc->getClient(fd)->authenticate(); + sendResponse(E464, fd); irc->removeClient(fd); } diff --git a/src/CommandDispatcher.cpp b/src/CommandDispatcher.cpp index 8bd7a31..882cc7f 100644 --- a/src/CommandDispatcher.cpp +++ b/src/CommandDispatcher.cpp @@ -35,12 +35,12 @@ bool CommandDispatcher::dispatch(const std::unique_ptr &msg, int fd) if (auto cmd = _handlers.find(msg->command); cmd != _handlers.end()) { if (!irc->checkPassword() && - !irc->getClient(fd).isAuthenticated() && + !irc->getClient(fd)->isAuthenticated() && msg->command != "PASS" && msg->command != "CAP") { std::string response("464 "); - response.append(irc->getClient(fd).getUser()->getNick()); + response.append(irc->getClient(fd)->getUser().getNick()); response.append(" :Password incorrect\r\n"); send(fd, response.c_str(), response.size(), 0); irc->removeClient(fd); @@ -48,9 +48,9 @@ bool CommandDispatcher::dispatch(const std::unique_ptr &msg, int fd) } cmd->second->execute(*msg, fd); if (msg->command != "QUIT" && - not irc->getClient(fd).getUser()->getNick().empty() && - not irc->getClient(fd).getUser()->getUser().empty() && - not irc->getClient(fd).accessRegistered()) + not irc->getClient(fd)->getUser().getNick().empty() && + not irc->getClient(fd)->getUser().getUser().empty() && + not irc->getClient(fd)->accessRegistered()) _welcome(fd); } else @@ -65,16 +65,16 @@ bool CommandDispatcher::dispatch(const std::unique_ptr &msg, int fd) void CommandDispatcher::_welcome(int fd) { - irc->getClient(fd).accessRegistered() = true; - std::string nick = irc->getClient(fd).getUser()->getNick(); + irc->getClient(fd)->accessRegistered() = true; + std::string nick = irc->getClient(fd)->getUser().getNick(); std::string response = "001 " + nick + " :Welcome to Hive network\r\n"; send(fd, response.c_str(), response.size(), 0); response = "002 " + nick + " :Your hostname is " + - irc->getClient(fd).getUser()->getHost() + "\r\n"; + irc->getClient(fd)->getUser().getHost() + "\r\n"; send(fd, response.c_str(), response.size(), 0); response = "003 " + nick + " :This server was started " + irc->getTime() + "\r\n"; send(fd, response.c_str(), response.size(), 0); response = "004 " + nick + " :Your username is " + - irc->getClient(fd).getUser()->getUser() + "\r\n"; + irc->getClient(fd)->getUser().getUser() + "\r\n"; send(fd, response.c_str(), response.size(), 0); } diff --git a/src/Handler.cpp b/src/Handler.cpp index f44ff0d..345dc65 100644 --- a/src/Handler.cpp +++ b/src/Handler.cpp @@ -15,7 +15,7 @@ void Handler::clientWrite(int fd) { while (!msg_queue.empty()) { const unique_ptr &msg = msg_queue.front(); - if (!irc->getClient(fd).getDispatch()->dispatch(msg, fd)) + if (!irc->getClient(fd)->getDispatch()->dispatch(msg, fd)) return ; msg_queue.pop(); } diff --git a/src/Server.cpp b/src/Server.cpp index 54469a9..c8984de 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -40,9 +40,6 @@ _password(passwd) } Server::~Server() { - for(auto client : _clients) { - delete client.second.getDispatch(); - } close(_fd); close(_sock); } @@ -76,12 +73,11 @@ Channel* Server::findChannel(std::string name) { } void Server::addClient(int fd) { - _clients.try_emplace(fd, fd); + _clients.try_emplace(fd, std::make_shared(fd)); } void Server::removeClient(const int fd) { if(not _clients.empty()) { - delete getClient(fd).getDispatch(); _clients.erase(fd); } close(fd); @@ -130,9 +126,9 @@ void Server::poll(int tout) { for (uint32_t type : eventTypes) { if (_clients.count(fd) == 0) return ; - if (_clients.at(fd).handler(type & event)) { + if (_clients.at(fd)->handler(type & event)) { [[maybe_unused]] - auto fut = std::async(std::launch::async, _clients.at(fd).getHandler(type & event), fd); + auto fut = std::async(std::launch::async, _clients.at(fd)->getHandler(type & event), fd); } } @@ -146,15 +142,15 @@ void Server::registerHandler(const int fd, uint32_t eventType, std::functionsetHandler(eventType, handler); } } - _reloadHandler(cli); + _reloadHandler(*cli); } std::string Server::getTime(void) const { @@ -163,13 +159,13 @@ std::string Server::getTime(void) const { return ret; } -const std::unordered_map& Server::getClients() const { +const std::unordered_map>& Server::getClients() const { return (_clients); } -Client& Server::getClient(int fd) { +Client* Server::getClient(int fd) { if (_clients.count(fd) == 0) throw std::runtime_error("Server::getClient: Error - no such file descriptor"); - return (_clients.at(fd)); + return (_clients.at(fd).get()); } diff --git a/src/main.cpp b/src/main.cpp index e61cf92..b104870 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,13 @@ #include "Server.hpp" #include "Handler.hpp" +#include "Client.hpp" +#include "Channel.hpp" +#include "Command.hpp" +#include "CommandDispatcher.hpp" +#include "RecvParser.hpp" +#include "macro.h" +#include "Message.hpp" +#include "User.hpp" #include #include #include From 552c525c6ecef7c8cd2978b72e9b7c480fd5ef49 Mon Sep 17 00:00:00 2001 From: jtuomi Date: Tue, 23 Sep 2025 17:17:26 +0300 Subject: [PATCH 3/7] Fixed Ctrl+D by adding RecvParser to every client, including the Message queue. --- inc/Client.hpp | 7 ++++++- inc/RecvParser.hpp | 3 ++- src/Client.cpp | 6 +++++- src/CommandDispatcher.cpp | 6 ++++-- src/Handler.cpp | 5 +++-- src/RecvParser.cpp | 5 +++++ 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/inc/Client.hpp b/inc/Client.hpp index e9df9da..976123b 100644 --- a/inc/Client.hpp +++ b/inc/Client.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -6,7 +7,9 @@ #include #include #include "User.hpp" +#include "RecvParser.hpp" +class User; class CommandDispatcher; /* * @class Client @@ -27,7 +30,8 @@ class Client { bool _authenticated = false; bool _registered = false; CommandDispatcher* _dispatch; - + std::queue> _msg_queue; + RecvParser _parser; public: explicit Client(int fd); ~Client(); @@ -38,6 +42,7 @@ class Client { std::function& getHandler(uint32_t eventType); User& getUser(void); CommandDispatcher* getDispatch(void); + RecvParser& getParser(void); void authenticate(void); bool isAuthenticated(void) const; bool& accessRegistered(void); diff --git a/inc/RecvParser.hpp b/inc/RecvParser.hpp index 2731dcf..c19f8e1 100644 --- a/inc/RecvParser.hpp +++ b/inc/RecvParser.hpp @@ -7,7 +7,7 @@ #include #include "Message.hpp" -/** +/** * @class RecvParser * @brief A class for parsing data read by recv() into a message queue */ @@ -16,6 +16,7 @@ class RecvParser public: RecvParser(std::queue> &msg_queue); + std::queue> &getQueue(void); void feed(const char *read_buf, size_t len); private: diff --git a/src/Client.cpp b/src/Client.cpp index 6ba1f46..a606b3f 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -1,6 +1,6 @@ #include "Client.hpp" -Client::Client(int fd) : _self(User()), _dispatch(new CommandDispatcher()), _fd(fd) {} +Client::Client(int fd) : _self(User()), _dispatch(new CommandDispatcher()), _parser(RecvParser(_msg_queue)), _fd(fd) {} Client::~Client() { delete _dispatch; @@ -14,6 +14,10 @@ CommandDispatcher* Client::getDispatch(void) { return _dispatch; } +RecvParser& Client::getParser(void) { + return _parser; +} + void Client::setHandler(uint32_t eventType, std::function handler) { switch (eventType) { case EPOLLIN: diff --git a/src/CommandDispatcher.cpp b/src/CommandDispatcher.cpp index 882cc7f..4d08714 100644 --- a/src/CommandDispatcher.cpp +++ b/src/CommandDispatcher.cpp @@ -34,8 +34,8 @@ bool CommandDispatcher::dispatch(const std::unique_ptr &msg, int fd) { if (auto cmd = _handlers.find(msg->command); cmd != _handlers.end()) { - if (!irc->checkPassword() && - !irc->getClient(fd)->isAuthenticated() && + if (not irc->checkPassword() && + not irc->getClient(fd)->isAuthenticated() && msg->command != "PASS" && msg->command != "CAP") { @@ -46,6 +46,8 @@ bool CommandDispatcher::dispatch(const std::unique_ptr &msg, int fd) irc->removeClient(fd); return false; } + if (msg->command == "QUIT") + return cmd->second->execute(*msg, fd), false; cmd->second->execute(*msg, fd); if (msg->command != "QUIT" && not irc->getClient(fd)->getUser().getNick().empty() && diff --git a/src/Handler.cpp b/src/Handler.cpp index 345dc65..b9a7342 100644 --- a/src/Handler.cpp +++ b/src/Handler.cpp @@ -5,8 +5,9 @@ using namespace std; void Handler::clientWrite(int fd) { ssize_t messageLen = 0; vector buf(BUFSIZ); - queue> msg_queue; - RecvParser parser(msg_queue); + Client& client = irc->getClient(fd); + RecvParser& parser = client.getParser(); + std::queue> &msg_queue = parser.getQueue(); messageLen = recv(fd, &buf[0], buf.size(), 0); if (messageLen == -1) diff --git a/src/RecvParser.cpp b/src/RecvParser.cpp index db68470..7ae2add 100644 --- a/src/RecvParser.cpp +++ b/src/RecvParser.cpp @@ -3,6 +3,11 @@ RecvParser::RecvParser(std::queue> &msg_queue) : _output(msg_queue){} +std::queue> &RecvParser::getQueue(void) +{ + return _output; +} + /** * Feed the recv() buffer into std::string buffer * @param read_buf The buffer to read From e76a5a71d4b388a5290d8bf2d52cbd5c7d7c92fd Mon Sep 17 00:00:00 2001 From: jtuomi Date: Tue, 23 Sep 2025 20:23:23 +0300 Subject: [PATCH 4/7] Major refactoring to make User in Client non pointer and Client in Server map a shared_ptr. So syntax changes mostly, except in Client and Server. --- inc/Client.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inc/Client.hpp b/inc/Client.hpp index 976123b..1660bab 100644 --- a/inc/Client.hpp +++ b/inc/Client.hpp @@ -10,6 +10,7 @@ #include "RecvParser.hpp" class User; + class CommandDispatcher; /* * @class Client @@ -32,6 +33,7 @@ class Client { CommandDispatcher* _dispatch; std::queue> _msg_queue; RecvParser _parser; + public: explicit Client(int fd); ~Client(); From ce912afecc33bfdc03f4e917fa0e0eb2a2021a82 Mon Sep 17 00:00:00 2001 From: jtuomi Date: Wed, 24 Sep 2025 20:35:07 +0300 Subject: [PATCH 5/7] Changed the Client reference to be a pointer instead (as merged other branch had Client stored as a shared_ptr) --- src/Handler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Handler.cpp b/src/Handler.cpp index b9a7342..1be6422 100644 --- a/src/Handler.cpp +++ b/src/Handler.cpp @@ -5,8 +5,8 @@ using namespace std; void Handler::clientWrite(int fd) { ssize_t messageLen = 0; vector buf(BUFSIZ); - Client& client = irc->getClient(fd); - RecvParser& parser = client.getParser(); + Client* client = irc->getClient(fd); + RecvParser& parser = client->getParser(); std::queue> &msg_queue = parser.getQueue(); messageLen = recv(fd, &buf[0], buf.size(), 0); @@ -16,7 +16,7 @@ void Handler::clientWrite(int fd) { while (!msg_queue.empty()) { const unique_ptr &msg = msg_queue.front(); - if (!irc->getClient(fd)->getDispatch()->dispatch(msg, fd)) + if (not irc->getClient(fd)->getDispatch()->dispatch(msg, fd)) return ; msg_queue.pop(); } From 1ab394f103f8282f4bb6b15875077a0cb74b594b Mon Sep 17 00:00:00 2001 From: Joonas Tuomi Date: Thu, 25 Sep 2025 15:03:30 +0300 Subject: [PATCH 6/7] Cleaning up. --- inc/macro.h | 9 +++++++-- src/Client.cpp | 2 +- src/Command.cpp | 51 ++++++++++++++++++++++++++++++++----------------- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/inc/macro.h b/inc/macro.h index 2f99f02..97ecf25 100644 --- a/inc/macro.h +++ b/inc/macro.h @@ -4,11 +4,15 @@ #define NICK irc->getClient(fd)->getUser().getNick() #define USER(X) irc->getClient(X)->getUser() #define PREFIX irc->getClient(fd)->getUser().createPrefix() -#define MSG ":" + USER(user).getNick() + " " + type + " " + _name + " :" + msg + "\r\n" +#define MSG ":" + USER(user).getNick() + " " + type \ ++ " " + _name + " :" + msg + "\r\n" #define PARAM msg.params[0] #define PARAM1 msg.params[1] #define PARAM2 msg.params[2] #define PRIVMSG ":" + NICK + " PRIVMSG " + PARAM + " :" + PARAM1 +#define PONG "PONG localhost :" + PARAM +#define INVITE PREFIX + " INVITE " + PARAM + " :" + PARAM1 +#define KICK PREFIX + " KICK " + PARAM + " " + PARAM1 #define CAP ":localhost CAP * LS :" #define CAP410 "410 CAP :Unsupported subcommand" #define CAP461 "461 CAP :Not enough parameters" @@ -19,7 +23,8 @@ #define R331 "331 " + NICK + " " + PARAM + " :No topic is set" #define R332 "332 " + NICK + " " + PARAM + " :" + topic #define R341 "341 " + NICK + " " + PARAM + " " + PARAM1 + " :Invitation send " -#define R352 "352 " + PARAM + " " + user.getUser() + " " + user.getHost() + " localhost " + user.getNick() + " H" +#define R352 "352 " + PARAM + " " + user.getUser() + " "\ ++ user.getHost() + " localhost " + user.getNick() + " H" #define R353 "353 " + NICK + " @ " + PARAM + " :" + names #define R366 "366 " + NICK + " " + PARAM + " :End of NAMES list" #define E401 "401 :No such nick" diff --git a/src/Client.cpp b/src/Client.cpp index a606b3f..6adec86 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -56,7 +56,7 @@ std::function& Client::getHandler(uint32_t eventType) { case EPOLLHUP: return _HUP; default: - throw std::runtime_error("Client::getHandler: Error; invalid eventType"); + throw std::runtime_error("Client::getHandler: Error: invalid eventType"); } } diff --git a/src/Command.cpp b/src/Command.cpp index 1ca24e4..1656d32 100644 --- a/src/Command.cpp +++ b/src/Command.cpp @@ -29,7 +29,8 @@ void NickCommand::execute(const Message &msg, int fd) std::string newNick = PARAM; User& usr = USER(fd); std::regex nickname_regex("^[A-Za-z][A-Za-z0-9-_]*"); - if (newNick.size() < 1 || newNick.size() > 9 || not std::regex_match(newNick, nickname_regex)) + if (newNick.size() < 1 || newNick.size() > 9 || + not std::regex_match(newNick, nickname_regex)) return sendResponse(E432, fd); for (auto client : irc->getClients()) if (client.second->getUser().getNick() == newNick) @@ -148,7 +149,7 @@ void KickCommand::execute(const Message &msg, int fd) if (not target) return sendResponse(E441, fd); std::string nick = ":" + NICK; - std::string response = PREFIX + " KICK " + PARAM + " " + PARAM1; + std::string response = KICK; if (msg.params.size() < 3) response.append(" " + nick); else @@ -164,11 +165,13 @@ void InviteCommand::execute(const Message &msg, int fd) return sendResponse(E461, fd); Client *target = nullptr; for (auto client : irc->getClients()) + { if (client.second->getUser().getNick() == PARAM) { target = client.second.get(); break ; } + } if (not target) return sendResponse(E401, fd); if (target->_fd == fd) @@ -176,9 +179,11 @@ void InviteCommand::execute(const Message &msg, int fd) Channel *ch = irc->findChannel(PARAM1); if (ch) { - if (not ch->getUsers().contains(fd) && not ch->getOperators().contains(fd)) + if (not ch->getUsers().contains(fd) && + not ch->getOperators().contains(fd)) return sendResponse(E442, fd); - else if (ch->getMode().contains('i') && not ch->getOperators().contains(fd)) + else if (ch->getMode().contains('i') && + not ch->getOperators().contains(fd)) return sendResponse(E482, fd); else if (ch->getUsers().contains(target->_fd) || ch->getOperators().contains(target->_fd)) @@ -186,8 +191,7 @@ void InviteCommand::execute(const Message &msg, int fd) else ch->invite(target->_fd); } - std::string invitation = PREFIX + " INVITE " + PARAM + " :" + PARAM1; - sendResponse(invitation, target->_fd); + sendResponse(INVITE, target->_fd); sendResponse(R341, fd); } @@ -197,7 +201,7 @@ void TopicCommand::execute(const Message &msg, int fd) return sendResponse(E461, fd); if (not irc->channelExists(PARAM)) return sendResponse(E403, fd); - Channel *ch = irc->getClient(fd)->getUser().getChannel(PARAM); + Channel *ch = USER(fd).getChannel(PARAM); if (not ch) return sendResponse(E442, fd); if (msg.params.size() < 2) @@ -207,7 +211,8 @@ void TopicCommand::execute(const Message &msg, int fd) return sendResponse(R331, fd); return sendResponse(R332, fd); } - if (ch->getMode().contains('t') && not ch->getOperators().contains(fd)) + if (ch->getMode().contains('t') && + not ch->getOperators().contains(fd)) return sendResponse(E482, fd); ch->setTopic(fd, PARAM1); } @@ -217,7 +222,7 @@ constexpr std::string supported = "itkol", required = "klo"; void ModeCommand::execute(const Message &msg, int fd) { auto channel = [&]() { - Channel *ch = irc->getClient(fd)->getUser().getChannel(PARAM); + Channel *ch = USER(fd).getChannel(PARAM); if (!ch) return sendResponse(E442, fd); if (msg.params.size() < 2) { @@ -228,41 +233,52 @@ void ModeCommand::execute(const Message &msg, int fd) std::string input = PARAM1, enable, disable; bool plus, valid = true; for (auto c = input.begin(); c != input.end(); ) + { if ((*c == '+'|| *c == '-') && valid) { plus = (*c == '+'); ++c; - for (valid = false; c != input.end() && std::isalpha(*c); c++) { + for (valid = false; c != input.end() && + std::isalpha(*c); c++) { plus ? enable += *c : disable += *c; valid = true; } } else return sendResponse(E472, fd); + } size_t paramsNeeded = 2; for (auto c = enable.begin(); c != enable.end(); ++c) + { if ((supported.find(*c) == std::string::npos) || (std::find(c + 1, enable.end(), *c) != enable.end())) return sendResponse(E472, fd); else if (required.find(*c) != std::string::npos) paramsNeeded++; + } for (auto c = disable.begin(); c != disable.end(); ++c) - if ((supported.find(*c) == std::string::npos) - || (std::find(c + 1, disable.end(), *c) != disable.end())) + { + if ((supported.find(*c) == std::string::npos)|| + (std::find(c + 1, disable.end(), *c) != disable.end())) return sendResponse(E472, fd); + } if (msg.params.size() < paramsNeeded) return sendResponse(E461, fd); Channel backup = *ch; int index = 2; for (auto c : enable) + { if (c == 'k') { ch->setPassword(msg.params[index++]); - } else if (c == 'l' && not ch->setLimit(msg.params[index++])) { + } else if (c == 'l' && + not ch->setLimit(msg.params[index++])) { *ch = backup; return ; - } else if (c == 'o' && not ch->makeOperator(fd, msg.params[index++])) { + } else if (c == 'o' && + not ch->makeOperator(fd, msg.params[index++])) { sendResponse(E441, fd); *ch = backup; return ; } + } if (disable.empty() && enable == "o") return ; ch->setMode(enable); @@ -311,7 +327,7 @@ void WhoCommand::execute(const Message &msg, int fd) { if (msg.params.empty()) sendResponse(E461, fd); - Channel *ch = irc->getClient(fd)->getUser().getChannel(PARAM); + Channel *ch = USER(fd).getChannel(PARAM); if (not ch) return sendResponse(E442, fd); const std::string &nick = NICK; @@ -332,15 +348,14 @@ void PingCommand::execute(const Message &msg, int fd) { if (msg.params.empty()) return sendResponse(E409, fd); - std::string response = "PONG localhost :" + PARAM; - sendResponse(response, fd); + sendResponse(PONG, fd); } void PassCommand::execute(const Message &msg, int fd) { if (irc->checkPassword()) return ; - else if (!msg.params.empty() && irc->checkPassword(PARAM)) + else if (!msg.params.empty() &&irc->checkPassword(PARAM)) return irc->getClient(fd)->authenticate(); sendResponse(E464, fd); irc->removeClient(fd); From 9a1bbf4e14a0d2b0b833d101319a368ddea13f0c Mon Sep 17 00:00:00 2001 From: Joonas Tuomi Date: Fri, 26 Sep 2025 11:44:03 +0300 Subject: [PATCH 7/7] As I thought the latest changes caused a segfault with wrong password but these changes fix that. --- src/Command.cpp | 5 +---- src/CommandDispatcher.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/Command.cpp b/src/Command.cpp index 1656d32..6356a23 100644 --- a/src/Command.cpp +++ b/src/Command.cpp @@ -353,12 +353,9 @@ void PingCommand::execute(const Message &msg, int fd) void PassCommand::execute(const Message &msg, int fd) { - if (irc->checkPassword()) - return ; - else if (!msg.params.empty() &&irc->checkPassword(PARAM)) + if (!msg.params.empty() && irc->checkPassword(PARAM)) return irc->getClient(fd)->authenticate(); sendResponse(E464, fd); - irc->removeClient(fd); } void UnknownCommand::execute(const Message &msg, int fd) diff --git a/src/CommandDispatcher.cpp b/src/CommandDispatcher.cpp index 4d08714..ec3e413 100644 --- a/src/CommandDispatcher.cpp +++ b/src/CommandDispatcher.cpp @@ -34,24 +34,24 @@ bool CommandDispatcher::dispatch(const std::unique_ptr &msg, int fd) { if (auto cmd = _handlers.find(msg->command); cmd != _handlers.end()) { - if (not irc->checkPassword() && + if ((not irc->checkPassword() && not irc->getClient(fd)->isAuthenticated() && msg->command != "PASS" && - msg->command != "CAP") + msg->command != "CAP") || + (msg->command == "PASS" && + not msg->params.empty() && + not irc->checkPassword(msg->params[0]))) { - std::string response("464 "); - response.append(irc->getClient(fd)->getUser().getNick()); - response.append(" :Password incorrect\r\n"); - send(fd, response.c_str(), response.size(), 0); + std::string response(E464); + send(fd, response.data(), response.size(), 0); irc->removeClient(fd); return false; } if (msg->command == "QUIT") return cmd->second->execute(*msg, fd), false; cmd->second->execute(*msg, fd); - if (msg->command != "QUIT" && - not irc->getClient(fd)->getUser().getNick().empty() && - not irc->getClient(fd)->getUser().getUser().empty() && + if (not USER(fd).getNick().empty() && + not USER(fd).getUser().empty() && not irc->getClient(fd)->accessRegistered()) _welcome(fd); }