Archived
1
0

storage works

This commit is contained in:
Jeff Becker 2017-05-03 13:33:04 -04:00
parent 0965d34fbb
commit e4a9db3f11
13 changed files with 223 additions and 52 deletions

View File

@ -22,6 +22,7 @@ int main(int argc, char * argv[]) {
nntpchan::NNTPServer nntp(loop); nntpchan::NNTPServer nntp(loop);
std::string fname(argv[1]); std::string fname(argv[1]);
std::ifstream i(fname); std::ifstream i(fname);
@ -29,7 +30,7 @@ int main(int argc, char * argv[]) {
if(i.is_open()) { if(i.is_open()) {
INI::Parser conf(i); INI::Parser conf(i);
std::vector<std::string> requiredSections = {"nntp", "storage"}; std::vector<std::string> requiredSections = {"nntp", "articles"};
auto & level = conf.top(); 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()) { if (storeconf.find("store_path") == storeconf.end()) {
std::cerr << "storage section does not have 'path' value" << std::endl; std::cerr << "storage section does not have 'store_path' value" << std::endl;
return 1; return 1;
} }
nntp.SetStoragePath(storeconf["path"]); nntp.SetStoragePath(storeconf["store_path"]);
auto & nntpconf = level.sections["nntp"].values; auto & nntpconf = level.sections["nntp"].values;
@ -57,6 +58,13 @@ int main(int argc, char * argv[]) {
return 1; 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()) { if (nntpconf.find("authdb") != nntpconf.end()) {
nntp.SetLoginDB(nntpconf["authdb"]); nntp.SetLoginDB(nntpconf["authdb"]);
} }
@ -77,6 +85,7 @@ int main(int argc, char * argv[]) {
nntp.SetFrontend(new nntpchan::ExecFrontend(frontconf["exec"])); nntp.SetFrontend(new nntpchan::ExecFrontend(frontconf["exec"]));
} else { } else {
std::cerr << "unknown frontend type '" << ftype << "'" << std::endl; std::cerr << "unknown frontend type '" << ftype << "'" << std::endl;
return 1;
} }
} }
@ -90,9 +99,9 @@ int main(int argc, char * argv[]) {
return 1; return 1;
} }
std::cerr << "run mainloop" << std::endl; std::cerr << "nntpd for " << nntp.InstanceName() << " bound to " << a << std::endl;
loop.Run(); loop.Run();
std::cerr << "stopped mainloop" << std::endl;
} else { } else {
std::cerr << "failed to open " << fname << std::endl; std::cerr << "failed to open " << fname << std::endl;

View File

@ -1,6 +1,7 @@
[nntp] [nntp]
instance_name=nntp.server.tld
bind=[::]:1199 bind=[::]:1199
authdb=auth.txt authdb=auth.txt
[storage] [articles]
path = ./storage/ store_path=./storage/

View File

@ -15,16 +15,21 @@ namespace nntpchan {
return; return;
} }
std::size_t idx = 0; std::size_t idx = 0;
std::size_t pos = 0;
ssize_t begin = l; ssize_t begin = l;
const char * data = current.c_str(); const char * data = current.c_str();
while(l-- > 0) { while(l-- > 0) {
char c = data[idx++]; char c = data[idx++];
if(c == '\n') { if(c == '\n') {
OnLine(data, idx-1); OnLine(data, pos);
pos = 0;
data += idx; data += idx;
} else if (c == '\r' && data[idx] == '\n') { } else if (c == '\r' && data[idx] == '\n') {
OnLine(data, idx-1); OnLine(data, pos);
data += idx + 1; data += idx + 1;
pos = 0;
} else {
pos ++;
} }
} }
if (idx < begin) if (idx < begin)

View File

