This commit is contained in:
root 2026-05-25 10:51:13 +00:00
parent 332cb7501a
commit 6b978d0673
6 changed files with 277 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.claude

31
Makefile Normal file
View File

@ -0,0 +1,31 @@
NAME = ircserv
CXX = c++
CXXFLAGS = -Wall -Wextra -Werror -std=c++98
INCLUDES = -I includes
SRCS = main.cpp \
srcs/Server.cpp \
srcs/ParseBuffer.cpp \
srcs/IrcParser.cpp \
srcs/IrcMessage.cpp \
srcs/CommandValidator.cpp
OBJS = $(SRCS:.cpp=.o)
all: $(NAME)
$(NAME): $(OBJS)
$(CXX) $(CXXFLAGS) $(OBJS) -o $(NAME)
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@
clean:
rm -f $(OBJS)
fclean: clean
rm -f $(NAME)
re: fclean all
.PHONY: all clean fclean re

BIN
en.subject.pdf Normal file

Binary file not shown.

View File

@ -14,6 +14,7 @@
# include <stdexcept>
# include <cstring>
# include <cerrno>
# include "CommandValidator.hpp"
struct ConnectedUser
{
@ -27,6 +28,7 @@ struct ConnectedUser
bool userOk;
ParseBuffer parseBuffer;
ConnectedUser(void);
explicit ConnectedUser(int fd);
bool isRegistered(void) const;
};
@ -43,6 +45,12 @@ class Server
void acceptNew(void);
void handleRecv(int fd);
void removeUser(int fd);
void dispatch(int fd, const IrcMessage &msg);
void handlePass(int fd, const IrcMessage &msg);
void handleNick(int fd, const IrcMessage &msg);
void handleUser(int fd, const IrcMessage &msg);
void handlePing(int fd, const IrcMessage &msg);
void handleQuit(int fd, const IrcMessage &msg);
public:
Server(int port, const std::string &password);

30
main.cpp Normal file
View File

@ -0,0 +1,30 @@
#include <iostream>
#include <cstdlib>
#include "Server.hpp"
int main(int argc, char **argv)
{
if (argc != 3)
{
std::cerr << "Usage: ./ircserv <port> <password>" << std::endl;
return (1);
}
int port = std::atoi(argv[1]);
if (port <= 0 || port > 65535)
{
std::cerr << "Error: invalid port" << std::endl;
return (1);
}
try
{
Server server(port, argv[2]);
server.start();
server.run();
}
catch (std::exception &e)
{
std::cerr << "Error: " << e.what() << std::endl;
return (1);
}
return (0);
}

View File

