* more nntpchan-daemon code
* fix keepalive
This commit is contained in:
parent
dc2de0fbc9
commit
e67e7a20bd
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
REPO=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
REPO=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||||
SRC_PATH = $(REPO)/src
|
SRC_PATH = $(REPO)/libnntpchan
|
||||||
|
|
||||||
SOURCES := $(wildcard $(SRC_PATH)/*.cpp)
|
SOURCES := $(wildcard $(SRC_PATH)/*.cpp)
|
||||||
HEADERS := $(wildcard $(SRC_PATH)/*.hpp)
|
HEADERS := $(wildcard $(SRC_PATH)/*.hpp)
|
||||||
@ -15,9 +15,9 @@ DAEMON_SRC = $(REPO)/daemon
|
|||||||
|
|
||||||
PKGS := libuv libsodium
|
PKGS := libuv libsodium
|
||||||
|
|
||||||
LD_FLAGS := $(shell pkg-config --libs $(PKGS))
|
LD_FLAGS := $(shell pkg-config --libs $(PKGS)) -lstdc++fs
|
||||||
INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I $(REPO)/src
|
INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I$(SRC_PATH)
|
||||||
CXXFLAGS := -std=c++11 -Wall -Wextra $(INC_FLAGS)
|
CXXFLAGS := -std=c++17 -Wall -Wextra -pedantic $(INC_FLAGS)
|
||||||
|
|
||||||
ifeq ($(DEBUG),1)
|
ifeq ($(DEBUG),1)
|
||||||
CXXFLAGS += -g
|
CXXFLAGS += -g
|
||||||
@ -28,7 +28,9 @@ LIB = $(REPO)/libnntpchan.a
|
|||||||
EXE = $(REPO)/nntpd
|
EXE = $(REPO)/nntpd
|
||||||
|
|
||||||
|
|
||||||
all: $(EXE) $(TOOLS)
|
all: build
|
||||||
|
|
||||||
|
build: $(EXE) $(TOOLS)
|
||||||
|
|
||||||
$(LIB): $(OBJECTS)
|
$(LIB): $(OBJECTS)
|
||||||
$(AR) -r $(LIB) $(OBJECTS)
|
$(AR) -r $(LIB) $(OBJECTS)
|
||||||
|
19
contrib/backends/nntpchan-daemon/README.md
Normal file
19
contrib/backends/nntpchan-daemon/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# nntpchan-daemon
|
||||||
|
|
||||||
|
C++ rewrite
|
||||||
|
|
||||||
|
requirements:
|
||||||
|
|
||||||
|
* g++ 7.2.0 >= or clang 5.x >=
|
||||||
|
|
||||||
|
* pkg-config
|
||||||
|
|
||||||
|
* libsodium 1.x
|
||||||
|
|
||||||
|
* libuv 1.x
|
||||||
|
|
||||||
|
* GNU Make
|
||||||
|
|
||||||
|
building:
|
||||||
|
|
||||||
|
$ make
|
@ -5,6 +5,7 @@
|
|||||||
#include "nntp_server.hpp"
|
#include "nntp_server.hpp"
|
||||||
#include "event.hpp"
|
#include "event.hpp"
|
||||||
#include "exec_frontend.hpp"
|
#include "exec_frontend.hpp"
|
||||||
|
#include "staticfile_frontend.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -16,7 +17,7 @@ int main(int argc, char * argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nntpchan::Crypto crypto();
|
nntpchan::Crypto crypto;
|
||||||
|
|
||||||
nntpchan::Mainloop loop;
|
nntpchan::Mainloop loop;
|
||||||
|
|
||||||
@ -69,21 +70,48 @@ int main(int argc, char * argv[]) {
|
|||||||
nntp.SetLoginDB(nntpconf["authdb"]);
|
nntp.SetLoginDB(nntpconf["authdb"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( level.sections.find("frontend") != level.sections.end()) {
|
if ( level.sections.find("frontend") != level.sections.end())
|
||||||
|
{
|
||||||
// frontend enabled
|
// frontend enabled
|
||||||
auto & frontconf = level.sections["frontend"].values;
|
auto & frontconf = level.sections["frontend"].values;
|
||||||
if (frontconf.find("type") == frontconf.end()) {
|
if (frontconf.find("type") == frontconf.end())
|
||||||
|
{
|
||||||
std::cerr << "frontend section provided but 'type' value not provided" << std::endl;
|
std::cerr << "frontend section provided but 'type' value not provided" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
auto ftype = frontconf["type"];
|
auto ftype = frontconf["type"];
|
||||||
if (ftype == "exec") {
|
if (ftype == "exec")
|
||||||
if (frontconf.find("exec") == frontconf.end()) {
|
{
|
||||||
|
if (frontconf.find("exec") == frontconf.end())
|
||||||
|
{
|
||||||
std::cerr << "exec frontend specified but no 'exec' value provided" << std::endl;
|
std::cerr << "exec frontend specified but no 'exec' value provided" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
nntp.SetFrontend(new nntpchan::ExecFrontend(frontconf["exec"]));
|
nntp.SetFrontend(new nntpchan::ExecFrontend(frontconf["exec"]));
|
||||||
} else {
|
}
|
||||||
|
else if (ftype == "staticfile")
|
||||||
|
{
|
||||||
|
auto required = {
|
||||||
|
"template_dir", "out_dir", "template_dialect", "max_pages"
|
||||||
|
};
|
||||||
|
for (const auto & opt : required)
|
||||||
|
{
|
||||||
|
if(frontconf.find(opt) == frontconf.end())
|
||||||
|
{
|
||||||
|
std::cerr << "staticfile frontend specified but no '" << opt << "' value provided" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto maxPages = std::stoi(frontconf["max_pages"]);
|
||||||
|
if(maxPages <= 0)
|
||||||
|
{
|
||||||
|
std::cerr << "max_pages invalid value '" << frontconf["max_pages"] << "'" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
nntp.SetFrontend(new nntpchan::StaticFileFrontend(nntpchan::CreateTemplateEngine(frontconf["template_dialect"]), frontconf["template_dir"], frontconf["out_dir"], maxPages));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
std::cerr << "unknown frontend type '" << ftype << "'" << std::endl;
|
std::cerr << "unknown frontend type '" << ftype << "'" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
44
contrib/backends/nntpchan-daemon/libnntpchan/crypto_old.hpp
Normal file
44
contrib/backends/nntpchan-daemon/libnntpchan/crypto_old.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
|
||||||
|
#ifndef NNTPCHAN_CRYPTO_OLD_HPP
|
||||||
|
#define NNTPCHAN_CRYPTO_OLD_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t state[5];
|
||||||
|
uint32_t count[2];
|
||||||
|
unsigned char buffer[64];
|
||||||
|
} SHA1_CTX;
|
||||||
|
|
||||||
|
void SHA1Transform(
|
||||||
|
uint32_t state[5],
|
||||||
|
const unsigned char buffer[64]
|
||||||
|
);
|
||||||
|
|
||||||
|
void SHA1Init(
|
||||||
|
SHA1_CTX * context
|
||||||
|
);
|
||||||
|
|
||||||
|
void SHA1Update(
|
||||||
|
SHA1_CTX * context,
|
||||||
|
const unsigned char *data,
|
||||||
|
uint32_t len
|
||||||
|
);
|
||||||
|
|
||||||
|
void SHA1Final(
|
||||||
|
unsigned char digest[20],
|
||||||
|
SHA1_CTX * context
|
||||||
|
);
|
||||||
|
|
||||||
|
void sha1(
|
||||||
|
uint8_t *hash_out,
|
||||||
|
const uint8_t *str,
|
||||||
|
size_t len);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -14,7 +14,7 @@ namespace nntpchan
|
|||||||
|
|
||||||
ExecFrontend::~ExecFrontend() {}
|
ExecFrontend::~ExecFrontend() {}
|
||||||
|
|
||||||
void ExecFrontend::ProcessNewMessage(const std::string & fpath)
|
void ExecFrontend::ProcessNewMessage(const fs::path & fpath)
|
||||||
{
|
{
|
||||||
Exec({"post", fpath});
|
Exec({"post", fpath});
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ namespace nntpchan
|
|||||||
|
|
||||||
~ExecFrontend();
|
~ExecFrontend();
|
||||||
|
|
||||||
void ProcessNewMessage(const std::string & fpath);
|
void ProcessNewMessage(const fs::path & fpath);
|
||||||
bool AcceptsNewsgroup(const std::string & newsgroup);
|
bool AcceptsNewsgroup(const std::string & newsgroup);
|
||||||
bool AcceptsMessage(const std::string & msgid);
|
bool AcceptsMessage(const std::string & msgid);
|
||||||
|
|
22
contrib/backends/nntpchan-daemon/libnntpchan/file_handle.cpp
Normal file
22
contrib/backends/nntpchan-daemon/libnntpchan/file_handle.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "file_handle.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
FileHandle_ptr OpenFile(const fs::path & fname, FileMode mode)
|
||||||
|
{
|
||||||
|
std::fstream * f = new std::fstream;
|
||||||
|
if(mode == eRead)
|
||||||
|
{
|
||||||
|
f->open(fname, std::ios::in);
|
||||||
|
}
|
||||||
|
else if (mode == eWrite)
|
||||||
|
{
|
||||||
|
f->open(fname, std::ios::out);
|
||||||
|
}
|
||||||
|
if(f->is_open())
|
||||||
|
return FileHandle_ptr(f);
|
||||||
|
delete f;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
23
contrib/backends/nntpchan-daemon/libnntpchan/file_handle.hpp
Normal file
23
contrib/backends/nntpchan-daemon/libnntpchan/file_handle.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef NNTPCHAN_FILE_HANDLE_HPP
|
||||||
|
#define NNTPCHAN_FILE_HANDLE_HPP
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <fstream>
|
||||||
|
#include <experimental/filesystem>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
typedef std::unique_ptr<std::fstream> FileHandle_ptr;
|
||||||
|
|
||||||
|
enum FileMode
|
||||||
|
{
|
||||||
|
eRead,
|
||||||
|
eWrite
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace fs = std::experimental::filesystem;
|
||||||
|
|
||||||
|
FileHandle_ptr OpenFile(const fs::path & fname, FileMode mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,17 +1,19 @@
|
|||||||
#ifndef NNTPCHAN_FRONTEND_HPP
|
#ifndef NNTPCHAN_FRONTEND_HPP
|
||||||
#define NNTPCHAN_FRONTEND_HPP
|
#define NNTPCHAN_FRONTEND_HPP
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <experimental/filesystem>
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace fs = std::experimental::filesystem;
|
||||||
/** @brief nntpchan frontend ui interface */
|
/** @brief nntpchan frontend ui interface */
|
||||||
class Frontend
|
class Frontend
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~Frontend() {}
|
|
||||||
|
|
||||||
/** @brief process an inbound message stored at fpath that we have accepted. */
|
/** @brief process an inbound message stored at fpath that we have accepted. */
|
||||||
virtual void ProcessNewMessage(const std::string & fpath) = 0;
|
virtual void ProcessNewMessage(const fs::path & fpath) = 0;
|
||||||
|
|
||||||
/** @brief return true if we take posts in a newsgroup */
|
/** @brief return true if we take posts in a newsgroup */
|
||||||
virtual bool AcceptsNewsgroup(const std::string & newsgroup) = 0;
|
virtual bool AcceptsNewsgroup(const std::string & newsgroup) = 0;
|
||||||
@ -20,6 +22,8 @@ namespace nntpchan
|
|||||||
virtual bool AcceptsMessage(const std::string & msgid) = 0;
|
virtual bool AcceptsMessage(const std::string & msgid) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::unique_ptr<Frontend> Frontend_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
12
contrib/backends/nntpchan-daemon/libnntpchan/io_handle.hpp
Normal file
12
contrib/backends/nntpchan-daemon/libnntpchan/io_handle.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef NNTPCHAN_IO_HANDLE_HPP
|
||||||
|
#define NNTPCHAN_IO_HANDLE_HPP
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
typedef std::unique_ptr<std::iostream> IOHandle_ptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
26
contrib/backends/nntpchan-daemon/libnntpchan/mime.cpp
Normal file
26
contrib/backends/nntpchan-daemon/libnntpchan/mime.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "mime.hpp"
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
bool ReadHeader(const FileHandle_ptr & file, RawHeader & header)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
while(std::getline(*file, line) && !(line == "\r" || line == ""))
|
||||||
|
{
|
||||||
|
std::string k, v;
|
||||||
|
auto idx = line.find(": ");
|
||||||
|
auto endidx = line.size() - 1;
|
||||||
|
|
||||||
|
while(line[endidx] == '\r') --endidx;
|
||||||
|
|
||||||
|
if(idx != std::string::npos && idx + 2 < endidx)
|
||||||
|
{
|
||||||
|
k = line.substr(0, idx);
|
||||||
|
v = line.substr(idx+2, endidx);
|
||||||
|
header[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file->good();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
contrib/backends/nntpchan-daemon/libnntpchan/mime.hpp
Normal file
30
contrib/backends/nntpchan-daemon/libnntpchan/mime.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef NNTPCHAN_MIME_HPP
|
||||||
|
#define NNTPCHAN_MIME_HPP
|
||||||
|
#include "file_handle.hpp"
|
||||||
|
#include "io_handle.hpp"
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef std::map<std::string, std::string> RawHeader;
|
||||||
|
|
||||||
|
bool ReadHeader(const FileHandle_ptr & f, RawHeader & h);
|
||||||
|
|
||||||
|
|
||||||
|
struct MimePart
|
||||||
|
{
|
||||||
|
virtual RawHeader & Header() = 0;
|
||||||
|
virtual IOHandle_ptr OpenPart() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unique_ptr<MimePart> MimePart_ptr;
|
||||||
|
|
||||||
|
typedef std::function<bool(MimePart_ptr)> PartReader;
|
||||||
|
|
||||||
|
bool ReadParts(const FileHandle_ptr & f, PartReader r);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
59
contrib/backends/nntpchan-daemon/libnntpchan/model.hpp
Normal file
59
contrib/backends/nntpchan-daemon/libnntpchan/model.hpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef NNTPCHAN_MODEL_HPP
|
||||||
|
#define NNTPCHAN_MODEL_HPP
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
namespace model
|
||||||
|
{
|
||||||
|
// MIME Header
|
||||||
|
typedef std::map<std::string, std::set<std::string> > PostHeader;
|
||||||
|
// text post contents
|
||||||
|
typedef std::string PostBody;
|
||||||
|
// single file attachment, (orig_filename, hexdigest, thumb_filename)
|
||||||
|
typedef std::tuple<std::string, std::string, std::string> PostAttachment;
|
||||||
|
// all attachments on a post
|
||||||
|
typedef std::vector<PostAttachment> Attachments;
|
||||||
|
// a post (header, Post Text, Attachments)
|
||||||
|
typedef std::tuple<PostHeader, PostBody, Attachments> Post;
|
||||||
|
// a thread (many posts in post order)
|
||||||
|
typedef std::vector<Post> Thread;
|
||||||
|
|
||||||
|
|
||||||
|
static inline std::string & GetFilename(PostAttachment & att)
|
||||||
|
{
|
||||||
|
return std::get<0>(att);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string & GetHexDigest(PostAttachment & att)
|
||||||
|
{
|
||||||
|
return std::get<1>(att);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string & GetThumbnail(PostAttachment & att)
|
||||||
|
{
|
||||||
|
return std::get<2>(att);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline PostHeader & GetHeader(Post & post)
|
||||||
|
{
|
||||||
|
return std::get<0>(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline PostBody & GetBody(Post & post)
|
||||||
|
{
|
||||||
|
return std::get<1>(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Attachments & GetAttachments(Post & post)
|
||||||
|
{
|
||||||
|
return std::get<2>(post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -4,6 +4,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
#include "line.hpp"
|
#include "line.hpp"
|
||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
@ -21,6 +22,8 @@ namespace nntpchan
|
|||||||
virtual ~NNTPCredentialDB() {}
|
virtual ~NNTPCredentialDB() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::shared_ptr<NNTPCredentialDB> CredDB_ptr;
|
||||||
|
|
||||||
/** @brief nntp credential db using hashed+salted passwords */
|
/** @brief nntp credential db using hashed+salted passwords */
|
||||||
class HashedCredDB : public NNTPCredentialDB, public LineReader
|
class HashedCredDB : public NNTPCredentialDB, public LineReader
|
||||||
{
|
{
|
@ -1,5 +1,5 @@
|
|||||||
#include "nntp_handler.hpp"
|
#include "nntp_handler.hpp"
|
||||||
#include "message.hpp"
|
#include "sanitize.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -21,7 +21,6 @@ namespace nntpchan
|
|||||||
|
|
||||||
NNTPServerHandler::~NNTPServerHandler()
|
NNTPServerHandler::~NNTPServerHandler()
|
||||||
{
|
{
|
||||||
if(m_auth) delete m_auth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NNTPServerHandler::HandleLine(const std::string &line)
|
void NNTPServerHandler::HandleLine(const std::string &line)
|
||||||
@ -144,7 +143,6 @@ namespace nntpchan
|
|||||||
{
|
{
|
||||||
m_article->flush();
|
m_article->flush();
|
||||||
m_article->close();
|
m_article->close();
|
||||||
delete m_article;
|
|
||||||
m_article = nullptr;
|
m_article = nullptr;
|
||||||
QueueLine("239 "+m_articleName);
|
QueueLine("239 "+m_articleName);
|
||||||
std::cerr << "stored " << m_articleName << std::endl;
|
std::cerr << "stored " << m_articleName << std::endl;
|
||||||
@ -209,9 +207,8 @@ namespace nntpchan
|
|||||||
QueueLine("201 Posting not allowed");
|
QueueLine("201 Posting not allowed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void NNTPServerHandler::SetAuth(NNTPCredentialDB *creds)
|
void NNTPServerHandler::SetAuth(CredDB_ptr creds)
|
||||||
{
|
{
|
||||||
if(m_auth) delete m_auth;
|
|
||||||
m_auth = creds;
|
m_auth = creds;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ namespace nntpchan
|
|||||||
|
|
||||||
virtual bool ShouldClose();
|
virtual bool ShouldClose();
|
||||||
|
|
||||||
void SetAuth(NNTPCredentialDB * creds);
|
void SetAuth(CredDB_ptr creds);
|
||||||
|
|
||||||
virtual void OnData(const char *, ssize_t);
|
virtual void OnData(const char *, ssize_t);
|
||||||
|
|
||||||
@ -49,8 +49,8 @@ namespace nntpchan
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_articleName;
|
std::string m_articleName;
|
||||||
std::fstream * m_article;
|
FileHandle_ptr m_article;
|
||||||
NNTPCredentialDB * m_auth;
|
CredDB_ptr m_auth;
|
||||||
ArticleStorage m_store;
|
ArticleStorage m_store;
|
||||||
std::string m_mode;
|
std::string m_mode;
|
||||||
bool m_authed;
|
bool m_authed;
|
@ -14,16 +14,15 @@ namespace nntpchan
|
|||||||
|
|
||||||
NNTPServer::~NNTPServer()
|
NNTPServer::~NNTPServer()
|
||||||
{
|
{
|
||||||
if (m_frontend) delete m_frontend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IServerConn * NNTPServer::CreateConn(uv_stream_t * s)
|
IServerConn * NNTPServer::CreateConn(uv_stream_t * s)
|
||||||
{
|
{
|
||||||
NNTPCredentialDB * creds = nullptr;
|
CredDB_ptr creds;
|
||||||
|
|
||||||
std::ifstream i;
|
std::ifstream i;
|
||||||
i.open(m_logindbpath);
|
i.open(m_logindbpath);
|
||||||
if(i.is_open()) creds = new HashedFileDB(m_logindbpath);
|
if(i.is_open()) creds = std::make_shared<HashedFileDB>(m_logindbpath);
|
||||||
|
|
||||||
NNTPServerHandler * handler = new NNTPServerHandler(m_storagePath);
|
NNTPServerHandler * handler = new NNTPServerHandler(m_storagePath);
|
||||||
if(creds)
|
if(creds)
|
||||||
@ -49,17 +48,16 @@ namespace nntpchan
|
|||||||
m_servername = name;
|
m_servername = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NNTPServer::SetFrontend(Frontend * f)
|
||||||
|
{
|
||||||
|
m_frontend.reset(f);
|
||||||
|
}
|
||||||
|
|
||||||
std::string NNTPServer::InstanceName() const
|
std::string NNTPServer::InstanceName() const
|
||||||
{
|
{
|
||||||
return m_servername;
|
return m_servername;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NNTPServer::SetFrontend(Frontend * f)
|
|
||||||
{
|
|
||||||
if(m_frontend) delete m_frontend;
|
|
||||||
m_frontend = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NNTPServer::OnAcceptError(int status)
|
void NNTPServer::OnAcceptError(int status)
|
||||||
{
|
{
|
||||||
std::cerr << "nntpserver::accept() " << uv_strerror(status) << std::endl;
|
std::cerr << "nntpserver::accept() " << uv_strerror(status) << std::endl;
|
||||||
@ -70,7 +68,7 @@ namespace nntpchan
|
|||||||
IConnHandler * handler = GetHandler();
|
IConnHandler * handler = GetHandler();
|
||||||
while(handler->HasNextLine()) {
|
while(handler->HasNextLine()) {
|
||||||
auto line = handler->GetNextLine();
|
auto line = handler->GetNextLine();
|
||||||
SendString(line + "\n");
|
SendString(line + "\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -25,21 +25,21 @@ namespace nntpchan
|
|||||||
|
|
||||||
std::string InstanceName() const;
|
std::string InstanceName() const;
|
||||||
|
|
||||||
void SetFrontend(Frontend * f);
|
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
virtual IServerConn * CreateConn(uv_stream_t * s);
|
virtual IServerConn * CreateConn(uv_stream_t * s);
|
||||||
|
|
||||||
virtual void OnAcceptError(int status);
|
virtual void OnAcceptError(int status);
|
||||||
|
|
||||||
|
void SetFrontend(Frontend * f);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
std::string m_logindbpath;
|
std::string m_logindbpath;
|
||||||
std::string m_storagePath;
|
std::string m_storagePath;
|
||||||
std::string m_servername;
|
std::string m_servername;
|
||||||
|
|
||||||
Frontend * m_frontend;
|
Frontend_ptr m_frontend;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
24
contrib/backends/nntpchan-daemon/libnntpchan/sanitize.cpp
Normal file
24
contrib/backends/nntpchan-daemon/libnntpchan/sanitize.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include "sanitize.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
std::string NNTPSanitize(const std::string & str)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToLower(const std::string & str)
|
||||||
|
{
|
||||||
|
std::string lower = str;
|
||||||
|
std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char ch) -> unsigned char { return std::tolower(ch); } );
|
||||||
|
return lower;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::regex re_ValidMessageID("^<[a-zA-Z0-9$\\._]{2,128}@[a-zA-Z0-9\\-\\.]{2,63}>$");
|
||||||
|
|
||||||
|
bool IsValidMessageID(const std::string & msgid)
|
||||||
|
{
|
||||||
|
return std::regex_search(msgid, re_ValidMessageID) == 1;
|
||||||
|
}
|
||||||
|
}
|
12
contrib/backends/nntpchan-daemon/libnntpchan/sanitize.hpp
Normal file
12
contrib/backends/nntpchan-daemon/libnntpchan/sanitize.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef NNTPCHAN_SANITIZE_HPP
|
||||||
|
#define NNTPCHAN_SANITIZE_HPP
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
std::string NNTPSanitize(const std::string & str);
|
||||||
|
std::string ToLower(const std::string & str);
|
||||||
|
bool IsValidMessageID(const std::string & msgid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
327
contrib/backends/nntpchan-daemon/libnntpchan/sha1.cpp
Normal file
327
contrib/backends/nntpchan-daemon/libnntpchan/sha1.cpp
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
SHA-1 in C
|
||||||
|
By Steve Reid <steve@edmweb.com>
|
||||||
|
100% Public Domain
|
||||||
|
|
||||||
|
Test Vectors (from FIPS PUB 180-1)
|
||||||
|
"abc"
|
||||||
|
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||||
|
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||||
|
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||||
|
A million repetitions of "a"
|
||||||
|
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
||||||
|
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
||||||
|
|
||||||
|
#include "crypto_old.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#define SHA1HANDSOFF
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* for uint32_t */
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||||
|
|
||||||
|
/* blk0() and blk() perform the initial expand. */
|
||||||
|
/* I got the idea of expanding during the round function from SSLeay */
|
||||||
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||||
|
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||||
|
|(rol(block->l[i],8)&0x00FF00FF))
|
||||||
|
#elif BYTE_ORDER == BIG_ENDIAN
|
||||||
|
#define blk0(i) block->l[i]
|
||||||
|
#else
|
||||||
|
#error "Endianness not defined!"
|
||||||
|
#endif
|
||||||
|
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||||
|
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||||
|
|
||||||
|
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||||
|
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||||
|
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||||
|
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||||
|
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||||
|
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||||
|
|
||||||
|
|
||||||
|
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||||
|
|
||||||
|
void SHA1Transform(
|
||||||
|
uint32_t state[5],
|
||||||
|
const unsigned char buffer[64]
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint32_t a, b, c, d, e;
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
unsigned char c[64];
|
||||||
|
uint32_t l[16];
|
||||||
|
} CHAR64LONG16;
|
||||||
|
|
||||||
|
#ifdef SHA1HANDSOFF
|
||||||
|
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||||
|
|
||||||
|
memcpy(block, buffer, 64);
|
||||||
|
#else
|
||||||
|
/* The following had better never be used because it causes the
|
||||||
|
* pointer-to-const buffer to be cast into a pointer to non-const.
|
||||||
|
* And the result is written through. I threw a "const" in, hoping
|
||||||
|
* this will cause a diagnostic.
|
||||||
|
*/
|
||||||
|
CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer;
|
||||||
|
#endif
|
||||||
|
/* Copy context->state[] to working vars */
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
e = state[4];
|
||||||
|
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||||
|
R0(a, b, c, d, e, 0);
|
||||||
|
R0(e, a, b, c, d, 1);
|
||||||
|
R0(d, e, a, b, c, 2);
|
||||||
|
R0(c, d, e, a, b, 3);
|
||||||
|
R0(b, c, d, e, a, 4);
|
||||||
|
R0(a, b, c, d, e, 5);
|
||||||
|
R0(e, a, b, c, d, 6);
|
||||||
|
R0(d, e, a, b, c, 7);
|
||||||
|
R0(c, d, e, a, b, 8);
|
||||||
|
R0(b, c, d, e, a, 9);
|
||||||
|
R0(a, b, c, d, e, 10);
|
||||||
|
R0(e, a, b, c, d, 11);
|
||||||
|
R0(d, e, a, b, c, 12);
|
||||||
|
R0(c, d, e, a, b, 13);
|
||||||
|
R0(b, c, d, e, a, 14);
|
||||||
|
R0(a, b, c, d, e, 15);
|
||||||
|
R1(e, a, b, c, d, 16);
|
||||||
|
R1(d, e, a, b, c, 17);
|
||||||
|
R1(c, d, e, a, b, 18);
|
||||||
|
R1(b, c, d, e, a, 19);
|
||||||
|
R2(a, b, c, d, e, 20);
|
||||||
|
R2(e, a, b, c, d, 21);
|
||||||
|
R2(d, e, a, b, c, 22);
|
||||||
|
R2(c, d, e, a, b, 23);
|
||||||
|
R2(b, c, d, e, a, 24);
|
||||||
|
R2(a, b, c, d, e, 25);
|
||||||
|
R2(e, a, b, c, d, 26);
|
||||||
|
R2(d, e, a, b, c, 27);
|
||||||
|
R2(c, d, e, a, b, 28);
|
||||||
|
R2(b, c, d, e, a, 29);
|
||||||
|
R2(a, b, c, d, e, 30);
|
||||||
|
R2(e, a, b, c, d, 31);
|
||||||
|
R2(d, e, a, b, c, 32);
|
||||||
|
R2(c, d, e, a, b, 33);
|
||||||
|
R2(b, c, d, e, a, 34);
|
||||||
|
R2(a, b, c, d, e, 35);
|
||||||
|
R2(e, a, b, c, d, 36);
|
||||||
|
R2(d, e, a, b, c, 37);
|
||||||
|
R2(c, d, e, a, b, 38);
|
||||||
|
R2(b, c, d, e, a, 39);
|
||||||
|
R3(a, b, c, d, e, 40);
|
||||||
|
R3(e, a, b, c, d, 41);
|
||||||
|
R3(d, e, a, b, c, 42);
|
||||||
|
R3(c, d, e, a, b, 43);
|
||||||
|
R3(b, c, d, e, a, 44);
|
||||||
|
R3(a, b, c, d, e, 45);
|
||||||
|
R3(e, a, b, c, d, 46);
|
||||||
|
R3(d, e, a, b, c, 47);
|
||||||
|
R3(c, d, e, a, b, 48);
|
||||||
|
R3(b, c, d, e, a, 49);
|
||||||
|
R3(a, b, c, d, e, 50);
|
||||||
|
R3(e, a, b, c, d, 51);
|
||||||
|
R3(d, e, a, b, c, 52);
|
||||||
|
R3(c, d, e, a, b, 53);
|
||||||
|
R3(b, c, d, e, a, 54);
|
||||||
|
R3(a, b, c, d, e, 55);
|
||||||
|
R3(e, a, b, c, d, 56);
|
||||||
|
R3(d, e, a, b, c, 57);
|
||||||
|
R3(c, d, e, a, b, 58);
|
||||||
|
R3(b, c, d, e, a, 59);
|
||||||
|
R4(a, b, c, d, e, 60);
|
||||||
|
R4(e, a, b, c, d, 61);
|
||||||
|
R4(d, e, a, b, c, 62);
|
||||||
|
R4(c, d, e, a, b, 63);
|
||||||
|
R4(b, c, d, e, a, 64);
|
||||||
|
R4(a, b, c, d, e, 65);
|
||||||
|
R4(e, a, b, c, d, 66);
|
||||||
|
R4(d, e, a, b, c, 67);
|
||||||
|
R4(c, d, e, a, b, 68);
|
||||||
|
R4(b, c, d, e, a, 69);
|
||||||
|
R4(a, b, c, d, e, 70);
|
||||||
|
R4(e, a, b, c, d, 71);
|
||||||
|
R4(d, e, a, b, c, 72);
|
||||||
|
R4(c, d, e, a, b, 73);
|
||||||
|
R4(b, c, d, e, a, 74);
|
||||||
|
R4(a, b, c, d, e, 75);
|
||||||
|
R4(e, a, b, c, d, 76);
|
||||||
|
R4(d, e, a, b, c, 77);
|
||||||
|
R4(c, d, e, a, b, 78);
|
||||||
|
R4(b, c, d, e, a, 79);
|
||||||
|
/* Add the working vars back into context.state[] */
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
state[4] += e;
|
||||||
|
/* Wipe variables */
|
||||||
|
a = b = c = d = e = 0;
|
||||||
|
#ifdef SHA1HANDSOFF
|
||||||
|
memset(block, '\0', sizeof(block));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* SHA1Init - Initialize new context */
|
||||||
|
|
||||||
|
void SHA1Init(
|
||||||
|
SHA1_CTX * context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
/* SHA1 initialization constants */
|
||||||
|
context->state[0] = 0x67452301;
|
||||||
|
context->state[1] = 0xEFCDAB89;
|
||||||
|
context->state[2] = 0x98BADCFE;
|
||||||
|
context->state[3] = 0x10325476;
|
||||||
|
context->state[4] = 0xC3D2E1F0;
|
||||||
|
context->count[0] = context->count[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Run your data through this. */
|
||||||
|
|
||||||
|
void SHA1Update(
|
||||||
|
SHA1_CTX * context,
|
||||||
|
const unsigned char *data,
|
||||||
|
uint32_t len
|
||||||
|
)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
uint32_t j;
|
||||||
|
|
||||||
|
j = context->count[0];
|
||||||
|
if ((context->count[0] += len << 3) < j)
|
||||||
|
context->count[1]++;
|
||||||
|
context->count[1] += (len >> 29);
|
||||||
|
j = (j >> 3) & 63;
|
||||||
|
if ((j + len) > 63)
|
||||||
|
{
|
||||||
|
memcpy(&context->buffer[j], data, (i = 64 - j));
|
||||||
|
SHA1Transform(context->state, context->buffer);
|
||||||
|
for (; i + 63 < len; i += 64)
|
||||||
|
{
|
||||||
|
SHA1Transform(context->state, &data[i]);
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
i = 0;
|
||||||
|
memcpy(&context->buffer[j], &data[i], len - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Add padding and return the message digest. */
|
||||||
|
|
||||||
|
void SHA1Final(
|
||||||
|
unsigned char digest[20],
|
||||||
|
SHA1_CTX * context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
unsigned char finalcount[8];
|
||||||
|
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
#if 0 /* untested "improvement" by DHR */
|
||||||
|
/* Convert context->count to a sequence of bytes
|
||||||
|
* in finalcount. Second element first, but
|
||||||
|
* big-endian order within element.
|
||||||
|
* But we do it all backwards.
|
||||||
|
*/
|
||||||
|
unsigned char *fcp = &finalcount[8];
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
uint32_t t = context->count[i];
|
||||||
|
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = 0; j < 4; t >>= 8, j++)
|
||||||
|
*--fcp = (unsigned char) t}
|
||||||
|
#else
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
c = 0200;
|
||||||
|
SHA1Update(context, &c, 1);
|
||||||
|
while ((context->count[0] & 504) != 448)
|
||||||
|
{
|
||||||
|
c = 0000;
|
||||||
|
SHA1Update(context, &c, 1);
|
||||||
|
}
|
||||||
|
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||||
|
for (i = 0; i < 20; i++)
|
||||||
|
{
|
||||||
|
digest[i] = (unsigned char)
|
||||||
|
((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
|
||||||
|
}
|
||||||
|
/* Wipe variables */
|
||||||
|
memset(context, '\0', sizeof(*context));
|
||||||
|
memset(&finalcount, '\0', sizeof(finalcount));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha1(
|
||||||
|
uint8_t *hash_out,
|
||||||
|
const uint8_t *str,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
SHA1_CTX ctx;
|
||||||
|
size_t ii;
|
||||||
|
|
||||||
|
SHA1Init(&ctx);
|
||||||
|
for (ii=0; ii<len; ii+=1)
|
||||||
|
SHA1Update(&ctx, str + ii, 1);
|
||||||
|
SHA1Final(hash_out, &ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
|
||||||
|
static inline char nibble_to_char(uint8_t n)
|
||||||
|
{
|
||||||
|
if(n >= 10)
|
||||||
|
return n + 87;
|
||||||
|
else
|
||||||
|
return n + 48;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sha1_hex(const std::string & data)
|
||||||
|
{
|
||||||
|
uint8_t digest[20];
|
||||||
|
const uint8_t * ptr = (uint8_t*) data.c_str();
|
||||||
|
sha1(digest, ptr, data.size());
|
||||||
|
std::string out;
|
||||||
|
std::size_t idx = 0;
|
||||||
|
while(idx < 20)
|
||||||
|
{
|
||||||
|
out += nibble_to_char((digest[idx] & 0xf0) >> 8) + nibble_to_char(digest[idx] & 0x0f);
|
||||||
|
++idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
11
contrib/backends/nntpchan-daemon/libnntpchan/sha1.hpp
Normal file
11
contrib/backends/nntpchan-daemon/libnntpchan/sha1.hpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef NNTPCHAN_SHA1_HPP
|
||||||
|
#define NNTPCHAN_SHA1_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
std::string sha1_hex(const std::string & data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,108 @@
|
|||||||
|
#include "staticfile_frontend.hpp"
|
||||||
|
#include "file_handle.hpp"
|
||||||
|
#include "sanitize.hpp"
|
||||||
|
#include "mime.hpp"
|
||||||
|
#include "sha1.hpp"
|
||||||
|
#include <any>
|
||||||
|
#include <iostream>
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
StaticFileFrontend::StaticFileFrontend(TemplateEngine * tmpl, const std::string & templateDir, const std::string & outDir, uint32_t pages) :
|
||||||
|
m_TemplateEngine(tmpl),
|
||||||
|
m_TemplateDir(templateDir),
|
||||||
|
m_OutDir(outDir),
|
||||||
|
m_Pages(pages)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void StaticFileFrontend::ProcessNewMessage(const fs::path & fpath)
|
||||||
|
{
|
||||||
|
std::clog << "process message " << fpath << std::endl;
|
||||||
|
auto file = OpenFile(fpath, eRead);
|
||||||
|
if(file)
|
||||||
|
{
|
||||||
|
// read header
|
||||||
|
RawHeader header;
|
||||||
|
if(!ReadHeader(file, header))
|
||||||
|
{
|
||||||
|
std::clog << "failed to read mime header" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read body
|
||||||
|
|
||||||
|
// render templates
|
||||||
|
if(m_TemplateEngine)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::any> thread_args;
|
||||||
|
|
||||||
|
auto findMsgidFunc = [](const std::pair<std::string, std::string> & item) -> bool {
|
||||||
|
auto lower = ToLower(item.first);
|
||||||
|
return (lower == "message-id") || (lower == "messageid");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto msgid = std::find_if(header.begin(), header.end(), findMsgidFunc);
|
||||||
|
|
||||||
|
std::string msgid_hash = sha1_hex(msgid->second);
|
||||||
|
|
||||||
|
fs::path threadFilePath = m_OutDir / fs::path("thread-" + msgid_hash + ".html");
|
||||||
|
FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
|
||||||
|
if(!m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out))
|
||||||
|
{
|
||||||
|
std::clog << "failed to write " << threadFilePath << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string> newsgroups_list;
|
||||||
|
|
||||||
|
auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> & item) -> bool
|
||||||
|
{
|
||||||
|
return ToLower(item.first) == "newsgroups";
|
||||||
|
};
|
||||||
|
|
||||||
|
auto group = std::find_if(header.begin(), header.end(), findNewsgroupsFunc);
|
||||||
|
if(group == std::end(header))
|
||||||
|
{
|
||||||
|
std::clog << "no newsgroups header" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::istringstream input(group->second);
|
||||||
|
|
||||||
|
std::string newsgroup;
|
||||||
|
while(std::getline(input, newsgroup, ' '))
|
||||||
|
{
|
||||||
|
newsgroups_list.insert(NNTPSanitize(newsgroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto & name : newsgroups_list)
|
||||||
|
{
|
||||||
|
auto board = GetThreadsPaginated(name, 10, m_Pages);
|
||||||
|
uint32_t pageno = 0;
|
||||||
|
for(Threads_t threads : board)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::any> board_args;
|
||||||
|
board_args["group"] = std::make_any<std::string>(name);
|
||||||
|
board_args["pageno"] = std::make_any<uint32_t>(pageno);
|
||||||
|
board_args["threads"] = std::make_any<Threads_t>(threads);
|
||||||
|
|
||||||
|
fs::path boardPageFilename(newsgroup + "-" + std::to_string(pageno) + ".html");
|
||||||
|
out = OpenFile(m_OutDir / boardPageFilename, eWrite);
|
||||||
|
m_TemplateEngine->WriteTemplate("board.mustache", board_args, out);
|
||||||
|
|
||||||
|
++pageno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticFileFrontend::BoardPage_t StaticFileFrontend::GetThreadsPaginated(const std::string & group, uint32_t perpage, uint32_t pages)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef NNTPCHAN_STATICFILE_FRONTEND_HPP
|
||||||
|
#define NNTPCHAN_STATICFILE_FRONTEND_HPP
|
||||||
|
#include "frontend.hpp"
|
||||||
|
#include "template_engine.hpp"
|
||||||
|
#include "model.hpp"
|
||||||
|
#include <experimental/filesystem>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace fs = std::experimental::filesystem;
|
||||||
|
|
||||||
|
class StaticFileFrontend : public Frontend
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
StaticFileFrontend(TemplateEngine * tmpl, const std::string & templateDir, const std::string & outDir, uint32_t pages);
|
||||||
|
|
||||||
|
~StaticFileFrontend();
|
||||||
|
|
||||||
|
void ProcessNewMessage(const fs::path & fpath);
|
||||||
|
bool AcceptsNewsgroup(const std::string & newsgroup) { (void) newsgroup; return true; }
|
||||||
|
bool AcceptsMessage(const std::string & msgid) { (void) msgid; return true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
typedef nntpchan::model::Thread Thread_t;
|
||||||
|
|
||||||
|
typedef std::vector<Thread_t> Threads_t;
|
||||||
|
|
||||||
|
typedef std::vector<Threads_t> BoardPage_t;
|
||||||
|
|
||||||
|
BoardPage_t GetThreadsPaginated(const std::string & group, uint32_t perpage, uint32_t pages);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
TemplateEngine_ptr m_TemplateEngine;
|
||||||
|
fs::path m_TemplateDir;
|
||||||
|
fs::path m_OutDir;
|
||||||
|
uint32_t m_Pages;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
47
contrib/backends/nntpchan-daemon/libnntpchan/storage.cpp
Normal file
47
contrib/backends/nntpchan-daemon/libnntpchan/storage.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "storage.hpp"
|
||||||
|
#include "sanitize.hpp"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
ArticleStorage::ArticleStorage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ArticleStorage::ArticleStorage(const fs::path & fpath) {
|
||||||
|
SetPath(fpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArticleStorage::~ArticleStorage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArticleStorage::SetPath(const fs::path & fpath)
|
||||||
|
{
|
||||||
|
basedir = fpath;
|
||||||
|
fs::create_directories(basedir);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArticleStorage::Accept(const std::string& msgid)
|
||||||
|
{
|
||||||
|
if (!IsValidMessageID(msgid)) return false;
|
||||||
|
auto p = MessagePath(msgid);
|
||||||
|
return !fs::exists(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::path ArticleStorage::MessagePath(const std::string & msgid)
|
||||||
|
{
|
||||||
|
return basedir / msgid;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileHandle_ptr ArticleStorage::OpenRead(const std::string & msgid)
|
||||||
|
{
|
||||||
|
return OpenFile(MessagePath(msgid), eRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileHandle_ptr ArticleStorage::OpenWrite(const std::string & msgid)
|
||||||
|
{
|
||||||
|
return OpenFile(MessagePath(msgid), eWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
40
contrib/backends/nntpchan-daemon/libnntpchan/storage.hpp
Normal file
40
contrib/backends/nntpchan-daemon/libnntpchan/storage.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef NNTPCHAN_STORAGE_HPP
|
||||||
|
#define NNTPCHAN_STORAGE_HPP
|
||||||
|
|
||||||
|
#include <experimental/filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include "file_handle.hpp"
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace fs = std::experimental::filesystem;
|
||||||
|
|
||||||
|
class ArticleStorage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ArticleStorage();
|
||||||
|
ArticleStorage(const fs::path & fpath);
|
||||||
|
~ArticleStorage();
|
||||||
|
|
||||||
|
void SetPath(const fs::path & fpath);
|
||||||
|
|
||||||
|
FileHandle_ptr OpenWrite(const std::string & msgid);
|
||||||
|
FileHandle_ptr OpenRead(const std::string & msgid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
return true if we should accept a new message give its message id
|
||||||
|
*/
|
||||||
|
bool Accept(const std::string & msgid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
fs::path MessagePath(const std::string & msgid);
|
||||||
|
|
||||||
|
fs::path basedir;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,35 @@
|
|||||||
|
#include "template_engine.hpp"
|
||||||
|
#include "sanitize.hpp"
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
|
||||||
|
struct MustacheTemplateEngine : public TemplateEngine
|
||||||
|
{
|
||||||
|
struct Impl
|
||||||
|
{
|
||||||
|
bool RenderFile(const std::string & fname, const Args_t & args, const FileHandle_ptr & out)
|
||||||
|
{
|
||||||
|
auto file = OpenFile(fname, eRead);
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual bool WriteTemplate(const std::string & fname, const Args_t & args, const FileHandle_ptr & out)
|
||||||
|
{
|
||||||
|
auto impl = std::make_unique<Impl>();
|
||||||
|
return impl->RenderFile(fname, args, out);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TemplateEngine * CreateTemplateEngine(const std::string & dialect)
|
||||||
|
{
|
||||||
|
auto d = ToLower(dialect);
|
||||||
|
if(d == "mustache")
|
||||||
|
return new MustacheTemplateEngine;
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
#ifndef NNTPCHAN_TEMPLATE_ENGINE_HPP
|
||||||
|
#define NNTPCHAN_TEMPLATE_ENGINE_HPP
|
||||||
|
#include "file_handle.hpp"
|
||||||
|
#include <any>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace nntpchan
|
||||||
|
{
|
||||||
|
|
||||||
|
struct TemplateEngine
|
||||||
|
{
|
||||||
|
using Args_t = std::map<std::string, std::any>;
|
||||||
|
virtual bool WriteTemplate(const std::string & template_fname, const Args_t & args, const FileHandle_ptr & out) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
TemplateEngine * CreateTemplateEngine(const std::string & dialect);
|
||||||
|
|
||||||
|
typedef std::unique_ptr<TemplateEngine> TemplateEngine_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,28 +0,0 @@
|
|||||||
#include "message.hpp"
|
|
||||||
|
|
||||||
namespace nntpchan
|
|
||||||
{
|
|
||||||
bool IsValidMessageID(const std::string & msgid)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
++itr;
|
|
||||||
if(atfound && c == '@') return false;
|
|
||||||
if(c == '@') {
|
|
||||||
atfound = true;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
#ifndef NNTPCHAN_MESSAGE_HPP
|
|
||||||
#define NNTPCHAN_MESSAGE_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace nntpchan
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
typedef std::function<bool(const MIMEPartHeader &)> MIMEPartFilter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
bool StoreMIMEMessage(std::istream & i, MessageHeaderFilter h, MIMEPartHeader p, std::ostream & o);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,262 +0,0 @@
|
|||||||
/*
|
|
||||||
Author: José Bollo <jobol@nonadev.net>
|
|
||||||
Author: José Bollo <jose.bollo@iot.bzh>
|
|
||||||
|
|
||||||
https://gitlab.com/jobol/mustach
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include "mustache.hpp"
|
|
||||||
|
|
||||||
#define NAME_LENGTH_MAX 1024
|
|
||||||
#define DEPTH_MAX 256
|
|
||||||
|
|
||||||
namespace nntpchan
|
|
||||||
{
|
|
||||||
namespace mustache
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
static int getpartial(struct mustach_itf *itf, void *closure, const char *name, char **result)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
FILE *file;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
*result = NULL;
|
|
||||||
file = open_memstream(result, &size);
|
|
||||||
if (file == NULL)
|
|
||||||
rc = MUSTACH_ERROR_SYSTEM;
|
|
||||||
else {
|
|
||||||
rc = itf->put(closure, name, 0, file);
|
|
||||||
if (rc == 0)
|
|
||||||
/* adds terminating null */
|
|
||||||
rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
|
|
||||||
fclose(file);
|
|
||||||
if (rc < 0) {
|
|
||||||
free(*result);
|
|
||||||
*result = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process(const char *templ, struct mustach_itf *itf, void *closure, FILE *file, const char *opstr, const char *clstr)
|
|
||||||
{
|
|
||||||
char name[NAME_LENGTH_MAX + 1], *partial, c;
|
|
||||||
const char *beg, *term;
|
|
||||||
struct { const char *name, *again; size_t length; int emit, entered; } stack[DEPTH_MAX];
|
|
||||||
size_t oplen, cllen, len, l;
|
|
||||||
int depth, rc, emit;
|
|
||||||
|
|
||||||
emit = 1;
|
|
||||||
oplen = strlen(opstr);
|
|
||||||
cllen = strlen(clstr);
|
|
||||||
depth = 0;
|
|
||||||
for(;;) {
|
|
||||||
beg = strstr(templ, opstr);
|
|
||||||
if (beg == NULL) {
|
|
||||||
/* no more mustach */
|
|
||||||
if (emit)
|
|
||||||
fwrite(templ, strlen(templ), 1, file);
|
|
||||||
return depth ? MUSTACH_ERROR_UNEXPECTED_END : 0;
|
|
||||||
}
|
|
||||||
if (emit)
|
|
||||||
fwrite(templ, (size_t)(beg - templ), 1, file);
|
|
||||||
beg += oplen;
|
|
||||||
term = strstr(beg, clstr);
|
|
||||||
if (term == NULL)
|
|
||||||
return MUSTACH_ERROR_UNEXPECTED_END;
|
|
||||||
templ = term + cllen;
|
|
||||||
len = (size_t)(term - beg);
|
|
||||||
c = *beg;
|
|
||||||
switch(c) {
|
|
||||||
case '!':
|
|
||||||
case '=':
|
|
||||||
break;
|
|
||||||
case '{':
|
|
||||||
for (l = 0 ; clstr[l] == '}' ; l++);
|
|
||||||
if (clstr[l]) {
|
|
||||||
if (!len || beg[len-1] != '}')
|
|
||||||
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
|
|
||||||
len--;
|
|
||||||
} else {
|
|
||||||
if (term[l] != '}')
|
|
||||||
return MUSTACH_ERROR_BAD_UNESCAPE_TAG;
|
|
||||||
templ++;
|
|
||||||
}
|
|
||||||
c = '&';
|
|
||||||
case '^':
|
|
||||||
case '#':
|
|
||||||
case '/':
|
|
||||||
case '&':
|
|
||||||
case '>':
|
|
||||||
#if !defined(NO_EXTENSION_FOR_MUSTACH) && !defined(NO_COLON_EXTENSION_FOR_MUSTACH)
|
|
||||||
case ':':
|
|
||||||
#endif
|
|
||||||
beg++; len--;
|
|
||||||
default:
|
|
||||||
while (len && isspace(beg[0])) { beg++; len--; }
|
|
||||||
while (len && isspace(beg[len-1])) len--;
|
|
||||||
if (len == 0)
|
|
||||||
return MUSTACH_ERROR_EMPTY_TAG;
|
|
||||||
if (len > NAME_LENGTH_MAX)
|
|
||||||
return MUSTACH_ERROR_TAG_TOO_LONG;
|
|
||||||
memcpy(name, beg, len);
|
|
||||||
name[len] = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch(c) {
|
|
||||||
case '!':
|
|
||||||
/* comment */
|
|
||||||
/* nothing to do */
|
|
||||||
break;
|
|
||||||
case '=':
|
|
||||||
/* defines separators */
|
|
||||||
if (len < 5 || beg[len - 1] != '=')
|
|
||||||
return MUSTACH_ERROR_BAD_SEPARATORS;
|
|
||||||
beg++;
|
|
||||||
len -= 2;
|
|
||||||
for (l = 0; l < len && !isspace(beg[l]) ; l++);
|
|
||||||
if (l == len)
|
|
||||||
return MUSTACH_ERROR_BAD_SEPARATORS;
|
|
||||||
opstr = strndupa(beg, l);
|
|
||||||
while (l < len && isspace(beg[l])) l++;
|
|
||||||
if (l == len)
|
|
||||||
return MUSTACH_ERROR_BAD_SEPARATORS;
|
|
||||||
clstr = strndupa(beg + l, len - l);
|
|
||||||
oplen = strlen(opstr);
|
|
||||||
cllen = strlen(clstr);
|
|
||||||
break;
|
|
||||||
case '^':
|
|
||||||
case '#':
|
|
||||||
/* begin section */
|
|
||||||
if (depth == DEPTH_MAX)
|
|
||||||
return MUSTACH_ERROR_TOO_DEPTH;
|
|
||||||
rc = emit;
|
|
||||||
if (rc) {
|
|
||||||
rc = itf->enter(closure, name);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
stack[depth].name = beg;
|
|
||||||
stack[depth].again = templ;
|
|
||||||
stack[depth].length = len;
|
|
||||||
stack[depth].emit = emit;
|
|
||||||
stack[depth].entered = rc;
|
|
||||||
if ((c == '#') == (rc == 0))
|
|
||||||
emit = 0;
|
|
||||||
depth++;
|
|
||||||
break;
|
|
||||||
case '/':
|
|
||||||
/* end section */
|
|
||||||
if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len))
|
|
||||||
return MUSTACH_ERROR_CLOSING;
|
|
||||||
rc = emit && stack[depth].entered ? itf->next(closure) : 0;
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
if (rc) {
|
|
||||||
templ = stack[depth++].again;
|
|
||||||
} else {
|
|
||||||
emit = stack[depth].emit;
|
|
||||||
if (emit && stack[depth].entered)
|
|
||||||
itf->leave(closure);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
/* partials */
|
|
||||||
if (emit) {
|
|
||||||
rc = getpartial(itf, closure, name, &partial);
|
|
||||||
if (rc == 0) {
|
|
||||||
rc = process(partial, itf, closure, file, opstr, clstr);
|
|
||||||
free(partial);
|
|
||||||
}
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* replacement */
|
|
||||||
if (emit) {
|
|
||||||
rc = itf->put(closure, name, c != '&', file);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int fmustach(const char *templ, struct mustach_itf *itf, void *closure, FILE *file)
|
|
||||||
{
|
|
||||||
int rc = itf->start ? itf->start(closure) : 0;
|
|
||||||
if (rc == 0)
|
|
||||||
rc = process(templ, itf, closure, file, "{{", "}}");
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fdmustach(const char *templ, struct mustach_itf *itf, void *closure, int fd)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
FILE *file;
|
|
||||||
|
|
||||||
file = fdopen(fd, "w");
|
|
||||||
if (file == NULL) {
|
|
||||||
rc = MUSTACH_ERROR_SYSTEM;
|
|
||||||
errno = ENOMEM;
|
|
||||||
} else {
|
|
||||||
rc = fmustach(templ, itf, closure, file);
|
|
||||||
fclose(file);
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mustach(const char *templ, struct mustach_itf *itf, void *closure, char **result, size_t *size)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
FILE *file;
|
|
||||||
size_t s;
|
|
||||||
|
|
||||||
*result = NULL;
|
|
||||||
if (size == NULL)
|
|
||||||
size = &s;
|
|
||||||
file = open_memstream(result, size);
|
|
||||||
if (file == NULL) {
|
|
||||||
rc = MUSTACH_ERROR_SYSTEM;
|
|
||||||
errno = ENOMEM;
|
|
||||||
} else {
|
|
||||||
rc = fmustach(templ, itf, closure, file);
|
|
||||||
if (rc == 0)
|
|
||||||
/* adds terminating null */
|
|
||||||
rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0;
|
|
||||||
fclose(file);
|
|
||||||
if (rc >= 0)
|
|
||||||
/* removes terminating null of the length */
|
|
||||||
(*size)--;
|
|
||||||
else {
|
|
||||||
free(*result);
|
|
||||||
*result = NULL;
|
|
||||||
*size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
#ifndef NNTPCHAN_MUSTACHE
|
|
||||||
#define NNTPCHAN_MUSTACHE
|
|
||||||
|
|
||||||
/*
|
|
||||||
Author: José Bollo <jobol@nonadev.net>
|
|
||||||
Author: José Bollo <jose.bollo@iot.bzh>
|
|
||||||
|
|
||||||
https://gitlab.com/jobol/mustach
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define MUSTACH_OK 0
|
|
||||||
#define MUSTACH_ERROR_SYSTEM -1
|
|
||||||
#define MUSTACH_ERROR_UNEXPECTED_END -2
|
|
||||||
#define MUSTACH_ERROR_EMPTY_TAG -3
|
|
||||||
#define MUSTACH_ERROR_TAG_TOO_LONG -4
|
|
||||||
#define MUSTACH_ERROR_BAD_SEPARATORS -5
|
|
||||||
#define MUSTACH_ERROR_TOO_DEPTH -6
|
|
||||||
#define MUSTACH_ERROR_CLOSING -7
|
|
||||||
#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
|
|
||||||
namespace nntpchan
|
|
||||||
{
|
|
||||||
namespace mustache
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mustach_itf - interface for callbacks
|
|
||||||
*
|
|
||||||
* All of this function should return a negative value to stop
|
|
||||||
* the mustache processing. The returned negative value will be
|
|
||||||
* then returned to the caller of mustach as is.
|
|
||||||
*
|
|
||||||
* The functions enter and next should return 0 or 1.
|
|
||||||
*
|
|
||||||
* All other functions should normally return 0.
|
|
||||||
*
|
|
||||||
* @start: Starts the mustach processing of the closure
|
|
||||||
* 'start' is optional (can be NULL)
|
|
||||||
*
|
|
||||||
* @put: Writes the value of 'name' to 'file' with 'escape' or not
|
|
||||||
*
|
|
||||||
* @enter: Enters the section of 'name' if possible.
|
|
||||||
* Musts return 1 if entered or 0 if not entered.
|
|
||||||
* When 1 is returned, the function 'leave' will always be called.
|
|
||||||
* Conversely 'leave' is never called when enter returns 0 or
|
|
||||||
* a negative value.
|
|
||||||
* When 1 is returned, the function must activate the first
|
|
||||||
* item of the section.
|
|
||||||
*
|
|
||||||
* @next: Activates the next item of the section if it exists.
|
|
||||||
* Musts return 1 when the next item is activated.
|
|
||||||
* Musts return 0 when there is no item to activate.
|
|
||||||
*
|
|
||||||
* @leave: Leaves the last entered section
|
|
||||||
*/
|
|
||||||
struct mustach_itf {
|
|
||||||
int (*start)(void *closure);
|
|
||||||
int (*put)(void *closure, const char *name, int escape, FILE *file);
|
|
||||||
int (*enter)(void *closure, const char *name);
|
|
||||||
int (*next)(void *closure);
|
|
||||||
int (*leave)(void *closure);
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'.
|
|
||||||
*
|
|
||||||
* @template: the template string to instanciate
|
|
||||||
* @itf: the interface to the functions that mustach calls
|
|
||||||
* @closure: the closure to pass to functions called
|
|
||||||
* @file: the file where to write the result
|
|
||||||
*
|
|
||||||
* Returns 0 in case of success, -1 with errno set in case of system error
|
|
||||||
* a other negative value in case of error.
|
|
||||||
*/
|
|
||||||
int fmustach(const char *templ, struct mustach_itf *itf, void *closure, FILE *file);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,59 +0,0 @@
|
|||||||
#include "storage.hpp"
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace nntpchan
|
|
||||||
{
|
|
||||||
ArticleStorage::ArticleStorage()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ArticleStorage::ArticleStorage(const std::string & fpath) {
|
|
||||||
SetPath(fpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArticleStorage::~ArticleStorage()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ArticleStorage::SetPath(const std::string & fpath)
|
|
||||||
{
|
|
||||||
basedir = fpath;
|
|
||||||
// quiet fail
|
|
||||||
// TODO: check for errors
|
|
||||||
mkdir(basedir.c_str(), 0700);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ArticleStorage::Accept(const std::string& msgid)
|
|
||||||
{
|
|
||||||
if (!IsValidMessageID(msgid)) return false;
|
|
||||||
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,55 +0,0 @@
|
|||||||
#ifndef NNTPCHAN_STORAGE_HPP
|
|
||||||
#define NNTPCHAN_STORAGE_HPP
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include "message.hpp"
|
|
||||||
|
|
||||||
namespace nntpchan
|
|
||||||
{
|
|
||||||
class ArticleStorage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ArticleStorage();
|
|
||||||
ArticleStorage(const std::string & fpath);
|
|
||||||
~ArticleStorage();
|
|
||||||
|
|
||||||
void SetPath(const std::string & fpath);
|
|
||||||
|
|
||||||
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 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;
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,4 +1,5 @@
|
|||||||
#include "exec_frontend.hpp"
|
#include "exec_frontend.hpp"
|
||||||
|
#include "sanitize.hpp"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@ -6,8 +7,10 @@
|
|||||||
|
|
||||||
int main(int , char * [])
|
int main(int , char * [])
|
||||||
{
|
{
|
||||||
nntpchan::Frontend * f = new nntpchan::ExecFrontend("./contrib/nntpchan.sh");
|
nntpchan::Frontend_ptr f (new nntpchan::ExecFrontend("./contrib/nntpchan.sh"));
|
||||||
assert(f->AcceptsMessage("<test@server>"));
|
assert(f->AcceptsMessage("<test@server>"));
|
||||||
assert(f->AcceptsNewsgroup("overchan.test"));
|
assert(f->AcceptsNewsgroup("overchan.test"));
|
||||||
|
assert(nntpchan::IsValidMessageID("<test@test>"));
|
||||||
|
assert(!nntpchan::IsValidMessageID("asd"));
|
||||||
std::cout << "all good" << std::endl;
|
std::cout << "all good" << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "exec_frontend.hpp"
|
#include "exec_frontend.hpp"
|
||||||
#include "message.hpp"
|
#include "sanitize.hpp"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
int main(int , char * [])
|
int main(int , char * [])
|
||||||
{
|
{
|
||||||
nntpchan::Frontend * f = new nntpchan::ExecFrontend("./contrib/nntpchan.sh");
|
nntpchan::Frontend_ptr f(new nntpchan::ExecFrontend("./contrib/nntpchan.sh"));
|
||||||
assert(nntpchan::IsValidMessageID("<a28a71493831188@web.oniichan.onion>"));
|
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;
|
||||||
|
@ -862,7 +862,7 @@ func (self *httpFrontend) handle_postRequest(pr *postRequest, b bannedFunc, e er
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(cites) > 0 {
|
if len(cites) > 0 {
|
||||||
nntp.headers.Set("In-Reply-To", strings.Join(cites, " "))
|
nntp.headers.Set("Reply-To", strings.Join(cites, " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// set date
|
// set date
|
||||||
|
@ -353,12 +353,16 @@ func (self *nntpConnection) handleStreaming(daemon *NNTPDaemon, conn *textproto.
|
|||||||
select {
|
select {
|
||||||
case chnl := <-self.die:
|
case chnl := <-self.die:
|
||||||
// someone asked us to die
|
// someone asked us to die
|
||||||
|
self.pending_access.Lock()
|
||||||
conn.PrintfLine("QUIT")
|
conn.PrintfLine("QUIT")
|
||||||
conn.Close()
|
conn.Close()
|
||||||
|
self.pending_access.Unlock()
|
||||||
chnl <- true
|
chnl <- true
|
||||||
return
|
return
|
||||||
case <-self.keepalive.C:
|
case <-self.keepalive.C:
|
||||||
|
self.pending_access.Lock()
|
||||||
err = conn.PrintfLine("CHECK %s", nntpDummyArticle)
|
err = conn.PrintfLine("CHECK %s", nntpDummyArticle)
|
||||||
|
self.pending_access.Unlock()
|
||||||
default:
|
default:
|
||||||
if len(self.pending) > 0 {
|
if len(self.pending) > 0 {
|
||||||
self.pending_access.Lock()
|
self.pending_access.Lock()
|
||||||
@ -562,7 +566,7 @@ func (self *nntpConnection) storeMessage(daemon *NNTPDaemon, hdr textproto.MIMEH
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ask for replies
|
// ask for replies
|
||||||
replyTos := strings.Split(hdr.Get("In-Reply-To"), " ")
|
replyTos := strings.Split(hdr.Get("Reply-To"), " ")
|
||||||
for _, reply := range replyTos {
|
for _, reply := range replyTos {
|
||||||
if ValidMessageID(reply) {
|
if ValidMessageID(reply) {
|
||||||
if !daemon.store.HasArticle(reply) {
|
if !daemon.store.HasArticle(reply) {
|
||||||
|
Reference in New Issue
Block a user