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);
|
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;
|
||||||
|
@ -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/
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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())
|
||||||
|
@ -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 '/';
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user