/******************************************************************************/ /* */ /* ::: :::::::: */ /* server.cpp :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: vvaas +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/01/21 09:31:17 by maldavid #+# #+# */ /* Updated: 2024/01/22 14:54:21 by maldavid ### ########.fr */ /* */ /******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include namespace irc { Server::Server(int port, const std::string& password) : _s_len(sizeof(_s_data)), _password(password), _port(port), _active(true) { std::memset(&_s_data, 0, sizeof(sockaddr)); initSocket(); } void Server::initSocketData() { _s_data.sin_family = AF_INET; _s_data.sin_addr.s_addr = INADDR_ANY; _s_data.sin_port = htons(_port); _main_socket = socket(AF_INET, SOCK_STREAM, 0); // AF_INET == IPv4, SOCK_STREAM == TCP if(_main_socket < 0) logs::report(log_fatal_error, "socket error"); logs::report(log_message, "socket succesfully started"); } void Server::initSocket() { int opt = 0; initSocketData(); if(setsockopt(_main_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) // SOL_SOCKET : modify socket only, SO_REUSEADDR : Reusable after program ends logs::report(log_fatal_error, "setsockopt() error (tout a pete)"); if(bind(_main_socket, reinterpret_cast(&_s_data), sizeof(_s_data)) != 0) // Bind _main_socket to localhost logs::report(log_fatal_error, "bind error"); logs::report(log_message, "bind successful, starting listen loop"); if(listen(_main_socket, MAX_USERS) != 0) // init the listen with MAX_USERS logs::report(log_fatal_error, "listen error"); logs::report(log_message, "listen queue created successfully"); logs::report(log_message, "server is up and running"); } void Server::handleInput() { char buffer[INPUT_SIZE] = { 0 }; for(std::vector >::iterator it = _client.begin(); it != _client.end(); ++it) { if(!FD_ISSET((*it)->getFD(), &_fd_set)) continue; while(recv((*it)->getFD(), buffer, INPUT_SIZE, 0) > 0) // read() but for socket fd { (*it)->newMsgInFlight(buffer); while(handleMessage(*it)); std::memset(buffer, 0, sizeof(buffer)); // clear the buffer to avoid trash remaining } if(recv((*it)->getFD(), buffer, INPUT_SIZE, 0) == 0) // recv return 0 if an user disconnect { logs::report(log_message, "User %d disconnected", (*it)->getID()); it = _client.erase(it) - 1; } } } void Server::wait() { int tmp; int fd = 0; int i = 0; socklen_t len = sizeof(sockaddr_in); while(_active) { FD_ZERO(&_fd_set); FD_SET(_main_socket, &_fd_set); for(std::vector >::iterator it = _client.begin(); it != _client.end(); ++it) FD_SET((*it)->getFD(), &_fd_set); tmp = select(MAX_USERS, &_fd_set, NULL, NULL, NULL); // SELECT blocks till a connection or message is received, and let only those in _fd_set if(tmp < 0) logs::report(log_fatal_error, "select fd error"); if(FD_ISSET(_main_socket, &_fd_set)) // if it's a new connection { sockaddr_in cli_sock; fd = accept(_main_socket, (sockaddr *)&cli_sock, &len); // adds the new connection if(fd < 0) logs::report(log_fatal_error, "accept() error"); if(fcntl(fd, F_SETFL, O_NONBLOCK) < 0) logs::report(log_fatal_error, "fcntl() error"); unstd::SharedPtr new_client(new Client(fd, cli_sock, i++)); _client.push_back(new_client); // put the client into the vector used in handle_input logs::report(log_message, "User %d connected", _client.back()->getID()); } handleInput(); } } bool Server::handleMessage(unstd::SharedPtr client) { if(client->getMsgInFlight().empty()) // if there are no commands just return return false; const Message msg(client, client->getNextMsg()); if(msg.getCmd() == "NICK") handleNick(client, msg); else if(msg.getCmd() == "USER") handleUser(client, msg); else if(msg.getCmd() == "QUIT") handleQuit(client, msg); else if(msg.getCmd() == "PART") handlePart(client, msg); else if(msg.getCmd() == "JOIN") handleJoin(client, msg); else if(msg.getCmd() == "PRIVMSG") handlePrivMsg(client, msg); else if(msg.getCmd() == "NOTICE") handleNotice(client, msg); else if(msg.getCmd() == "KICK") handleKick(client, msg); else if(msg.getCmd() == "MOTD") handleMotD(client, msg); else if(msg.getCmd() == "TOPIC") handleTopic(client, msg); else if(msg.getCmd() == "PING") handlePing(client, msg); else if(msg.getCmd() == "MODE") handleMode(client, msg); return true; } void Server::handleNick(unstd::SharedPtr client, const Message& msg) { // TODO : handle nick collisions if(msg.getTokens().size() != 2 && msg.getTokens().size() != 3) { logs::report(log_error, "NICK, invalid command '%s'", msg.getRawMsg().c_str()); return; } client->printUserHeader(); client->setNewNickName(msg.getTokens()[1]); std::cout << "new nickname, " << client->getNickName() << std::endl; } void Server::handleUser(unstd::SharedPtr client, const Message& msg) { if(msg.getTokens().size() != 5) { logs::report(log_error, "USER, invalid command '%s'", msg.getRawMsg().c_str()); return; } client->printUserHeader(); client->setNewUserName(msg.getTokens()[1]); std::cout << "new username, " << client->getUserName() << std::endl; //client->printUserHeader(); //client->setNewRealName(msg.getTokens()[1]); //std::cout << "new realname, " << client->getRealName() << std::endl; } void Server::handleQuit(unstd::SharedPtr client, const Message& msg) { (void)msg; client->printUserHeader(); std::cout << "quit" << std::endl; } void Server::handlePart(unstd::SharedPtr client, const Message& msg) { (void)client; (void)msg; } void Server::handleJoin(unstd::SharedPtr client, const Message& msg) { if(msg.getTokens().size() > 3) { logs::report(log_error, "JOIN, invalid command '%s'", msg.getRawMsg().c_str()); return; } std::vector::const_iterator it; for(it = _channels.begin(); it != _channels.end(); ++it) { if(msg.getTokens()[1] == it->getName()) break; } if(it == _channels.end()) _channels.push_back(Channel(msg.getTokens()[1])); client->printUserHeader(); std::cout << "joining new channel, " << msg.getTokens()[1] << std::endl; } void Server::handlePrivMsg(unstd::SharedPtr client, const Message& msg) { (void)client; (void)msg; } void Server::handleNotice(unstd::SharedPtr client, const Message& msg) { (void)client; (void)msg; } void Server::handleKick(unstd::SharedPtr client, const Message& msg) { (void)client; (void)msg; } void Server::handleMotD(unstd::SharedPtr client, const Message& msg) { (void)client; (void)msg; } void Server::handleTopic(unstd::SharedPtr client, const Message& msg) { (void)client; (void)msg; } void Server::handlePing(unstd::SharedPtr client, const Message& msg) { (void)client; (void)msg; } void Server::handleMode(unstd::SharedPtr client, const Message& msg) { (void)client; (void)msg; } Server::~Server() { if (_main_socket > 0) close(_main_socket); } }