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);
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<std::string> requiredSections = {"nntp", "storage"};
std::vector<std::string> 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;

View File

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

View File

@ -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)

View File

@ -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;

View File

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

View File

@ -1,6 +1,8 @@
#include "nntp_handler.hpp"
#include "message.hpp"
#include <algorithm>
#include <cctype>
#include <cstring>
#include <string>
#include <sstream>
#include <iostream>
@ -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<std::string> command;
std::istringstream s;
s.str(line);
@ -35,10 +39,51 @@ 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)
{
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);
}
@ -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");
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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())

View File

@ -25,18 +25,31 @@ 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 '/';

View File

@ -1,6 +1,7 @@
#ifndef NNTPCHAN_STORAGE_HPP
#define NNTPCHAN_STORAGE_HPP
#include <fstream>
#include <string>
#include "message.hpp"
@ -15,16 +16,34 @@ 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<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();
std::string basedir;

View File

@ -1,4 +1,5 @@
#include "exec_frontend.hpp"
#include "message.hpp"
#include <cassert>
#include <iostream>
@ -7,7 +8,7 @@
int main(int , char * [])
{
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"));
std::cout << "all good" << std::endl;
}