@ -2,13 +2,12 @@
namespace nntpchan namespace nntpchan
{ {
bool IsValidMessageID(const MessageID & msgid) bool IsValidMessageID(const std::string & msgid)
{ {
auto itr = msgid.begin(); if(msgid[0] != '<') return false;
auto end = msgid.end(); if(msgid[msgid.size()-1] != '>') return false;
--end; auto itr = msgid.begin() + 1;
if (*itr != '<') return false; auto end = msgid.end() - 1;
if (*end != '>') return false;
bool atfound = false; bool atfound = false;
while(itr != end) { while(itr != end) {
auto c = *itr; auto c = *itr;
@ -18,10 +17,10 @@ namespace nntpchan
atfound = true; atfound = true;
continue; continue;
} }
if (c == '$' || c == '_' || c == '-') continue; if (c == '$' || c == '_' || c == '-' || c == '.') continue;
if (c > '0' && c < '9') continue; if (c >= '0' && c <= '9') continue;
if (c > 'A' && c < 'Z') continue; if (c >= 'A' && c <= 'Z') continue;
if (c > 'a' && c < 'z') continue; if (c >= 'a' && c <= 'z') continue;
return false; return false;
} }
return true; return true;

View File

@ -8,9 +8,7 @@
namespace nntpchan namespace nntpchan
{ {
typedef std::string MessageID; bool IsValidMessageID(const std::string & msgid);
bool IsValidMessageID(const MessageID & msgid);
typedef std::pair<std::string, std::string> MessageHeader; typedef std::pair<std::string, std::string> MessageHeader;

View File

@ -1,6 +1,8 @@
#include "nntp_handler.hpp" #include "nntp_handler.hpp"
#include "message.hpp"
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cstring>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
@ -9,6 +11,7 @@ namespace nntpchan
{ {
NNTPServerHandler::NNTPServerHandler(const std::string & storage) : NNTPServerHandler::NNTPServerHandler(const std::string & storage) :
LineReader(1024), LineReader(1024),
m_article(nullptr),
m_auth(nullptr), m_auth(nullptr),
m_store(storage), m_store(storage),
m_authed(false), m_authed(false),
@ -23,7 +26,8 @@ namespace nntpchan
void NNTPServerHandler::HandleLine(const std::string &line) void NNTPServerHandler::HandleLine(const std::string &line)
{ {
if(m_state == eStateReadCommand) { if(m_state == eStateReadCommand)
{
std::deque<std::string> command; std::deque<std::string> command;
std::istringstream s; std::istringstream s;
s.str(line); s.str(line);
@ -35,10 +39,51 @@ namespace nntpchan
else else
QueueLine("501 Syntax error"); 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) void NNTPServerHandler::OnData(const char * data, ssize_t 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); Data(data, l);
} }
@ -47,11 +92,18 @@ namespace nntpchan
auto cmd = command[0]; auto cmd = command[0];
std::transform(cmd.begin(), cmd.end(), cmd.begin(), ::toupper); std::transform(cmd.begin(), cmd.end(), cmd.begin(), ::toupper);
std::size_t cmdlen = command.size(); 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") { if (cmd == "QUIT") {
Quit(); Quit();
return; return;
} else if (cmd == "MODE" ) { }
else if (cmd[0] == '5')
{
return;
}
else if (cmd == "MODE" ) {
if(cmdlen == 2) { if(cmdlen == 2) {
// set mode // set mode
SwitchMode(command[1]); SwitchMode(command[1]);
@ -62,12 +114,61 @@ namespace nntpchan
// get mode // get mode
QueueLine("500 wrong arguments"); 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 { } else {
// unknown command // unknown command
QueueLine("500 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) void NNTPServerHandler::SwitchMode(const std::string & mode)
{ {
std::string m = 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() void NNTPServerHandler::Quit()
{ {
std::cerr << "quitting" << std::endl; EnterState(eStateQuit);
m_state = eStateQuit;
QueueLine("205 quitting"); QueueLine("205 quitting");
} }

View File

@ -34,6 +34,11 @@ namespace nntpchan
}; };
private: private:
void EnterState(State st);
void ArticleObtained();
// handle quit command, this queues a reply // handle quit command, this queues a reply
void Quit(); void Quit();
@ -43,6 +48,8 @@ namespace nntpchan
bool PostingAllowed(); bool PostingAllowed();
private: private:
std::string m_articleName;
std::fstream * m_article;
NNTPCredentialDB * m_auth; NNTPCredentialDB * m_auth;
ArticleStorage m_store; ArticleStorage m_store;
std::string m_mode; std::string m_mode;

View File

@ -44,6 +44,16 @@ namespace nntpchan
m_storagePath = path; 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) void NNTPServer::SetFrontend(Frontend * f)
{ {
if(m_frontend) delete m_frontend; if(m_frontend) delete m_frontend;

View File

@ -21,6 +21,10 @@ namespace nntpchan
void SetLoginDB(const std::string path); void SetLoginDB(const std::string path);
void SetInstanceName(const std::string & name);
std::string InstanceName() const;
void SetFrontend(Frontend * f); void SetFrontend(Frontend * f);
void Close(); void Close();
@ -33,6 +37,7 @@ namespace nntpchan
std::string m_logindbpath; std::string m_logindbpath;
std::string m_storagePath; std::string m_storagePath;
std::string m_servername;
Frontend * m_frontend; Frontend * m_frontend;

View File

@ -40,7 +40,6 @@ namespace nntpchan
OnAcceptError(status); OnAcceptError(status);
return; return;
} }
std::cout << "new conn" << std::endl;
IServerConn * conn = CreateConn(s); IServerConn * conn = CreateConn(s);
assert(conn); assert(conn);
m_conns.push_back(conn); m_conns.push_back(conn);
@ -96,7 +95,6 @@ namespace nntpchan
IServerConn * self = (IServerConn*) s->data; IServerConn * self = (IServerConn*) s->data;
if(self == nullptr) return; if(self == nullptr) return;
if(nread > 0) { if(nread > 0) {
std::cout << "read " << nread << std::endl;
self->m_handler->OnData(b->base, nread); self->m_handler->OnData(b->base, nread);
self->SendNextReply(); self->SendNextReply();
if(self->m_handler->ShouldClose()) if(self->m_handler->ShouldClose())

View File

@ -25,18 +25,31 @@ namespace nntpchan
mkdir(basedir.c_str(), 0700); mkdir(basedir.c_str(), 0700);
} }
bool ArticleStorage::Accept(const MessageID & msgid) bool ArticleStorage::Accept(const std::string& msgid)
{ {
if (!IsValidMessageID(msgid)) return false; if (!IsValidMessageID(msgid)) return false;
std::stringstream ss; auto s = MessagePath(msgid);
ss << basedir << GetPathSep() << msgid;
auto s = ss.str();
FILE * f = fopen(s.c_str(), "r"); FILE * f = fopen(s.c_str(), "r");
if ( f == nullptr) return errno == ENOENT; if ( f == nullptr) return errno == ENOENT;
fclose(f); fclose(f);
return false; 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() char ArticleStorage::GetPathSep()
{ {
return '/'; return '/';

View File

@ -1,6 +1,7 @@
#ifndef NNTPCHAN_STORAGE_HPP #ifndef NNTPCHAN_STORAGE_HPP
#define NNTPCHAN_STORAGE_HPP #define NNTPCHAN_STORAGE_HPP
#include <fstream>
#include <string> #include <string>
#include "message.hpp" #include "message.hpp"
@ -15,16 +16,34 @@ namespace nntpchan
void SetPath(const std::string & fpath); void SetPath(const std::string & fpath);
std::ostream & OpenWrite(const MessageID & msgid); std::fstream * OpenWrite(const std::string & msgid);
std::istream & OpenRead(const MessageID & msgid); std::fstream * OpenRead(const std::string & msgid);
/** /**
return true if we should accept a new message give its message id 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: private:
template<typename Mode>
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(); static char GetPathSep();
std::string basedir; std::string basedir;

View File

@ -1,4 +1,5 @@
#include "exec_frontend.hpp" #include "exec_frontend.hpp"
#include "message.hpp"
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
@ -7,7 +8,7 @@
int main(int , char * []) int main(int , char * [])
{ {
nntpchan::Frontend * f = new nntpchan::ExecFrontend("./contrib/nntpchan.sh"); nntpchan::Frontend * f = new nntpchan::ExecFrontend("./contrib/nntpchan.sh");
assert(f->AcceptsMessage("<test@server>")); assert(nntpchan::IsValidMessageID("<a28a71493831188@web.oniichan.onion>"));
assert(f->AcceptsNewsgroup("overchan.test")); assert(f->AcceptsNewsgroup("overchan.test"));
std::cout << "all good" << std::endl; std::cout << "all good" << std::endl;
} }