storage works
This commit is contained in:
parent
0965d34fbb
commit
e4a9db3f11
@ -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;
|
||||
|
@ -1,6 +1,7 @@
|
||||
[nntp]
|
||||
bind = [::]:1199
|
||||
instance_name=nntp.server.tld
|
||||
bind=[::]:1199
|
||||
authdb=auth.txt
|
||||
|
||||
[storage]
|
||||
path = ./storage/
|
||||
[articles]
|
||||
store_path=./storage/
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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 '/';
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user