#include #include #include #include #include #include #include #include namespace nntpchan { NNTPServerHandler::NNTPServerHandler(const fs::path &storage) : LineReader(1024), m_article(nullptr), m_auth(nullptr), m_store(std::make_unique(storage)), m_authed(false), m_state(eStateReadCommand) { } NNTPServerHandler::~NNTPServerHandler() {} void NNTPServerHandler::HandleLine(const std::string &line) { if (m_state == eStateReadCommand) { std::deque command; std::istringstream s; s.str(line); for (std::string part; std::getline(s, part, ' ');) { if (part.size()) command.push_back(part); } if (command.size()) HandleCommand(command); else QueueLine("501 Syntax error"); } else if (m_state == eStateStoreArticle) { std::string l = line + "\r\n"; OnData(l.c_str(), l.size()); } else { std::cerr << "invalid state" << std::endl; } } void NNTPServerHandler::OnData(const char *data, ssize_t l) { if (l <= 0) return; if (m_state == eStateStoreArticle) { const char *end = strstr(data, "\r\n.\r\n"); if (end) { std::size_t diff = end - data; if (m_article) { m_article->write(data, diff + 2); m_article->flush(); } ArticleObtained(); diff += 5; Data(end + 5, l - diff); return; } if (m_article) { m_article->write(data, l); m_article->flush(); } } else Data(data, l); } void NNTPServerHandler::HandleCommand(const std::deque &command) { auto cmd = command[0]; std::transform(cmd.begin(), cmd.end(), cmd.begin(), ::toupper); std::size_t cmdlen = command.size(); for (const auto &part : command) std::cerr << " " << part; std::cerr << std::endl; if (cmd == "QUIT") { Quit(); return; } else if (cmd[0] == '5') { return; } else if (cmd == "MODE") { if (cmdlen == 2) { // set mode SwitchMode(command[1]); } else if (cmdlen) { // too many arguments QueueLine("500 too many arguments"); } else { // get mode QueueLine("500 wrong arguments"); } } else if (cmd == "CAPABILITIES") { QueueLine("101 I support the following:"); QueueLine("READER"); QueueLine("IMPLEMENTATION nntpchan-daemon"); QueueLine("VERSION 2"); QueueLine("STREAMING"); QueueLine("."); } else if (cmd == "CHECK") { if (cmdlen >= 2) { const std::string &msgid = command[1]; if (IsValidMessageID(msgid) && m_store->Accept(msgid)) { QueueLine("238 " + msgid); } else QueueLine("438 " + msgid); } else QueueLine("501 syntax error"); } else if (cmd == "TAKETHIS") { if (cmdlen >= 2) { const std::string &msgid = command[1]; if (m_store->Accept(msgid)) { m_article = m_store->OpenWrite(msgid); } m_articleName = msgid; EnterState(eStateStoreArticle); return; } QueueLine("501 invalid syntax"); } else { // unknown command QueueLine("500 Unknown Command"); } } void NNTPServerHandler::ArticleObtained() { if (m_article) { m_article->close(); m_article = nullptr; QueueLine("239 " + m_articleName); std::cerr << "stored " << m_articleName << std::endl; } else QueueLine("439 " + m_articleName); m_articleName = ""; EnterState(eStateReadCommand); } void NNTPServerHandler::SwitchMode(const std::string &mode) { std::string m = mode; std::transform(m.begin(), m.end(), m.begin(), ::toupper); if (m == "READER") { m_mode = m; if (PostingAllowed()) { QueueLine("200 Posting is permitted yo"); } else { QueueLine("201 Posting is not permitted yo"); } } else if (m == "STREAM") { m_mode = m; if (PostingAllowed()) { QueueLine("203 Streaming enabled"); } else { QueueLine("483 Streaming Denied"); } } else { // unknown mode QueueLine("500 Unknown mode"); } } void NNTPServerHandler::EnterState(State st) { std::cerr << "enter state " << st << std::endl; m_state = st; } void NNTPServerHandler::Quit() { EnterState(eStateQuit); QueueLine("205 quitting"); } bool NNTPServerHandler::ShouldClose() { return m_state == eStateQuit; } bool NNTPServerHandler::PostingAllowed() { return m_authed || m_auth == nullptr; } void NNTPServerHandler::Greet() { if (PostingAllowed()) QueueLine("200 Posting allowed"); else QueueLine("201 Posting not allowed"); } void NNTPServerHandler::SetAuth(CredDB_ptr creds) { m_auth = creds; } }