@ -36,4 +36,211 @@ void Server::start(void)
pollfd.events = POLLIN;
pollfd.revents = 0;
_fds.push_back(pollfd);
}
ConnectedUser::ConnectedUser(void) : fd(-1), passOk(false), nickOk(false), userOk(false)
{
}
ConnectedUser::ConnectedUser(int fd) : fd(fd), passOk(false), nickOk(false), userOk(false)
{
}
bool ConnectedUser::isRegistered(void) const
{
return (passOk && nickOk && userOk);
}
Server::Server(int port, const std::string &password)
{
_port = port;
_password = password;
_serverFd = -1;
}
Server::Server(const Server &other)
{
*this = other;
}
Server &Server::operator=(const Server &other)
{
if (this != &other)
{
_serverFd = other._serverFd;
_port = other._port;
_password = other._password;
_fds = other._fds;
_users = other._users;
}
return *this;
}
Server::~Server(void)
{
for (size_t i = 0; i < _fds.size(); i++)
{
close(_fds[i].fd);
}
}
void Server::sendReply(int fd, const std::string &msg)
{
send(fd, msg.c_str(), msg.size(), 0);
}
void Server::removeUser(int fd)
{
close(fd);
_users.erase(fd);
for (size_t i = 0; i < _fds.size(); i++)
{
if (_fds[i].fd == fd)
{
_fds.erase(_fds.begin() + i);
break ;
}
}
}
void Server::acceptNew(void)
{
int newFd = accept(_serverFd, NULL, NULL);
if (newFd == -1)
throw std::runtime_error(strerror(errno));
fcntl(newFd, F_SETFL, O_NONBLOCK);
struct pollfd pfd;
pfd.fd = newFd;
pfd.events = POLLIN;
pfd.revents = 0;
_fds.push_back(pfd);
_users.insert(std::make_pair(newFd, ConnectedUser(newFd)));
}
void Server::handleRecv(int fd)
{
char buf[512];
int n;
n = recv(fd, buf, sizeof(buf), 0);
if (n == 0)
{
removeUser(fd);
return ;
}
else if (n == -1)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
return ;
else
{
removeUser(fd);
return ;
}
}
_users[fd].parseBuffer.append(buf, n);
std::vector<IrcMessage> messages = _users[fd].parseBuffer.extractMessages();
for (size_t i = 0; i < messages.size(); i++)
dispatch(fd, messages[i]);
}
void Server::run(void)
{
while (true)
{
int ret = poll(_fds.data(), _fds.size(), -1);
if (ret == -1)
break ;
for (size_t i = 0; i < _fds.size(); i++)
{
if (i == 0 && _fds[0].revents & POLLIN)
acceptNew();
else if (_fds[i].revents & (POLLHUP | POLLERR))
{
int fd = _fds[i].fd;
removeUser(fd);
i--;
}
else if ( _fds[i].revents & POLLIN)
handleRecv(_fds[i].fd);
}
}
}
void Server::dispatch(int fd, const IrcMessage &msg)
{
if (!msg.isValid())
return ;
const std::string &cmd = msg.getCommand();
if (CommandValidator::needsRegistration(cmd) && !_users[fd].isRegistered())
{
sendReply(fd, "451 :You have not registered\r\n");
return ;
}
else if (!CommandValidator::hasEnoughParams(msg))
{
sendReply(fd, "461 " + cmd + " :Not enough parameters\r\n");
return ;
}
else if (cmd == "PASS")
handlePass(fd, msg);
else if (cmd == "NICK")
handleNick(fd, msg);
else if (cmd == "USER")
handleUser(fd, msg);
else if (cmd == "PING")
handlePing(fd, msg);
else if (cmd == "QUIT")
handleQuit(fd, msg);
}
void Server::handlePass(int fd, const IrcMessage &msg)
{
if (msg.param(0) == _password)
_users[fd].passOk = true;
else
{
sendReply(fd, "464 :Password incorrect\r\n");
removeUser(fd);
}
}
void Server::handleNick(int fd, const IrcMessage &msg)
{
std::string newNick = msg.param(0);
std::map<int, ConnectedUser>::iterator it = _users.begin();
for (; it != _users.end(); it++)
{
if (it->second.nick == newNick && it->first != fd)
{
sendReply(fd, "433 * " + newNick + " :Nickname is already in use\r\n");
return ;
}
}
_users[fd].nick = newNick;
_users[fd].nickOk = true;
}
void Server::handleUser(int fd, const IrcMessage &msg)
{
if (_users[fd].isRegistered() == true)
return ;
_users[fd].username = msg.param(0);
_users[fd].realname = msg.param(3);
_users[fd].userOk = true;
if (_users[fd].isRegistered())
{
sendReply(fd, ":server 001 " + _users[fd].nick + " :Welcome to the IRC server\r\n");
sendReply(fd, ":server 002 " + _users[fd].nick + " :Your host is server\r\n");
}
}
void Server::handlePing(int fd, const IrcMessage &msg)
{
sendReply(fd, "PONG :" + msg.param(0) + "\r\n");
}
void Server::handleQuit(int fd, const IrcMessage &msg)
{
removeUser(fd);
(void)msg;
}