more nntpchan-daemon stuff
This commit is contained in:
parent
ec7a17a647
commit
12bb8c4936
@ -1,29 +1,44 @@
|
|||||||
|
|
||||||
REPO=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
REPO=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||||
SRC_PATH = $(REPO)/libnntpchan
|
NNTPCHAN_PATH = $(REPO)/libnntpchan
|
||||||
|
|
||||||
SOURCES := $(wildcard $(SRC_PATH)/*.cpp)
|
NNTPCHAN_SRC := $(wildcard $(NNTPCHAN_PATH)/*.cpp)
|
||||||
HEADERS := $(wildcard $(SRC_PATH)/*.hpp)
|
NNTPCHAN_HDR := $(wildcard $(NNTPCHAN_PATH)/*.hpp)
|
||||||
OBJECTS := $(SOURCES:.cpp=.o)
|
NNTPCHAN_OBJ := $(NNTPCHAN_SRC:.cpp=.o)
|
||||||
|
|
||||||
TOOL_SRC_PATH := $(REPO)/tools
|
MUSTACHE_PATH = $(REPO)/libmustache
|
||||||
|
|
||||||
TOOL_SRC := $(wildcard $(TOOL_SRC_PATH)/*.cpp)
|
MUSTACHE_SRC := $(wildcard $(MUSTACHE_PATH)/*.cpp)
|
||||||
|
MUSTACHE_SRC += $(wildcard $(MUSTACHE_PATH)/*/*.cpp)
|
||||||
|
|
||||||
|
MUSTACHE_HDR := $(wildcard $(MUSTACHE_PATH)/*.hpp)
|
||||||
|
MUSTACHE_OBJ := $(MUSTACHE_SRC:.cpp=.o)
|
||||||
|
|
||||||
|
HEADERS_PATH=$(REPO)/include
|
||||||
|
|
||||||
|
TOOL_PATH := $(REPO)/tools
|
||||||
|
|
||||||
|
TOOL_SRC := $(wildcard $(TOOL_PATH)/*.cpp)
|
||||||
TOOLS := $(TOOL_SRC:.cpp=)
|
TOOLS := $(TOOL_SRC:.cpp=)
|
||||||
|
|
||||||
|
OBJ := $(NNTPCHAN_OBJ) $(MUSTACHE_OBJ)
|
||||||
|
|
||||||
DAEMON_SRC = $(REPO)/daemon
|
DAEMON_SRC = $(REPO)/daemon
|
||||||
|
|
||||||
PKGS := libuv libsodium
|
PKGS := libuv libsodium
|
||||||
|
|
||||||
LD_FLAGS := $(shell pkg-config --libs $(PKGS)) -lstdc++fs
|
LD_FLAGS := $(shell pkg-config --libs $(PKGS)) -lstdc++fs
|
||||||
INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I$(SRC_PATH)
|
INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I$(HEADERS_PATH)
|
||||||
CXXFLAGS := -std=c++17 -Wall -Wextra -pedantic $(INC_FLAGS)
|
CXXFLAGS := -std=c++17 -Wall -Wextra -pedantic $(INC_FLAGS)
|
||||||
|
|
||||||
ifeq ($(DEBUG),1)
|
ifeq ($(DEBUG),1)
|
||||||
CXXFLAGS += -g
|
CXXFLAGS += -g
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIB = $(REPO)/libnntpchan.a
|
NNTPCHAN_LIB = $(REPO)/libnntpchan.a
|
||||||
|
MUSTACHE_LIB = $(REPO)/libmustache.a
|
||||||
|
|
||||||
|
LIBS = $(NNTPCHAN_LIB) $(MUSTACHE_LIB)
|
||||||
|
|
||||||
EXE = $(REPO)/nntpd
|
EXE = $(REPO)/nntpd
|
||||||
|
|
||||||
@ -32,25 +47,25 @@ all: build
|
|||||||
|
|
||||||
build: $(EXE) $(TOOLS)
|
build: $(EXE) $(TOOLS)
|
||||||
|
|
||||||
$(LIB): $(OBJECTS)
|
$(MUSTACHE_LIB): $(MUSTACHE_OBJ)
|
||||||
$(AR) -r $(LIB) $(OBJECTS)
|
$(AR) -r $(MUSTACHE_LIB) $(MUSTACHE_OBJ)
|
||||||
|
|
||||||
$(EXE): $(LIB)
|
$(NNTPCHAN_LIB): $(NNTPCHAN_OBJ)
|
||||||
$(CXX) $(CXXFLAGS) $(DAEMON_SRC)/main.cpp $(LIB) $(LD_FLAGS) -o $(EXE)
|
$(AR) -r $(NNTPCHAN_LIB) $(NNTPCHAN_OBJ)
|
||||||
|
|
||||||
$(TOOL_SRC): $(LIB)
|
$(EXE): $(LIBS)
|
||||||
|
$(CXX) $(CXXFLAGS) $(DAEMON_SRC)/main.cpp $(NNTPCHAN_LIB) $(LD_FLAGS) -o $(EXE)
|
||||||
|
|
||||||
|
$(TOOL_SRC): $(NNTPCHAN_LIB)
|
||||||
|
|
||||||
$(TOOLS): $(TOOL_SRC)
|
$(TOOLS): $(TOOL_SRC)
|
||||||
$(CXX) $(CXXFLAGS) $< $(LIB) $(LD_FLAGS) -o $@
|
$(CXX) $(CXXFLAGS) $< $(NNTPCHAN_LIB) $(LD_FLAGS) -o $@
|
||||||
|
|
||||||
build-test: $(LIB)
|
build-test: $(LIB)
|
||||||
$(CXX) -o $(REPO)/test $(CXXFLAGS) test.cpp $(LIB) $(LD_FLAGS)
|
$(CXX) -o $(REPO)/test $(CXXFLAGS) test.cpp $(NNTPCHAN_LIB) $(LD_FLAGS)
|
||||||
|
|
||||||
test: build-test
|
test: build-test
|
||||||
$(REPO)/test
|
$(REPO)/test
|
||||||
|
|
||||||
%.o: src/%.cpp
|
|
||||||
$(CXX) $(CXXFLAGS) -c -o $@
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJECTS) $(LIB) $(EXE) $(TOOLS)
|
rm -f $(OBJ) $(NNTPCHAN_LIB) $(EXE) $(TOOLS)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#include "ini.hpp"
|
#include "ini.hpp"
|
||||||
|
|
||||||
#include "crypto.hpp"
|
#include <nntpchan/crypto.hpp>
|
||||||
#include "storage.hpp"
|
#include <nntpchan/storage.hpp>
|
||||||
#include "nntp_server.hpp"
|
#include <nntpchan/nntp_server.hpp>
|
||||||
#include "event.hpp"
|
#include <nntpchan/event.hpp>
|
||||||
#include "exec_frontend.hpp"
|
#include <nntpchan/exec_frontend.hpp>
|
||||||
#include "staticfile_frontend.hpp"
|
#include <nntpchan/staticfile_frontend.hpp>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
113
contrib/backends/nntpchan-daemon/include/mstch/mstch.hpp
Normal file
113
contrib/backends/nntpchan-daemon/include/mstch/mstch.hpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
struct config {
|
||||||
|
static std::function<std::string(const std::string&)> escape;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template<class N>
|
||||||
|
class object_t {
|
||||||
|
public:
|
||||||
|
const N& at(const std::string& name) const {
|
||||||
|
cache[name] = (methods.at(name))();
|
||||||
|
return cache[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const std::string name) const {
|
||||||
|
return methods.count(name) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<class S>
|
||||||
|
void register_methods(S* s, std::map<std::string,N(S::*)()> methods) {
|
||||||
|
for(auto& item: methods)
|
||||||
|
this->methods.insert({item.first, std::bind(item.second, s)});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<std::string, std::function<N()>> methods;
|
||||||
|
mutable std::map<std::string, N> cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, class N>
|
||||||
|
class is_fun {
|
||||||
|
private:
|
||||||
|
using not_fun = char;
|
||||||
|
using fun_without_args = char[2];
|
||||||
|
using fun_with_args = char[3];
|
||||||
|
template <typename U, U> struct really_has;
|
||||||
|
template <typename C> static fun_without_args& test(
|
||||||
|
really_has<N(C::*)() const, &C::operator()>*);
|
||||||
|
template <typename C> static fun_with_args& test(
|
||||||
|
really_has<N(C::*)(const std::string&) const,
|
||||||
|
&C::operator()>*);
|
||||||
|
template <typename> static not_fun& test(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool const no_args = sizeof(test<T>(0)) == sizeof(fun_without_args);
|
||||||
|
static bool const has_args = sizeof(test<T>(0)) == sizeof(fun_with_args);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class N>
|
||||||
|
using node_renderer = std::function<std::string(const N& n)>;
|
||||||
|
|
||||||
|
template<class N>
|
||||||
|
class lambda_t {
|
||||||
|
public:
|
||||||
|
template<class F>
|
||||||
|
lambda_t(F f, typename std::enable_if<is_fun<F, N>::no_args>::type* = 0):
|
||||||
|
fun([f](node_renderer<N> renderer, const std::string&) {
|
||||||
|
return renderer(f());
|
||||||
|
})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class F>
|
||||||
|
lambda_t(F f, typename std::enable_if<is_fun<F, N>::has_args>::type* = 0):
|
||||||
|
fun([f](node_renderer<N> renderer, const std::string& text) {
|
||||||
|
return renderer(f(text));
|
||||||
|
})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(node_renderer<N> renderer,
|
||||||
|
const std::string& text = "") const
|
||||||
|
{
|
||||||
|
return fun(renderer, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<std::string(node_renderer<N> renderer, const std::string&)> fun;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using node = boost::make_recursive_variant<
|
||||||
|
std::nullptr_t, std::string, int, double, bool,
|
||||||
|
internal::lambda_t<boost::recursive_variant_>,
|
||||||
|
std::shared_ptr<internal::object_t<boost::recursive_variant_>>,
|
||||||
|
std::map<const std::string, boost::recursive_variant_>,
|
||||||
|
std::vector<boost::recursive_variant_>>::type;
|
||||||
|
using object = internal::object_t<node>;
|
||||||
|
using lambda = internal::lambda_t<node>;
|
||||||
|
using map = std::map<const std::string, node>;
|
||||||
|
using array = std::vector<node>;
|
||||||
|
|
||||||
|
std::string render(
|
||||||
|
const std::string& tmplt,
|
||||||
|
const node& root,
|
||||||
|
const std::map<std::string,std::string>& partials =
|
||||||
|
std::map<std::string,std::string>());
|
||||||
|
|
||||||
|
}
|
@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
{
|
{
|
||||||
std::string NNTPSanitize(const std::string & str);
|
std::string NNTPSanitizeLine(const std::string & str);
|
||||||
std::string ToLower(const std::string & str);
|
std::string ToLower(const std::string & str);
|
||||||
bool IsValidMessageID(const std::string & msgid);
|
bool IsValidMessageID(const std::string & msgid);
|
||||||
|
bool IsValidNewsgroup(const std::string & group);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -19,8 +19,8 @@ namespace nntpchan
|
|||||||
~StaticFileFrontend();
|
~StaticFileFrontend();
|
||||||
|
|
||||||
void ProcessNewMessage(const fs::path & fpath);
|
void ProcessNewMessage(const fs::path & fpath);
|
||||||
bool AcceptsNewsgroup(const std::string & newsgroup) { (void) newsgroup; return true; }
|
bool AcceptsNewsgroup(const std::string & newsgroup);
|
||||||
bool AcceptsMessage(const std::string & msgid) { (void) msgid; return true; }
|
bool AcceptsMessage(const std::string & msgid);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -12,7 +12,7 @@ namespace nntpchan
|
|||||||
struct TemplateEngine
|
struct TemplateEngine
|
||||||
{
|
{
|
||||||
using Args_t = std::map<std::string, std::any>;
|
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;
|
virtual bool WriteTemplate(const fs::path & template_fpath, const Args_t & args, const FileHandle_ptr & out) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
TemplateEngine * CreateTemplateEngine(const std::string & dialect);
|
TemplateEngine * CreateTemplateEngine(const std::string & dialect);
|
20
contrib/backends/nntpchan-daemon/libmustache/mstch.cpp
Normal file
20
contrib/backends/nntpchan-daemon/libmustache/mstch.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "mstch/mstch.hpp"
|
||||||
|
#include "render_context.hpp"
|
||||||
|
|
||||||
|
using namespace mstch;
|
||||||
|
|
||||||
|
std::function<std::string(const std::string&)> mstch::config::escape;
|
||||||
|
|
||||||
|
std::string mstch::render(
|
||||||
|
const std::string& tmplt,
|
||||||
|
const node& root,
|
||||||
|
const std::map<std::string,std::string>& partials)
|
||||||
|
{
|
||||||
|
std::map<std::string, template_type> partial_templates;
|
||||||
|
for (auto& partial: partials)
|
||||||
|
partial_templates.insert({partial.first, {partial.second}});
|
||||||
|
|
||||||
|
return render_context(root, partial_templates).render(tmplt);
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
#include "render_context.hpp"
|
||||||
|
#include "state/outside_section.hpp"
|
||||||
|
#include "visitor/get_token.hpp"
|
||||||
|
|
||||||
|
using namespace mstch;
|
||||||
|
|
||||||
|
const mstch::node render_context::null_node;
|
||||||
|
|
||||||
|
render_context::push::push(render_context& context, const mstch::node& node):
|
||||||
|
m_context(context)
|
||||||
|
{
|
||||||
|
context.m_nodes.emplace_front(node);
|
||||||
|
context.m_node_ptrs.emplace_front(&node);
|
||||||
|
context.m_state.push(std::unique_ptr<render_state>(new outside_section));
|
||||||
|
}
|
||||||
|
|
||||||
|
render_context::push::~push() {
|
||||||
|
m_context.m_nodes.pop_front();
|
||||||
|
m_context.m_node_ptrs.pop_front();
|
||||||
|
m_context.m_state.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string render_context::push::render(const template_type& templt) {
|
||||||
|
return m_context.render(templt);
|
||||||
|
}
|
||||||
|
|
||||||
|
render_context::render_context(
|
||||||
|
const mstch::node& node,
|
||||||
|
const std::map<std::string, template_type>& partials):
|
||||||
|
m_partials(partials), m_nodes(1, node), m_node_ptrs(1, &node)
|
||||||
|
{
|
||||||
|
m_state.push(std::unique_ptr<render_state>(new outside_section));
|
||||||
|
}
|
||||||
|
|
||||||
|
const mstch::node& render_context::find_node(
|
||||||
|
const std::string& token,
|
||||||
|
std::list<node const*> current_nodes)
|
||||||
|
{
|
||||||
|
if (token != "." && token.find('.') != std::string::npos)
|
||||||
|
return find_node(token.substr(token.rfind('.') + 1),
|
||||||
|
{&find_node(token.substr(0, token.rfind('.')), current_nodes)});
|
||||||
|
else
|
||||||
|
for (auto& node: current_nodes)
|
||||||
|
if (visit(has_token(token), *node))
|
||||||
|
return visit(get_token(token, *node), *node);
|
||||||
|
return null_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mstch::node& render_context::get_node(const std::string& token) {
|
||||||
|
return find_node(token, m_node_ptrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string render_context::render(
|
||||||
|
const template_type& templt, const std::string& prefix)
|
||||||
|
{
|
||||||
|
std::string output;
|
||||||
|
bool prev_eol = true;
|
||||||
|
for (auto& token: templt) {
|
||||||
|
if (prev_eol && prefix.length() != 0)
|
||||||
|
output += m_state.top()->render(*this, {prefix});
|
||||||
|
output += m_state.top()->render(*this, token);
|
||||||
|
prev_eol = token.eol();
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string render_context::render_partial(
|
||||||
|
const std::string& partial_name, const std::string& prefix)
|
||||||
|
{
|
||||||
|
return m_partials.count(partial_name) ?
|
||||||
|
render(m_partials.at(partial_name), prefix) : "";
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <list>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
#include <mstch/mstch.hpp>
|
||||||
|
#include "state/render_state.hpp"
|
||||||
|
#include "template_type.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class render_context {
|
||||||
|
public:
|
||||||
|
class push {
|
||||||
|
public:
|
||||||
|
push(render_context& context, const mstch::node& node = {});
|
||||||
|
~push();
|
||||||
|
std::string render(const template_type& templt);
|
||||||
|
private:
|
||||||
|
render_context& m_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
render_context(
|
||||||
|
const mstch::node& node,
|
||||||
|
const std::map<std::string, template_type>& partials);
|
||||||
|
const mstch::node& get_node(const std::string& token);
|
||||||
|
std::string render(
|
||||||
|
const template_type& templt, const std::string& prefix = "");
|
||||||
|
std::string render_partial(
|
||||||
|
const std::string& partial_name, const std::string& prefix);
|
||||||
|
template<class T, class... Args>
|
||||||
|
void set_state(Args&& ... args) {
|
||||||
|
m_state.top() = std::unique_ptr<render_state>(
|
||||||
|
new T(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const mstch::node null_node;
|
||||||
|
const mstch::node& find_node(
|
||||||
|
const std::string& token,
|
||||||
|
std::list<node const*> current_nodes);
|
||||||
|
std::map<std::string, template_type> m_partials;
|
||||||
|
std::deque<mstch::node> m_nodes;
|
||||||
|
std::list<const mstch::node*> m_node_ptrs;
|
||||||
|
std::stack<std::unique_ptr<render_state>> m_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
#include "in_section.hpp"
|
||||||
|
#include "outside_section.hpp"
|
||||||
|
#include "../visitor/is_node_empty.hpp"
|
||||||
|
#include "../visitor/render_section.hpp"
|
||||||
|
|
||||||
|
using namespace mstch;
|
||||||
|
|
||||||
|
in_section::in_section(type type, const token& start_token):
|
||||||
|
m_type(type), m_start_token(start_token), m_skipped_openings(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string in_section::render(render_context& ctx, const token& token) {
|
||||||
|
if (token.token_type() == token::type::section_close)
|
||||||
|
if (token.name() == m_start_token.name() && m_skipped_openings == 0) {
|
||||||
|
auto& node = ctx.get_node(m_start_token.name());
|
||||||
|
std::string out;
|
||||||
|
|
||||||
|
if (m_type == type::normal && !visit(is_node_empty(), node))
|
||||||
|
out = visit(render_section(ctx, m_section, m_start_token.delims()), node);
|
||||||
|
else if (m_type == type::inverted && visit(is_node_empty(), node))
|
||||||
|
out = render_context::push(ctx).render(m_section);
|
||||||
|
|
||||||
|
ctx.set_state<outside_section>();
|
||||||
|
return out;
|
||||||
|
} else
|
||||||
|
m_skipped_openings--;
|
||||||
|
else if (token.token_type() == token::type::inverted_section_open ||
|
||||||
|
token.token_type() == token::type::section_open)
|
||||||
|
m_skipped_openings++;
|
||||||
|
|
||||||
|
m_section << token;
|
||||||
|
return "";
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "render_state.hpp"
|
||||||
|
#include "../template_type.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class in_section: public render_state {
|
||||||
|
public:
|
||||||
|
enum class type { inverted, normal };
|
||||||
|
in_section(type type, const token& start_token);
|
||||||
|
std::string render(render_context& context, const token& token) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const type m_type;
|
||||||
|
const token& m_start_token;
|
||||||
|
template_type m_section;
|
||||||
|
int m_skipped_openings;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
#include "outside_section.hpp"
|
||||||
|
|
||||||
|
#include "../visitor/render_node.hpp"
|
||||||
|
#include "in_section.hpp"
|
||||||
|
#include "../render_context.hpp"
|
||||||
|
|
||||||
|
using namespace mstch;
|
||||||
|
|
||||||
|
std::string outside_section::render(
|
||||||
|
render_context& ctx, const token& token)
|
||||||
|
{
|
||||||
|
using flag = render_node::flag;
|
||||||
|
switch (token.token_type()) {
|
||||||
|
case token::type::section_open:
|
||||||
|
ctx.set_state<in_section>(in_section::type::normal, token);
|
||||||
|
break;
|
||||||
|
case token::type::inverted_section_open:
|
||||||
|
ctx.set_state<in_section>(in_section::type::inverted, token);
|
||||||
|
break;
|
||||||
|
case token::type::variable:
|
||||||
|
return visit(render_node(ctx, flag::escape_html), ctx.get_node(token.name()));
|
||||||
|
case token::type::unescaped_variable:
|
||||||
|
return visit(render_node(ctx, flag::none), ctx.get_node(token.name()));
|
||||||
|
case token::type::text:
|
||||||
|
return token.raw();
|
||||||
|
case token::type::partial:
|
||||||
|
return ctx.render_partial(token.name(), token.partial_prefix());
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "render_state.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class outside_section: public render_state {
|
||||||
|
public:
|
||||||
|
std::string render(render_context& context, const token& token) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "../token.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class render_context;
|
||||||
|
|
||||||
|
class render_state {
|
||||||
|
public:
|
||||||
|
virtual ~render_state() {}
|
||||||
|
virtual std::string render(render_context& context, const token& token) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
104
contrib/backends/nntpchan-daemon/libmustache/template_type.cpp
Normal file
104
contrib/backends/nntpchan-daemon/libmustache/template_type.cpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#include "template_type.hpp"
|
||||||
|
|
||||||
|
using namespace mstch;
|
||||||
|
|
||||||
|
template_type::template_type(const std::string& str, const delim_type& delims):
|
||||||
|
m_open(delims.first), m_close(delims.second)
|
||||||
|
{
|
||||||
|
tokenize(str);
|
||||||
|
strip_whitespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
template_type::template_type(const std::string& str):
|
||||||
|
m_open("{{"), m_close("}}")
|
||||||
|
{
|
||||||
|
tokenize(str);
|
||||||
|
strip_whitespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
void template_type::process_text(citer begin, citer end) {
|
||||||
|
if (begin == end)
|
||||||
|
return;
|
||||||
|
auto start = begin;
|
||||||
|
for (auto it = begin; it != end; ++it)
|
||||||
|
if (*it == '\n' || it == end - 1) {
|
||||||
|
m_tokens.push_back({{start, it + 1}});
|
||||||
|
start = it + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void template_type::tokenize(const std::string& tmp) {
|
||||||
|
citer beg = tmp.begin();
|
||||||
|
auto npos = std::string::npos;
|
||||||
|
|
||||||
|
for (std::size_t cur_pos = 0; cur_pos < tmp.size();) {
|
||||||
|
auto open_pos = tmp.find(m_open, cur_pos);
|
||||||
|
auto close_pos = tmp.find(
|
||||||
|
m_close, open_pos == npos ? open_pos : open_pos + 1);
|
||||||
|
|
||||||
|
if (close_pos != npos && open_pos != npos) {
|
||||||
|
if (*(beg + open_pos + m_open.size()) == '{' &&
|
||||||
|
*(beg + close_pos + m_close.size()) == '}')
|
||||||
|
++close_pos;
|
||||||
|
|
||||||
|
process_text(beg + cur_pos, beg + open_pos);
|
||||||
|
cur_pos = close_pos + m_close.size();
|
||||||
|
m_tokens.push_back({{beg + open_pos, beg + close_pos + m_close.size()},
|
||||||
|
m_open.size(), m_close.size()});
|
||||||
|
|
||||||
|
if (cur_pos == tmp.size()) {
|
||||||
|
m_tokens.push_back({{""}});
|
||||||
|
m_tokens.back().eol(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*(beg + open_pos + m_open.size()) == '=' &&
|
||||||
|
*(beg + close_pos - 1) == '=')
|
||||||
|
{
|
||||||
|
auto tok_beg = beg + open_pos + m_open.size() + 1;
|
||||||
|
auto tok_end = beg + close_pos - 1;
|
||||||
|
auto front_skip = first_not_ws(tok_beg, tok_end);
|
||||||
|
auto back_skip = first_not_ws(reverse(tok_end), reverse(tok_beg));
|
||||||
|
m_open = {front_skip, beg + tmp.find(' ', front_skip - beg)};
|
||||||
|
m_close = {beg + tmp.rfind(' ', back_skip - beg) + 1, back_skip + 1};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
process_text(beg + cur_pos, tmp.end());
|
||||||
|
cur_pos = close_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void template_type::strip_whitespace() {
|
||||||
|
auto line_begin = m_tokens.begin();
|
||||||
|
bool has_tag = false, non_space = false;
|
||||||
|
|
||||||
|
for (auto it = m_tokens.begin(); it != m_tokens.end(); ++it) {
|
||||||
|
auto type = (*it).token_type();
|
||||||
|
if (type != token::type::text && type != token::type::variable &&
|
||||||
|
type != token::type::unescaped_variable)
|
||||||
|
has_tag = true;
|
||||||
|
else if (!(*it).ws_only())
|
||||||
|
non_space = true;
|
||||||
|
|
||||||
|
if ((*it).eol()) {
|
||||||
|
if (has_tag && !non_space) {
|
||||||
|
store_prefixes(line_begin);
|
||||||
|
|
||||||
|
auto c = line_begin;
|
||||||
|
for (bool end = false; !end; (*c).ws_only() ? c = m_tokens.erase(c) : ++c)
|
||||||
|
if ((end = (*c).eol()))
|
||||||
|
it = c - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
non_space = has_tag = false;
|
||||||
|
line_begin = it + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void template_type::store_prefixes(std::vector<token>::iterator beg) {
|
||||||
|
for (auto cur = beg; !(*cur).eol(); ++cur)
|
||||||
|
if ((*cur).token_type() == token::type::partial &&
|
||||||
|
cur != beg && (*(cur - 1)).ws_only())
|
||||||
|
(*cur).partial_prefix((*(cur - 1)).raw());
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "token.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class template_type {
|
||||||
|
public:
|
||||||
|
template_type() = default;
|
||||||
|
template_type(const std::string& str);
|
||||||
|
template_type(const std::string& str, const delim_type& delims);
|
||||||
|
std::vector<token>::const_iterator begin() const { return m_tokens.begin(); }
|
||||||
|
std::vector<token>::const_iterator end() const { return m_tokens.end(); }
|
||||||
|
void operator<<(const token& token) { m_tokens.push_back(token); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<token> m_tokens;
|
||||||
|
std::string m_open;
|
||||||
|
std::string m_close;
|
||||||
|
void strip_whitespace();
|
||||||
|
void process_text(citer beg, citer end);
|
||||||
|
void tokenize(const std::string& tmp);
|
||||||
|
void store_prefixes(std::vector<token>::iterator beg);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
42
contrib/backends/nntpchan-daemon/libmustache/token.cpp
Normal file
42
contrib/backends/nntpchan-daemon/libmustache/token.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include "token.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
using namespace mstch;
|
||||||
|
|
||||||
|
token::type token::token_info(char c) {
|
||||||
|
switch (c) {
|
||||||
|
case '>': return type::partial;
|
||||||
|
case '^': return type::inverted_section_open;
|
||||||
|
case '/': return type::section_close;
|
||||||
|
case '&': return type::unescaped_variable;
|
||||||
|
case '#': return type::section_open;
|
||||||
|
case '!': return type::comment;
|
||||||
|
default: return type::variable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token::token(const std::string& str, std::size_t left, std::size_t right):
|
||||||
|
m_raw(str), m_eol(false), m_ws_only(false)
|
||||||
|
{
|
||||||
|
if (left != 0 && right != 0) {
|
||||||
|
if (str[left] == '=' && str[str.size() - right - 1] == '=') {
|
||||||
|
m_type = type::delimiter_change;
|
||||||
|
} else if (str[left] == '{' && str[str.size() - right - 1] == '}') {
|
||||||
|
m_type = type::unescaped_variable;
|
||||||
|
m_name = {first_not_ws(str.begin() + left + 1, str.end() - right),
|
||||||
|
first_not_ws(str.rbegin() + 1 + right, str.rend() - left) + 1};
|
||||||
|
} else {
|
||||||
|
auto c = first_not_ws(str.begin() + left, str.end() - right);
|
||||||
|
m_type = token_info(*c);
|
||||||
|
if (m_type != type::variable)
|
||||||
|
c = first_not_ws(c + 1, str.end() - right);
|
||||||
|
m_name = {c, first_not_ws(str.rbegin() + right, str.rend() - left) + 1};
|
||||||
|
m_delims = {{str.begin(), str.begin() + left},
|
||||||
|
{str.end() - right, str.end()}};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_type = type::text;
|
||||||
|
m_eol = (str.size() > 0 && str[str.size() - 1] == '\n');
|
||||||
|
m_ws_only = (str.find_first_not_of(" \r\n\t") == std::string::npos);
|
||||||
|
}
|
||||||
|
}
|
39
contrib/backends/nntpchan-daemon/libmustache/token.hpp
Normal file
39
contrib/backends/nntpchan-daemon/libmustache/token.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
using delim_type = std::pair<std::string, std::string>;
|
||||||
|
|
||||||
|
class token {
|
||||||
|
public:
|
||||||
|
enum class type {
|
||||||
|
text, variable, section_open, section_close, inverted_section_open,
|
||||||
|
unescaped_variable, comment, partial, delimiter_change
|
||||||
|
};
|
||||||
|
token(const std::string& str, std::size_t left = 0, std::size_t right = 0);
|
||||||
|
type token_type() const { return m_type; };
|
||||||
|
const std::string& raw() const { return m_raw; };
|
||||||
|
const std::string& name() const { return m_name; };
|
||||||
|
const std::string& partial_prefix() const { return m_partial_prefix; };
|
||||||
|
const delim_type& delims() const { return m_delims; };
|
||||||
|
void partial_prefix(const std::string& p_partial_prefix) {
|
||||||
|
m_partial_prefix = p_partial_prefix;
|
||||||
|
};
|
||||||
|
bool eol() const { return m_eol; }
|
||||||
|
void eol(bool eol) { m_eol = eol; }
|
||||||
|
bool ws_only() const { return m_ws_only; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
type m_type;
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_raw;
|
||||||
|
std::string m_partial_prefix;
|
||||||
|
delim_type m_delims;
|
||||||
|
bool m_eol;
|
||||||
|
bool m_ws_only;
|
||||||
|
type token_info(char c);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
44
contrib/backends/nntpchan-daemon/libmustache/utils.cpp
Normal file
44
contrib/backends/nntpchan-daemon/libmustache/utils.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "utils.hpp"
|
||||||
|
#include "mstch/mstch.hpp"
|
||||||
|
|
||||||
|
mstch::citer mstch::first_not_ws(mstch::citer begin, mstch::citer end) {
|
||||||
|
for (auto it = begin; it != end; ++it)
|
||||||
|
if (*it != ' ') return it;
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
mstch::citer mstch::first_not_ws(mstch::criter begin, mstch::criter end) {
|
||||||
|
for (auto rit = begin; rit != end; ++rit)
|
||||||
|
if (*rit != ' ') return --(rit.base());
|
||||||
|
return --(end.base());
|
||||||
|
}
|
||||||
|
|
||||||
|
mstch::criter mstch::reverse(mstch::citer it) {
|
||||||
|
return std::reverse_iterator<mstch::citer>(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mstch::html_escape(const std::string& str) {
|
||||||
|
if (mstch::config::escape)
|
||||||
|
return mstch::config::escape(str);
|
||||||
|
|
||||||
|
std::string out;
|
||||||
|
citer start = str.begin();
|
||||||
|
|
||||||
|
auto add_escape = [&out, &start](const std::string& escaped, citer& it) {
|
||||||
|
out += std::string{start, it} + escaped;
|
||||||
|
start = it + 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto it = str.begin(); it != str.end(); ++it)
|
||||||
|
switch (*it) {
|
||||||
|
case '&': add_escape("&", it); break;
|
||||||
|
case '\'': add_escape("'", it); break;
|
||||||
|
case '"': add_escape(""", it); break;
|
||||||
|
case '<': add_escape("<", it); break;
|
||||||
|
case '>': add_escape(">", it); break;
|
||||||
|
case '/': add_escape("/", it); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out + std::string{start, str.end()};
|
||||||
|
}
|
23
contrib/backends/nntpchan-daemon/libmustache/utils.hpp
Normal file
23
contrib/backends/nntpchan-daemon/libmustache/utils.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <boost/variant/apply_visitor.hpp>
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
using citer = std::string::const_iterator;
|
||||||
|
using criter = std::string::const_reverse_iterator;
|
||||||
|
|
||||||
|
citer first_not_ws(citer begin, citer end);
|
||||||
|
citer first_not_ws(criter begin, criter end);
|
||||||
|
std::string html_escape(const std::string& str);
|
||||||
|
criter reverse(citer it);
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
auto visit(Args&&... args) -> decltype(boost::apply_visitor(
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
return boost::apply_visitor(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/variant/static_visitor.hpp>
|
||||||
|
|
||||||
|
#include "mstch/mstch.hpp"
|
||||||
|
#include "has_token.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class get_token: public boost::static_visitor<const mstch::node&> {
|
||||||
|
public:
|
||||||
|
get_token(const std::string& token, const mstch::node& node):
|
||||||
|
m_token(token), m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
const mstch::node& operator()(const T&) const {
|
||||||
|
return m_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mstch::node& operator()(const map& map) const {
|
||||||
|
return map.at(m_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mstch::node& operator()(const std::shared_ptr<object>& object) const {
|
||||||
|
return object->at(m_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string& m_token;
|
||||||
|
const mstch::node& m_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/variant/static_visitor.hpp>
|
||||||
|
|
||||||
|
#include "mstch/mstch.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class has_token: public boost::static_visitor<bool> {
|
||||||
|
public:
|
||||||
|
has_token(const std::string& token): m_token(token) {
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool operator()(const T&) const {
|
||||||
|
return m_token == ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const map& map) const {
|
||||||
|
return map.count(m_token) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const std::shared_ptr<object>& object) const {
|
||||||
|
return object->has(m_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string& m_token;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/variant/static_visitor.hpp>
|
||||||
|
|
||||||
|
#include "mstch/mstch.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class is_node_empty: public boost::static_visitor<bool> {
|
||||||
|
public:
|
||||||
|
template<class T>
|
||||||
|
bool operator()(const T&) const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const std::nullptr_t&) const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const int& value) const {
|
||||||
|
return value == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const double& value) const {
|
||||||
|
return value == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const bool& value) const {
|
||||||
|
return !value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const std::string& value) const {
|
||||||
|
return value == "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const array& array) const {
|
||||||
|
return array.size() == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <boost/variant/static_visitor.hpp>
|
||||||
|
|
||||||
|
#include "../render_context.hpp"
|
||||||
|
#include "mstch/mstch.hpp"
|
||||||
|
#include "../utils.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class render_node: public boost::static_visitor<std::string> {
|
||||||
|
public:
|
||||||
|
enum class flag { none, escape_html };
|
||||||
|
render_node(render_context& ctx, flag p_flag = flag::none):
|
||||||
|
m_ctx(ctx), m_flag(p_flag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
std::string operator()(const T&) const {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const int& value) const {
|
||||||
|
return std::to_string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const double& value) const {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const bool& value) const {
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const lambda& value) const {
|
||||||
|
template_type interpreted{value([this](const mstch::node& n) {
|
||||||
|
return visit(render_node(m_ctx), n);
|
||||||
|
})};
|
||||||
|
auto rendered = render_context::push(m_ctx).render(interpreted);
|
||||||
|
return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const std::string& value) const {
|
||||||
|
return (m_flag == flag::escape_html) ? html_escape(value) : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
render_context& m_ctx;
|
||||||
|
flag m_flag;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/variant/static_visitor.hpp>
|
||||||
|
|
||||||
|
#include "../render_context.hpp"
|
||||||
|
#include "mstch/mstch.hpp"
|
||||||
|
#include "../utils.hpp"
|
||||||
|
#include "render_node.hpp"
|
||||||
|
|
||||||
|
namespace mstch {
|
||||||
|
|
||||||
|
class render_section: public boost::static_visitor<std::string> {
|
||||||
|
public:
|
||||||
|
enum class flag { none, keep_array };
|
||||||
|
render_section(
|
||||||
|
render_context& ctx,
|
||||||
|
const template_type& section,
|
||||||
|
const delim_type& delims,
|
||||||
|
flag p_flag = flag::none):
|
||||||
|
m_ctx(ctx), m_section(section), m_delims(delims), m_flag(p_flag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
std::string operator()(const T& t) const {
|
||||||
|
return render_context::push(m_ctx, t).render(m_section);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const lambda& fun) const {
|
||||||
|
std::string section_str;
|
||||||
|
for (auto& token: m_section)
|
||||||
|
section_str += token.raw();
|
||||||
|
template_type interpreted{fun([this](const mstch::node& n) {
|
||||||
|
return visit(render_node(m_ctx), n);
|
||||||
|
}, section_str), m_delims};
|
||||||
|
return render_context::push(m_ctx).render(interpreted);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(const array& array) const {
|
||||||
|
std::string out;
|
||||||
|
if (m_flag == flag::keep_array)
|
||||||
|
return render_context::push(m_ctx, array).render(m_section);
|
||||||
|
else
|
||||||
|
for (auto& item: array)
|
||||||
|
out += visit(render_section(
|
||||||
|
m_ctx, m_section, m_delims, flag::keep_array), item);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
render_context& m_ctx;
|
||||||
|
const template_type& m_section;
|
||||||
|
const delim_type& m_delims;
|
||||||
|
flag m_flag;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
#include "base64.hpp"
|
#include <nntpchan/base64.hpp>
|
||||||
|
|
||||||
|
|
||||||
// taken from i2pd
|
// taken from i2pd
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "buffer.hpp"
|
#include <nntpchan/buffer.hpp>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
@ -9,7 +9,7 @@ namespace nntpchan
|
|||||||
std::memcpy(buf, b, s);
|
std::memcpy(buf, b, s);
|
||||||
this->b = uv_buf_init(buf, s);
|
this->b = uv_buf_init(buf, s);
|
||||||
w.data = this;
|
w.data = this;
|
||||||
};
|
}
|
||||||
|
|
||||||
WriteBuffer::WriteBuffer(const std::string & s) : WriteBuffer(s.c_str(), s.size()) {}
|
WriteBuffer::WriteBuffer(const std::string & s) : WriteBuffer(s.c_str(), s.size()) {}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "crypto.hpp"
|
#include <nntpchan/crypto.hpp>
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "event.hpp"
|
#include <nntpchan/event.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "exec_frontend.hpp"
|
#include <nntpchan/exec_frontend.hpp>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "file_handle.hpp"
|
#include <nntpchan/file_handle.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "line.hpp"
|
#include <nntpchan/line.hpp>
|
||||||
|
|
||||||
namespace nntpchan {
|
namespace nntpchan {
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "mime.hpp"
|
#include <nntpchan/mime.hpp>
|
||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "net.hpp"
|
#include <nntpchan/net.hpp>
|
||||||
#include <uv.h>
|
#include <uv.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "nntp_auth.hpp"
|
#include <nntpchan/nntp_auth.hpp>
|
||||||
#include "crypto.hpp"
|
#include <nntpchan/crypto.hpp>
|
||||||
#include "base64.hpp"
|
#include <nntpchan/base64.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "nntp_handler.hpp"
|
#include <nntpchan/nntp_handler.hpp>
|
||||||
#include "sanitize.hpp"
|
#include <nntpchan/sanitize.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
#include "nntp_server.hpp"
|
#include <nntpchan/nntp_server.hpp>
|
||||||
#include "nntp_auth.hpp"
|
#include <nntpchan/nntp_auth.hpp>
|
||||||
#include "nntp_handler.hpp"
|
#include <nntpchan/nntp_handler.hpp>
|
||||||
#include "net.hpp"
|
#include <nntpchan/net.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
#include "sanitize.hpp"
|
#include <nntpchan/sanitize.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
{
|
{
|
||||||
std::string NNTPSanitize(const std::string & str)
|
|
||||||
|
std::string NNTPSanitizeLine(const std::string & str)
|
||||||
{
|
{
|
||||||
|
if(str == ".") return " .";
|
||||||
|
std::string sane;
|
||||||
|
sane += str;
|
||||||
|
const char ch = ' ';
|
||||||
|
std::replace_if(sane.begin(), sane.end(), [](unsigned char ch) -> bool { return iscntrl(ch); } , ch);
|
||||||
|
return sane;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ToLower(const std::string & str)
|
std::string ToLower(const std::string & str)
|
||||||
@ -21,4 +29,11 @@ namespace nntpchan
|
|||||||
{
|
{
|
||||||
return std::regex_search(msgid, re_ValidMessageID) == 1;
|
return std::regex_search(msgid, re_ValidMessageID) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const std::regex re_ValidNewsgroup("^[a-zA-Z][a-zA-Z0-9.]{1,128}$");
|
||||||
|
|
||||||
|
bool IsValidNewsgroup(const std::string & msgid)
|
||||||
|
{
|
||||||
|
return std::regex_search(msgid, re_ValidNewsgroup) == 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "buffer.hpp"
|
#include <nntpchan/buffer.hpp>
|
||||||
#include "server.hpp"
|
#include <nntpchan/server.hpp>
|
||||||
#include "net.hpp"
|
#include <nntpchan/net.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "staticfile_frontend.hpp"
|
#include <nntpchan/staticfile_frontend.hpp>
|
||||||
#include "file_handle.hpp"
|
#include <nntpchan/file_handle.hpp>
|
||||||
#include "sanitize.hpp"
|
#include <nntpchan/sanitize.hpp>
|
||||||
#include "mime.hpp"
|
#include <nntpchan/mime.hpp>
|
||||||
#include "sha1.hpp"
|
#include <nntpchan/sha1.hpp>
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -36,9 +36,6 @@ namespace nntpchan
|
|||||||
|
|
||||||
// read body
|
// read body
|
||||||
|
|
||||||
// render templates
|
|
||||||
if(m_TemplateEngine)
|
|
||||||
{
|
|
||||||
std::map<std::string, std::any> thread_args;
|
std::map<std::string, std::any> thread_args;
|
||||||
|
|
||||||
auto findMsgidFunc = [](const std::pair<std::string, std::string> & item) -> bool {
|
auto findMsgidFunc = [](const std::pair<std::string, std::string> & item) -> bool {
|
||||||
@ -48,16 +45,24 @@ namespace nntpchan
|
|||||||
|
|
||||||
auto msgid = std::find_if(header.begin(), header.end(), findMsgidFunc);
|
auto msgid = std::find_if(header.begin(), header.end(), findMsgidFunc);
|
||||||
|
|
||||||
|
if(!IsValidMessageID(msgid->second))
|
||||||
|
{
|
||||||
|
std::clog << "invalid message-id: " << msgid->second << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string msgid_hash = sha1_hex(msgid->second);
|
std::string msgid_hash = sha1_hex(msgid->second);
|
||||||
|
|
||||||
fs::path threadFilePath = m_OutDir / fs::path("thread-" + msgid_hash + ".html");
|
fs::path threadFilePath = m_OutDir / fs::path("thread-" + msgid_hash + ".html");
|
||||||
|
if(m_TemplateEngine)
|
||||||
|
{
|
||||||
FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
|
FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
|
||||||
if(!m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out))
|
if(!m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out))
|
||||||
{
|
{
|
||||||
std::clog << "failed to write " << threadFilePath << std::endl;
|
std::clog << "failed to write " << threadFilePath << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
std::set<std::string> newsgroups_list;
|
std::set<std::string> newsgroups_list;
|
||||||
|
|
||||||
auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> & item) -> bool
|
auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> & item) -> bool
|
||||||
@ -76,7 +81,8 @@ namespace nntpchan
|
|||||||
std::string newsgroup;
|
std::string newsgroup;
|
||||||
while(std::getline(input, newsgroup, ' '))
|
while(std::getline(input, newsgroup, ' '))
|
||||||
{
|
{
|
||||||
newsgroups_list.insert(NNTPSanitize(newsgroup));
|
if(IsValidNewsgroup(newsgroup))
|
||||||
|
newsgroups_list.insert(newsgroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto & name : newsgroups_list)
|
for(const auto & name : newsgroups_list)
|
||||||
@ -91,14 +97,27 @@ namespace nntpchan
|
|||||||
board_args["threads"] = std::make_any<Threads_t>(threads);
|
board_args["threads"] = std::make_any<Threads_t>(threads);
|
||||||
|
|
||||||
fs::path boardPageFilename(newsgroup + "-" + std::to_string(pageno) + ".html");
|
fs::path boardPageFilename(newsgroup + "-" + std::to_string(pageno) + ".html");
|
||||||
out = OpenFile(m_OutDir / boardPageFilename, eWrite);
|
if(m_TemplateEngine)
|
||||||
|
{
|
||||||
|
FileHandle_ptr out = OpenFile(m_OutDir / boardPageFilename, eWrite);
|
||||||
m_TemplateEngine->WriteTemplate("board.mustache", board_args, out);
|
m_TemplateEngine->WriteTemplate("board.mustache", board_args, out);
|
||||||
|
}
|
||||||
|
|
||||||
++pageno;
|
++pageno;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool StaticFileFrontend::AcceptsNewsgroup(const std::string & newsgroup)
|
||||||
|
{
|
||||||
|
return IsValidNewsgroup(newsgroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StaticFileFrontend::AcceptsMessage(const std::string & msgid)
|
||||||
|
{
|
||||||
|
return IsValidMessageID(msgid);
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticFileFrontend::BoardPage_t StaticFileFrontend::GetThreadsPaginated(const std::string & group, uint32_t perpage, uint32_t pages)
|
StaticFileFrontend::BoardPage_t StaticFileFrontend::GetThreadsPaginated(const std::string & group, uint32_t perpage, uint32_t pages)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "storage.hpp"
|
#include <nntpchan/storage.hpp>
|
||||||
#include "sanitize.hpp"
|
#include <nntpchan/sanitize.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "template_engine.hpp"
|
#include <nntpchan/template_engine.hpp>
|
||||||
#include "sanitize.hpp"
|
#include <nntpchan/sanitize.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace nntpchan
|
namespace nntpchan
|
||||||
{
|
{
|
||||||
@ -8,19 +9,33 @@ namespace nntpchan
|
|||||||
{
|
{
|
||||||
struct Impl
|
struct Impl
|
||||||
{
|
{
|
||||||
bool RenderFile(const std::string & fname, const Args_t & args, const FileHandle_ptr & out)
|
|
||||||
|
bool ParseTemplate(const FileHandle_ptr & in)
|
||||||
{
|
{
|
||||||
auto file = OpenFile(fname, eRead);
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RenderFile(const Args_t & args, const FileHandle_ptr & out)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual bool WriteTemplate(const std::string & fname, const Args_t & args, const FileHandle_ptr & out)
|
virtual bool WriteTemplate(const fs::path & fpath, const Args_t & args, const FileHandle_ptr & out)
|
||||||
{
|
{
|
||||||
|
auto templFile = OpenFile(fpath, eRead);
|
||||||
|
if(!templFile)
|
||||||
|
{
|
||||||
|
std::clog << "no such template at " << fpath << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
auto impl = std::make_unique<Impl>();
|
auto impl = std::make_unique<Impl>();
|
||||||
return impl->RenderFile(fname, args, out);
|
if(impl->ParseTemplate(templFile))
|
||||||
|
return impl->RenderFile(args, out);
|
||||||
|
|
||||||
|
std::clog << "failed to parse template " << fpath << std::endl;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "exec_frontend.hpp"
|
#include <nntpchan/exec_frontend.hpp>
|
||||||
#include "sanitize.hpp"
|
#include <nntpchan/sanitize.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "exec_frontend.hpp"
|
#include <nntpchan/exec_frontend.hpp>
|
||||||
#include "sanitize.hpp"
|
#include <nntpchan/sanitize.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user