#include "libsock/server/client.hpp" #include "libsock/server/clients.hpp" #include "libsock/server/server.hpp" #include "libsock/types.hpp" #include "misc/FileDescriptor.hpp" #include #include #include #include #include #include #include using namespace LibSock::Server; Client::Client(SP server, SP clients, bool track, bool oneShot) : m_track(track) , m_oneShot(oneShot) { pServer = std::move(server); pClients = std::move(clients); if (!pClients) throw std::runtime_error("server:client: ClientManager doesn't exist"); if (!pServer) throw std::runtime_error("server:client: Server doesn't exist"); m_sockfd = std::make_shared(accept(pServer->getSocket()->get(), reinterpret_cast(&m_addr), &m_addrLen)); // if this is in the init list, it will run before // m_addrLen, so it won't work :/ if (!m_sockfd->isValid()) throw std::runtime_error("server:client: Failed to create socket"); m_ip.resize(INET_ADDRSTRLEN); inet_ntop(AF_INET, &m_addr.sin_addr, &m_ip[0], INET_ADDRSTRLEN); m_ip.resize(strlen(m_ip.c_str())); m_port = ntohs(m_addr.sin_port); } Client::~Client() { m_sockfd.reset(); } void Client::recvLoop(std::optional> cb) { while (true) { auto recvData = read(); if (!recvData->good) break; recvData->sanitize(); if (recvData->isEmpty()) continue; pClients->m_vDatas.push_back({recvData->data, self}); if (cb && !(*cb)(*recvData)) return; } } bool SRecvData::isEmpty() const { return data.empty() || std::ranges::all_of(data, [](char c) { return std::isspace(c); }); } void SRecvData::sanitize() { if (!data.empty() && data.back() == '\n') data.pop_back(); data.erase(std::remove(data.begin(), data.end(), asciiEscape), data.end()); // don't accept ASCII code, might mess up terminal // // NOLINT if (const auto start = data.find_first_not_of(" \t\r\n"); start != std::string::npos) { const auto end = data.find_last_not_of(" \t\r\n"); data = data.substr(start, end - start + 1); } else data.clear(); } UP Client::read(std::optional> cb) { auto recvData = std::make_unique(); recvData->data.resize(recvData->size); ssize_t size = recv(m_sockfd->get(), &recvData->data[0], recvData->size, 0); m_szReading.reset(); if (size < 0 || size == 0) { recvData->good = false; if (cb && !(*cb)(*recvData)) return nullptr; } else { recvData->data.resize(size); recvData->good = true; } return recvData; } UP Client::read(const std::string &msg, std::optional> cb) { write("{}", msg); m_szReading = msg; return read(cb); } void Client::runLoop(bool resumeHist, std::optional> cb) { if (resumeHist) resumeHistory(); recvLoop(cb); pClients->kick(self); } void Client::resumeHistory() { for (const auto &chat : pClients->getDatas()) write(chat.msg); } bool Client::isValid() { if (!m_sockfd->isValid()) return false; int err = 0; socklen_t size = sizeof(err); return getsockopt(m_sockfd->get(), SOL_SOCKET, SO_ERROR, &err, &size) >= 0 && err == 0; } bool Client::write(const std::string &msg, std::optional> cb) { auto r = write("{}", msg); if (cb) r = (*cb)({.data = msg, .size = msg.size(), .good = r}); return r; } const std::string &Client::getIp() const { return m_ip; } template bool Client::write(std::format_string fmt, Args &&...args) { std::string msg = std::format(fmt, std::forward(args)...); if (m_szReading) { msg.insert(0, "\r"); msg.append(*m_szReading); } if (send(m_sockfd->get(), msg.c_str(), msg.size(), 0) < 0) { // cb somehow return false; } return true; }