diff --git a/contrib/backends/nntpchan-daemon/daemon/main.cpp b/contrib/backends/nntpchan-daemon/daemon/main.cpp index d77b165..aedc90b 100644 --- a/contrib/backends/nntpchan-daemon/daemon/main.cpp +++ b/contrib/backends/nntpchan-daemon/daemon/main.cpp @@ -22,6 +22,7 @@ int main(int argc, char * argv[]) { nntpchan::NNTPServer nntp(loop); + std::string fname(argv[1]); std::ifstream i(fname); @@ -29,7 +30,7 @@ int main(int argc, char * argv[]) { if(i.is_open()) { INI::Parser conf(i); - std::vector requiredSections = {"nntp", "storage"}; + std::vector requiredSections = {"nntp", "articles"}; auto & level = conf.top(); @@ -41,14 +42,14 @@ int main(int argc, char * argv[]) { } } - auto & storeconf = level.sections["storage"].values; + auto & storeconf = level.sections["articles"].values; - if (storeconf.find("path") == storeconf.end()) { - std::cerr << "storage section does not have 'path' value" << std::endl; + if (storeconf.find("store_path") == storeconf.end()) { + std::cerr << "storage section does not have 'store_path' value" << std::endl; return 1; } - nntp.SetStoragePath(storeconf["path"]); + nntp.SetStoragePath(storeconf["store_path"]); auto & nntpconf = level.sections["nntp"].values; @@ -57,6 +58,13 @@ int main(int argc, char * argv[]) { return 1; } + if(nntpconf.find("instance_name") == nntpconf.end()) { + std::cerr << "nntp section lacks 'instance_name' value" << std::endl; + return 1; + } + + nntp.SetInstanceName(nntpconf["instance_name"]); + if (nntpconf.find("authdb") != nntpconf.end()) { nntp.SetLoginDB(nntpconf["authdb"]); } @@ -77,6 +85,7 @@ int main(int argc, char * argv[]) { nntp.SetFrontend(new nntpchan::ExecFrontend(frontconf["exec"])); } else { std::cerr << "unknown frontend type '" << ftype << "'" << std::endl; + return 1; } } @@ -90,9 +99,9 @@ int main(int argc, char * argv[]) { return 1; } - std::cerr << "run mainloop" << std::endl; + std::cerr << "nntpd for " << nntp.InstanceName() << " bound to " << a << std::endl; + loop.Run(); - std::cerr << "stopped mainloop" << std::endl; } else { std::cerr << "failed to open " << fname << std::endl; diff --git a/contrib/backends/nntpchan-daemon/nntpchan.ini b/contrib/backends/nntpchan-daemon/nntpchan.ini index cbb82d6..6311fd5 100644 --- a/contrib/backends/nntpchan-daemon/nntpchan.ini +++ b/contrib/backends/nntpchan-daemon/nntpchan.ini @@ -1,6 +1,7 @@ [nntp] -bind = [::]:1199 +instance_name=nntp.server.tld +bind=[::]:1199 authdb=auth.txt -[storage] -path = ./storage/ +[articles] +store_path=./storage/ diff --git a/contrib/backends/nntpchan-daemon/src/line.cpp b/contrib/backends/nntpchan-daemon/src/line.cpp index 9ccd3fb..d9d9010 100644 --- a/contrib/backends/nntpchan-daemon/src/line.cpp +++ b/contrib/backends/nntpchan-daemon/src/line.cpp @@ -15,16 +15,21 @@ namespace nntpchan { return; } std::size_t idx = 0; + std::size_t pos = 0; ssize_t begin = l; const char * data = current.c_str(); while(l-- > 0) { char c = data[idx++]; if(c == '\n') { - OnLine(data, idx-1); + OnLine(data, pos); + pos = 0; data += idx; } else if (c == '\r' && data[idx] == '\n') { - OnLine(data, idx-1); + OnLine(data, pos); data += idx + 1; + pos = 0; + } else { + pos ++; } } if (idx < begin) diff --git a/contrib/backends/nntpchan-daemon/src/message.cpp b/contrib/backends/nntpchan-daemon/src/message.cpp index 772540b..dcbf4b9 100644 --- a/contrib/backends/nntpchan-daemon/src/message.cpp +++ b/contrib/backends/nntpchan-daemon/src/message.cpp @@ -2,13 +2,12 @@ namespace nntpchan { - bool IsValidMessageID(const MessageID & msgid) + bool IsValidMessageID(const std::string & msgid) { - auto itr = msgid.begin(); - auto end = msgid.end(); - --end; - if (*itr != '<') return false; - if (*end != '>') return false; + if(msgid[0] != '<') return false; + if(msgid[msgid.size()-1] != '>') return false; + auto itr = msgid.begin() + 1; + auto end = msgid.end() - 1; bool atfound = false; while(itr != end) { auto c = *itr; @@ -18,10 +17,10 @@ namespace nntpchan atfound = true; continue; } - if (c == '$' || c == '_' || c == '-') continue; - if (c > '0' && c < '9') continue; - if (c > 'A' && c < 'Z') continue; - if (c > 'a' && c < 'z') continue; + if (c == '$' || c == '_' || c == '-' || c == '.') continue; + if (c >= '0' && c <= '9') continue; + if (c >= 'A' && c <= 'Z') continue; + if (c >= 'a' && c <= 'z') continue; return false; } return true; diff --git a/contrib/backends/nntpchan-daemon/src/message.hpp b/contrib/backends/nntpchan-daemon/src/message.hpp index be4dddf..d9aa49b 100644 --- a/contrib/backends/nntpchan-daemon/src/message.hpp +++ b/contrib/backends/nntpchan-daemon/src/message.hpp @@ -8,12 +8,10 @@ namespace nntpchan { - typedef std::string MessageID; - - bool IsValidMessageID(const MessageID & msgid); + bool IsValidMessageID(const std::string & msgid); typedef std::pair MessageHeader; - + typedef std::map MIMEPartHeader; typedef std::function MessageHeaderFilter; @@ -21,15 +19,15 @@ namespace nntpchan typedef std::function MIMEPartFilter; /** - read MIME message from i, - filter each header with h, - filter each part with p, + read MIME message from i, + filter each header with h, + filter each part with p, store result in o - return true if we read the whole message, return false if there is remaining + return true if we read the whole message, return false if there is remaining */ bool StoreMIMEMessage(std::istream & i, MessageHeaderFilter h, MIMEPartHeader p, std::ostream & o); - + } diff --git a/contrib/backends/nntpchan-daemon/src/nntp_handler.cpp b/contrib/backends/nntpchan-daemon/src/nntp_handler.cpp index 794737f..a0ec1ba 100644 --- a/contrib/backends/nntpchan-daemon/src/nntp_handler.cpp +++ b/contrib/backends/nntpchan-daemon/src/nntp_handler.cpp @@ -1,6 +1,8 @@ #include "nntp_handler.hpp" +#include "message.hpp" #include #include +#include #include #include #include @@ -9,6 +11,7 @@ namespace nntpchan { NNTPServerHandler::NNTPServerHandler(const std::string & storage) : LineReader(1024), + m_article(nullptr), m_auth(nullptr), m_store(storage), m_authed(false), @@ -23,7 +26,8 @@ namespace nntpchan void NNTPServerHandler::HandleLine(const std::string &line) { - if(m_state == eStateReadCommand) { + if(m_state == eStateReadCommand) + { std::deque command; std::istringstream s; s.str(line); @@ -35,11 +39,52 @@ namespace nntpchan else QueueLine("501 Syntax error"); } + else if(m_state == eStateStoreArticle) + { + OnData(line.c_str(), line.size()); + OnData("\n", 1); + } + else + { + std::cerr << "invalid state" << std::endl; + } } void NNTPServerHandler::OnData(const char * data, ssize_t l) { - Data(data, l); + if(l <= 0 ) return; + if(m_state == eStateStoreArticle) + { + const char * end = strstr(data, "\n.\n"); + if(end) + { + std::size_t diff = end - data ; + std::cerr << diff << std::endl; + if(m_article) + m_article->write(data, diff); + ArticleObtained(); + diff += 3; + OnData(end+3, l-diff); + return; + } + end = strstr(data, "\r\n.\r\n"); + if(end) + { + std::size_t diff = end - data ; + std::cerr << diff << std::endl; + if(m_article) + m_article->write(data, diff); + ArticleObtained(); + diff += 5; + OnData(end+5, l-diff); + return; + return; + } + if(m_article) + m_article->write(data, l); + } + else + Data(data, l); } void NNTPServerHandler::HandleCommand(const std::deque & command) @@ -47,11 +92,18 @@ namespace nntpchan auto cmd = command[0]; std::transform(cmd.begin(), cmd.end(), cmd.begin(), ::toupper); std::size_t cmdlen = command.size(); - std::cerr << "handle command [" << cmd << "] " << (int) cmdlen << std::endl; + for(const auto & part : command) + std::cerr << " " << part; + std::cerr << std::endl; if (cmd == "QUIT") { Quit(); return; - } else if (cmd == "MODE" ) { + } + else if (cmd[0] == '5') + { + return; + } + else if (cmd == "MODE" ) { if(cmdlen == 2) { // set mode SwitchMode(command[1]); @@ -62,12 +114,61 @@ namespace nntpchan // 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); + return; + } + 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->flush(); + m_article->close(); + delete m_article; + 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; @@ -92,10 +193,15 @@ namespace nntpchan } } + void NNTPServerHandler::EnterState(State st) + { + std::cerr << "enter state " << st << std::endl; + m_state = st; + } + void NNTPServerHandler::Quit() { - std::cerr << "quitting" << std::endl; - m_state = eStateQuit; + EnterState(eStateQuit); QueueLine("205 quitting"); } diff --git a/contrib/backends/nntpchan-daemon/src/nntp_handler.hpp b/contrib/backends/nntpchan-daemon/src/nntp_handler.hpp index 8d9a649..64d708d 100644 --- a/contrib/backends/nntpchan-daemon/src/nntp_handler.hpp +++ b/contrib/backends/nntpchan-daemon/src/nntp_handler.hpp @@ -34,6 +34,11 @@ namespace nntpchan }; private: + + void EnterState(State st); + + void ArticleObtained(); + // handle quit command, this queues a reply void Quit(); @@ -43,6 +48,8 @@ namespace nntpchan bool PostingAllowed(); private: + std::string m_articleName; + std::fstream * m_article; NNTPCredentialDB * m_auth; ArticleStorage m_store; std::string m_mode; diff --git a/contrib/backends/nntpchan-daemon/src/nntp_server.cpp b/contrib/backends/nntpchan-daemon/src/nntp_server.cpp index b191115..9f9e41e 100644 --- a/contrib/backends/nntpchan-daemon/src/nntp_server.cpp +++ b/contrib/backends/nntpchan-daemon/src/nntp_server.cpp @@ -44,6 +44,16 @@ namespace nntpchan m_storagePath = path; } + void NNTPServer::SetInstanceName(const std::string & name) + { + m_servername = name; + } + + std::string NNTPServer::InstanceName() const + { + return m_servername; + } + void NNTPServer::SetFrontend(Frontend * f) { if(m_frontend) delete m_frontend; diff --git a/contrib/backends/nntpchan-daemon/src/nntp_server.hpp b/contrib/backends/nntpchan-daemon/src/nntp_server.hpp index cc8973c..f049a1e 100644 --- a/contrib/backends/nntpchan-daemon/src/nntp_server.hpp +++ b/contrib/backends/nntpchan-daemon/src/nntp_server.hpp @@ -21,6 +21,10 @@ namespace nntpchan void SetLoginDB(const std::string path); + void SetInstanceName(const std::string & name); + + std::string InstanceName() const; + void SetFrontend(Frontend * f); void Close(); @@ -33,6 +37,7 @@ namespace nntpchan std::string m_logindbpath; std::string m_storagePath; + std::string m_servername; Frontend * m_frontend; diff --git a/contrib/backends/nntpchan-daemon/src/server.cpp b/contrib/backends/nntpchan-daemon/src/server.cpp index 9570bc6..f1db683 100644 --- a/contrib/backends/nntpchan-daemon/src/server.cpp +++ b/contrib/backends/nntpchan-daemon/src/server.cpp @@ -40,7 +40,6 @@ namespace nntpchan OnAcceptError(status); return; } - std::cout << "new conn" << std::endl; IServerConn * conn = CreateConn(s); assert(conn); m_conns.push_back(conn); @@ -96,7 +95,6 @@ namespace nntpchan IServerConn * self = (IServerConn*) s->data; if(self == nullptr) return; if(nread > 0) { - std::cout << "read " << nread << std::endl; self->m_handler->OnData(b->base, nread); self->SendNextReply(); if(self->m_handler->ShouldClose()) diff --git a/contrib/backends/nntpchan-daemon/src/storage.cpp b/contrib/backends/nntpchan-daemon/src/storage.cpp index 02e34c7..201cd5c 100644 --- a/contrib/backends/nntpchan-daemon/src/storage.cpp +++ b/contrib/backends/nntpchan-daemon/src/storage.cpp @@ -12,7 +12,7 @@ namespace nntpchan ArticleStorage::ArticleStorage(const std::string & fpath) { SetPath(fpath); } - + ArticleStorage::~ArticleStorage() { } @@ -25,22 +25,35 @@ namespace nntpchan mkdir(basedir.c_str(), 0700); } - bool ArticleStorage::Accept(const MessageID & msgid) + bool ArticleStorage::Accept(const std::string& msgid) { if (!IsValidMessageID(msgid)) return false; - std::stringstream ss; - ss << basedir << GetPathSep() << msgid; - auto s = ss.str(); + auto s = MessagePath(msgid); FILE * f = fopen(s.c_str(), "r"); if ( f == nullptr) return errno == ENOENT; fclose(f); return false; } + std::string ArticleStorage::MessagePath(const std::string & msgid) + { + return basedir + GetPathSep() + msgid; + } + + std::fstream * ArticleStorage::OpenRead(const std::string & msgid) + { + return OpenMode(msgid, std::ios::in); + } + + std::fstream * ArticleStorage::OpenWrite(const std::string & msgid) + { + return OpenMode(msgid, std::ios::out); + } + char ArticleStorage::GetPathSep() { return '/'; } - + } diff --git a/contrib/backends/nntpchan-daemon/src/storage.hpp b/contrib/backends/nntpchan-daemon/src/storage.hpp index 9a7401f..98f2882 100644 --- a/contrib/backends/nntpchan-daemon/src/storage.hpp +++ b/contrib/backends/nntpchan-daemon/src/storage.hpp @@ -1,6 +1,7 @@ #ifndef NNTPCHAN_STORAGE_HPP #define NNTPCHAN_STORAGE_HPP +#include #include #include "message.hpp" @@ -15,20 +16,38 @@ namespace nntpchan void SetPath(const std::string & fpath); - std::ostream & OpenWrite(const MessageID & msgid); - std::istream & OpenRead(const MessageID & msgid); + std::fstream * OpenWrite(const std::string & msgid); + std::fstream * OpenRead(const std::string & msgid); /** return true if we should accept a new message give its message id */ - bool Accept(const MessageID & msgid); - + bool Accept(const std::string & msgid); + private: + template + std::fstream * OpenMode(const std::string & msgid, const Mode & m) + { + if(IsValidMessageID(msgid)) + { + std::fstream * f = new std::fstream; + f->open(MessagePath(msgid), m); + if(f->is_open()) + return f; + delete f; + return nullptr; + } + else + return nullptr; + }; + + std::string MessagePath(const std::string & msgid); + static char GetPathSep(); - + std::string basedir; - + }; } diff --git a/contrib/backends/nntpchan-daemon/tools/testtool.cpp b/contrib/backends/nntpchan-daemon/tools/testtool.cpp index a84c670..aac4297 100644 --- a/contrib/backends/nntpchan-daemon/tools/testtool.cpp +++ b/contrib/backends/nntpchan-daemon/tools/testtool.cpp @@ -1,4 +1,5 @@ #include "exec_frontend.hpp" +#include "message.hpp" #include #include @@ -7,7 +8,7 @@ int main(int , char * []) { nntpchan::Frontend * f = new nntpchan::ExecFrontend("./contrib/nntpchan.sh"); - assert(f->AcceptsMessage("")); + assert(nntpchan::IsValidMessageID("")); assert(f->AcceptsNewsgroup("overchan.test")); std::cout << "all good" << std::endl; }