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]
bind = [::]:1199 instance_name=nntp.server.tld
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,12 +8,10 @@
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;
typedef std::map<std::string, std::string> MIMEPartHeader; typedef std::map<std::string, std::string> MIMEPartHeader;
typedef std::function<bool(const MessageHeader &)> MessageHeaderFilter; typedef std::function<bool(const MessageHeader &)> MessageHeaderFilter;
@ -21,15 +19,15 @@ namespace nntpchan
typedef std::function<bool(const MIMEPartHeader &)> MIMEPartFilter; typedef std::function<bool(const MIMEPartHeader &)> MIMEPartFilter;
/** /**
read MIME message from i, read MIME message from i,
filter each header with h, filter each header with h,
filter each part with p, filter each part with p,
store result in o 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); bool StoreMIMEMessage(std::istream & i, MessageHeaderFilter h, MIMEPartHeader p, std::ostream & o);
} }

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,11 +39,52 @@ 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)
{ {
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<std::string> & command) void NNTPServerHandler::HandleCommand(const std::deque<std::string> & command)
@ -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

@ -12,7 +12,7 @@ namespace nntpchan
ArticleStorage::ArticleStorage(const std::string & fpath) { ArticleStorage::ArticleStorage(const std::string & fpath) {
SetPath(fpath); SetPath(fpath);
} }
ArticleStorage::~ArticleStorage() ArticleStorage::~ArticleStorage()
{ {
} }
@ -25,22 +25,35 @@ 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,20 +16,38 @@ 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;
} }