diff --git a/contrib/backends/nntpchan-daemon/Makefile b/contrib/backends/nntpchan-daemon/Makefile index eb5610d..5344fb7 100644 --- a/contrib/backends/nntpchan-daemon/Makefile +++ b/contrib/backends/nntpchan-daemon/Makefile @@ -1,29 +1,44 @@ REPO=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) -SRC_PATH = $(REPO)/libnntpchan +NNTPCHAN_PATH = $(REPO)/libnntpchan -SOURCES := $(wildcard $(SRC_PATH)/*.cpp) -HEADERS := $(wildcard $(SRC_PATH)/*.hpp) -OBJECTS := $(SOURCES:.cpp=.o) +NNTPCHAN_SRC := $(wildcard $(NNTPCHAN_PATH)/*.cpp) +NNTPCHAN_HDR := $(wildcard $(NNTPCHAN_PATH)/*.hpp) +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=) +OBJ := $(NNTPCHAN_OBJ) $(MUSTACHE_OBJ) + DAEMON_SRC = $(REPO)/daemon PKGS := libuv libsodium 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) ifeq ($(DEBUG),1) CXXFLAGS += -g endif -LIB = $(REPO)/libnntpchan.a +NNTPCHAN_LIB = $(REPO)/libnntpchan.a +MUSTACHE_LIB = $(REPO)/libmustache.a + +LIBS = $(NNTPCHAN_LIB) $(MUSTACHE_LIB) EXE = $(REPO)/nntpd @@ -32,25 +47,25 @@ all: build build: $(EXE) $(TOOLS) -$(LIB): $(OBJECTS) - $(AR) -r $(LIB) $(OBJECTS) +$(MUSTACHE_LIB): $(MUSTACHE_OBJ) + $(AR) -r $(MUSTACHE_LIB) $(MUSTACHE_OBJ) -$(EXE): $(LIB) - $(CXX) $(CXXFLAGS) $(DAEMON_SRC)/main.cpp $(LIB) $(LD_FLAGS) -o $(EXE) +$(NNTPCHAN_LIB): $(NNTPCHAN_OBJ) + $(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) - $(CXX) $(CXXFLAGS) $< $(LIB) $(LD_FLAGS) -o $@ + $(CXX) $(CXXFLAGS) $< $(NNTPCHAN_LIB) $(LD_FLAGS) -o $@ 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 $(REPO)/test -%.o: src/%.cpp - $(CXX) $(CXXFLAGS) -c -o $@ - clean: - rm -f $(OBJECTS) $(LIB) $(EXE) $(TOOLS) + rm -f $(OBJ) $(NNTPCHAN_LIB) $(EXE) $(TOOLS) diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/ini.hpp b/contrib/backends/nntpchan-daemon/daemon/ini.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/ini.hpp rename to contrib/backends/nntpchan-daemon/daemon/ini.hpp diff --git a/contrib/backends/nntpchan-daemon/daemon/main.cpp b/contrib/backends/nntpchan-daemon/daemon/main.cpp index 4b69d0d..33907c5 100644 --- a/contrib/backends/nntpchan-daemon/daemon/main.cpp +++ b/contrib/backends/nntpchan-daemon/daemon/main.cpp @@ -1,11 +1,11 @@ #include "ini.hpp" -#include "crypto.hpp" -#include "storage.hpp" -#include "nntp_server.hpp" -#include "event.hpp" -#include "exec_frontend.hpp" -#include "staticfile_frontend.hpp" +#include +#include +#include +#include +#include +#include #include #include diff --git a/contrib/backends/nntpchan-daemon/include/mstch/mstch.hpp b/contrib/backends/nntpchan-daemon/include/mstch/mstch.hpp new file mode 100644 index 0000000..58d3330 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/include/mstch/mstch.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace mstch { + +struct config { + static std::function escape; +}; + +namespace internal { + +template +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 + void register_methods(S* s, std::map methods) { + for(auto& item: methods) + this->methods.insert({item.first, std::bind(item.second, s)}); + } + + private: + std::map> methods; + mutable std::map cache; +}; + +template +class is_fun { + private: + using not_fun = char; + using fun_without_args = char[2]; + using fun_with_args = char[3]; + template struct really_has; + template static fun_without_args& test( + really_has*); + template static fun_with_args& test( + really_has*); + template static not_fun& test(...); + + public: + static bool const no_args = sizeof(test(0)) == sizeof(fun_without_args); + static bool const has_args = sizeof(test(0)) == sizeof(fun_with_args); +}; + +template +using node_renderer = std::function; + +template +class lambda_t { + public: + template + lambda_t(F f, typename std::enable_if::no_args>::type* = 0): + fun([f](node_renderer renderer, const std::string&) { + return renderer(f()); + }) + { + } + + template + lambda_t(F f, typename std::enable_if::has_args>::type* = 0): + fun([f](node_renderer renderer, const std::string& text) { + return renderer(f(text)); + }) + { + } + + std::string operator()(node_renderer renderer, + const std::string& text = "") const + { + return fun(renderer, text); + } + + private: + std::function renderer, const std::string&)> fun; +}; + +} + +using node = boost::make_recursive_variant< + std::nullptr_t, std::string, int, double, bool, + internal::lambda_t, + std::shared_ptr>, + std::map, + std::vector>::type; +using object = internal::object_t; +using lambda = internal::lambda_t; +using map = std::map; +using array = std::vector; + +std::string render( + const std::string& tmplt, + const node& root, + const std::map& partials = + std::map()); + +} diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/base64.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/base64.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/base64.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/base64.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/buffer.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/buffer.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/buffer.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/buffer.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/crypto.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/crypto.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/crypto.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/crypto.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/event.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/event.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/event.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/event.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/exec_frontend.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/exec_frontend.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/exec_frontend.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/exec_frontend.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/file_handle.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/file_handle.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/file_handle.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/file_handle.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/frontend.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/frontend.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/frontend.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/frontend.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/http.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/http.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/http.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/http.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/http_client.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/http_client.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/http_client.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/http_client.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/http_server.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/http_server.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/http_server.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/http_server.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/io_handle.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/io_handle.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/io_handle.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/io_handle.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/line.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/line.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/line.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/line.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/mime.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/mime.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/mime.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/mime.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/model.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/model.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/model.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/model.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/net.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/net.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/net.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/net.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_auth.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/nntp_auth.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/nntp_auth.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/nntp_auth.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_handler.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/nntp_handler.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/nntp_handler.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/nntp_handler.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_server.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/nntp_server.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/nntp_server.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/nntp_server.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/sanitize.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/sanitize.hpp similarity index 65% rename from contrib/backends/nntpchan-daemon/libnntpchan/sanitize.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/sanitize.hpp index 4f0a52c..5295280 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/sanitize.hpp +++ b/contrib/backends/nntpchan-daemon/include/nntpchan/sanitize.hpp @@ -4,9 +4,10 @@ namespace nntpchan { - std::string NNTPSanitize(const std::string & str); + std::string NNTPSanitizeLine(const std::string & str); std::string ToLower(const std::string & str); bool IsValidMessageID(const std::string & msgid); + bool IsValidNewsgroup(const std::string & group); } #endif diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/server.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/server.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/server.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/server.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/sha1.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/sha1.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/sha1.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/sha1.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/staticfile_frontend.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/staticfile_frontend.hpp similarity index 84% rename from contrib/backends/nntpchan-daemon/libnntpchan/staticfile_frontend.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/staticfile_frontend.hpp index 871a8b3..68c8bb7 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/staticfile_frontend.hpp +++ b/contrib/backends/nntpchan-daemon/include/nntpchan/staticfile_frontend.hpp @@ -19,8 +19,8 @@ namespace nntpchan ~StaticFileFrontend(); void ProcessNewMessage(const fs::path & fpath); - bool AcceptsNewsgroup(const std::string & newsgroup) { (void) newsgroup; return true; } - bool AcceptsMessage(const std::string & msgid) { (void) msgid; return true; } + bool AcceptsNewsgroup(const std::string & newsgroup); + bool AcceptsMessage(const std::string & msgid); private: diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/storage.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/storage.hpp similarity index 100% rename from contrib/backends/nntpchan-daemon/libnntpchan/storage.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/storage.hpp diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/template_engine.hpp b/contrib/backends/nntpchan-daemon/include/nntpchan/template_engine.hpp similarity index 77% rename from contrib/backends/nntpchan-daemon/libnntpchan/template_engine.hpp rename to contrib/backends/nntpchan-daemon/include/nntpchan/template_engine.hpp index 5047b55..548e8a7 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/template_engine.hpp +++ b/contrib/backends/nntpchan-daemon/include/nntpchan/template_engine.hpp @@ -12,7 +12,7 @@ namespace nntpchan struct TemplateEngine { using Args_t = std::map; - 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); diff --git a/contrib/backends/nntpchan-daemon/libmustache/mstch.cpp b/contrib/backends/nntpchan-daemon/libmustache/mstch.cpp new file mode 100644 index 0000000..4d84e97 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/mstch.cpp @@ -0,0 +1,20 @@ +#include + +#include "mstch/mstch.hpp" +#include "render_context.hpp" + +using namespace mstch; + +std::function mstch::config::escape; + +std::string mstch::render( + const std::string& tmplt, + const node& root, + const std::map& partials) +{ + std::map partial_templates; + for (auto& partial: partials) + partial_templates.insert({partial.first, {partial.second}}); + + return render_context(root, partial_templates).render(tmplt); +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/render_context.cpp b/contrib/backends/nntpchan-daemon/libmustache/render_context.cpp new file mode 100644 index 0000000..90b2ffc --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/render_context.cpp @@ -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(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& partials): + m_partials(partials), m_nodes(1, node), m_node_ptrs(1, &node) +{ + m_state.push(std::unique_ptr(new outside_section)); +} + +const mstch::node& render_context::find_node( + const std::string& token, + std::list 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) : ""; +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/render_context.hpp b/contrib/backends/nntpchan-daemon/libmustache/render_context.hpp new file mode 100644 index 0000000..b440b5c --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/render_context.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#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& 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 + void set_state(Args&& ... args) { + m_state.top() = std::unique_ptr( + new T(std::forward(args)...)); + } + + private: + static const mstch::node null_node; + const mstch::node& find_node( + const std::string& token, + std::list current_nodes); + std::map m_partials; + std::deque m_nodes; + std::list m_node_ptrs; + std::stack> m_state; +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/state/in_section.cpp b/contrib/backends/nntpchan-daemon/libmustache/state/in_section.cpp new file mode 100644 index 0000000..90180df --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/state/in_section.cpp @@ -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(); + 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 ""; +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/state/in_section.hpp b/contrib/backends/nntpchan-daemon/libmustache/state/in_section.hpp new file mode 100644 index 0000000..2ed45c4 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/state/in_section.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#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; +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/state/outside_section.cpp b/contrib/backends/nntpchan-daemon/libmustache/state/outside_section.cpp new file mode 100644 index 0000000..4dd92f3 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/state/outside_section.cpp @@ -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::type::normal, token); + break; + case token::type::inverted_section_open: + ctx.set_state(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 ""; +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/state/outside_section.hpp b/contrib/backends/nntpchan-daemon/libmustache/state/outside_section.hpp new file mode 100644 index 0000000..8617dce --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/state/outside_section.hpp @@ -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; +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/state/render_state.hpp b/contrib/backends/nntpchan-daemon/libmustache/state/render_state.hpp new file mode 100644 index 0000000..5d57601 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/state/render_state.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +#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; +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/template_type.cpp b/contrib/backends/nntpchan-daemon/libmustache/template_type.cpp new file mode 100644 index 0000000..5b40e40 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/template_type.cpp @@ -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::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()); +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/template_type.hpp b/contrib/backends/nntpchan-daemon/libmustache/template_type.hpp new file mode 100644 index 0000000..3c5bf91 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/template_type.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +#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::const_iterator begin() const { return m_tokens.begin(); } + std::vector::const_iterator end() const { return m_tokens.end(); } + void operator<<(const token& token) { m_tokens.push_back(token); } + + private: + std::vector 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::iterator beg); +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/token.cpp b/contrib/backends/nntpchan-daemon/libmustache/token.cpp new file mode 100644 index 0000000..9443a22 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/token.cpp @@ -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); + } +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/token.hpp b/contrib/backends/nntpchan-daemon/libmustache/token.hpp new file mode 100644 index 0000000..fde8017 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/token.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace mstch { + +using delim_type = std::pair; + +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); +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/utils.cpp b/contrib/backends/nntpchan-daemon/libmustache/utils.cpp new file mode 100644 index 0000000..1646170 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/utils.cpp @@ -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(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()}; +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/utils.hpp b/contrib/backends/nntpchan-daemon/libmustache/utils.hpp new file mode 100644 index 0000000..9041a3e --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/utils.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +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 +auto visit(Args&&... args) -> decltype(boost::apply_visitor( + std::forward(args)...)) +{ + return boost::apply_visitor(std::forward(args)...); +} + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/visitor/get_token.hpp b/contrib/backends/nntpchan-daemon/libmustache/visitor/get_token.hpp new file mode 100644 index 0000000..d41ab6e --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/visitor/get_token.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "mstch/mstch.hpp" +#include "has_token.hpp" + +namespace mstch { + +class get_token: public boost::static_visitor { + public: + get_token(const std::string& token, const mstch::node& node): + m_token(token), m_node(node) + { + } + + template + 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) const { + return object->at(m_token); + } + + private: + const std::string& m_token; + const mstch::node& m_node; +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/visitor/has_token.hpp b/contrib/backends/nntpchan-daemon/libmustache/visitor/has_token.hpp new file mode 100644 index 0000000..5ab30d4 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/visitor/has_token.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "mstch/mstch.hpp" + +namespace mstch { + +class has_token: public boost::static_visitor { + public: + has_token(const std::string& token): m_token(token) { + } + + template + 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) const { + return object->has(m_token); + } + + private: + const std::string& m_token; +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/visitor/is_node_empty.hpp b/contrib/backends/nntpchan-daemon/libmustache/visitor/is_node_empty.hpp new file mode 100644 index 0000000..a0ae432 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/visitor/is_node_empty.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include "mstch/mstch.hpp" + +namespace mstch { + +class is_node_empty: public boost::static_visitor { + public: + template + 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; + } +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/visitor/render_node.hpp b/contrib/backends/nntpchan-daemon/libmustache/visitor/render_node.hpp new file mode 100644 index 0000000..fb2a83e --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/visitor/render_node.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +#include "../render_context.hpp" +#include "mstch/mstch.hpp" +#include "../utils.hpp" + +namespace mstch { + +class render_node: public boost::static_visitor { + 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 + 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; +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libmustache/visitor/render_section.hpp b/contrib/backends/nntpchan-daemon/libmustache/visitor/render_section.hpp new file mode 100644 index 0000000..c6e56b5 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/libmustache/visitor/render_section.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include "../render_context.hpp" +#include "mstch/mstch.hpp" +#include "../utils.hpp" +#include "render_node.hpp" + +namespace mstch { + +class render_section: public boost::static_visitor { + 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 + 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; +}; + +} diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/base64.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/base64.cpp index 0150b30..0531bb5 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/base64.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/base64.cpp @@ -1,4 +1,4 @@ -#include "base64.hpp" +#include // taken from i2pd diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/buffer.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/buffer.cpp index 938cc7f..b1fada6 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/buffer.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/buffer.cpp @@ -1,4 +1,4 @@ -#include "buffer.hpp" +#include #include namespace nntpchan @@ -9,7 +9,7 @@ namespace nntpchan std::memcpy(buf, b, s); this->b = uv_buf_init(buf, s); w.data = this; - }; + } WriteBuffer::WriteBuffer(const std::string & s) : WriteBuffer(s.c_str(), s.size()) {} diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/crypto.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/crypto.cpp index be6fd06..add1b29 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/crypto.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/crypto.cpp @@ -1,4 +1,4 @@ -#include "crypto.hpp" +#include #include #include diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/event.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/event.cpp index 40dca28..e4a0f19 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/event.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/event.cpp @@ -1,4 +1,4 @@ -#include "event.hpp" +#include #include namespace nntpchan diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/exec_frontend.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/exec_frontend.cpp index 2ae9d97..b9d3174 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/exec_frontend.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/exec_frontend.cpp @@ -1,4 +1,4 @@ -#include "exec_frontend.hpp" +#include #include #include #include diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/file_handle.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/file_handle.cpp index 86d8d51..ac2d684 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/file_handle.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/file_handle.cpp @@ -1,4 +1,4 @@ -#include "file_handle.hpp" +#include namespace nntpchan diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/line.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/line.cpp index 74a0cd9..1d5cb54 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/line.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/line.cpp @@ -1,4 +1,4 @@ -#include "line.hpp" +#include namespace nntpchan { diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/mime.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/mime.cpp index f1d79dd..735d8cd 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/mime.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/mime.cpp @@ -1,4 +1,4 @@ -#include "mime.hpp" +#include namespace nntpchan { diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/net.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/net.cpp index 6aea582..f617227 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/net.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/net.cpp @@ -1,4 +1,4 @@ -#include "net.hpp" +#include #include #include #include diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_auth.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/nntp_auth.cpp index fa09e57..874810e 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_auth.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/nntp_auth.cpp @@ -1,6 +1,6 @@ -#include "nntp_auth.hpp" -#include "crypto.hpp" -#include "base64.hpp" +#include +#include +#include #include #include #include diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_handler.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/nntp_handler.cpp index dabcdd4..265420f 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_handler.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/nntp_handler.cpp @@ -1,5 +1,5 @@ -#include "nntp_handler.hpp" -#include "sanitize.hpp" +#include +#include #include #include #include diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_server.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/nntp_server.cpp index 19c10da..4875ec8 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/nntp_server.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/nntp_server.cpp @@ -1,8 +1,8 @@ -#include "nntp_server.hpp" -#include "nntp_auth.hpp" -#include "nntp_handler.hpp" -#include "net.hpp" +#include +#include +#include +#include #include #include #include diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/sanitize.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/sanitize.cpp index ef9424c..aa79b61 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/sanitize.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/sanitize.cpp @@ -1,11 +1,19 @@ -#include "sanitize.hpp" +#include #include #include +#include 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) @@ -21,4 +29,11 @@ namespace nntpchan { 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; + } } diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/server.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/server.cpp index 7888305..bdcf2c5 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/server.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/server.cpp @@ -1,6 +1,6 @@ -#include "buffer.hpp" -#include "server.hpp" -#include "net.hpp" +#include +#include +#include #include #include diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/staticfile_frontend.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/staticfile_frontend.cpp index ecaece9..fb0b4ed 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/staticfile_frontend.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/staticfile_frontend.cpp @@ -1,8 +1,8 @@ -#include "staticfile_frontend.hpp" -#include "file_handle.hpp" -#include "sanitize.hpp" -#include "mime.hpp" -#include "sha1.hpp" +#include +#include +#include +#include +#include #include #include #include @@ -36,70 +36,89 @@ namespace nntpchan // read body - // render templates + std::map thread_args; + + auto findMsgidFunc = [](const std::pair & 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) { - std::map thread_args; - - auto findMsgidFunc = [](const std::pair & item) -> bool { - auto lower = ToLower(item.first); - return (lower == "message-id") || (lower == "messageid"); - }; - - auto msgid = std::find_if(header.begin(), header.end(), findMsgidFunc); - - std::string msgid_hash = sha1_hex(msgid->second); - - fs::path threadFilePath = m_OutDir / fs::path("thread-" + msgid_hash + ".html"); FileHandle_ptr out = OpenFile(threadFilePath, eWrite); if(!m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out)) { std::clog << "failed to write " << threadFilePath << std::endl; return; } + } + std::set newsgroups_list; + + auto findNewsgroupsFunc = [](const std::pair & item) -> bool + { + return ToLower(item.first) == "newsgroups"; + }; - std::set newsgroups_list; - - auto findNewsgroupsFunc = [](const std::pair & 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"; - }; - - auto group = std::find_if(header.begin(), header.end(), findNewsgroupsFunc); - if(group == std::end(header)) - { - std::clog << "no newsgroups header" << std::endl; - return; - } - std::istringstream input(group->second); - - std::string newsgroup; - while(std::getline(input, newsgroup, ' ')) - { - newsgroups_list.insert(NNTPSanitize(newsgroup)); - } - - for(const auto & name : newsgroups_list) - { - auto board = GetThreadsPaginated(name, 10, m_Pages); - uint32_t pageno = 0; - for(Threads_t threads : board) + std::map board_args; + board_args["group"] = std::make_any(name); + board_args["pageno"] = std::make_any(pageno); + board_args["threads"] = std::make_any(threads); + + fs::path boardPageFilename(newsgroup + "-" + std::to_string(pageno) + ".html"); + if(m_TemplateEngine) { - std::map board_args; - board_args["group"] = std::make_any(name); - board_args["pageno"] = std::make_any(pageno); - board_args["threads"] = std::make_any(threads); - - fs::path boardPageFilename(newsgroup + "-" + std::to_string(pageno) + ".html"); - out = OpenFile(m_OutDir / boardPageFilename, eWrite); + FileHandle_ptr out = OpenFile(m_OutDir / boardPageFilename, eWrite); 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) { diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/storage.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/storage.cpp index 7e8f927..117fcfa 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/storage.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/storage.cpp @@ -1,5 +1,5 @@ -#include "storage.hpp" -#include "sanitize.hpp" +#include +#include #include namespace nntpchan diff --git a/contrib/backends/nntpchan-daemon/libnntpchan/template_engine.cpp b/contrib/backends/nntpchan-daemon/libnntpchan/template_engine.cpp index f603e72..7c31cc1 100644 --- a/contrib/backends/nntpchan-daemon/libnntpchan/template_engine.cpp +++ b/contrib/backends/nntpchan-daemon/libnntpchan/template_engine.cpp @@ -1,5 +1,6 @@ -#include "template_engine.hpp" -#include "sanitize.hpp" +#include +#include +#include namespace nntpchan { @@ -8,19 +9,33 @@ namespace nntpchan { 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; } + + 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(); - 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; } }; diff --git a/contrib/backends/nntpchan-daemon/test.cpp b/contrib/backends/nntpchan-daemon/test.cpp index 777a695..2a04459 100644 --- a/contrib/backends/nntpchan-daemon/test.cpp +++ b/contrib/backends/nntpchan-daemon/test.cpp @@ -1,5 +1,5 @@ -#include "exec_frontend.hpp" -#include "sanitize.hpp" +#include +#include #include #include diff --git a/contrib/backends/nntpchan-daemon/tools/testtool.cpp b/contrib/backends/nntpchan-daemon/tools/testtool.cpp index e09d633..751c5c4 100644 --- a/contrib/backends/nntpchan-daemon/tools/testtool.cpp +++ b/contrib/backends/nntpchan-daemon/tools/testtool.cpp @@ -1,5 +1,5 @@ -#include "exec_frontend.hpp" -#include "sanitize.hpp" +#include +#include #include #include