more nntpchan-daemon stuff
This commit is contained in:
66 files changed
+1059
-117
No files matched your search
@@ -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)
|
||||||
File renamed without changes.
@@ -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>
|
||||||
|
|||||||
@@ -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>());
|
||||||
|
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
-1
@@ -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
|
||||||
File renamed without changes.
File renamed without changes.
+2
-2
@@ -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:
|
||||||
|
|
||||||
File renamed without changes.
+1
-1
@@ -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);
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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()};
|
||||||
|
}
|
||||||
@@ -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,71 +36,90 @@ namespace nntpchan
|
|||||||
|
|
||||||
// read body
|
// read body
|
||||||
|
|
||||||
// render templates
|
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);
|
||||||
|
|
||||||
|
if(!IsValidMessageID(msgid->second))
|
||||||
|
{
|
||||||
|
std::clog << "invalid message-id: " << msgid->second << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string msgid_hash = sha1_hex(msgid->second);
|
||||||
|
|
||||||
|
fs::path threadFilePath = m_OutDir / fs::path("thread-" + msgid_hash + ".html");
|
||||||
if(m_TemplateEngine)
|
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);
|
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
|
||||||
|
{
|
||||||
|
return ToLower(item.first) == "newsgroups";
|
||||||
|
};
|
||||||
|
|
||||||
auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> & item) -> bool
|
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, ' '))
|
||||||
|
{
|
||||||
|
if(IsValidNewsgroup(newsgroup))
|
||||||
|
newsgroups_list.insert(newsgroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const auto & name : newsgroups_list)
|
||||||
|
{
|
||||||
|
auto board = GetThreadsPaginated(name, 10, m_Pages);
|
||||||
|
uint32_t pageno = 0;
|
||||||
|
for(Threads_t threads : board)
|
||||||
{
|
{
|
||||||
return ToLower(item.first) == "newsgroups";
|
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);
|
||||||
|
|
||||||
auto group = std::find_if(header.begin(), header.end(), findNewsgroupsFunc);
|
fs::path boardPageFilename(newsgroup + "-" + std::to_string(pageno) + ".html");
|
||||||
if(group == std::end(header))
|
if(m_TemplateEngine)
|
||||||
{
|
|
||||||
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;
|
FileHandle_ptr out = OpenFile(m_OutDir / boardPageFilename, eWrite);
|
||||||
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);
|
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)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -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