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]
bind = [::]:1199
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,12 +8,10 @@
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;
typedef std::map<std::string, std::string> MIMEPartHeader;
typedef std::function<bool(const MessageHeader &)> MessageHeaderFilter;
@ -21,15 +19,15 @@ namespace nntpchan
typedef std::function<bool(const MIMEPartHeader &)> 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);
}

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,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<std::string> & 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");
}

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

@ -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 '/';
}
}

View File

@ -1,6 +1,7 @@
#ifndef NNTPCHAN_STORAGE_HPP
#define NNTPCHAN_STORAGE_HPP
#include <fstream>
#include <string>
#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<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;
}