2016-10-15 21:37:59 +05:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cctype>
|
2017-05-03 22:33:04 +05:00
|
|
|
#include <cstring>
|
2016-10-15 21:37:59 +05:00
|
|
|
#include <iostream>
|
2017-10-17 19:29:56 +05:00
|
|
|
#include <nntpchan/nntp_handler.hpp>
|
|
|
|
#include <nntpchan/sanitize.hpp>
|
|
|
|
#include <sstream>
|
|
|
|
#include <string>
|
2016-10-15 21:37:59 +05:00
|
|
|
|
|
|
|
namespace nntpchan
|
|
|
|
{
|
2018-05-03 22:38:35 +05:00
|
|
|
NNTPServerHandler::NNTPServerHandler(fs::path storage)
|
2018-05-06 18:53:03 +05:00
|
|
|
: LineReader(), m_article(nullptr), m_auth(nullptr), m_store(storage), m_authed(false),
|
2018-05-06 17:10:20 +05:00
|
|
|
m_state(eStateReadCommand)
|
2017-10-17 19:29:56 +05:00
|
|
|
{
|
|
|
|
}
|
2016-10-15 22:53:35 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
NNTPServerHandler::~NNTPServerHandler() {}
|
2017-04-14 15:24:53 +05:00
|
|
|
|
2018-05-03 22:38:35 +05:00
|
|
|
void NNTPServerHandler::HandleLine(const std::string line)
|
2017-10-17 19:29:56 +05:00
|
|
|
{
|
|
|
|
if (m_state == eStateReadCommand)
|
2016-10-15 21:37:59 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
std::deque<std::string> command;
|
|
|
|
std::istringstream s;
|
|
|
|
s.str(line);
|
|
|
|
for (std::string part; std::getline(s, part, ' ');)
|
2017-05-03 22:33:04 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
if (part.size())
|
|
|
|
command.push_back(part);
|
2017-05-03 22:33:04 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
if (command.size())
|
|
|
|
HandleCommand(command);
|
2017-05-03 22:33:04 +05:00
|
|
|
else
|
2017-10-17 19:29:56 +05:00
|
|
|
QueueLine("501 Syntax error");
|
|
|
|
}
|
|
|
|
else if (m_state == eStateStoreArticle)
|
|
|
|
{
|
|
|
|
std::string l = line + "\r\n";
|
|
|
|
OnData(l.c_str(), l.size());
|
2016-10-15 21:37:59 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
std::cerr << "invalid state" << std::endl;
|
|
|
|
}
|
|
|
|
}
|
2016-10-15 21:37:59 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
void NNTPServerHandler::OnData(const char *data, ssize_t l)
|
|
|
|
{
|
|
|
|
if (l <= 0)
|
|
|
|
return;
|
|
|
|
if (m_state == eStateStoreArticle)
|
2017-05-03 18:44:42 +05:00
|
|
|
{
|
2018-05-03 22:38:35 +05:00
|
|
|
std::cerr << "storing " << l << " bytes" << std::endl;
|
2018-05-06 17:10:20 +05:00
|
|
|
if (strncmp(data, ".\r\n", l) == 0)
|
2018-05-03 22:38:35 +05:00
|
|
|
{
|
|
|
|
ArticleObtained();
|
|
|
|
return;
|
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
const char *end = strstr(data, "\r\n.\r\n");
|
|
|
|
if (end)
|
2017-05-03 22:33:04 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
std::size_t diff = end - data;
|
|
|
|
if (m_article)
|
2017-10-09 21:51:49 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
m_article->write(data, diff + 2);
|
2017-10-09 21:51:49 +05:00
|
|
|
m_article->flush();
|
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
ArticleObtained();
|
|
|
|
diff += 5;
|
2018-05-06 17:10:20 +05:00
|
|
|
if (l - diff)
|
2018-05-03 23:05:35 +05:00
|
|
|
Data(end + 5, l - diff);
|
2017-10-17 19:29:56 +05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_article)
|
|
|
|
{
|
|
|
|
m_article->write(data, l);
|
|
|
|
m_article->flush();
|
2017-05-03 22:33:04 +05:00
|
|
|
}
|
2017-05-03 18:44:42 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else
|
|
|
|
Data(data, l);
|
|
|
|
}
|
2017-05-03 18:44:42 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
void NNTPServerHandler::HandleCommand(const std::deque<std::string> &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")
|
2016-10-15 21:37:59 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
Quit();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (cmd[0] == '5')
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (cmd == "MODE")
|
|
|
|
{
|
|
|
|
if (cmdlen == 2)
|
|
|
|
{
|
|
|
|
// set mode
|
|
|
|
SwitchMode(command[1]);
|
2017-05-03 22:33:04 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else if (cmdlen)
|
2017-05-03 22:33:04 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
// too many arguments
|
|
|
|
QueueLine("500 too many arguments");
|
2017-05-03 22:33:04 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// get mode
|
|
|
|
QueueLine("500 wrong arguments");
|
2016-10-15 21:37:59 +05:00
|
|
|
}
|
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else if (cmd == "CAPABILITIES")
|
2017-05-03 22:33:04 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
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)
|
2017-05-03 22:33:04 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
const std::string &msgid = command[1];
|
2018-05-03 22:38:35 +05:00
|
|
|
if (IsValidMessageID(msgid) && m_store.Accept(msgid))
|
2017-10-17 19:29:56 +05:00
|
|
|
{
|
|
|
|
QueueLine("238 " + msgid);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
QueueLine("438 " + msgid);
|
2017-05-03 22:33:04 +05:00
|
|
|
}
|
|
|
|
else
|
2017-10-17 19:29:56 +05:00
|
|
|
QueueLine("501 syntax error");
|
2017-05-03 22:33:04 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else if (cmd == "TAKETHIS")
|
2016-10-15 21:37:59 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
if (cmdlen >= 2)
|
|
|
|
{
|
|
|
|
const std::string &msgid = command[1];
|
2018-05-03 22:38:35 +05:00
|
|
|
if (m_store.Accept(msgid))
|
2017-10-17 19:29:56 +05:00
|
|
|
{
|
2018-05-03 22:38:35 +05:00
|
|
|
m_article = m_store.OpenWrite(msgid);
|
2017-04-14 15:24:53 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
m_articleName = msgid;
|
|
|
|
EnterState(eStateStoreArticle);
|
|
|
|
return;
|
2017-04-14 15:24:53 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
QueueLine("501 invalid syntax");
|
2016-10-15 21:37:59 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else
|
2016-10-15 21:37:59 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
// unknown command
|
|
|
|
QueueLine("500 Unknown Command");
|
2016-10-15 21:37:59 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
}
|
2016-10-15 21:37:59 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
void NNTPServerHandler::ArticleObtained()
|
|
|
|
{
|
|
|
|
if (m_article)
|
2016-10-15 21:37:59 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
m_article->close();
|
|
|
|
m_article = nullptr;
|
2018-05-06 21:35:03 +05:00
|
|
|
m_store.EnsureSymlinks(m_articleName);
|
2017-10-17 19:29:56 +05:00
|
|
|
QueueLine("239 " + m_articleName);
|
|
|
|
std::cerr << "stored " << m_articleName << std::endl;
|
2016-10-15 21:37:59 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else
|
|
|
|
QueueLine("439 " + m_articleName);
|
|
|
|
m_articleName = "";
|
|
|
|
EnterState(eStateReadCommand);
|
|
|
|
}
|
2016-10-15 22:53:35 +05:00
|
|
|
|
2017-10-17 19:29:56 +05:00
|
|
|
void NNTPServerHandler::SwitchMode(const std::string &mode)
|
|
|
|
{
|
|
|
|
std::string m = mode;
|
|
|
|
std::transform(m.begin(), m.end(), m.begin(), ::toupper);
|
|
|
|
if (m == "READER")
|
2016-10-15 22:53:35 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
m_mode = m;
|
|
|
|
if (PostingAllowed())
|
|
|
|
{
|
|
|
|
QueueLine("200 Posting is permitted yo");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QueueLine("201 Posting is not permitted yo");
|
|
|
|
}
|
2016-10-15 22:53:35 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else if (m == "STREAM")
|
2016-10-15 22:53:35 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
m_mode = m;
|
|
|
|
if (PostingAllowed())
|
|
|
|
{
|
|
|
|
QueueLine("203 Streaming enabled");
|
|
|
|
}
|
2016-10-15 22:53:35 +05:00
|
|
|
else
|
2017-10-17 19:29:56 +05:00
|
|
|
{
|
|
|
|
QueueLine("483 Streaming Denied");
|
|
|
|
}
|
2016-10-15 22:53:35 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
else
|
2016-10-15 22:53:35 +05:00
|
|
|
{
|
2017-10-17 19:29:56 +05:00
|
|
|
// unknown mode
|
|
|
|
QueueLine("500 Unknown mode");
|
2016-10-15 22:53:35 +05:00
|
|
|
}
|
2016-10-15 21:37:59 +05:00
|
|
|
}
|
2017-10-17 19:29:56 +05:00
|
|
|
|
|
|
|
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; }
|
|
|
|
}
|