Archived
1
0

clang format

This commit is contained in:
Jeff Becker 2017-10-17 10:29:56 -04:00
parent 85248787fc
commit 841c5c6afe
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05
69 changed files with 2381 additions and 2448 deletions

View File

@ -0,0 +1,14 @@
TabWidth: 2
UseTab: Never
ColumnLimit: 120
IndentWidth: 2
Language: Cpp
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
BeforeElse: true

View File

@ -31,14 +31,16 @@ 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$(HEADERS_PATH) INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I$(HEADERS_PATH)
CXXFLAGS := -std=c++17 -Wall -Wextra -Werror -pedantic $(INC_FLAGS) REQUIRED_CXXFLAGS = -std=c++17 -Wall -Wextra -Werror -pedantic $(INC_FLAGS)
DEBUG = 1 DEBUG = 1
ifeq ($(DEBUG),1) ifeq ($(DEBUG),1)
CXXFLAGS += -g REQUIRED_CXXFLAGS += -g
endif endif
CXXFLAGS += $(REQUIRED_CXXFLAGS)
NNTPCHAN_LIB = $(REPO)/libnntpchan.a NNTPCHAN_LIB = $(REPO)/libnntpchan.a
MUSTACHE_LIB = $(REPO)/libmustache.a MUSTACHE_LIB = $(REPO)/libmustache.a

View File

@ -26,20 +26,21 @@
#define INI_HPP #define INI_HPP
#include <cassert> #include <cassert>
#include <map> #include <cstring>
#include <fstream>
#include <iostream>
#include <list> #include <list>
#include <map>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <cstring>
#include <iostream>
#include <fstream>
namespace INI { namespace INI
{
struct Level struct Level
{ {
Level() : parent(NULL), depth(0) {} Level() : parent(NULL), depth(0) {}
Level(Level* p) : parent(p), depth(0) {} Level(Level *p) : parent(p), depth(0) {}
typedef std::map<std::string, std::string> value_map_t; typedef std::map<std::string, std::string> value_map_t;
typedef std::map<std::string, Level> section_map_t; typedef std::map<std::string, Level> section_map_t;
@ -49,57 +50,57 @@ struct Level
section_map_t sections; section_map_t sections;
values_t ordered_values; // original order in the ini file values_t ordered_values; // original order in the ini file
sections_t ordered_sections; sections_t ordered_sections;
Level* parent; Level *parent;
size_t depth; size_t depth;
const std::string& operator[](const std::string& name) { return values[name]; } const std::string &operator[](const std::string &name) { return values[name]; }
Level& operator()(const std::string& name) { return sections[name]; } Level &operator()(const std::string &name) { return sections[name]; }
}; };
class Parser class Parser
{ {
public: public:
Parser(const char* fn); Parser(const char *fn);
Parser(std::istream& f) : f_(&f), ln_(0) { parse(top_); } Parser(std::istream &f) : f_(&f), ln_(0) { parse(top_); }
Level& top() { return top_; } Level &top() { return top_; }
void dump(std::ostream& s) { dump(s, top(), ""); } void dump(std::ostream &s) { dump(s, top(), ""); }
private: private:
void dump(std::ostream& s, const Level& l, const std::string& sname); void dump(std::ostream &s, const Level &l, const std::string &sname);
void parse(Level& l); void parse(Level &l);
void parseSLine(std::string& sname, size_t& depth); void parseSLine(std::string &sname, size_t &depth);
void err(const char* s); void err(const char *s);
private: private:
Level top_; Level top_;
std::ifstream f0_; std::ifstream f0_;
std::istream* f_; std::istream *f_;
std::string line_; std::string line_;
size_t ln_; size_t ln_;
}; };
inline void inline void Parser::err(const char *s)
Parser::err(const char* s)
{ {
char buf[256]; char buf[256];
sprintf(buf, "%s on line #%ld", s, ln_); sprintf(buf, "%s on line #%ld", s, ln_);
throw std::runtime_error(buf); throw std::runtime_error(buf);
} }
inline std::string trim(const std::string& s) inline std::string trim(const std::string &s)
{ {
char p[] = " \t\r\n"; char p[] = " \t\r\n";
long sp = 0; long sp = 0;
long ep = s.length() - 1; long ep = s.length() - 1;
for (; sp <= ep; ++sp) for (; sp <= ep; ++sp)
if (!strchr(p, s[sp])) break; if (!strchr(p, s[sp]))
break;
for (; ep >= 0; --ep) for (; ep >= 0; --ep)
if (!strchr(p, s[ep])) break; if (!strchr(p, s[ep]))
return s.substr(sp, ep-sp+1); break;
return s.substr(sp, ep - sp + 1);
} }
inline inline Parser::Parser(const char *fn) : f0_(fn), f_(&f0_), ln_(0)
Parser::Parser(const char* fn) : f0_(fn), f_(&f0_), ln_(0)
{ {
if (!f0_) if (!f0_)
throw std::runtime_error(std::string("failed to open file: ") + fn); throw std::runtime_error(std::string("failed to open file: ") + fn);
@ -107,56 +108,63 @@ Parser::Parser(const char* fn) : f0_(fn), f_(&f0_), ln_(0)
parse(top_); parse(top_);
} }
inline void inline void Parser::parseSLine(std::string &sname, size_t &depth)
Parser::parseSLine(std::string& sname, size_t& depth)
{ {
depth = 0; depth = 0;
for (; depth < line_.length(); ++depth) for (; depth < line_.length(); ++depth)
if (line_[depth] != '[') break; if (line_[depth] != '[')
break;
sname = line_.substr(depth, line_.length() - 2*depth); sname = line_.substr(depth, line_.length() - 2 * depth);
} }
inline void inline void Parser::parse(Level &l)
Parser::parse(Level& l)
{ {
while (std::getline(*f_, line_)) { while (std::getline(*f_, line_))
{
++ln_; ++ln_;
if (line_[0] == '#' || line_[0] == ';') continue; if (line_[0] == '#' || line_[0] == ';')
continue;
line_ = trim(line_); line_ = trim(line_);
if (line_.empty()) continue; if (line_.empty())
if (line_[0] == '[') { continue;
if (line_[0] == '[')
{
size_t depth; size_t depth;
std::string sname; std::string sname;
parseSLine(sname, depth); parseSLine(sname, depth);
Level* lp = NULL; Level *lp = NULL;
Level* parent = &l; Level *parent = &l;
if (depth > l.depth + 1) if (depth > l.depth + 1)
err("section with wrong depth"); err("section with wrong depth");
if (l.depth == depth-1) if (l.depth == depth - 1)
lp = &l.sections[sname]; lp = &l.sections[sname];
else { else
{
lp = l.parent; lp = l.parent;
size_t n = l.depth - depth; size_t n = l.depth - depth;
for (size_t i = 0; i < n; ++i) lp = lp->parent; for (size_t i = 0; i < n; ++i)
lp = lp->parent;
parent = lp; parent = lp;
lp = &lp->sections[sname]; lp = &lp->sections[sname];
} }
if (lp->depth != 0) if (lp->depth != 0)
err("duplicate section name on the same level"); err("duplicate section name on the same level");
if (!lp->parent) { if (!lp->parent)
{
lp->depth = depth; lp->depth = depth;
lp->parent = parent; lp->parent = parent;
} }
parent->ordered_sections.push_back(parent->sections.find(sname)); parent->ordered_sections.push_back(parent->sections.find(sname));
parse(*lp); parse(*lp);
} else { }
else
{
size_t n = line_.find('='); size_t n = line_.find('=');
if (n == std::string::npos) if (n == std::string::npos)
err("no '=' found"); err("no '=' found");
std::pair<Level::value_map_t::const_iterator, bool> res = std::pair<Level::value_map_t::const_iterator, bool> res =
l.values.insert(std::make_pair(trim(line_.substr(0, n)), l.values.insert(std::make_pair(trim(line_.substr(0, n)), trim(line_.substr(n + 1, line_.length() - n - 1))));
trim(line_.substr(n+1, line_.length()-n-1))));
if (!res.second) if (!res.second)
err("duplicated key found"); err("duplicated key found");
l.ordered_values.push_back(res.first); l.ordered_values.push_back(res.first);
@ -164,23 +172,26 @@ Parser::parse(Level& l)
} }
} }
inline void inline void Parser::dump(std::ostream &s, const Level &l, const std::string &sname)
Parser::dump(std::ostream& s, const Level& l, const std::string& sname)
{ {
if (!sname.empty()) s << '\n'; if (!sname.empty())
for (size_t i = 0; i < l.depth; ++i) s << '['; s << '\n';
if (!sname.empty()) s << sname; for (size_t i = 0; i < l.depth; ++i)
for (size_t i = 0; i < l.depth; ++i) s << ']'; s << '[';
if (!sname.empty()) s << std::endl; if (!sname.empty())
s << sname;
for (size_t i = 0; i < l.depth; ++i)
s << ']';
if (!sname.empty())
s << std::endl;
for (Level::values_t::const_iterator it = l.ordered_values.begin(); it != l.ordered_values.end(); ++it) for (Level::values_t::const_iterator it = l.ordered_values.begin(); it != l.ordered_values.end(); ++it)
s << (*it)->first << '=' << (*it)->second << std::endl; s << (*it)->first << '=' << (*it)->second << std::endl;
for (Level::sections_t::const_iterator it = l.ordered_sections.begin(); it != l.ordered_sections.end(); ++it) { for (Level::sections_t::const_iterator it = l.ordered_sections.begin(); it != l.ordered_sections.end(); ++it)
assert((*it)->second.depth == l.depth+1); {
assert((*it)->second.depth == l.depth + 1);
dump(s, (*it)->second, (*it)->first); dump(s, (*it)->second, (*it)->first);
} }
} }
} }
#endif // INI_HPP #endif // INI_HPP

View File

@ -1,18 +1,19 @@
#include "ini.hpp" #include "ini.hpp"
#include <nntpchan/crypto.hpp> #include <nntpchan/crypto.hpp>
#include <nntpchan/storage.hpp>
#include <nntpchan/nntp_server.hpp>
#include <nntpchan/event.hpp> #include <nntpchan/event.hpp>
#include <nntpchan/exec_frontend.hpp> #include <nntpchan/exec_frontend.hpp>
#include <nntpchan/nntp_server.hpp>
#include <nntpchan/staticfile_frontend.hpp> #include <nntpchan/staticfile_frontend.hpp>
#include <nntpchan/storage.hpp>
#include <vector>
#include <string> #include <string>
#include <vector>
int main(int argc, char *argv[])
int main(int argc, char * argv[]) { {
if (argc != 2) { if (argc != 2)
{
std::cerr << "usage: " << argv[0] << " config.ini" << std::endl; std::cerr << "usage: " << argv[0] << " config.ini" << std::endl;
return 1; return 1;
} }
@ -23,57 +24,63 @@ int main(int argc, char * argv[]) {
nntpchan::NNTPServer nntp(loop); nntpchan::NNTPServer nntp(loop);
std::string fname(argv[1]); std::string fname(argv[1]);
std::ifstream i(fname); std::ifstream i(fname);
if(i.is_open()) { if (i.is_open())
{
INI::Parser conf(i); INI::Parser conf(i);
std::vector<std::string> requiredSections = {"nntp", "articles"}; std::vector<std::string> requiredSections = {"nntp", "articles"};
auto & level = conf.top(); auto &level = conf.top();
for ( const auto & section : requiredSections ) { for (const auto &section : requiredSections)
if(level.sections.find(section) == level.sections.end()) { {
if (level.sections.find(section) == level.sections.end())
{
std::cerr << "config file " << fname << " does not have required section: "; std::cerr << "config file " << fname << " does not have required section: ";
std::cerr << section << std::endl; std::cerr << section << std::endl;
return 1; return 1;
} }
} }
auto & storeconf = level.sections["articles"].values; auto &storeconf = level.sections["articles"].values;
if (storeconf.find("store_path") == storeconf.end()) { if (storeconf.find("store_path") == storeconf.end())
{
std::cerr << "storage section does not have 'store_path' value" << std::endl; std::cerr << "storage section does not have 'store_path' value" << std::endl;
return 1; return 1;
} }
nntp.SetStoragePath(storeconf["store_path"]); nntp.SetStoragePath(storeconf["store_path"]);
auto & nntpconf = level.sections["nntp"].values; auto &nntpconf = level.sections["nntp"].values;
if (nntpconf.find("bind") == nntpconf.end()) { if (nntpconf.find("bind") == nntpconf.end())
{
std::cerr << "nntp section does not have 'bind' value" << std::endl; std::cerr << "nntp section does not have 'bind' value" << std::endl;
return 1; return 1;
} }
if(nntpconf.find("instance_name") == nntpconf.end()) { if (nntpconf.find("instance_name") == nntpconf.end())
{
std::cerr << "nntp section lacks 'instance_name' value" << std::endl; std::cerr << "nntp section lacks 'instance_name' value" << std::endl;
return 1; return 1;
} }
nntp.SetInstanceName(nntpconf["instance_name"]); nntp.SetInstanceName(nntpconf["instance_name"]);
if (nntpconf.find("authdb") != nntpconf.end()) { if (nntpconf.find("authdb") != nntpconf.end())
{
nntp.SetLoginDB(nntpconf["authdb"]); nntp.SetLoginDB(nntpconf["authdb"]);
} }
if ( level.sections.find("frontend") != level.sections.end()) if (level.sections.find("frontend") != level.sections.end())
{ {
// frontend enabled // frontend enabled
auto & frontconf = level.sections["frontend"].values; auto &frontconf = level.sections["frontend"].values;
if (frontconf.find("type") == frontconf.end()) if (frontconf.find("type") == frontconf.end())
{ {
std::cerr << "frontend section provided but 'type' value not provided" << std::endl; std::cerr << "frontend section provided but 'type' value not provided" << std::endl;
@ -91,38 +98,38 @@ int main(int argc, char * argv[]) {
} }
else if (ftype == "staticfile") else if (ftype == "staticfile")
{ {
auto required = { auto required = {"template_dir", "out_dir", "template_dialect", "max_pages"};
"template_dir", "out_dir", "template_dialect", "max_pages" for (const auto &opt : required)
};
for (const auto & opt : required)
{ {
if(frontconf.find(opt) == frontconf.end()) if (frontconf.find(opt) == frontconf.end())
{ {
std::cerr << "staticfile frontend specified but no '" << opt << "' value provided" << std::endl; std::cerr << "staticfile frontend specified but no '" << opt << "' value provided" << std::endl;
return 1; return 1;
} }
} }
auto maxPages = std::stoi(frontconf["max_pages"]); auto maxPages = std::stoi(frontconf["max_pages"]);
if(maxPages <= 0) if (maxPages <= 0)
{ {
std::cerr << "max_pages invalid value '" << frontconf["max_pages"] << "'" << std::endl; std::cerr << "max_pages invalid value '" << frontconf["max_pages"] << "'" << std::endl;
return 1; return 1;
} }
nntp.SetFrontend(new nntpchan::StaticFileFrontend(nntpchan::CreateTemplateEngine(frontconf["template_dialect"]), frontconf["template_dir"], frontconf["out_dir"], maxPages)); nntp.SetFrontend(new nntpchan::StaticFileFrontend(nntpchan::CreateTemplateEngine(frontconf["template_dialect"]),
frontconf["template_dir"], frontconf["out_dir"], maxPages));
} }
else else
{ {
std::cerr << "unknown frontend type '" << ftype << "'" << std::endl; std::cerr << "unknown frontend type '" << ftype << "'" << std::endl;
return 1; return 1;
} }
} }
auto & a = nntpconf["bind"]; auto &a = nntpconf["bind"];
try { try
{
nntp.Bind(a); nntp.Bind(a);
} catch ( std::exception & ex ) { } catch (std::exception &ex)
{
std::cerr << "failed to bind: " << ex.what() << std::endl; std::cerr << "failed to bind: " << ex.what() << std::endl;
return 1; return 1;
} }
@ -130,11 +137,10 @@ int main(int argc, char * argv[]) {
std::cerr << "nntpd for " << nntp.InstanceName() << " bound to " << a << std::endl; std::cerr << "nntpd for " << nntp.InstanceName() << " bound to " << a << std::endl;
loop.Run(); loop.Run();
}
} else { else
{
std::cerr << "failed to open " << fname << std::endl; std::cerr << "failed to open " << fname << std::endl;
return 1; return 1;
} }
} }

View File

@ -5,13 +5,11 @@
namespace nntpchan namespace nntpchan
{ {
/** returns base64 encoded string */ /** returns base64 encoded string */
std::string B64Encode(const uint8_t * data, const std::size_t l); std::string B64Encode(const uint8_t *data, const std::size_t l);
/** @brief returns true if decode was successful */
bool B64Decode(const std::string & data, std::vector<uint8_t> & out);
/** @brief returns true if decode was successful */
bool B64Decode(const std::string &data, std::vector<uint8_t> &out);
} }
#endif #endif

View File

@ -1,19 +1,19 @@
#ifndef NNTPCHAN_BUFFER_HPP #ifndef NNTPCHAN_BUFFER_HPP
#define NNTPCHAN_BUFFER_HPP #define NNTPCHAN_BUFFER_HPP
#include <uv.h>
#include <string> #include <string>
#include <uv.h>
namespace nntpchan namespace nntpchan
{ {
struct WriteBuffer struct WriteBuffer
{ {
uv_write_t w; uv_write_t w;
uv_buf_t b; uv_buf_t b;
WriteBuffer(const std::string & s); WriteBuffer(const std::string &s);
WriteBuffer(const char * b, const size_t s); WriteBuffer(const char *b, const size_t s);
~WriteBuffer(); ~WriteBuffer();
}; };
} }
#endif #endif

View File

@ -1,22 +1,21 @@
#ifndef NNTPCHAN_CRYPTO_HPP #ifndef NNTPCHAN_CRYPTO_HPP
#define NNTPCHAN_CRYPTO_HPP #define NNTPCHAN_CRYPTO_HPP
#include <sodium/crypto_hash.h>
#include <array> #include <array>
#include <sodium/crypto_hash.h>
namespace nntpchan namespace nntpchan
{ {
typedef std::array<uint8_t, crypto_hash_BYTES> SHA512Digest; typedef std::array<uint8_t, crypto_hash_BYTES> SHA512Digest;
void SHA512(const uint8_t * d, std::size_t l, SHA512Digest & h); void SHA512(const uint8_t *d, std::size_t l, SHA512Digest &h);
/** global crypto initializer */ /** global crypto initializer */
struct Crypto struct Crypto
{ {
Crypto(); Crypto();
~Crypto(); ~Crypto();
}; };
} }
#endif #endif

View File

@ -4,23 +4,20 @@
namespace nntpchan namespace nntpchan
{ {
class Mainloop class Mainloop
{ {
public: public:
Mainloop(); Mainloop();
~Mainloop(); ~Mainloop();
operator uv_loop_t * () const { return m_loop; } operator uv_loop_t *() const { return m_loop; }
void Run(uv_run_mode mode = UV_RUN_DEFAULT); void Run(uv_run_mode mode = UV_RUN_DEFAULT);
void Stop(); void Stop();
private: private:
uv_loop_t *m_loop;
uv_loop_t * m_loop; };
};
} }
#endif #endif

View File

@ -5,26 +5,23 @@
namespace nntpchan namespace nntpchan
{ {
class ExecFrontend : public Frontend class ExecFrontend : public Frontend
{ {
public: public:
ExecFrontend(const std::string &exe);
ExecFrontend(const std::string & exe);
~ExecFrontend(); ~ExecFrontend();
void ProcessNewMessage(const fs::path & fpath); void ProcessNewMessage(const fs::path &fpath);
bool AcceptsNewsgroup(const std::string & newsgroup); bool AcceptsNewsgroup(const std::string &newsgroup);
bool AcceptsMessage(const std::string & msgid); bool AcceptsMessage(const std::string &msgid);
private:
private:
int Exec(std::deque<std::string> args); int Exec(std::deque<std::string> args);
private: private:
std::string m_exec; std::string m_exec;
};
};
} }
#endif #endif

View File

@ -1,23 +1,23 @@
#ifndef NNTPCHAN_FILE_HANDLE_HPP #ifndef NNTPCHAN_FILE_HANDLE_HPP
#define NNTPCHAN_FILE_HANDLE_HPP #define NNTPCHAN_FILE_HANDLE_HPP
#include <memory>
#include <fstream>
#include <experimental/filesystem> #include <experimental/filesystem>
#include <fstream>
#include <memory>
namespace nntpchan namespace nntpchan
{ {
typedef std::unique_ptr<std::fstream> FileHandle_ptr; typedef std::unique_ptr<std::fstream> FileHandle_ptr;
enum FileMode enum FileMode
{ {
eRead, eRead,
eWrite eWrite
}; };
namespace fs = std::experimental::filesystem; namespace fs = std::experimental::filesystem;
FileHandle_ptr OpenFile(const fs::path & fname, FileMode mode); FileHandle_ptr OpenFile(const fs::path &fname, FileMode mode);
} }
#endif #endif

View File

@ -1,29 +1,27 @@
#ifndef NNTPCHAN_FRONTEND_HPP #ifndef NNTPCHAN_FRONTEND_HPP
#define NNTPCHAN_FRONTEND_HPP #define NNTPCHAN_FRONTEND_HPP
#include <string>
#include <memory>
#include <experimental/filesystem> #include <experimental/filesystem>
#include <memory>
#include <string>
namespace nntpchan namespace nntpchan
{ {
namespace fs = std::experimental::filesystem; namespace fs = std::experimental::filesystem;
/** @brief nntpchan frontend ui interface */ /** @brief nntpchan frontend ui interface */
class Frontend class Frontend
{ {
public: public:
/** @brief process an inbound message stored at fpath that we have accepted. */ /** @brief process an inbound message stored at fpath that we have accepted. */
virtual void ProcessNewMessage(const fs::path & fpath) = 0; virtual void ProcessNewMessage(const fs::path &fpath) = 0;
/** @brief return true if we take posts in a newsgroup */ /** @brief return true if we take posts in a newsgroup */
virtual bool AcceptsNewsgroup(const std::string & newsgroup) = 0; virtual bool AcceptsNewsgroup(const std::string &newsgroup) = 0;
/** @brief return true if we will accept a message given its message-id */ /** @brief return true if we will accept a message given its message-id */
virtual bool AcceptsMessage(const std::string & msgid) = 0; virtual bool AcceptsMessage(const std::string &msgid) = 0;
};
}; typedef std::unique_ptr<Frontend> Frontend_ptr;
typedef std::unique_ptr<Frontend> Frontend_ptr;
} }
#endif #endif

View File

@ -1,6 +1,4 @@
#ifndef NNTPCHAN_HTTP_HPP #ifndef NNTPCHAN_HTTP_HPP
#define NNTPCHAN_HTTP_HPP #define NNTPCHAN_HTTP_HPP
#endif #endif

View File

@ -1,5 +1,4 @@
#ifndef NNTPCHAN_HTTP_CLIENT_HPP #ifndef NNTPCHAN_HTTP_CLIENT_HPP
#define NNTPCHAN_HTTP_CLIENT_HPP #define NNTPCHAN_HTTP_CLIENT_HPP
#endif #endif

View File

@ -1,6 +1,4 @@
#ifndef NNTPCHAN_HTTP_SERVER_HPP #ifndef NNTPCHAN_HTTP_SERVER_HPP
#define NNTPCHAN_HTTP_SERVER_HPP #define NNTPCHAN_HTTP_SERVER_HPP
#endif #endif

View File

@ -1,12 +1,11 @@
#ifndef NNTPCHAN_IO_HANDLE_HPP #ifndef NNTPCHAN_IO_HANDLE_HPP
#define NNTPCHAN_IO_HANDLE_HPP #define NNTPCHAN_IO_HANDLE_HPP
#include <memory>
#include <iostream> #include <iostream>
#include <memory>
namespace nntpchan namespace nntpchan
{ {
typedef std::unique_ptr<std::iostream> IOHandle_ptr; typedef std::unique_ptr<std::iostream> IOHandle_ptr;
} }
#endif #endif

View File

@ -5,30 +5,28 @@
namespace nntpchan namespace nntpchan
{ {
/** @brief a buffered line reader */ /** @brief a buffered line reader */
class LineReader class LineReader
{ {
public: public:
LineReader(size_t lineLimit); LineReader(size_t lineLimit);
/** @brief queue inbound data from connection */ /** @brief queue inbound data from connection */
void Data(const char * data, ssize_t s); void Data(const char *data, ssize_t s);
/** implements IConnHandler */ /** implements IConnHandler */
virtual bool ShouldClose(); virtual bool ShouldClose();
protected: protected:
/** @brief handle a line from the client */ /** @brief handle a line from the client */
virtual void HandleLine(const std::string & line) = 0; virtual void HandleLine(const std::string &line) = 0;
private:
private: void OnLine(const char *d, const size_t l);
void OnLine(const char * d, const size_t l);
std::string m_leftovers; std::string m_leftovers;
bool m_close; bool m_close;
const size_t lineLimit; const size_t lineLimit;
}; };
} }
#endif #endif

View File

@ -6,16 +6,16 @@
namespace nntpchan namespace nntpchan
{ {
struct MessageDB struct MessageDB
{ {
using BoardPage = nntpchan::model::BoardPage; using BoardPage = nntpchan::model::BoardPage;
using Thread = nntpchan::model::Thread; using Thread = nntpchan::model::Thread;
virtual bool LoadBoardPage(BoardPage & board, const std::string & newsgroup, uint32_t perpage, uint32_t page) const = 0; virtual bool LoadBoardPage(BoardPage &board, const std::string &newsgroup, uint32_t perpage, uint32_t page) const = 0;
virtual bool FindThreadByHash(const std::string & hashhex, std::string & msgid) const = 0; virtual bool FindThreadByHash(const std::string &hashhex, std::string &msgid) const = 0;
virtual bool LoadThread(Thread & thread, const std::string & rootmsgid) const = 0; virtual bool LoadThread(Thread &thread, const std::string &rootmsgid) const = 0;
}; };
typedef std::unique_ptr<MessageDB> MessageDB_ptr; typedef std::unique_ptr<MessageDB> MessageDB_ptr;
} }
#endif #endif

View File

@ -9,22 +9,21 @@
namespace nntpchan namespace nntpchan
{ {
typedef std::map<std::string, std::string> RawHeader; typedef std::map<std::string, std::string> RawHeader;
bool ReadHeader(const FileHandle_ptr & f, RawHeader & h); bool ReadHeader(const FileHandle_ptr &f, RawHeader &h);
struct MimePart
struct MimePart {
{ virtual RawHeader &Header() = 0;
virtual RawHeader & Header() = 0;
virtual IOHandle_ptr OpenPart() = 0; virtual IOHandle_ptr OpenPart() = 0;
}; };
typedef std::unique_ptr<MimePart> MimePart_ptr; typedef std::unique_ptr<MimePart> MimePart_ptr;
typedef std::function<bool(MimePart_ptr)> PartReader; typedef std::function<bool(MimePart_ptr)> PartReader;
bool ReadParts(const FileHandle_ptr & f, PartReader r); bool ReadParts(const FileHandle_ptr &f, PartReader r);
} }
#endif #endif

View File

@ -2,74 +2,58 @@
#define NNTPCHAN_MODEL_HPP #define NNTPCHAN_MODEL_HPP
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <nntpchan/sanitize.hpp>
#include <set> #include <set>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <vector>
#include <variant> #include <variant>
#include <nntpchan/sanitize.hpp> #include <vector>
namespace nntpchan namespace nntpchan
{ {
namespace model namespace model
{ {
// MIME Header // MIME Header
typedef std::map<std::string, std::vector<std::string> > PostHeader; typedef std::map<std::string, std::vector<std::string>> PostHeader;
// text post contents // text post contents
typedef std::string PostBody; typedef std::string PostBody;
// single file attachment, (orig_filename, hexdigest, thumb_filename) // single file attachment, (orig_filename, hexdigest, thumb_filename)
typedef std::tuple<std::string, std::string, std::string> PostAttachment; typedef std::tuple<std::string, std::string, std::string> PostAttachment;
// all attachments on a post // all attachments on a post
typedef std::vector<PostAttachment> Attachments; typedef std::vector<PostAttachment> Attachments;
// a post (header, Post Text, Attachments) // a post (header, Post Text, Attachments)
typedef std::tuple<PostHeader, PostBody, Attachments> Post; typedef std::tuple<PostHeader, PostBody, Attachments> Post;
// a thread (many posts in post order) // a thread (many posts in post order)
typedef std::vector<Post> Thread; typedef std::vector<Post> Thread;
// a board page is many threads in bump order // a board page is many threads in bump order
typedef std::vector<Thread> BoardPage; typedef std::vector<Thread> BoardPage;
static inline const std::string & GetFilename(const PostAttachment & att) static inline const std::string &GetFilename(const PostAttachment &att) { return std::get<0>(att); }
{
return std::get<0>(att);
}
static inline const std::string & GetHexDigest(const PostAttachment & att) static inline const std::string &GetHexDigest(const PostAttachment &att) { return std::get<1>(att); }
{
return std::get<1>(att);
}
static inline const std::string & GetThumbnail(const PostAttachment & att) static inline const std::string &GetThumbnail(const PostAttachment &att) { return std::get<2>(att); }
{
return std::get<2>(att);
}
static inline const PostHeader & GetHeader(const Post & post) static inline const PostHeader &GetHeader(const Post &post) { return std::get<0>(post); }
{
return std::get<0>(post);
}
static inline const PostBody & GetBody(const Post & post) static inline const PostBody &GetBody(const Post &post) { return std::get<1>(post); }
{
return std::get<1>(post);
}
static inline const Attachments & GetAttachments(const Post & post) static inline const Attachments &GetAttachments(const Post &post) { return std::get<2>(post); }
{
return std::get<2>(post);
}
static inline const std::string & HeaderIFind(const PostHeader & header, const std::string & val, const std::string & fallback) static inline const std::string &HeaderIFind(const PostHeader &header, const std::string &val,
{ const std::string &fallback)
{
std::string ival = ToLower(val); std::string ival = ToLower(val);
auto itr = std::find_if(header.begin(), header.end(), [ival](const auto & item) -> bool { return ToLower(item.first) == ival; }); auto itr = std::find_if(header.begin(), header.end(),
[ival](const auto &item) -> bool { return ToLower(item.first) == ival; });
if (itr == std::end(header)) if (itr == std::end(header))
return fallback; return fallback;
else else
return itr->second[0]; return itr->second[0];
} }
using Model = std::variant<Thread, BoardPage>; using Model = std::variant<Thread, BoardPage>;
} }
} }
#endif #endif

View File

@ -1,23 +1,23 @@
#ifndef NNTPCHAN_NET_HPP #ifndef NNTPCHAN_NET_HPP
#define NNTPCHAN_NET_HPP #define NNTPCHAN_NET_HPP
#include <sys/types.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <string> #include <string>
#include <sys/types.h>
namespace nntpchan namespace nntpchan
{ {
struct NetAddr struct NetAddr
{ {
NetAddr(); NetAddr();
sockaddr_in6 addr; sockaddr_in6 addr;
operator sockaddr * () { return (sockaddr *) &addr; } operator sockaddr *() { return (sockaddr *)&addr; }
operator const sockaddr * () const { return (sockaddr *) &addr; } operator const sockaddr *() const { return (sockaddr *)&addr; }
std::string to_string(); std::string to_string();
}; };
NetAddr ParseAddr(const std::string & addr); NetAddr ParseAddr(const std::string &addr);
} }
#endif #endif

View File

@ -1,61 +1,64 @@
#ifndef NNTPCHAN_NNTP_AUTH_HPP #ifndef NNTPCHAN_NNTP_AUTH_HPP
#define NNTPCHAN_NNTP_AUTH_HPP #define NNTPCHAN_NNTP_AUTH_HPP
#include <string>
#include <iostream>
#include <fstream>
#include <mutex>
#include <memory>
#include "line.hpp" #include "line.hpp"
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <string>
namespace nntpchan namespace nntpchan
{ {
/** @brief nntp credential db interface */ /** @brief nntp credential db interface */
class NNTPCredentialDB class NNTPCredentialDB
{ {
public: public:
/** @brief open connection to database, return false on error otherwise return true */ /** @brief open connection to database, return false on error otherwise return true */
virtual bool Open() = 0; virtual bool Open() = 0;
/** @brief close connection to database */ /** @brief close connection to database */
virtual void Close() = 0; virtual void Close() = 0;
/** @brief return true if username password combo is correct */ /** @brief return true if username password combo is correct */
virtual bool CheckLogin(const std::string & user, const std::string & passwd) = 0; virtual bool CheckLogin(const std::string &user, const std::string &passwd) = 0;
virtual ~NNTPCredentialDB() {} virtual ~NNTPCredentialDB() {}
}; };
typedef std::shared_ptr<NNTPCredentialDB> CredDB_ptr; typedef std::shared_ptr<NNTPCredentialDB> CredDB_ptr;
/** @brief nntp credential db using hashed+salted passwords */ /** @brief nntp credential db using hashed+salted passwords */
class HashedCredDB : public NNTPCredentialDB, public LineReader class HashedCredDB : public NNTPCredentialDB, public LineReader
{ {
public: public:
HashedCredDB(); HashedCredDB();
bool CheckLogin(const std::string & user, const std::string & passwd); bool CheckLogin(const std::string &user, const std::string &passwd);
protected:
void SetStream(std::istream * i);
std::string Hash(const std::string & data, const std::string & salt); protected:
void HandleLine(const std::string & line); void SetStream(std::istream *i);
private:
bool ProcessLine(const std::string & line); std::string Hash(const std::string &data, const std::string &salt);
void HandleLine(const std::string &line);
private:
bool ProcessLine(const std::string &line);
std::mutex m_access; std::mutex m_access;
std::string m_user, m_passwd; std::string m_user, m_passwd;
bool m_found; bool m_found;
/** return true if we have a line that matches this username / password combo */ /** return true if we have a line that matches this username / password combo */
std::istream * m_instream; std::istream *m_instream;
}; };
class HashedFileDB : public HashedCredDB class HashedFileDB : public HashedCredDB
{ {
public: public:
HashedFileDB(const std::string & fname); HashedFileDB(const std::string &fname);
~HashedFileDB(); ~HashedFileDB();
bool Open(); bool Open();
void Close(); void Close();
private:
private:
std::string m_fname; std::string m_fname;
std::ifstream f; std::ifstream f;
}; };
} }
#endif #endif

View File

@ -1,17 +1,17 @@
#ifndef NNTPCHAN_NNTP_HANDLER_HPP #ifndef NNTPCHAN_NNTP_HANDLER_HPP
#define NNTPCHAN_NNTP_HANDLER_HPP #define NNTPCHAN_NNTP_HANDLER_HPP
#include <deque>
#include <string>
#include "line.hpp" #include "line.hpp"
#include "nntp_auth.hpp" #include "nntp_auth.hpp"
#include "storage.hpp" #include "storage.hpp"
#include <deque>
#include <string>
namespace nntpchan namespace nntpchan
{ {
class NNTPServerHandler : public LineReader, public IConnHandler class NNTPServerHandler : public LineReader, public IConnHandler
{ {
public: public:
NNTPServerHandler(const fs::path & storage); NNTPServerHandler(const fs::path &storage);
~NNTPServerHandler(); ~NNTPServerHandler();
virtual bool ShouldClose(); virtual bool ShouldClose();
@ -22,19 +22,19 @@ namespace nntpchan
void Greet(); void Greet();
protected: protected:
void HandleLine(const std::string & line); void HandleLine(const std::string &line);
void HandleCommand(const std::deque<std::string> & command); void HandleCommand(const std::deque<std::string> &command);
private:
enum State { private:
enum State
{
eStateReadCommand, eStateReadCommand,
eStateStoreArticle, eStateStoreArticle,
eStateQuit eStateQuit
}; };
private: private:
void EnterState(State st); void EnterState(State st);
void ArticleObtained(); void ArticleObtained();
@ -43,11 +43,11 @@ namespace nntpchan
void Quit(); void Quit();
// switch nntp modes, this queues a reply // switch nntp modes, this queues a reply
void SwitchMode(const std::string & mode); void SwitchMode(const std::string &mode);
bool PostingAllowed(); bool PostingAllowed();
private: private:
std::string m_articleName; std::string m_articleName;
FileHandle_ptr m_article; FileHandle_ptr m_article;
CredDB_ptr m_auth; CredDB_ptr m_auth;
@ -55,8 +55,7 @@ namespace nntpchan
std::string m_mode; std::string m_mode;
bool m_authed; bool m_authed;
State m_state; State m_state;
}; };
} }
#endif #endif

View File

@ -1,53 +1,49 @@
#ifndef NNTPCHAN_NNTP_SERVER_HPP #ifndef NNTPCHAN_NNTP_SERVER_HPP
#define NNTPCHAN_NNTP_SERVER_HPP #define NNTPCHAN_NNTP_SERVER_HPP
#include <uv.h>
#include <string>
#include <deque>
#include "frontend.hpp" #include "frontend.hpp"
#include "server.hpp" #include "server.hpp"
#include <deque>
#include <string>
#include <uv.h>
namespace nntpchan namespace nntpchan
{ {
class NNTPServer : public Server class NNTPServer : public Server
{ {
public: public:
NNTPServer(uv_loop_t *loop);
NNTPServer(uv_loop_t * loop);
virtual ~NNTPServer(); virtual ~NNTPServer();
void SetStoragePath(const std::string & path); void SetStoragePath(const std::string &path);
void SetLoginDB(const std::string path); void SetLoginDB(const std::string path);
void SetInstanceName(const std::string & name); void SetInstanceName(const std::string &name);
std::string InstanceName() const; std::string InstanceName() const;
void Close(); void Close();
virtual IServerConn * CreateConn(uv_stream_t * s); virtual IServerConn *CreateConn(uv_stream_t *s);
virtual void OnAcceptError(int status); virtual void OnAcceptError(int status);
void SetFrontend(Frontend * f); void SetFrontend(Frontend *f);
private:
private:
std::string m_logindbpath; std::string m_logindbpath;
std::string m_storagePath; std::string m_storagePath;
std::string m_servername; std::string m_servername;
Frontend_ptr m_frontend; Frontend_ptr m_frontend;
};
}; class NNTPServerConn : public IServerConn
{
class NNTPServerConn : public IServerConn public:
{ NNTPServerConn(uv_loop_t *l, uv_stream_t *s, Server *parent, IConnHandler *h) : IServerConn(l, s, parent, h) {}
public:
NNTPServerConn(uv_loop_t * l, uv_stream_t * s, Server * parent, IConnHandler * h) : IServerConn(l, s, parent, h) {}
virtual bool IsTimedOut() { return false; }; virtual bool IsTimedOut() { return false; };
@ -55,8 +51,7 @@ namespace nntpchan
virtual void SendNextReply(); virtual void SendNextReply();
virtual void Greet(); virtual void Greet();
};
};
} }
#endif #endif

View File

@ -4,11 +4,11 @@
namespace nntpchan namespace nntpchan
{ {
std::string NNTPSanitizeLine(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);
std::string StripWhitespaces(const std::string & str); std::string StripWhitespaces(const std::string &str);
bool IsValidMessageID(const std::string & msgid); bool IsValidMessageID(const std::string &msgid);
bool IsValidNewsgroup(const std::string & group); bool IsValidNewsgroup(const std::string &group);
} }
#endif #endif

View File

@ -1,23 +1,22 @@
#ifndef NNTPCHAN_SERVER_HPP #ifndef NNTPCHAN_SERVER_HPP
#define NNTPCHAN_SERVER_HPP #define NNTPCHAN_SERVER_HPP
#include <uv.h>
#include <deque> #include <deque>
#include <functional> #include <functional>
#include <string> #include <string>
#include <uv.h>
namespace nntpchan namespace nntpchan
{ {
class Server; class Server;
struct IConnHandler
{
struct IConnHandler virtual ~IConnHandler(){};
{
virtual ~IConnHandler() {};
/** got inbound data */ /** got inbound data */
virtual void OnData(const char * data, ssize_t s) = 0; virtual void OnData(const char *data, ssize_t s) = 0;
/** get next line of data to send */ /** get next line of data to send */
std::string GetNextLine(); std::string GetNextLine();
@ -29,47 +28,47 @@ namespace nntpchan
virtual bool ShouldClose() = 0; virtual bool ShouldClose() = 0;
/** queue a data send */ /** queue a data send */
void QueueLine(const std::string & line); void QueueLine(const std::string &line);
virtual void Greet() = 0; virtual void Greet() = 0;
private:
private:
std::deque<std::string> m_sendlines; std::deque<std::string> m_sendlines;
}; };
/** server connection handler interface */ /** server connection handler interface */
struct IServerConn struct IServerConn
{ {
IServerConn(uv_loop_t * l, uv_stream_t * s, Server * parent, IConnHandler * h); IServerConn(uv_loop_t *l, uv_stream_t *s, Server *parent, IConnHandler *h);
virtual ~IServerConn(); virtual ~IServerConn();
virtual void Close(); virtual void Close();
virtual void Greet() = 0; virtual void Greet() = 0;
virtual void SendNextReply() = 0; virtual void SendNextReply() = 0;
virtual bool IsTimedOut() = 0; virtual bool IsTimedOut() = 0;
void SendString(const std::string & str); void SendString(const std::string &str);
Server * Parent() { return m_parent; }; Server *Parent() { return m_parent; };
IConnHandler * GetHandler() { return m_handler; }; IConnHandler *GetHandler() { return m_handler; };
uv_loop_t * GetLoop() { return m_loop; }; uv_loop_t *GetLoop() { return m_loop; };
private:
uv_tcp_t m_conn;
uv_loop_t * m_loop;
Server * m_parent;
IConnHandler * m_handler;
};
class Server private:
{ uv_tcp_t m_conn;
public: uv_loop_t *m_loop;
Server(uv_loop_t * loop); Server *m_parent;
IConnHandler *m_handler;
};
class Server
{
public:
Server(uv_loop_t *loop);
/** called after socket close, NEVER call directly */ /** called after socket close, NEVER call directly */
virtual ~Server() {} virtual ~Server() {}
/** create connection handler from open stream */ /** create connection handler from open stream */
virtual IServerConn * CreateConn(uv_stream_t * s) = 0; virtual IServerConn *CreateConn(uv_stream_t *s) = 0;
/** close all sockets and stop */ /** close all sockets and stop */
void Close(); void Close();
/** bind to address */ /** bind to address */
void Bind(const std::string & addr); void Bind(const std::string &addr);
typedef std::function<void(IServerConn *)> ConnVisitor; typedef std::function<void(IServerConn *)> ConnVisitor;
@ -77,22 +76,22 @@ namespace nntpchan
void VisitConns(ConnVisitor v); void VisitConns(ConnVisitor v);
/** remove connection from server, called after proper close */ /** remove connection from server, called after proper close */
void RemoveConn(IServerConn * conn); void RemoveConn(IServerConn *conn);
protected: protected:
uv_loop_t * GetLoop() { return m_loop; } uv_loop_t *GetLoop() { return m_loop; }
virtual void OnAcceptError(int status) = 0; virtual void OnAcceptError(int status) = 0;
private:
operator uv_handle_t * () { return (uv_handle_t*) &m_server; }
operator uv_tcp_t * () { return &m_server; }
operator uv_stream_t * () { return (uv_stream_t *) &m_server; }
void OnAccept(uv_stream_t * s, int status); private:
operator uv_handle_t *() { return (uv_handle_t *)&m_server; }
operator uv_tcp_t *() { return &m_server; }
operator uv_stream_t *() { return (uv_stream_t *)&m_server; }
void OnAccept(uv_stream_t *s, int status);
std::deque<IServerConn *> m_conns; std::deque<IServerConn *> m_conns;
uv_tcp_t m_server; uv_tcp_t m_server;
uv_loop_t * m_loop; uv_loop_t *m_loop;
}; };
} }
#endif #endif

View File

@ -5,7 +5,7 @@
namespace nntpchan namespace nntpchan
{ {
std::string sha1_hex(const std::string & data); std::string sha1_hex(const std::string &data);
} }
#endif #endif

View File

@ -2,34 +2,33 @@
#define NNTPCHAN_STATICFILE_FRONTEND_HPP #define NNTPCHAN_STATICFILE_FRONTEND_HPP
#include "frontend.hpp" #include "frontend.hpp"
#include "message.hpp" #include "message.hpp"
#include "template_engine.hpp"
#include "model.hpp" #include "model.hpp"
#include "template_engine.hpp"
#include <experimental/filesystem> #include <experimental/filesystem>
namespace nntpchan namespace nntpchan
{ {
namespace fs = std::experimental::filesystem; namespace fs = std::experimental::filesystem;
class StaticFileFrontend : public Frontend class StaticFileFrontend : public Frontend
{ {
public: public:
StaticFileFrontend(TemplateEngine *tmpl, const std::string &templateDir, const std::string &outDir, uint32_t pages);
StaticFileFrontend(TemplateEngine * tmpl, const std::string & templateDir, const std::string & outDir, uint32_t pages);
~StaticFileFrontend(); ~StaticFileFrontend();
void ProcessNewMessage(const fs::path & fpath); void ProcessNewMessage(const fs::path &fpath);
bool AcceptsNewsgroup(const std::string & newsgroup); bool AcceptsNewsgroup(const std::string &newsgroup);
bool AcceptsMessage(const std::string & msgid); bool AcceptsMessage(const std::string &msgid);
private: private:
MessageDB_ptr m_MessageDB; MessageDB_ptr m_MessageDB;
TemplateEngine_ptr m_TemplateEngine; TemplateEngine_ptr m_TemplateEngine;
fs::path m_TemplateDir; fs::path m_TemplateDir;
fs::path m_OutDir; fs::path m_OutDir;
uint32_t m_Pages; uint32_t m_Pages;
}; };
} }
#endif #endif

View File

@ -1,53 +1,50 @@
#ifndef NNTPCHAN_STORAGE_HPP #ifndef NNTPCHAN_STORAGE_HPP
#define NNTPCHAN_STORAGE_HPP #define NNTPCHAN_STORAGE_HPP
#include <experimental/filesystem>
#include <string>
#include "file_handle.hpp" #include "file_handle.hpp"
#include "message.hpp" #include "message.hpp"
#include <experimental/filesystem>
#include <string>
namespace nntpchan namespace nntpchan
{ {
namespace fs = std::experimental::filesystem; namespace fs = std::experimental::filesystem;
class ArticleStorage : public MessageDB class ArticleStorage : public MessageDB
{ {
public: public:
ArticleStorage(); ArticleStorage(const fs::path &fpath);
ArticleStorage(const fs::path & fpath);
~ArticleStorage(); ~ArticleStorage();
FileHandle_ptr OpenWrite(const std::string & msgid) const; FileHandle_ptr OpenWrite(const std::string &msgid) const;
FileHandle_ptr OpenRead(const std::string & msgid) const; FileHandle_ptr OpenRead(const std::string &msgid) const;
/** /**
return true if we should accept a new message give its message id return true if we should accept a new message give its message id
*/ */
bool Accept(const std::string & msgid) const; bool Accept(const std::string &msgid) const;
bool LoadBoardPage(BoardPage & board, const std::string & newsgroup, uint32_t perpage, uint32_t page) const; bool LoadBoardPage(BoardPage &board, const std::string &newsgroup, uint32_t perpage, uint32_t page) const;
bool FindThreadByHash(const std::string & hashhex, std::string & msgid) const; bool FindThreadByHash(const std::string &hashhex, std::string &msgid) const;
bool LoadThread(Thread & thread, const std::string & rootmsgid) const; bool LoadThread(Thread &thread, const std::string &rootmsgid) const;
/** ensure symlinks are formed for this article by message id */ /** ensure symlinks are formed for this article by message id */
void EnsureSymlinks(const std::string & msgid) const; void EnsureSymlinks(const std::string &msgid) const;
private: private:
void SetPath(const fs::path & fpath); void SetPath(const fs::path &fpath);
fs::path MessagePath(const std::string & msgid) const; fs::path MessagePath(const std::string &msgid) const;
bool init_skiplist(const std::string & subdir) const; bool init_skiplist(const std::string &subdir) const;
fs::path skiplist_root(const std::string & name) const; fs::path skiplist_root(const std::string &name) const;
fs::path basedir; fs::path basedir;
};
}; typedef std::unique_ptr<ArticleStorage> ArticleStorage_ptr;
typedef std::unique_ptr<ArticleStorage> ArticleStorage_ptr;
} }
#endif #endif

View File

@ -10,15 +10,15 @@
namespace nntpchan namespace nntpchan
{ {
struct TemplateEngine struct TemplateEngine
{ {
typedef std::map<std::string, std::variant<nntpchan::model::Model, std::string>> Args_t; typedef std::map<std::string, std::variant<nntpchan::model::Model, std::string>> Args_t;
virtual bool WriteTemplate(const fs::path & template_fpath, 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);
typedef std::unique_ptr<TemplateEngine> TemplateEngine_ptr; typedef std::unique_ptr<TemplateEngine> TemplateEngine_ptr;
} }
#endif #endif

View File

@ -6,56 +6,48 @@ using namespace mstch;
const mstch::node render_context::null_node; const mstch::node render_context::null_node;
render_context::push::push(render_context& context, const mstch::node& node): render_context::push::push(render_context &context, const mstch::node &node) : m_context(context)
m_context(context)
{ {
context.m_nodes.emplace_front(node); context.m_nodes.emplace_front(node);
context.m_node_ptrs.emplace_front(&node); context.m_node_ptrs.emplace_front(&node);
context.m_state.push(std::unique_ptr<render_state>(new outside_section)); context.m_state.push(std::unique_ptr<render_state>(new outside_section));
} }
render_context::push::~push() { render_context::push::~push()
{
m_context.m_nodes.pop_front(); m_context.m_nodes.pop_front();
m_context.m_node_ptrs.pop_front(); m_context.m_node_ptrs.pop_front();
m_context.m_state.pop(); m_context.m_state.pop();
} }
std::string render_context::push::render(const template_type& templt) { std::string render_context::push::render(const template_type &templt) { return m_context.render(templt); }
return m_context.render(templt);
}
render_context::render_context( render_context::render_context(const mstch::node &node, const std::map<std::string, template_type> &partials)
const mstch::node& node, : m_partials(partials), m_nodes(1, node), m_node_ptrs(1, &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)); m_state.push(std::unique_ptr<render_state>(new outside_section));
} }
const mstch::node& render_context::find_node( const mstch::node &render_context::find_node(const std::string &token, std::list<node const *> current_nodes)
const std::string& token,
std::list<node const*> current_nodes)
{ {
if (token != "." && token.find('.') != std::string::npos) if (token != "." && token.find('.') != std::string::npos)
return find_node(token.substr(token.rfind('.') + 1), return find_node(token.substr(token.rfind('.') + 1),
{&find_node(token.substr(0, token.rfind('.')), current_nodes)}); {&find_node(token.substr(0, token.rfind('.')), current_nodes)});
else else
for (auto& node: current_nodes) for (auto &node : current_nodes)
if (visit(has_token(token), *node)) if (visit(has_token(token), *node))
return visit(get_token(token, *node), *node); return visit(get_token(token, *node), *node);
return null_node; return null_node;
} }
const mstch::node& render_context::get_node(const std::string& token) { const mstch::node &render_context::get_node(const std::string &token) { return find_node(token, m_node_ptrs); }
return find_node(token, m_node_ptrs);
}
std::string render_context::render( std::string render_context::render(const template_type &templt, const std::string &prefix)
const template_type& templt, const std::string& prefix)
{ {
std::string output; std::string output;
bool prev_eol = true; bool prev_eol = true;
for (auto& token: templt) { for (auto &token : templt)
{
if (prev_eol && prefix.length() != 0) if (prev_eol && prefix.length() != 0)
output += m_state.top()->render(*this, {prefix}); output += m_state.top()->render(*this, {prefix});
output += m_state.top()->render(*this, token); output += m_state.top()->render(*this, token);
@ -64,9 +56,7 @@ std::string render_context::render(
return output; return output;
} }
std::string render_context::render_partial( std::string render_context::render_partial(const std::string &partial_name, const std::string &prefix)
const std::string& partial_name, const std::string& prefix)
{ {
return m_partials.count(partial_name) ? return m_partials.count(partial_name) ? render(m_partials.at(partial_name), prefix) : "";
render(m_partials.at(partial_name), prefix) : "";
} }

View File

@ -3,49 +3,45 @@
#include <deque> #include <deque>
#include <list> #include <list>
#include <sstream> #include <sstream>
#include <string>
#include <stack> #include <stack>
#include <string>
#include <mstch/mstch.hpp>
#include "state/render_state.hpp" #include "state/render_state.hpp"
#include "template_type.hpp" #include "template_type.hpp"
#include <mstch/mstch.hpp>
namespace mstch { namespace mstch
{
class render_context { class render_context
{
public:
class push
{
public: public:
class push { push(render_context &context, const mstch::node &node = {});
public:
push(render_context& context, const mstch::node& node = {});
~push(); ~push();
std::string render(const template_type& templt); std::string render(const template_type &templt);
private: private:
render_context& m_context; render_context &m_context;
}; };
render_context( render_context(const mstch::node &node, const std::map<std::string, template_type> &partials);
const mstch::node& node, const mstch::node &get_node(const std::string &token);
const std::map<std::string, template_type>& partials); std::string render(const template_type &templt, const std::string &prefix = "");
const mstch::node& get_node(const std::string& token); std::string render_partial(const std::string &partial_name, const std::string &prefix);
std::string render( template <class T, class... Args> void set_state(Args &&... args)
const template_type& templt, const std::string& prefix = ""); {
std::string render_partial( m_state.top() = std::unique_ptr<render_state>(new T(std::forward<Args>(args)...));
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: private:
static const mstch::node null_node; static const mstch::node null_node;
const mstch::node& find_node( const mstch::node &find_node(const std::string &token, std::list<node const *> current_nodes);
const std::string& token,
std::list<node const*> current_nodes);
std::map<std::string, template_type> m_partials; std::map<std::string, template_type> m_partials;
std::deque<mstch::node> m_nodes; std::deque<mstch::node> m_nodes;
std::list<const mstch::node*> m_node_ptrs; std::list<const mstch::node *> m_node_ptrs;
std::stack<std::unique_ptr<render_state>> m_state; std::stack<std::unique_ptr<render_state>> m_state;
}; };
} }

View File

@ -1,19 +1,21 @@
#include "in_section.hpp" #include "in_section.hpp"
#include "outside_section.hpp"
#include "../visitor/is_node_empty.hpp" #include "../visitor/is_node_empty.hpp"
#include "../visitor/render_section.hpp" #include "../visitor/render_section.hpp"
#include "outside_section.hpp"
using namespace mstch; using namespace mstch;
in_section::in_section(type type, const token& start_token): in_section::in_section(type type, const token &start_token)
m_type(type), m_start_token(start_token), m_skipped_openings(0) : m_type(type), m_start_token(start_token), m_skipped_openings(0)
{ {
} }
std::string in_section::render(render_context& ctx, const token& token) { std::string in_section::render(render_context &ctx, const token &token)
{
if (token.token_type() == token::type::section_close) if (token.token_type() == token::type::section_close)
if (token.name() == m_start_token.name() && m_skipped_openings == 0) { if (token.name() == m_start_token.name() && m_skipped_openings == 0)
auto& node = ctx.get_node(m_start_token.name()); {
auto &node = ctx.get_node(m_start_token.name());
std::string out; std::string out;
if (m_type == type::normal && !visit(is_node_empty(), node)) if (m_type == type::normal && !visit(is_node_empty(), node))
@ -23,10 +25,10 @@ std::string in_section::render(render_context& ctx, const token& token) {
ctx.set_state<outside_section>(); ctx.set_state<outside_section>();
return out; return out;
} else }
else
m_skipped_openings--; m_skipped_openings--;
else if (token.token_type() == token::type::inverted_section_open || else if (token.token_type() == token::type::inverted_section_open || token.token_type() == token::type::section_open)
token.token_type() == token::type::section_open)
m_skipped_openings++; m_skipped_openings++;
m_section << token; m_section << token;

View File

@ -3,22 +3,27 @@
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include "render_state.hpp"
#include "../template_type.hpp" #include "../template_type.hpp"
#include "render_state.hpp"
namespace mstch { namespace mstch
{
class in_section: public render_state { class in_section : public render_state
public: {
enum class type { inverted, normal }; public:
in_section(type type, const token& start_token); enum class type
std::string render(render_context& context, const token& token) override; {
inverted,
normal
};
in_section(type type, const token &start_token);
std::string render(render_context &context, const token &token) override;
private: private:
const type m_type; const type m_type;
const token& m_start_token; const token &m_start_token;
template_type m_section; template_type m_section;
int m_skipped_openings; int m_skipped_openings;
}; };
} }

View File

@ -1,16 +1,16 @@
#include "outside_section.hpp" #include "outside_section.hpp"
#include "../render_context.hpp"
#include "../visitor/render_node.hpp" #include "../visitor/render_node.hpp"
#include "in_section.hpp" #include "in_section.hpp"
#include "../render_context.hpp"
using namespace mstch; using namespace mstch;
std::string outside_section::render( std::string outside_section::render(render_context &ctx, const token &token)
render_context& ctx, const token& token)
{ {
using flag = render_node::flag; using flag = render_node::flag;
switch (token.token_type()) { switch (token.token_type())
{
case token::type::section_open: case token::type::section_open:
ctx.set_state<in_section>(in_section::type::normal, token); ctx.set_state<in_section>(in_section::type::normal, token);
break; break;

View File

@ -2,11 +2,12 @@
#include "render_state.hpp" #include "render_state.hpp"
namespace mstch { namespace mstch
{
class outside_section: public render_state { class outside_section : public render_state
public: {
std::string render(render_context& context, const token& token) override; public:
std::string render(render_context &context, const token &token) override;
}; };
} }

View File

@ -4,14 +4,15 @@
#include "../token.hpp" #include "../token.hpp"
namespace mstch { namespace mstch
{
class render_context; class render_context;
class render_state { class render_state
public: {
public:
virtual ~render_state() {} virtual ~render_state() {}
virtual std::string render(render_context& context, const token& token) = 0; virtual std::string render(render_context &context, const token &token) = 0;
}; };
} }

View File

@ -2,57 +2,58 @@
using namespace mstch; using namespace mstch;
template_type::template_type(const std::string& str, const delim_type& delims): template_type::template_type(const std::string &str, const delim_type &delims)
m_open(delims.first), m_close(delims.second) : m_open(delims.first), m_close(delims.second)
{ {
tokenize(str); tokenize(str);
strip_whitespace(); strip_whitespace();
} }
template_type::template_type(const std::string& str): template_type::template_type(const std::string &str) : m_open("{{"), m_close("}}")
m_open("{{"), m_close("}}")
{ {
tokenize(str); tokenize(str);
strip_whitespace(); strip_whitespace();
} }
void template_type::process_text(citer begin, citer end) { void template_type::process_text(citer begin, citer end)
{
if (begin == end) if (begin == end)
return; return;
auto start = begin; auto start = begin;
for (auto it = begin; it != end; ++it) for (auto it = begin; it != end; ++it)
if (*it == '\n' || it == end - 1) { if (*it == '\n' || it == end - 1)
{
m_tokens.push_back({{start, it + 1}}); m_tokens.push_back({{start, it + 1}});
start = it + 1; start = it + 1;
} }
} }
void template_type::tokenize(const std::string& tmp) { void template_type::tokenize(const std::string &tmp)
{
citer beg = tmp.begin(); citer beg = tmp.begin();
auto npos = std::string::npos; auto npos = std::string::npos;
for (std::size_t cur_pos = 0; cur_pos < tmp.size();) { for (std::size_t cur_pos = 0; cur_pos < tmp.size();)
{
auto open_pos = tmp.find(m_open, cur_pos); auto open_pos = tmp.find(m_open, cur_pos);
auto close_pos = tmp.find( auto close_pos = tmp.find(m_close, open_pos == npos ? open_pos : open_pos + 1);
m_close, open_pos == npos ? open_pos : open_pos + 1);
if (close_pos != npos && open_pos != npos) { if (close_pos != npos && open_pos != npos)
if (*(beg + open_pos + m_open.size()) == '{' && {
*(beg + close_pos + m_close.size()) == '}') if (*(beg + open_pos + m_open.size()) == '{' && *(beg + close_pos + m_close.size()) == '}')
++close_pos; ++close_pos;
process_text(beg + cur_pos, beg + open_pos); process_text(beg + cur_pos, beg + open_pos);
cur_pos = close_pos + m_close.size(); cur_pos = close_pos + m_close.size();
m_tokens.push_back({{beg + open_pos, beg + close_pos + m_close.size()}, m_tokens.push_back({{beg + open_pos, beg + close_pos + m_close.size()}, m_open.size(), m_close.size()});
m_open.size(), m_close.size()});
if (cur_pos == tmp.size()) { if (cur_pos == tmp.size())
{
m_tokens.push_back({{""}}); m_tokens.push_back({{""}});
m_tokens.back().eol(true); m_tokens.back().eol(true);
} }
if (*(beg + open_pos + m_open.size()) == '=' && if (*(beg + open_pos + m_open.size()) == '=' && *(beg + close_pos - 1) == '=')
*(beg + close_pos - 1) == '=')
{ {
auto tok_beg = beg + open_pos + m_open.size() + 1; auto tok_beg = beg + open_pos + m_open.size() + 1;
auto tok_end = beg + close_pos - 1; auto tok_end = beg + close_pos - 1;
@ -61,27 +62,32 @@ void template_type::tokenize(const std::string& tmp) {
m_open = {front_skip, beg + tmp.find(' ', front_skip - beg)}; m_open = {front_skip, beg + tmp.find(' ', front_skip - beg)};
m_close = {beg + tmp.rfind(' ', back_skip - beg) + 1, back_skip + 1}; m_close = {beg + tmp.rfind(' ', back_skip - beg) + 1, back_skip + 1};
} }
} else { }
else
{
process_text(beg + cur_pos, tmp.end()); process_text(beg + cur_pos, tmp.end());
cur_pos = close_pos; cur_pos = close_pos;
} }
} }
} }
void template_type::strip_whitespace() { void template_type::strip_whitespace()
{
auto line_begin = m_tokens.begin(); auto line_begin = m_tokens.begin();
bool has_tag = false, non_space = false; bool has_tag = false, non_space = false;
for (auto it = m_tokens.begin(); it != m_tokens.end(); ++it) { for (auto it = m_tokens.begin(); it != m_tokens.end(); ++it)
{
auto type = (*it).token_type(); auto type = (*it).token_type();
if (type != token::type::text && type != token::type::variable && if (type != token::type::text && type != token::type::variable && type != token::type::unescaped_variable)
type != token::type::unescaped_variable)
has_tag = true; has_tag = true;
else if (!(*it).ws_only()) else if (!(*it).ws_only())
non_space = true; non_space = true;
if ((*it).eol()) { if ((*it).eol())
if (has_tag && !non_space) { {
if (has_tag && !non_space)
{
store_prefixes(line_begin); store_prefixes(line_begin);
auto c = line_begin; auto c = line_begin;
@ -96,9 +102,9 @@ void template_type::strip_whitespace() {
} }
} }
void template_type::store_prefixes(std::vector<token>::iterator beg) { void template_type::store_prefixes(std::vector<token>::iterator beg)
{
for (auto cur = beg; !(*cur).eol(); ++cur) for (auto cur = beg; !(*cur).eol(); ++cur)
if ((*cur).token_type() == token::type::partial && if ((*cur).token_type() == token::type::partial && cur != beg && (*(cur - 1)).ws_only())
cur != beg && (*(cur - 1)).ws_only())
(*cur).partial_prefix((*(cur - 1)).raw()); (*cur).partial_prefix((*(cur - 1)).raw());
} }

View File

@ -6,25 +6,26 @@
#include "token.hpp" #include "token.hpp"
#include "utils.hpp" #include "utils.hpp"
namespace mstch { namespace mstch
{
class template_type { class template_type
public: {
public:
template_type() = default; template_type() = default;
template_type(const std::string& str); template_type(const std::string &str);
template_type(const std::string& str, const delim_type& delims); 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 begin() const { return m_tokens.begin(); }
std::vector<token>::const_iterator end() const { return m_tokens.end(); } std::vector<token>::const_iterator end() const { return m_tokens.end(); }
void operator<<(const token& token) { m_tokens.push_back(token); } void operator<<(const token &token) { m_tokens.push_back(token); }
private: private:
std::vector<token> m_tokens; std::vector<token> m_tokens;
std::string m_open; std::string m_open;
std::string m_close; std::string m_close;
void strip_whitespace(); void strip_whitespace();
void process_text(citer beg, citer end); void process_text(citer beg, citer end);
void tokenize(const std::string& tmp); void tokenize(const std::string &tmp);
void store_prefixes(std::vector<token>::iterator beg); void store_prefixes(std::vector<token>::iterator beg);
}; };
} }

View File

@ -3,38 +3,53 @@
using namespace mstch; using namespace mstch;
token::type token::token_info(char c) { token::type token::token_info(char c)
switch (c) { {
case '>': return type::partial; switch (c)
case '^': return type::inverted_section_open; {
case '/': return type::section_close; case '>':
case '&': return type::unescaped_variable; return type::partial;
case '#': return type::section_open; case '^':
case '!': return type::comment; return type::inverted_section_open;
default: return type::variable; 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): token::token(const std::string &str, std::size_t left, std::size_t right) : m_raw(str), m_eol(false), m_ws_only(false)
m_raw(str), m_eol(false), m_ws_only(false)
{ {
if (left != 0 && right != 0) { if (left != 0 && right != 0)
if (str[left] == '=' && str[str.size() - right - 1] == '=') { {
if (str[left] == '=' && str[str.size() - right - 1] == '=')
{
m_type = type::delimiter_change; m_type = type::delimiter_change;
} else if (str[left] == '{' && str[str.size() - right - 1] == '}') { }
else if (str[left] == '{' && str[str.size() - right - 1] == '}')
{
m_type = type::unescaped_variable; m_type = type::unescaped_variable;
m_name = {first_not_ws(str.begin() + left + 1, str.end() - right), m_name = {first_not_ws(str.begin() + left + 1, str.end() - right),
first_not_ws(str.rbegin() + 1 + right, str.rend() - left) + 1}; first_not_ws(str.rbegin() + 1 + right, str.rend() - left) + 1};
} else { }
else
{
auto c = first_not_ws(str.begin() + left, str.end() - right); auto c = first_not_ws(str.begin() + left, str.end() - right);
m_type = token_info(*c); m_type = token_info(*c);
if (m_type != type::variable) if (m_type != type::variable)
c = first_not_ws(c + 1, str.end() - right); c = first_not_ws(c + 1, str.end() - right);
m_name = {c, first_not_ws(str.rbegin() + right, str.rend() - left) + 1}; m_name = {c, first_not_ws(str.rbegin() + right, str.rend() - left) + 1};
m_delims = {{str.begin(), str.begin() + left}, m_delims = {{str.begin(), str.begin() + left}, {str.end() - right, str.end()}};
{str.end() - right, str.end()}};
} }
} else { }
else
{
m_type = type::text; m_type = type::text;
m_eol = (str.size() > 0 && str[str.size() - 1] == '\n'); m_eol = (str.size() > 0 && str[str.size() - 1] == '\n');
m_ws_only = (str.find_first_not_of(" \r\n\t") == std::string::npos); m_ws_only = (str.find_first_not_of(" \r\n\t") == std::string::npos);

View File

@ -2,30 +2,38 @@
#include <string> #include <string>
namespace mstch { namespace mstch
{
using delim_type = std::pair<std::string, std::string>; using delim_type = std::pair<std::string, std::string>;
class token { class token
public: {
enum class type { public:
text, variable, section_open, section_close, inverted_section_open, enum class type
unescaped_variable, comment, partial, delimiter_change {
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); token(const std::string &str, std::size_t left = 0, std::size_t right = 0);
type token_type() const { return m_type; }; type token_type() const { return m_type; };
const std::string& raw() const { return m_raw; }; const std::string &raw() const { return m_raw; };
const std::string& name() const { return m_name; }; const std::string &name() const { return m_name; };
const std::string& partial_prefix() const { return m_partial_prefix; }; const std::string &partial_prefix() const { return m_partial_prefix; };
const delim_type& delims() const { return m_delims; }; const delim_type &delims() const { return m_delims; };
void partial_prefix(const std::string& p_partial_prefix) { void partial_prefix(const std::string &p_partial_prefix) { m_partial_prefix = p_partial_prefix; };
m_partial_prefix = p_partial_prefix;
};
bool eol() const { return m_eol; } bool eol() const { return m_eol; }
void eol(bool eol) { m_eol = eol; } void eol(bool eol) { m_eol = eol; }
bool ws_only() const { return m_ws_only; } bool ws_only() const { return m_ws_only; }
private: private:
type m_type; type m_type;
std::string m_name; std::string m_name;
std::string m_raw; std::string m_raw;
@ -35,5 +43,4 @@ class token {
bool m_ws_only; bool m_ws_only;
type token_info(char c); type token_info(char c);
}; };
} }

View File

@ -1,43 +1,60 @@
#include "utils.hpp" #include "utils.hpp"
#include "mstch/mstch.hpp" #include "mstch/mstch.hpp"
mstch::citer mstch::first_not_ws(mstch::citer begin, mstch::citer end) { mstch::citer mstch::first_not_ws(mstch::citer begin, mstch::citer end)
{
for (auto it = begin; it != end; ++it) for (auto it = begin; it != end; ++it)
if (*it != ' ') return it; if (*it != ' ')
return it;
return end; return end;
} }
mstch::citer mstch::first_not_ws(mstch::criter begin, mstch::criter end) { mstch::citer mstch::first_not_ws(mstch::criter begin, mstch::criter end)
{
for (auto rit = begin; rit != end; ++rit) for (auto rit = begin; rit != end; ++rit)
if (*rit != ' ') return --(rit.base()); if (*rit != ' ')
return --(rit.base());
return --(end.base()); return --(end.base());
} }
mstch::criter mstch::reverse(mstch::citer it) { mstch::criter mstch::reverse(mstch::citer it) { return std::reverse_iterator<mstch::citer>(it); }
return std::reverse_iterator<mstch::citer>(it);
}
std::string mstch::html_escape(const std::string& str) { std::string mstch::html_escape(const std::string &str)
{
if (mstch::config::escape) if (mstch::config::escape)
return mstch::config::escape(str); return mstch::config::escape(str);
std::string out; std::string out;
citer start = str.begin(); citer start = str.begin();
auto add_escape = [&out, &start](const std::string& escaped, citer& it) { auto add_escape = [&out, &start](const std::string &escaped, citer &it) {
out += std::string{start, it} + escaped; out += std::string{start, it} + escaped;
start = it + 1; start = it + 1;
}; };
for (auto it = str.begin(); it != str.end(); ++it) for (auto it = str.begin(); it != str.end(); ++it)
switch (*it) { switch (*it)
case '&': add_escape("&amp;", it); break; {
case '\'': add_escape("&#39;", it); break; case '&':
case '"': add_escape("&quot;", it); break; add_escape("&amp;", it);
case '<': add_escape("&lt;", it); break; break;
case '>': add_escape("&gt;", it); break; case '\'':
case '/': add_escape("&#x2F;", it); break; add_escape("&#39;", it);
default: break; break;
case '"':
add_escape("&quot;", it);
break;
case '<':
add_escape("&lt;", it);
break;
case '>':
add_escape("&gt;", it);
break;
case '/':
add_escape("&#x2F;", it);
break;
default:
break;
} }
return out + std::string{start, str.end()}; return out + std::string{start, str.end()};

View File

@ -1,23 +1,21 @@
#pragma once #pragma once
#include <string>
#include <boost/variant/apply_visitor.hpp> #include <boost/variant/apply_visitor.hpp>
#include <string>
namespace mstch { namespace mstch
{
using citer = std::string::const_iterator; using citer = std::string::const_iterator;
using criter = std::string::const_reverse_iterator; using criter = std::string::const_reverse_iterator;
citer first_not_ws(citer begin, citer end); citer first_not_ws(citer begin, citer end);
citer first_not_ws(criter begin, criter end); citer first_not_ws(criter begin, criter end);
std::string html_escape(const std::string& str); std::string html_escape(const std::string &str);
criter reverse(citer it); criter reverse(citer it);
template<class... Args> template <class... Args> auto visit(Args &&... args) -> decltype(boost::apply_visitor(std::forward<Args>(args)...))
auto visit(Args&&... args) -> decltype(boost::apply_visitor(
std::forward<Args>(args)...))
{ {
return boost::apply_visitor(std::forward<Args>(args)...); return boost::apply_visitor(std::forward<Args>(args)...);
} }
} }

View File

@ -2,34 +2,25 @@
#include <boost/variant/static_visitor.hpp> #include <boost/variant/static_visitor.hpp>
#include "mstch/mstch.hpp"
#include "has_token.hpp" #include "has_token.hpp"
#include "mstch/mstch.hpp"
namespace mstch { namespace mstch
{
class get_token: public boost::static_visitor<const mstch::node&> { class get_token : public boost::static_visitor<const mstch::node &>
public: {
get_token(const std::string& token, const mstch::node& node): public:
m_token(token), m_node(node) get_token(const std::string &token, const mstch::node &node) : m_token(token), m_node(node) {}
{
}
template<class T> template <class T> const mstch::node &operator()(const T &) const { return m_node; }
const mstch::node& operator()(const T&) const {
return m_node;
}
const mstch::node& operator()(const map& map) const { const mstch::node &operator()(const map &map) const { return map.at(m_token); }
return map.at(m_token);
}
const mstch::node& operator()(const std::shared_ptr<object>& object) const { const mstch::node &operator()(const std::shared_ptr<object> &object) const { return object->at(m_token); }
return object->at(m_token);
}
private: private:
const std::string& m_token; const std::string &m_token;
const mstch::node& m_node; const mstch::node &m_node;
}; };
} }

View File

@ -4,28 +4,21 @@
#include "mstch/mstch.hpp" #include "mstch/mstch.hpp"
namespace mstch { namespace mstch
{
class has_token: public boost::static_visitor<bool> { class has_token : public boost::static_visitor<bool>
public: {
has_token(const std::string& token): m_token(token) { public:
} has_token(const std::string &token) : m_token(token) {}
template<class T> template <class T> bool operator()(const T &) const { return m_token == "."; }
bool operator()(const T&) const {
return m_token == ".";
}
bool operator()(const map& map) const { bool operator()(const map &map) const { return map.count(m_token) == 1; }
return map.count(m_token) == 1;
}
bool operator()(const std::shared_ptr<object>& object) const { bool operator()(const std::shared_ptr<object> &object) const { return object->has(m_token); }
return object->has(m_token);
}
private: private:
const std::string& m_token; const std::string &m_token;
}; };
} }

View File

@ -4,38 +4,24 @@
#include "mstch/mstch.hpp" #include "mstch/mstch.hpp"
namespace mstch { namespace mstch
{
class is_node_empty: public boost::static_visitor<bool> { class is_node_empty : public boost::static_visitor<bool>
public: {
template<class T> public:
bool operator()(const T&) const { template <class T> bool operator()(const T &) const { return false; }
return false;
}
bool operator()(const std::nullptr_t&) const { bool operator()(const std::nullptr_t &) const { return true; }
return true;
}
bool operator()(const int& value) const { bool operator()(const int &value) const { return value == 0; }
return value == 0;
}
bool operator()(const double& value) const { bool operator()(const double &value) const { return value == 0; }
return value == 0;
}
bool operator()(const bool& value) const { bool operator()(const bool &value) const { return !value; }
return !value;
}
bool operator()(const std::string& value) const { bool operator()(const std::string &value) const { return value == ""; }
return value == "";
}
bool operator()(const array& array) const { bool operator()(const array &array) const { return array.size() == 0; }
return array.size() == 0;
}
}; };
} }

View File

@ -1,56 +1,52 @@
#pragma once #pragma once
#include <sstream>
#include <boost/variant/static_visitor.hpp> #include <boost/variant/static_visitor.hpp>
#include <sstream>
#include "../render_context.hpp" #include "../render_context.hpp"
#include "mstch/mstch.hpp"
#include "../utils.hpp" #include "../utils.hpp"
#include "mstch/mstch.hpp"
namespace mstch { namespace mstch
{
class render_node: public boost::static_visitor<std::string> { class render_node : public boost::static_visitor<std::string>
public: {
enum class flag { none, escape_html }; public:
render_node(render_context& ctx, flag p_flag = flag::none): enum class flag
m_ctx(ctx), m_flag(p_flag)
{ {
} none,
escape_html
};
render_node(render_context &ctx, flag p_flag = flag::none) : m_ctx(ctx), m_flag(p_flag) {}
template<class T> template <class T> std::string operator()(const T &) const { return ""; }
std::string operator()(const T&) const {
return "";
}
std::string operator()(const int& value) const { std::string operator()(const int &value) const { return std::to_string(value); }
return std::to_string(value);
}
std::string operator()(const double& value) const { std::string operator()(const double &value) const
{
std::stringstream ss; std::stringstream ss;
ss << value; ss << value;
return ss.str(); return ss.str();
} }
std::string operator()(const bool& value) const { std::string operator()(const bool &value) const { return value ? "true" : "false"; }
return value ? "true" : "false";
}
std::string operator()(const lambda& value) const { std::string operator()(const lambda &value) const
template_type interpreted{value([this](const mstch::node& n) { {
return visit(render_node(m_ctx), n); 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); auto rendered = render_context::push(m_ctx).render(interpreted);
return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered; return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered;
} }
std::string operator()(const std::string& value) const { std::string operator()(const std::string &value) const
{
return (m_flag == flag::escape_html) ? html_escape(value) : value; return (m_flag == flag::escape_html) ? html_escape(value) : value;
} }
private: private:
render_context& m_ctx; render_context &m_ctx;
flag m_flag; flag m_flag;
}; };
} }

View File

@ -3,55 +3,56 @@
#include <boost/variant/static_visitor.hpp> #include <boost/variant/static_visitor.hpp>
#include "../render_context.hpp" #include "../render_context.hpp"
#include "mstch/mstch.hpp"
#include "../utils.hpp" #include "../utils.hpp"
#include "mstch/mstch.hpp"
#include "render_node.hpp" #include "render_node.hpp"
namespace mstch { namespace mstch
{
class render_section: public boost::static_visitor<std::string> { class render_section : public boost::static_visitor<std::string>
public: {
enum class flag { none, keep_array }; public:
render_section( enum class flag
render_context& ctx, {
const template_type& section, none,
const delim_type& delims, keep_array
flag p_flag = flag::none): };
m_ctx(ctx), m_section(section), m_delims(delims), m_flag(p_flag) 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> template <class T> std::string operator()(const T &t) const
std::string operator()(const T& t) const { {
return render_context::push(m_ctx, t).render(m_section); return render_context::push(m_ctx, t).render(m_section);
} }
std::string operator()(const lambda& fun) const { std::string operator()(const lambda &fun) const
{
std::string section_str; std::string section_str;
for (auto& token: m_section) for (auto &token : m_section)
section_str += token.raw(); section_str += token.raw();
template_type interpreted{fun([this](const mstch::node& n) { template_type interpreted{fun([this](const mstch::node &n) { return visit(render_node(m_ctx), n); }, section_str),
return visit(render_node(m_ctx), n); m_delims};
}, section_str), m_delims};
return render_context::push(m_ctx).render(interpreted); return render_context::push(m_ctx).render(interpreted);
} }
std::string operator()(const array& array) const { std::string operator()(const array &array) const
{
std::string out; std::string out;
if (m_flag == flag::keep_array) if (m_flag == flag::keep_array)
return render_context::push(m_ctx, array).render(m_section); return render_context::push(m_ctx, array).render(m_section);
else else
for (auto& item: array) for (auto &item : array)
out += visit(render_section( out += visit(render_section(m_ctx, m_section, m_delims, flag::keep_array), item);
m_ctx, m_section, m_delims, flag::keep_array), item);
return out; return out;
} }
private: private:
render_context& m_ctx; render_context &m_ctx;
const template_type& m_section; const template_type &m_section;
const delim_type& m_delims; const delim_type &m_delims;
flag m_flag; flag m_flag;
}; };
} }

View File

@ -1,65 +1,56 @@
#include <nntpchan/base64.hpp> #include <nntpchan/base64.hpp>
// taken from i2pd // taken from i2pd
namespace i2p namespace i2p
{ {
namespace data namespace data
{ {
static void iT64Build(void); static void iT64Build(void);
/* /*
* *
* BASE64 Substitution Table * BASE64 Substitution Table
* ------------------------- * -------------------------
* *
* Direct Substitution Table * Direct Substitution Table
*/ */
static const char T64[64] = { static const char T64[64] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
/*
* Reverse Substitution Table (built in run time)
*/
/* static char iT64[256];
* Reverse Substitution Table (built in run time) static int isFirstTime = 1;
*/
static char iT64[256]; /*
static int isFirstTime = 1; * Padding
*/
/* static char P64 = '=';
* Padding
*/
static char P64 = '='; /*
*
/* * ByteStreamToBase64
* * ------------------
* ByteStreamToBase64 *
* ------------------ * Converts binary encoded data to BASE64 format.
* *
* Converts binary encoded data to BASE64 format. */
* static size_t /* Number of bytes in the encoded buffer */
*/ ByteStreamToBase64(const uint8_t *InBuffer, /* Input buffer, binary data */
static size_t /* Number of bytes in the encoded buffer */
ByteStreamToBase64 (
const uint8_t * InBuffer, /* Input buffer, binary data */
size_t InCount, /* Number of bytes in the input buffer */ size_t InCount, /* Number of bytes in the input buffer */
char * OutBuffer, /* output buffer */ char *OutBuffer, /* output buffer */
size_t len /* length of output buffer */ size_t len /* length of output buffer */
) )
{ {
unsigned char * ps; unsigned char *ps;
unsigned char * pd; unsigned char *pd;
unsigned char acc_1; unsigned char acc_1;
unsigned char acc_2; unsigned char acc_2;
int i; int i;
@ -68,77 +59,78 @@ namespace data
size_t outCount; size_t outCount;
ps = (unsigned char *)InBuffer; ps = (unsigned char *)InBuffer;
n = InCount/3; n = InCount / 3;
m = InCount%3; m = InCount % 3;
if (!m) if (!m)
outCount = 4*n; outCount = 4 * n;
else else
outCount = 4*(n+1); outCount = 4 * (n + 1);
if (outCount > len) return 0; if (outCount > len)
return 0;
pd = (unsigned char *)OutBuffer; pd = (unsigned char *)OutBuffer;
for ( i = 0; i<n; i++ ){ for (i = 0; i < n; i++)
{
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x30; acc_2 = (acc_1 << 4) & 0x30;
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
acc_1 = *ps++; acc_1 = *ps++;
acc_2 |= acc_1 >> 4; /* base64 digit #2 */ acc_2 |= acc_1 >> 4; /* base64 digit #2 */
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
acc_1 &= 0x0f; acc_1 &= 0x0f;
acc_1 <<=2; acc_1 <<= 2;
acc_2 = *ps++; acc_2 = *ps++;
acc_1 |= acc_2>>6; /* base64 digit #3 */ acc_1 |= acc_2 >> 6; /* base64 digit #3 */
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
acc_2 &= 0x3f; /* base64 digit #4 */ acc_2 &= 0x3f; /* base64 digit #4 */
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
} }
if ( m == 1 ){ if (m == 1)
{
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x3f; /* base64 digit #2 */ acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
*pd++ = P64; *pd++ = P64;
*pd++ = P64; *pd++ = P64;
} }
else if ( m == 2 ){ else if (m == 2)
{
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1<<4)&0x3f; acc_2 = (acc_1 << 4) & 0x3f;
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; /* base64 digit #1 */
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
acc_1 = *ps++; acc_1 = *ps++;
acc_2 |= acc_1 >> 4; /* base64 digit #2 */ acc_2 |= acc_1 >> 4; /* base64 digit #2 */
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
acc_1 &= 0x0f; acc_1 &= 0x0f;
acc_1 <<=2; /* base64 digit #3 */ acc_1 <<= 2; /* base64 digit #3 */
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
*pd++ = P64; *pd++ = P64;
} }
return outCount; return outCount;
} }
/* /*
* *
* Base64ToByteStream * Base64ToByteStream
* ------------------ * ------------------
* *
* Converts BASE64 encoded data to binary format. If input buffer is * Converts BASE64 encoded data to binary format. If input buffer is
* not properly padded, buffer of negative length is returned * not properly padded, buffer of negative length is returned
* *
*/ */
static static ssize_t /* Number of output bytes */
ssize_t /* Number of output bytes */ Base64ToByteStream(const char *InBuffer, /* BASE64 encoded buffer */
Base64ToByteStream (
const char * InBuffer, /* BASE64 encoded buffer */
size_t InCount, /* Number of input bytes */ size_t InCount, /* Number of input bytes */
uint8_t * OutBuffer, /* output buffer length */ uint8_t *OutBuffer, /* output buffer length */
size_t len /* length of output buffer */ size_t len /* length of output buffer */
) )
{ {
unsigned char * ps; unsigned char *ps;
unsigned char * pd; unsigned char *pd;
unsigned char acc_1; unsigned char acc_1;
unsigned char acc_2; unsigned char acc_2;
int i; int i;
@ -146,36 +138,43 @@ namespace data
int m; int m;
size_t outCount; size_t outCount;
if (isFirstTime) iT64Build(); if (isFirstTime)
n = InCount/4; iT64Build();
m = InCount%4; n = InCount / 4;
m = InCount % 4;
if (InCount && !m) if (InCount && !m)
outCount = 3*n; outCount = 3 * n;
else { else
{
outCount = 0; outCount = 0;
return 0; return 0;
} }
ps = (unsigned char *)(InBuffer + InCount - 1); ps = (unsigned char *)(InBuffer + InCount - 1);
while ( *ps-- == P64 ) outCount--; while (*ps-- == P64)
outCount--;
ps = (unsigned char *)InBuffer; ps = (unsigned char *)InBuffer;
if (outCount > len) return -1; if (outCount > len)
return -1;
pd = OutBuffer; pd = OutBuffer;
auto endOfOutBuffer = OutBuffer + outCount; auto endOfOutBuffer = OutBuffer + outCount;
for ( i = 0; i < n; i++ ){ for (i = 0; i < n; i++)
{
acc_1 = iT64[*ps++]; acc_1 = iT64[*ps++];
acc_2 = iT64[*ps++]; acc_2 = iT64[*ps++];
acc_1 <<= 2; acc_1 <<= 2;
acc_1 |= acc_2>>4; acc_1 |= acc_2 >> 4;
*pd++ = acc_1; *pd++ = acc_1;
if (pd >= endOfOutBuffer) break; if (pd >= endOfOutBuffer)
break;
acc_2 <<= 4; acc_2 <<= 4;
acc_1 = iT64[*ps++]; acc_1 = iT64[*ps++];
acc_2 |= acc_1 >> 2; acc_2 |= acc_1 >> 2;
*pd++ = acc_2; *pd++ = acc_2;
if (pd >= endOfOutBuffer) break; if (pd >= endOfOutBuffer)
break;
acc_2 = iT64[*ps++]; acc_2 = iT64[*ps++];
acc_2 |= acc_1 << 6; acc_2 |= acc_1 << 6;
@ -183,52 +182,54 @@ namespace data
} }
return outCount; return outCount;
} }
static size_t Base64EncodingBufferSize (const size_t input_size) static size_t Base64EncodingBufferSize(const size_t input_size)
{ {
auto d = div (input_size, 3); auto d = div(input_size, 3);
if (d.rem) d.quot++; if (d.rem)
return 4*d.quot; d.quot++;
} return 4 * d.quot;
}
/* /*
* *
* iT64 * iT64
* ---- * ----
* Reverse table builder. P64 character is replaced with 0 * Reverse table builder. P64 character is replaced with 0
* *
* *
*/ */
static void iT64Build() static void iT64Build()
{ {
int i; int i;
isFirstTime = 0; isFirstTime = 0;
for ( i=0; i<256; i++ ) iT64[i] = -1; for (i = 0; i < 256; i++)
for ( i=0; i<64; i++ ) iT64[(int)T64[i]] = i; iT64[i] = -1;
for (i = 0; i < 64; i++)
iT64[(int)T64[i]] = i;
iT64[(int)P64] = 0; iT64[(int)P64] = 0;
} }
} }
} }
namespace nntpchan namespace nntpchan
{ {
std::string B64Encode(const uint8_t * data, const std::size_t l) std::string B64Encode(const uint8_t *data, const std::size_t l)
{ {
std::string out; std::string out;
out.resize(i2p::data::Base64EncodingBufferSize(l)); out.resize(i2p::data::Base64EncodingBufferSize(l));
i2p::data::ByteStreamToBase64(data, l, &out[0], out.size()); i2p::data::ByteStreamToBase64(data, l, &out[0], out.size());
return out; return out;
} }
bool B64Decode(const std::string & data, std::vector<uint8_t> & out) bool B64Decode(const std::string &data, std::vector<uint8_t> &out)
{ {
out.resize(data.size()); out.resize(data.size());
if(i2p::data::Base64ToByteStream(data.c_str(), data.size(), &out[0], out.size()) == -1) return false; if (i2p::data::Base64ToByteStream(data.c_str(), data.size(), &out[0], out.size()) == -1)
return false;
out.shrink_to_fit(); out.shrink_to_fit();
return true; return true;
} }
} }

View File

@ -1,21 +1,17 @@
#include <nntpchan/buffer.hpp>
#include <cstring> #include <cstring>
#include <nntpchan/buffer.hpp>
namespace nntpchan namespace nntpchan
{ {
WriteBuffer::WriteBuffer(const char * b, const size_t s) WriteBuffer::WriteBuffer(const char *b, const size_t s)
{ {
char * buf = new char[s]; char *buf = new char[s];
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()
{
delete [] b.base;
}
} }
WriteBuffer::WriteBuffer(const std::string &s) : WriteBuffer(s.c_str(), s.size()) {}
WriteBuffer::~WriteBuffer() { delete[] b.base; }
}

View File

@ -1,20 +1,12 @@
#include <cassert>
#include <nntpchan/crypto.hpp> #include <nntpchan/crypto.hpp>
#include <sodium.h> #include <sodium.h>
#include <cassert>
namespace nntpchan namespace nntpchan
{ {
void SHA512(const uint8_t * d, const std::size_t l, SHA512Digest & h) void SHA512(const uint8_t *d, const std::size_t l, SHA512Digest &h) { crypto_hash(h.data(), d, l); }
{
crypto_hash(h.data(), d, l);
}
Crypto::Crypto() Crypto::Crypto() { assert(sodium_init() == 0); }
{
assert(sodium_init() == 0);
}
Crypto::~Crypto() Crypto::~Crypto() {}
{
}
} }

View File

@ -14,31 +14,15 @@ typedef struct
unsigned char buffer[64]; unsigned char buffer[64];
} SHA1_CTX; } SHA1_CTX;
void SHA1Transform( void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
uint32_t state[5],
const unsigned char buffer[64]
);
void SHA1Init( void SHA1Init(SHA1_CTX *context);
SHA1_CTX * context
);
void SHA1Update( void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32_t len);
SHA1_CTX * context,
const unsigned char *data,
uint32_t len
);
void SHA1Final( void SHA1Final(unsigned char digest[20], SHA1_CTX *context);
unsigned char digest[20],
SHA1_CTX * context
);
void sha1(
uint8_t *hash_out,
const uint8_t *str,
size_t len);
void sha1(uint8_t *hash_out, const uint8_t *str, size_t len);
} }
#endif #endif

View File

@ -1,27 +1,17 @@
#include <nntpchan/event.hpp>
#include <cassert> #include <cassert>
#include <nntpchan/event.hpp>
namespace nntpchan namespace nntpchan
{ {
Mainloop::Mainloop() Mainloop::Mainloop()
{ {
m_loop = uv_default_loop(); m_loop = uv_default_loop();
assert(uv_loop_init(m_loop) == 0); assert(uv_loop_init(m_loop) == 0);
} }
Mainloop::~Mainloop() Mainloop::~Mainloop() { uv_loop_close(m_loop); }
{
uv_loop_close(m_loop); void Mainloop::Stop() { uv_stop(m_loop); }
}
void Mainloop::Run(uv_run_mode mode) { assert(uv_run(m_loop, mode) == 0); }
void Mainloop::Stop()
{
uv_stop(m_loop);
}
void Mainloop::Run(uv_run_mode mode)
{
assert(uv_run(m_loop, mode) == 0);
}
} }

View File

@ -1,57 +1,51 @@
#include <nntpchan/exec_frontend.hpp>
#include <cstring> #include <cstring>
#include <iostream>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <iostream>
#include <nntpchan/exec_frontend.hpp>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h>
namespace nntpchan namespace nntpchan
{ {
ExecFrontend::ExecFrontend(const std::string & fname) : ExecFrontend::ExecFrontend(const std::string &fname) : m_exec(fname) {}
m_exec(fname)
{
}
ExecFrontend::~ExecFrontend() {} ExecFrontend::~ExecFrontend() {}
void ExecFrontend::ProcessNewMessage(const fs::path & fpath) void ExecFrontend::ProcessNewMessage(const fs::path &fpath) { Exec({"post", fpath}); }
{
Exec({"post", fpath});
}
bool ExecFrontend::AcceptsNewsgroup(const std::string & newsgroup) bool ExecFrontend::AcceptsNewsgroup(const std::string &newsgroup) { return Exec({"newsgroup", newsgroup}) == 0; }
{
return Exec({"newsgroup", newsgroup}) == 0;
}
bool ExecFrontend::AcceptsMessage(const std::string & msgid) bool ExecFrontend::AcceptsMessage(const std::string &msgid) { return Exec({"msgid", msgid}) == 0; }
{
return Exec({"msgid", msgid}) == 0;
}
int ExecFrontend::Exec(std::deque<std::string> args) int ExecFrontend::Exec(std::deque<std::string> args)
{ {
// set up arguments // set up arguments
const char ** cargs = new char const *[args.size() +2]; const char **cargs = new char const *[args.size() + 2];
std::size_t l = 0; std::size_t l = 0;
cargs[l++] = m_exec.c_str(); cargs[l++] = m_exec.c_str();
while (args.size()) { while (args.size())
{
cargs[l++] = args.front().c_str(); cargs[l++] = args.front().c_str();
args.pop_front(); args.pop_front();
} }
cargs[l] = 0; cargs[l] = 0;
int retcode = 0; int retcode = 0;
pid_t child = fork(); pid_t child = fork();
if(child) { if (child)
{
waitpid(child, &retcode, 0); waitpid(child, &retcode, 0);
} else { }
int r = execvpe(m_exec.c_str(),(char * const *) cargs, environ); else
if ( r == -1 ) { {
int r = execvpe(m_exec.c_str(), (char *const *)cargs, environ);
if (r == -1)
{
std::cout << strerror(errno) << std::endl; std::cout << strerror(errno) << std::endl;
exit( errno ); exit(errno);
} else }
else
exit(r); exit(r);
} }
return retcode; return retcode;
} }
} }

View File

@ -1,12 +1,11 @@
#include <nntpchan/file_handle.hpp> #include <nntpchan/file_handle.hpp>
namespace nntpchan namespace nntpchan
{ {
FileHandle_ptr OpenFile(const fs::path & fname, FileMode mode) FileHandle_ptr OpenFile(const fs::path &fname, FileMode mode)
{ {
std::fstream * f = new std::fstream; std::fstream *f = new std::fstream;
if(mode == eRead) if (mode == eRead)
{ {
f->open(fname, std::ios::in); f->open(fname, std::ios::in);
} }
@ -14,9 +13,9 @@ namespace nntpchan
{ {
f->open(fname, std::ios::out); f->open(fname, std::ios::out);
} }
if(f->is_open()) if (f->is_open())
return FileHandle_ptr(f); return FileHandle_ptr(f);
delete f; delete f;
return nullptr; return nullptr;
} }
} }

View File

@ -1,40 +1,45 @@
#include <nntpchan/line.hpp> #include <nntpchan/line.hpp>
namespace nntpchan { namespace nntpchan
{
LineReader::LineReader(size_t limit) : m_close(false), lineLimit(limit) {} LineReader::LineReader(size_t limit) : m_close(false), lineLimit(limit) {}
void LineReader::Data(const char * data, ssize_t l) void LineReader::Data(const char *data, ssize_t l)
{ {
if(l <= 0) return; if (l <= 0)
return;
// process leftovers // process leftovers
std::size_t idx = 0; std::size_t idx = 0;
std::size_t pos = 0; std::size_t pos = 0;
while(l-- > 0) { while (l-- > 0)
{
char c = data[idx++]; char c = data[idx++];
if(c == '\n') { if (c == '\n')
{
OnLine(data, pos); OnLine(data, pos);
pos = 0; pos = 0;
data += idx; data += idx;
} else if (c == '\r' && data[idx] == '\n') { }
else if (c == '\r' && data[idx] == '\n')
{
OnLine(data, pos); OnLine(data, pos);
data += idx + 1; data += idx + 1;
pos = 0; pos = 0;
} else {
pos ++;
} }
} else
}
void LineReader::OnLine(const char *d, const size_t l)
{ {
pos++;
}
}
}
void LineReader::OnLine(const char *d, const size_t l)
{
std::string line; std::string line;
line += std::string(d, l); line += std::string(d, l);
HandleLine(line); HandleLine(line);
} }
bool LineReader::ShouldClose() bool LineReader::ShouldClose() { return m_close; }
{
return m_close;
}
} }

View File

@ -2,25 +2,25 @@
namespace nntpchan namespace nntpchan
{ {
bool ReadHeader(const FileHandle_ptr & file, RawHeader & header) bool ReadHeader(const FileHandle_ptr &file, RawHeader &header)
{ {
std::string line; std::string line;
while(std::getline(*file, line) && !(line == "\r" || line == "")) while (std::getline(*file, line) && !(line == "\r" || line == ""))
{ {
std::string k, v; std::string k, v;
auto idx = line.find(": "); auto idx = line.find(": ");
auto endidx = line.size() - 1; auto endidx = line.size() - 1;
while(line[endidx] == '\r') --endidx; while (line[endidx] == '\r')
--endidx;
if(idx != std::string::npos && idx + 2 < endidx) if (idx != std::string::npos && idx + 2 < endidx)
{ {
k = line.substr(0, idx); k = line.substr(0, idx);
v = line.substr(idx+2, endidx); v = line.substr(idx + 2, endidx);
header[k] = v; header[k] = v;
} }
} }
return file->good(); return file->good();
} }
} }

View File

@ -1,44 +1,44 @@
#include <cstring>
#include <nntpchan/net.hpp> #include <nntpchan/net.hpp>
#include <uv.h>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <cstring> #include <uv.h>
namespace nntpchan namespace nntpchan
{ {
std::string NetAddr::to_string() std::string NetAddr::to_string()
{ {
std::string str("invalid"); std::string str("invalid");
const size_t s = 128; const size_t s = 128;
char * buff = new char[s]; char *buff = new char[s];
if(uv_ip6_name(&addr, buff, s) == 0) { if (uv_ip6_name(&addr, buff, s) == 0)
{
str = std::string(buff); str = std::string(buff);
delete [] buff; delete[] buff;
} }
std::stringstream ss; std::stringstream ss;
ss << "[" << str << "]:" << ntohs(addr.sin6_port); ss << "[" << str << "]:" << ntohs(addr.sin6_port);
return ss.str(); return ss.str();
} }
NetAddr::NetAddr() NetAddr::NetAddr() { std::memset(&addr, 0, sizeof(addr)); }
{
std::memset(&addr, 0, sizeof(addr));
}
NetAddr ParseAddr(const std::string & addr) NetAddr ParseAddr(const std::string &addr)
{ {
NetAddr saddr; NetAddr saddr;
auto n = addr.rfind("]:"); auto n = addr.rfind("]:");
if (n == std::string::npos) { if (n == std::string::npos)
throw std::runtime_error("invalid address: "+addr); {
throw std::runtime_error("invalid address: " + addr);
} }
if (addr[0] != '[') { if (addr[0] != '[')
throw std::runtime_error("invalid address: "+addr); {
throw std::runtime_error("invalid address: " + addr);
} }
auto p = addr.substr(n+2); auto p = addr.substr(n + 2);
int port = std::atoi(p.c_str()); int port = std::atoi(p.c_str());
auto a = addr.substr(0, n); auto a = addr.substr(0, n);
uv_ip6_addr(a.c_str(), port, &saddr.addr); uv_ip6_addr(a.c_str(), port, &saddr.addr);
return saddr; return saddr;
} }
} }

View File

@ -1,16 +1,16 @@
#include <nntpchan/nntp_auth.hpp>
#include <nntpchan/crypto.hpp>
#include <nntpchan/base64.hpp>
#include <array> #include <array>
#include <iostream>
#include <fstream> #include <fstream>
#include <iostream>
#include <nntpchan/base64.hpp>
#include <nntpchan/crypto.hpp>
#include <nntpchan/nntp_auth.hpp>
namespace nntpchan namespace nntpchan
{ {
HashedCredDB::HashedCredDB() : LineReader(1024) {} HashedCredDB::HashedCredDB() : LineReader(1024) {}
bool HashedCredDB::CheckLogin(const std::string & user, const std::string & passwd) bool HashedCredDB::CheckLogin(const std::string &user, const std::string &passwd)
{ {
std::unique_lock<std::mutex> lock(m_access); std::unique_lock<std::mutex> lock(m_access);
m_found = false; m_found = false;
m_user = user; m_user = user;
@ -18,82 +18,80 @@ namespace nntpchan
m_instream->seekg(0, std::ios::end); m_instream->seekg(0, std::ios::end);
const auto l = m_instream->tellg(); const auto l = m_instream->tellg();
m_instream->seekg(0, std::ios::beg); m_instream->seekg(0, std::ios::beg);
char * buff = new char[l]; char *buff = new char[l];
// read file // read file
m_instream->read(buff, l); m_instream->read(buff, l);
Data(buff, l); Data(buff, l);
delete [] buff; delete[] buff;
return m_found; return m_found;
} }
bool HashedCredDB::ProcessLine(const std::string & line) bool HashedCredDB::ProcessLine(const std::string &line)
{ {
// strip comments // strip comments
auto comment = line.find("#"); auto comment = line.find("#");
std::string part = line; std::string part = line;
for (; comment != std::string::npos; comment = part.find("#")) { for (; comment != std::string::npos; comment = part.find("#"))
if(comment) {
if (comment)
part = part.substr(0, comment); part = part.substr(0, comment);
else break; else
break;
} }
if(!part.size()) return false; // empty line after comments if (!part.size())
return false; // empty line after comments
auto idx = part.find(":"); auto idx = part.find(":");
if (idx == std::string::npos) return false; // bad format if (idx == std::string::npos)
if (m_user != part.substr(0, idx)) return false; // username mismatch return false; // bad format
part = part.substr(idx+1); if (m_user != part.substr(0, idx))
return false; // username mismatch
part = part.substr(idx + 1);
idx = part.find(":"); idx = part.find(":");
if (idx == std::string::npos) return false; // bad format if (idx == std::string::npos)
return false; // bad format
std::string cred = part.substr(0, idx); std::string cred = part.substr(0, idx);
std::string salt = part.substr(idx+1); std::string salt = part.substr(idx + 1);
return Hash(m_passwd, salt) == cred; return Hash(m_passwd, salt) == cred;
} }
void HashedCredDB::HandleLine(const std::string &line) void HashedCredDB::HandleLine(const std::string &line)
{ {
if(m_found) return; if (m_found)
if(ProcessLine(line)) return;
if (ProcessLine(line))
m_found = true; m_found = true;
} }
void HashedCredDB::SetStream(std::istream * s) void HashedCredDB::SetStream(std::istream *s) { m_instream = s; }
{
m_instream = s;
}
std::string HashedCredDB::Hash(const std::string & data, const std::string & salt) std::string HashedCredDB::Hash(const std::string &data, const std::string &salt)
{ {
SHA512Digest h; SHA512Digest h;
std::string d = data + salt; std::string d = data + salt;
SHA512((const uint8_t*)d.c_str(), d.size(), h); SHA512((const uint8_t *)d.c_str(), d.size(), h);
return B64Encode(h.data(), h.size()); return B64Encode(h.data(), h.size());
} }
HashedFileDB::HashedFileDB(const std::string & fname) : HashedFileDB::HashedFileDB(const std::string &fname) : m_fname(fname), f(nullptr) {}
m_fname(fname),
f(nullptr)
{
} HashedFileDB::~HashedFileDB() {}
HashedFileDB::~HashedFileDB() void HashedFileDB::Close()
{ {
} if (f.is_open())
void HashedFileDB::Close()
{
if(f.is_open())
f.close(); f.close();
} }
bool HashedFileDB::Open() bool HashedFileDB::Open()
{ {
if(!f.is_open()) if (!f.is_open())
f.open(m_fname); f.open(m_fname);
if(f.is_open()) { if (f.is_open())
{
SetStream(&f); SetStream(&f);
return true; return true;
} }
return false; return false;
} }
} }

View File

@ -1,44 +1,40 @@
#include <nntpchan/nntp_handler.hpp>
#include <nntpchan/sanitize.hpp>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cstring> #include <cstring>
#include <string>
#include <sstream>
#include <iostream> #include <iostream>
#include <nntpchan/nntp_handler.hpp>
#include <nntpchan/sanitize.hpp>
#include <sstream>
#include <string>
namespace nntpchan namespace nntpchan
{ {
NNTPServerHandler::NNTPServerHandler(const fs::path & storage) : NNTPServerHandler::NNTPServerHandler(const fs::path &storage)
LineReader(1024), : LineReader(1024), m_article(nullptr), m_auth(nullptr), m_store(std::make_unique<ArticleStorage>(storage)),
m_article(nullptr), m_authed(false), m_state(eStateReadCommand)
m_auth(nullptr), {
m_store(std::make_unique<ArticleStorage>(storage)), }
m_authed(false),
m_state(eStateReadCommand)
{
}
NNTPServerHandler::~NNTPServerHandler() NNTPServerHandler::~NNTPServerHandler() {}
{
}
void NNTPServerHandler::HandleLine(const std::string &line) void NNTPServerHandler::HandleLine(const std::string &line)
{ {
if(m_state == eStateReadCommand) if (m_state == eStateReadCommand)
{ {
std::deque<std::string> command; std::deque<std::string> command;
std::istringstream s; std::istringstream s;
s.str(line); s.str(line);
for (std::string part; std::getline(s, part, ' '); ) { for (std::string part; std::getline(s, part, ' ');)
if(part.size()) command.push_back(part); {
if (part.size())
command.push_back(part);
} }
if(command.size()) if (command.size())
HandleCommand(command); HandleCommand(command);
else else
QueueLine("501 Syntax error"); QueueLine("501 Syntax error");
} }
else if(m_state == eStateStoreArticle) else if (m_state == eStateStoreArticle)
{ {
std::string l = line + "\r\n"; std::string l = line + "\r\n";
OnData(l.c_str(), l.size()); OnData(l.c_str(), l.size());
@ -47,28 +43,29 @@ namespace nntpchan
{ {
std::cerr << "invalid state" << std::endl; std::cerr << "invalid state" << std::endl;
} }
} }
void NNTPServerHandler::OnData(const char * data, ssize_t l) void NNTPServerHandler::OnData(const char *data, ssize_t l)
{
if (l <= 0)
return;
if (m_state == eStateStoreArticle)
{ {
if(l <= 0 ) return; const char *end = strstr(data, "\r\n.\r\n");
if(m_state == eStateStoreArticle) if (end)
{ {
const char * end = strstr(data, "\r\n.\r\n"); std::size_t diff = end - data;
if(end) if (m_article)
{ {
std::size_t diff = end - data ; m_article->write(data, diff + 2);
if(m_article)
{
m_article->write(data, diff+2);
m_article->flush(); m_article->flush();
} }
ArticleObtained(); ArticleObtained();
diff += 5; diff += 5;
Data(end+5, l-diff); Data(end + 5, l - diff);
return; return;
} }
if(m_article) if (m_article)
{ {
m_article->write(data, l); m_article->write(data, l);
m_article->flush(); m_article->flush();
@ -76,17 +73,18 @@ namespace nntpchan
} }
else else
Data(data, l); Data(data, l);
} }
void NNTPServerHandler::HandleCommand(const std::deque<std::string> & command) void NNTPServerHandler::HandleCommand(const std::deque<std::string> &command)
{ {
auto cmd = command[0]; auto cmd = command[0];
std::transform(cmd.begin(), cmd.end(), cmd.begin(), ::toupper); std::transform(cmd.begin(), cmd.end(), cmd.begin(), ::toupper);
std::size_t cmdlen = command.size(); std::size_t cmdlen = command.size();
for(const auto & part : command) for (const auto &part : command)
std::cerr << " " << part; std::cerr << " " << part;
std::cerr << std::endl; std::cerr << std::endl;
if (cmd == "QUIT") { if (cmd == "QUIT")
{
Quit(); Quit();
return; return;
} }
@ -94,41 +92,54 @@ namespace nntpchan
{ {
return; return;
} }
else if (cmd == "MODE" ) { else if (cmd == "MODE")
if(cmdlen == 2) { {
if (cmdlen == 2)
{
// set mode // set mode
SwitchMode(command[1]); SwitchMode(command[1]);
} else if(cmdlen) { }
else if (cmdlen)
{
// too many arguments // too many arguments
QueueLine("500 too many arguments"); QueueLine("500 too many arguments");
} else { }
else
{
// get mode // get mode
QueueLine("500 wrong arguments"); QueueLine("500 wrong arguments");
} }
} else if(cmd == "CAPABILITIES") { }
else if (cmd == "CAPABILITIES")
{
QueueLine("101 I support the following:"); QueueLine("101 I support the following:");
QueueLine("READER"); QueueLine("READER");
QueueLine("IMPLEMENTATION nntpchan-daemon"); QueueLine("IMPLEMENTATION nntpchan-daemon");
QueueLine("VERSION 2"); QueueLine("VERSION 2");
QueueLine("STREAMING"); QueueLine("STREAMING");
QueueLine("."); QueueLine(".");
} else if (cmd == "CHECK") { }
if(cmdlen >= 2) { else if (cmd == "CHECK")
const std::string & msgid = command[1];
if(IsValidMessageID(msgid) && m_store->Accept(msgid))
{ {
QueueLine("238 "+msgid); if (cmdlen >= 2)
{
const std::string &msgid = command[1];
if (IsValidMessageID(msgid) && m_store->Accept(msgid))
{
QueueLine("238 " + msgid);
} }
else else
QueueLine("438 "+msgid); QueueLine("438 " + msgid);
} }
else else
QueueLine("501 syntax error"); QueueLine("501 syntax error");
} else if (cmd == "TAKETHIS") { }
else if (cmd == "TAKETHIS")
{
if (cmdlen >= 2) if (cmdlen >= 2)
{ {
const std::string & msgid = command[1]; const std::string &msgid = command[1];
if(m_store->Accept(msgid)) if (m_store->Accept(msgid))
{ {
m_article = m_store->OpenWrite(msgid); m_article = m_store->OpenWrite(msgid);
} }
@ -137,83 +148,87 @@ namespace nntpchan
return; return;
} }
QueueLine("501 invalid syntax"); QueueLine("501 invalid syntax");
} else { }
else
{
// unknown command // unknown command
QueueLine("500 Unknown Command"); QueueLine("500 Unknown Command");
} }
} }
void NNTPServerHandler::ArticleObtained() void NNTPServerHandler::ArticleObtained()
{ {
if(m_article) if (m_article)
{ {
m_article->close(); m_article->close();
m_article = nullptr; m_article = nullptr;
QueueLine("239 "+m_articleName); QueueLine("239 " + m_articleName);
std::cerr << "stored " << m_articleName << std::endl; std::cerr << "stored " << m_articleName << std::endl;
} }
else else
QueueLine("439 "+m_articleName); QueueLine("439 " + m_articleName);
m_articleName = ""; m_articleName = "";
EnterState(eStateReadCommand); EnterState(eStateReadCommand);
} }
void NNTPServerHandler::SwitchMode(const std::string & mode) void NNTPServerHandler::SwitchMode(const std::string &mode)
{ {
std::string m = mode; std::string m = mode;
std::transform(m.begin(), m.end(), m.begin(), ::toupper); std::transform(m.begin(), m.end(), m.begin(), ::toupper);
if (m == "READER") { if (m == "READER")
{
m_mode = m; m_mode = m;
if(PostingAllowed()) { if (PostingAllowed())
{
QueueLine("200 Posting is permitted yo"); QueueLine("200 Posting is permitted yo");
} else { }
else
{
QueueLine("201 Posting is not permitted yo"); QueueLine("201 Posting is not permitted yo");
} }
} else if (m == "STREAM") { }
else if (m == "STREAM")
{
m_mode = m; m_mode = m;
if (PostingAllowed()) { if (PostingAllowed())
{
QueueLine("203 Streaming enabled"); QueueLine("203 Streaming enabled");
} else { }
else
{
QueueLine("483 Streaming Denied"); QueueLine("483 Streaming Denied");
} }
} else { }
else
{
// unknown mode // unknown mode
QueueLine("500 Unknown mode"); QueueLine("500 Unknown mode");
} }
} }
void NNTPServerHandler::EnterState(State st) void NNTPServerHandler::EnterState(State st)
{ {
std::cerr << "enter state " << st << std::endl; std::cerr << "enter state " << st << std::endl;
m_state = st; m_state = st;
} }
void NNTPServerHandler::Quit() void NNTPServerHandler::Quit()
{ {
EnterState(eStateQuit); EnterState(eStateQuit);
QueueLine("205 quitting"); QueueLine("205 quitting");
} }
bool NNTPServerHandler::ShouldClose() bool NNTPServerHandler::ShouldClose() { return m_state == eStateQuit; }
{
return m_state == eStateQuit;
}
bool NNTPServerHandler::PostingAllowed() bool NNTPServerHandler::PostingAllowed() { return m_authed || m_auth == nullptr; }
{
return m_authed || m_auth == nullptr;
}
void NNTPServerHandler::Greet() void NNTPServerHandler::Greet()
{ {
if(PostingAllowed()) if (PostingAllowed())
QueueLine("200 Posting allowed"); QueueLine("200 Posting allowed");
else else
QueueLine("201 Posting not allowed"); QueueLine("201 Posting not allowed");
} }
void NNTPServerHandler::SetAuth(CredDB_ptr creds) void NNTPServerHandler::SetAuth(CredDB_ptr creds) { m_auth = creds; }
{
m_auth = creds;
}
} }

View File

@ -1,85 +1,62 @@
#include <nntpchan/nntp_server.hpp>
#include <nntpchan/nntp_auth.hpp>
#include <nntpchan/nntp_handler.hpp>
#include <nntpchan/net.hpp>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <nntpchan/net.hpp>
#include <nntpchan/nntp_auth.hpp>
#include <nntpchan/nntp_handler.hpp>
#include <nntpchan/nntp_server.hpp>
#include <sstream> #include <sstream>
namespace nntpchan namespace nntpchan
{ {
NNTPServer::NNTPServer(uv_loop_t * loop) : Server(loop), m_frontend(nullptr) {} NNTPServer::NNTPServer(uv_loop_t *loop) : Server(loop), m_frontend(nullptr) {}
NNTPServer::~NNTPServer() NNTPServer::~NNTPServer() {}
{
}
IServerConn * NNTPServer::CreateConn(uv_stream_t * s) IServerConn *NNTPServer::CreateConn(uv_stream_t *s)
{ {
CredDB_ptr creds; CredDB_ptr creds;
std::ifstream i; std::ifstream i;
i.open(m_logindbpath); i.open(m_logindbpath);
if(i.is_open()) creds = std::make_shared<HashedFileDB>(m_logindbpath); if (i.is_open())
creds = std::make_shared<HashedFileDB>(m_logindbpath);
NNTPServerHandler * handler = new NNTPServerHandler(m_storagePath); NNTPServerHandler *handler = new NNTPServerHandler(m_storagePath);
if(creds) if (creds)
handler->SetAuth(creds); handler->SetAuth(creds);
NNTPServerConn * conn = new NNTPServerConn(GetLoop(), s, this, handler); NNTPServerConn *conn = new NNTPServerConn(GetLoop(), s, this, handler);
return conn; return conn;
} }
void NNTPServer::SetLoginDB(const std::string path) void NNTPServer::SetLoginDB(const std::string path) { m_logindbpath = path; }
void NNTPServer::SetStoragePath(const std::string &path) { m_storagePath = path; }
void NNTPServer::SetInstanceName(const std::string &name) { m_servername = name; }
void NNTPServer::SetFrontend(Frontend *f) { m_frontend.reset(f); }
std::string NNTPServer::InstanceName() const { return m_servername; }
void NNTPServer::OnAcceptError(int status) { std::cerr << "nntpserver::accept() " << uv_strerror(status) << std::endl; }
void NNTPServerConn::SendNextReply()
{
IConnHandler *handler = GetHandler();
while (handler->HasNextLine())
{ {
m_logindbpath = path;
}
void NNTPServer::SetStoragePath(const std::string & path)
{
m_storagePath = path;
}
void NNTPServer::SetInstanceName(const std::string & name)
{
m_servername = name;
}
void NNTPServer::SetFrontend(Frontend * f)
{
m_frontend.reset(f);
}
std::string NNTPServer::InstanceName() const
{
return m_servername;
}
void NNTPServer::OnAcceptError(int status)
{
std::cerr << "nntpserver::accept() " << uv_strerror(status) << std::endl;
}
void NNTPServerConn::SendNextReply()
{
IConnHandler * handler = GetHandler();
while(handler->HasNextLine()) {
auto line = handler->GetNextLine(); auto line = handler->GetNextLine();
SendString(line + "\r\n"); SendString(line + "\r\n");
} }
} }
void NNTPServerConn::Greet()
void NNTPServerConn::Greet() {
{ IConnHandler *handler = GetHandler();
IConnHandler * handler = GetHandler();
handler->Greet(); handler->Greet();
SendNextReply(); SendNextReply();
} }
} }

View File

@ -1,49 +1,45 @@
#include <nntpchan/sanitize.hpp>
#include <algorithm> #include <algorithm>
#include <regex>
#include <cctype> #include <cctype>
#include <nntpchan/sanitize.hpp>
#include <regex>
namespace nntpchan namespace nntpchan
{ {
std::string NNTPSanitizeLine(const std::string & str) std::string NNTPSanitizeLine(const std::string &str)
{ {
if(str == ".") return " ."; if (str == ".")
return " .";
std::string sane; std::string sane;
sane += str; sane += str;
const char ch = ' '; const char ch = ' ';
std::replace_if(sane.begin(), sane.end(), [](unsigned char ch) -> bool { return iscntrl(ch); } , ch); std::replace_if(sane.begin(), sane.end(), [](unsigned char ch) -> bool { return iscntrl(ch); }, ch);
return sane; return sane;
} }
std::string ToLower(const std::string & str) std::string ToLower(const std::string &str)
{ {
std::string lower = str; std::string lower = str;
std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char ch) -> unsigned char { return std::tolower(ch); } ); std::transform(lower.begin(), lower.end(), lower.begin(),
[](unsigned char ch) -> unsigned char { return std::tolower(ch); });
return lower; return lower;
} }
static const std::regex re_ValidMessageID("^<[a-zA-Z0-9$\\._]{2,128}@[a-zA-Z0-9\\-\\.]{2,63}>$"); static const std::regex re_ValidMessageID("^<[a-zA-Z0-9$\\._]{2,128}@[a-zA-Z0-9\\-\\.]{2,63}>$");
bool IsValidMessageID(const std::string & msgid) bool IsValidMessageID(const std::string &msgid) { 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}$"); static const std::regex re_ValidNewsgroup("^[a-zA-Z][a-zA-Z0-9.]{1,128}$");
bool IsValidNewsgroup(const std::string & msgid) bool IsValidNewsgroup(const std::string &msgid) { return std::regex_search(msgid, re_ValidNewsgroup) == 1; }
{
return std::regex_search(msgid, re_ValidNewsgroup) == 1;
}
std::string StripWhitespaces(const std::string & str) std::string StripWhitespaces(const std::string &str)
{ {
std::string stripped; std::string stripped;
for(const auto & ch : str) for (const auto &ch : str)
if(!(std::isspace(ch)||std::iscntrl(ch))) if (!(std::isspace(ch) || std::iscntrl(ch)))
stripped += ch; stripped += ch;
return stripped; return stripped;
} }
} }

View File

@ -1,107 +1,111 @@
#include <nntpchan/buffer.hpp>
#include <nntpchan/server.hpp>
#include <nntpchan/net.hpp>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <nntpchan/buffer.hpp>
#include <nntpchan/net.hpp>
#include <nntpchan/server.hpp>
namespace nntpchan namespace nntpchan
{ {
Server::Server(uv_loop_t * loop) Server::Server(uv_loop_t *loop)
{ {
m_loop = loop; m_loop = loop;
uv_tcp_init(m_loop, &m_server); uv_tcp_init(m_loop, &m_server);
m_server.data = this; m_server.data = this;
} }
void Server::Close() void Server::Close()
{ {
std::cout << "Close server" << std::endl; std::cout << "Close server" << std::endl;
uv_close((uv_handle_t*)&m_server, [](uv_handle_t * s) { uv_close((uv_handle_t *)&m_server, [](uv_handle_t *s) {
Server * self = (Server*)s->data; Server *self = (Server *)s->data;
if (self) delete self; if (self)
delete self;
s->data = nullptr; s->data = nullptr;
}); });
} }
void Server::Bind(const std::string & addr) void Server::Bind(const std::string &addr)
{ {
auto saddr = ParseAddr(addr); auto saddr = ParseAddr(addr);
assert(uv_tcp_bind(*this, saddr, 0) == 0); assert(uv_tcp_bind(*this, saddr, 0) == 0);
auto cb = [] (uv_stream_t * s, int status) { auto cb = [](uv_stream_t *s, int status) {
Server * self = (Server *) s->data; Server *self = (Server *)s->data;
self->OnAccept(s, status); self->OnAccept(s, status);
}; };
assert(uv_listen(*this, 5, cb) == 0); assert(uv_listen(*this, 5, cb) == 0);
} }
void Server::OnAccept(uv_stream_t * s, int status) void Server::OnAccept(uv_stream_t *s, int status)
{
if (status < 0)
{ {
if(status < 0) {
OnAcceptError(status); OnAcceptError(status);
return; return;
} }
IServerConn * conn = CreateConn(s); IServerConn *conn = CreateConn(s);
assert(conn); assert(conn);
m_conns.push_back(conn); m_conns.push_back(conn);
conn->Greet(); conn->Greet();
} }
void Server::RemoveConn(IServerConn * conn) void Server::RemoveConn(IServerConn *conn)
{ {
auto itr = m_conns.begin(); auto itr = m_conns.begin();
while(itr != m_conns.end()) while (itr != m_conns.end())
{ {
if(*itr == conn) if (*itr == conn)
itr = m_conns.erase(itr); itr = m_conns.erase(itr);
else else
++itr; ++itr;
} }
} }
void IConnHandler::QueueLine(const std::string & line) void IConnHandler::QueueLine(const std::string &line) { m_sendlines.push_back(line); }
{
m_sendlines.push_back(line);
}
bool IConnHandler::HasNextLine() bool IConnHandler::HasNextLine() { return m_sendlines.size() > 0; }
{
return m_sendlines.size() > 0;
}
std::string IConnHandler::GetNextLine() std::string IConnHandler::GetNextLine()
{ {
std::string line = m_sendlines[0]; std::string line = m_sendlines[0];
m_sendlines.pop_front(); m_sendlines.pop_front();
return line; return line;
} }
IServerConn::IServerConn(uv_loop_t * l, uv_stream_t * st, Server * parent, IConnHandler * h) IServerConn::IServerConn(uv_loop_t *l, uv_stream_t *st, Server *parent, IConnHandler *h)
{ {
m_loop = l; m_loop = l;
m_parent = parent; m_parent = parent;
m_handler = h; m_handler = h;
uv_tcp_init(l, &m_conn); uv_tcp_init(l, &m_conn);
m_conn.data = this; m_conn.data = this;
uv_accept(st, (uv_stream_t*) &m_conn); uv_accept(st, (uv_stream_t *)&m_conn);
uv_read_start((uv_stream_t*) &m_conn, [] (uv_handle_t * h, size_t s, uv_buf_t * b) { uv_read_start((uv_stream_t *)&m_conn,
IServerConn * self = (IServerConn*) h->data; [](uv_handle_t *h, size_t s, uv_buf_t *b) {
if(self == nullptr) return; IServerConn *self = (IServerConn *)h->data;
if (self == nullptr)
return;
b->base = new char[s]; b->base = new char[s];
}, [] (uv_stream_t * s, ssize_t nread, const uv_buf_t * b) { },
IServerConn * self = (IServerConn*) s->data; [](uv_stream_t *s, ssize_t nread, const uv_buf_t *b) {
if(self == nullptr) { IServerConn *self = (IServerConn *)s->data;
if(b->base) if (self == nullptr)
delete [] b->base; {
if (b->base)
delete[] b->base;
return; return;
} }
if(nread > 0) { if (nread > 0)
{
self->m_handler->OnData(b->base, nread); self->m_handler->OnData(b->base, nread);
self->SendNextReply(); self->SendNextReply();
if(self->m_handler->ShouldClose()) if (self->m_handler->ShouldClose())
self->Close(); self->Close();
delete [] b->base; delete[] b->base;
} else { }
if (nread != UV_EOF) { else
{
if (nread != UV_EOF)
{
std::cerr << "error in nntp server conn alloc: "; std::cerr << "error in nntp server conn alloc: ";
std::cerr << uv_strerror(nread); std::cerr << uv_strerror(nread);
std::cerr << std::endl; std::cerr << std::endl;
@ -110,32 +114,29 @@ namespace nntpchan
self->Close(); self->Close();
} }
}); });
} }
IServerConn::~IServerConn() IServerConn::~IServerConn() { delete m_handler; }
{
delete m_handler;
}
void IServerConn::SendString(const std::string & str) void IServerConn::SendString(const std::string &str)
{ {
WriteBuffer * b = new WriteBuffer(str); WriteBuffer *b = new WriteBuffer(str);
uv_write(&b->w, (uv_stream_t*)&m_conn, &b->b, 1, [](uv_write_t * w, int status) { uv_write(&b->w, (uv_stream_t *)&m_conn, &b->b, 1, [](uv_write_t *w, int status) {
(void) status; (void)status;
WriteBuffer * wb = (WriteBuffer *) w->data; WriteBuffer *wb = (WriteBuffer *)w->data;
if(wb) if (wb)
delete wb; delete wb;
}); });
} }
void IServerConn::Close() void IServerConn::Close()
{ {
m_parent->RemoveConn(this); m_parent->RemoveConn(this);
uv_close((uv_handle_t*)&m_conn, [] (uv_handle_t * s) { uv_close((uv_handle_t *)&m_conn, [](uv_handle_t *s) {
IServerConn * self = (IServerConn*) s->data; IServerConn *self = (IServerConn *)s->data;
if(self) if (self)
delete self; delete self;
s->data = nullptr; s->data = nullptr;
}); });
} }
} }

View File

@ -27,42 +27,45 @@ extern "C" {
/* for uint32_t */ /* for uint32_t */
#include <stdint.h> #include <stdint.h>
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
/* blk0() and blk() perform the initial expand. */ /* blk0() and blk() perform the initial expand. */
/* I got the idea of expanding during the round function from SSLeay */ /* I got the idea of expanding during the round function from SSLeay */
#if BYTE_ORDER == LITTLE_ENDIAN #if BYTE_ORDER == LITTLE_ENDIAN
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ #define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF))
|(rol(block->l[i],8)&0x00FF00FF))
#elif BYTE_ORDER == BIG_ENDIAN #elif BYTE_ORDER == BIG_ENDIAN
#define blk0(i) block->l[i] #define blk0(i) block->l[i]
#else #else
#error "Endianness not defined!" #error "Endianness not defined!"
#endif #endif
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ #define blk(i) \
^block->l[(i+2)&15]^block->l[i&15],1)) (block->l[i & 15] = \
rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R0(v, w, x, y, z, i) \
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); w = rol(w, 30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R1(v, w, x, y, z, i) \
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
w = rol(w, 30);
#define R2(v, w, x, y, z, i) \
z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
w = rol(w, 30);
#define R3(v, w, x, y, z, i) \
z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
w = rol(w, 30);
#define R4(v, w, x, y, z, i) \
z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
w = rol(w, 30);
/* Hash a single 512-bit block. This is the core of the algorithm. */ /* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform( void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
uint32_t state[5],
const unsigned char buffer[64]
)
{ {
uint32_t a, b, c, d, e; uint32_t a, b, c, d, e;
typedef union typedef union {
{
unsigned char c[64]; unsigned char c[64];
uint32_t l[16]; uint32_t l[16];
} CHAR64LONG16; } CHAR64LONG16;
@ -77,7 +80,7 @@ void SHA1Transform(
* And the result is written through. I threw a "const" in, hoping * And the result is written through. I threw a "const" in, hoping
* this will cause a diagnostic. * this will cause a diagnostic.
*/ */
CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer; CHAR64LONG16 *block = (const CHAR64LONG16 *)buffer;
#endif #endif
/* Copy context->state[] to working vars */ /* Copy context->state[] to working vars */
a = state[0]; a = state[0];
@ -179,12 +182,9 @@ void SHA1Transform(
#endif #endif
} }
/* SHA1Init - Initialize new context */ /* SHA1Init - Initialize new context */
void SHA1Init( void SHA1Init(SHA1_CTX *context)
SHA1_CTX * context
)
{ {
/* SHA1 initialization constants */ /* SHA1 initialization constants */
context->state[0] = 0x67452301; context->state[0] = 0x67452301;
@ -195,14 +195,9 @@ void SHA1Init(
context->count[0] = context->count[1] = 0; context->count[0] = context->count[1] = 0;
} }
/* Run your data through this. */ /* Run your data through this. */
void SHA1Update( void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32_t len)
SHA1_CTX * context,
const unsigned char *data,
uint32_t len
)
{ {
uint32_t i; uint32_t i;
@ -228,13 +223,9 @@ void SHA1Update(
memcpy(&context->buffer[j], &data[i], len - i); memcpy(&context->buffer[j], &data[i], len - i);
} }
/* Add padding and return the message digest. */ /* Add padding and return the message digest. */
void SHA1Final( void SHA1Final(unsigned char digest[20], SHA1_CTX *context)
unsigned char digest[20],
SHA1_CTX * context
)
{ {
unsigned i; unsigned i;
@ -261,7 +252,8 @@ void SHA1Final(
#else #else
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ finalcount[i] =
(unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
} }
#endif #endif
c = 0200; c = 0200;
@ -274,54 +266,49 @@ void SHA1Final(
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
for (i = 0; i < 20; i++) for (i = 0; i < 20; i++)
{ {
digest[i] = (unsigned char) digest[i] = (unsigned char)((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);
} }
/* Wipe variables */ /* Wipe variables */
memset(context, '\0', sizeof(*context)); memset(context, '\0', sizeof(*context));
memset(&finalcount, '\0', sizeof(finalcount)); memset(&finalcount, '\0', sizeof(finalcount));
} }
void sha1( void sha1(uint8_t *hash_out, const uint8_t *str, size_t len)
uint8_t *hash_out,
const uint8_t *str,
size_t len)
{ {
SHA1_CTX ctx; SHA1_CTX ctx;
size_t ii; size_t ii;
SHA1Init(&ctx); SHA1Init(&ctx);
for (ii=0; ii<len; ii+=1) for (ii = 0; ii < len; ii += 1)
SHA1Update(&ctx, str + ii, 1); SHA1Update(&ctx, str + ii, 1);
SHA1Final(hash_out, &ctx); SHA1Final(hash_out, &ctx);
} }
} }
namespace nntpchan namespace nntpchan
{ {
static inline char nibble_to_char(uint8_t n) static inline char nibble_to_char(uint8_t n)
{ {
if(n >= 10) if (n >= 10)
return n + 87; return n + 87;
else else
return n + 48; return n + 48;
} }
std::string sha1_hex(const std::string & data) std::string sha1_hex(const std::string &data)
{ {
uint8_t digest[20]; uint8_t digest[20];
const uint8_t * ptr = (uint8_t*) data.c_str(); const uint8_t *ptr = (uint8_t *)data.c_str();
sha1(digest, ptr, data.size()); sha1(digest, ptr, data.size());
std::string out; std::string out;
std::size_t idx = 0; std::size_t idx = 0;
while(idx < 20) while (idx < 20)
{ {
out += nibble_to_char((digest[idx] & 0xf0) >> 8) + nibble_to_char(digest[idx] & 0x0f); out += nibble_to_char((digest[idx] & 0xf0) >> 8) + nibble_to_char(digest[idx] & 0x0f);
++idx; ++idx;
} }
return out; return out;
} }
} }

View File

@ -1,34 +1,31 @@
#include <nntpchan/staticfile_frontend.hpp>
#include <nntpchan/file_handle.hpp>
#include <nntpchan/sanitize.hpp>
#include <nntpchan/mime.hpp>
#include <nntpchan/sha1.hpp>
#include <any> #include <any>
#include <iostream> #include <iostream>
#include <nntpchan/file_handle.hpp>
#include <nntpchan/mime.hpp>
#include <nntpchan/sanitize.hpp>
#include <nntpchan/sha1.hpp>
#include <nntpchan/staticfile_frontend.hpp>
#include <set> #include <set>
#include <sstream> #include <sstream>
namespace nntpchan namespace nntpchan
{ {
StaticFileFrontend::StaticFileFrontend(TemplateEngine *tmpl, const std::string &templateDir, const std::string &outDir,
uint32_t pages)
: m_TemplateEngine(tmpl), m_TemplateDir(templateDir), m_OutDir(outDir), m_Pages(pages)
{
}
StaticFileFrontend::StaticFileFrontend(TemplateEngine * tmpl, const std::string & templateDir, const std::string & outDir, uint32_t pages) : void StaticFileFrontend::ProcessNewMessage(const fs::path &fpath)
m_TemplateEngine(tmpl), {
m_TemplateDir(templateDir),
m_OutDir(outDir),
m_Pages(pages)
{
}
void StaticFileFrontend::ProcessNewMessage(const fs::path & fpath)
{
std::clog << "process message " << fpath << std::endl; std::clog << "process message " << fpath << std::endl;
auto file = OpenFile(fpath, eRead); auto file = OpenFile(fpath, eRead);
if(file) if (file)
{ {
// read header // read header
RawHeader header; RawHeader header;
if(!ReadHeader(file, header)) if (!ReadHeader(file, header))
{ {
std::clog << "failed to read mime header" << std::endl; std::clog << "failed to read mime header" << std::endl;
return; return;
@ -36,14 +33,13 @@ namespace nntpchan
// read body // read body
auto findMsgidFunc = [](const std::pair<std::string, std::string> &item) -> bool {
auto findMsgidFunc = [](const std::pair<std::string, std::string> & item) -> bool {
auto lower = ToLower(item.first); auto lower = ToLower(item.first);
return (lower == "message-id") || (lower == "messageid"); return (lower == "message-id") || (lower == "messageid");
}; };
auto msgid_itr = std::find_if(header.begin(), header.end(), findMsgidFunc); auto msgid_itr = std::find_if(header.begin(), header.end(), findMsgidFunc);
if(msgid_itr == std::end(header)) if (msgid_itr == std::end(header))
{ {
std::clog << "no message id for file " << fpath << std::endl; std::clog << "no message id for file " << fpath << std::endl;
return; return;
@ -51,7 +47,7 @@ namespace nntpchan
std::string msgid = StripWhitespaces(msgid_itr->second); std::string msgid = StripWhitespaces(msgid_itr->second);
if(!IsValidMessageID(msgid)) if (!IsValidMessageID(msgid))
{ {
std::clog << "invalid message-id: " << msgid << std::endl; std::clog << "invalid message-id: " << msgid << std::endl;
return; return;
@ -59,21 +55,21 @@ namespace nntpchan
std::string rootmsgid; std::string rootmsgid;
auto findReferences = [](const std::pair<std::string, std::string> & item) -> bool { auto findReferences = [](const std::pair<std::string, std::string> &item) -> bool {
auto lower = ToLower(item.first); auto lower = ToLower(item.first);
return lower == "references"; return lower == "references";
}; };
auto references_itr = std::find_if(header.begin(), header.end(), findReferences); auto references_itr = std::find_if(header.begin(), header.end(), findReferences);
if(references_itr == std::end(header) || StripWhitespaces(references_itr->second).size() == 0) if (references_itr == std::end(header) || StripWhitespaces(references_itr->second).size() == 0)
{ {
rootmsgid = msgid; rootmsgid = msgid;
} }
else else
{ {
const auto & s = references_itr->second; const auto &s = references_itr->second;
auto checkfunc = [] (unsigned char ch) -> bool { return std::isspace(ch) || std::iscntrl(ch); }; auto checkfunc = [](unsigned char ch) -> bool { return std::isspace(ch) || std::iscntrl(ch); };
if(std::count_if(s.begin(), s.end(), checkfunc)) if (std::count_if(s.begin(), s.end(), checkfunc))
{ {
/** split off first element */ /** split off first element */
auto idx = std::find_if(s.begin(), s.end(), checkfunc); auto idx = std::find_if(s.begin(), s.end(), checkfunc);
@ -85,44 +81,16 @@ namespace nntpchan
} }
} }
std::string rootmsgid_hash = sha1_hex(rootmsgid); std::string rootmsgid_hash = sha1_hex(rootmsgid);
fs::path threadFilePath = m_OutDir / fs::path("thread-" + rootmsgid_hash + ".html");
nntpchan::model::Thread thread;
if(!m_MessageDB)
{
std::clog << "no message database" << std::endl;
return;
}
if(!m_MessageDB->LoadThread(thread, rootmsgid))
{
std::clog << "cannot find thread with root " << rootmsgid << std::endl;
return;
}
TemplateEngine::Args_t thread_args;
thread_args["posts"] = thread;
if(m_TemplateEngine)
{
FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
if(!out || !m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out))
{
std::clog << "failed to write " << threadFilePath << std::endl;
return;
}
}
std::set<std::string> newsgroups_list; std::set<std::string> newsgroups_list;
auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> & item) -> bool auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> &item) -> bool {
{
return ToLower(item.first) == "newsgroups"; return ToLower(item.first) == "newsgroups";
}; };
auto group = std::find_if(header.begin(), header.end(), findNewsgroupsFunc); auto group = std::find_if(header.begin(), header.end(), findNewsgroupsFunc);
if(group == std::end(header)) if (group == std::end(header))
{ {
std::clog << "no newsgroups header" << std::endl; std::clog << "no newsgroups header" << std::endl;
return; return;
@ -130,37 +98,63 @@ namespace nntpchan
std::istringstream input(group->second); std::istringstream input(group->second);
std::string newsgroup; std::string newsgroup;
while(std::getline(input, newsgroup, ' ')) while (std::getline(input, newsgroup, ' '))
{ {
if(IsValidNewsgroup(newsgroup)) if (IsValidNewsgroup(newsgroup))
newsgroups_list.insert(newsgroup); newsgroups_list.insert(newsgroup);
} }
fs::path threadFilePath = m_OutDir / fs::path("thread-" + rootmsgid_hash + ".html");
nntpchan::model::Thread thread;
if (!m_MessageDB)
{
std::clog << "no message database" << std::endl;
return;
}
if (!m_MessageDB->LoadThread(thread, rootmsgid))
{
std::clog << "cannot find thread with root " << rootmsgid << std::endl;
return;
}
TemplateEngine::Args_t thread_args;
thread_args["posts"] = thread;
if (m_TemplateEngine)
{
FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
if (!out || !m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out))
{
std::clog << "failed to write " << threadFilePath << std::endl;
return;
}
}
nntpchan::model::BoardPage page; nntpchan::model::BoardPage page;
for(const auto & name : newsgroups_list) for (const auto &name : newsgroups_list)
{ {
uint32_t pageno = 0; uint32_t pageno = 0;
while(pageno < m_Pages) while (pageno < m_Pages)
{ {
page.clear(); page.clear();
if(!m_MessageDB->LoadBoardPage(page, name, 10, m_Pages)) if (!m_MessageDB->LoadBoardPage(page, name, 10, m_Pages))
{ {
std::clog << "cannot load board page "<< pageno << " for " << name << std::endl; std::clog << "cannot load board page " << pageno << " for " << name << std::endl;
break; break;
} }
TemplateEngine::Args_t page_args; TemplateEngine::Args_t page_args;
page_args["group"] = name; page_args["group"] = name;
page_args["threads"] = page; page_args["threads"] = page;
page_args["pageno"] = std::to_string(pageno); page_args["pageno"] = std::to_string(pageno);
if(pageno) if (pageno)
page_args["prev_pageno"] = std::to_string(pageno-1); page_args["prev_pageno"] = std::to_string(pageno - 1);
if(pageno+1 < m_Pages) if (pageno + 1 < m_Pages)
page_args["next_pageno"] = std::to_string(pageno+1); page_args["next_pageno"] = std::to_string(pageno + 1);
fs::path boardPageFilename(name + "-" + std::to_string(pageno) + ".html"); fs::path boardPageFilename(name + "-" + std::to_string(pageno) + ".html");
if(m_TemplateEngine) if (m_TemplateEngine)
{ {
fs::path outfile = m_OutDir / boardPageFilename; fs::path outfile = m_OutDir / boardPageFilename;
FileHandle_ptr out = OpenFile(outfile, eWrite); FileHandle_ptr out = OpenFile(outfile, eWrite);
if(out) if (out)
m_TemplateEngine->WriteTemplate("board.mustache", page_args, out); m_TemplateEngine->WriteTemplate("board.mustache", page_args, out);
else else
std::clog << "failed to open board page " << outfile << std::endl; std::clog << "failed to open board page " << outfile << std::endl;
@ -170,17 +164,9 @@ namespace nntpchan
} }
} }
} }
} }
bool StaticFileFrontend::AcceptsNewsgroup(const std::string &newsgroup) { return IsValidNewsgroup(newsgroup); }
bool StaticFileFrontend::AcceptsNewsgroup(const std::string & newsgroup)
{ bool StaticFileFrontend::AcceptsMessage(const std::string &msgid) { return IsValidMessageID(msgid); }
return IsValidNewsgroup(newsgroup);
}
bool StaticFileFrontend::AcceptsMessage(const std::string & msgid)
{
return IsValidMessageID(msgid);
}
} }

View File

@ -1,59 +1,71 @@
#include <nntpchan/storage.hpp>
#include <nntpchan/sanitize.hpp>
#include <cassert> #include <cassert>
#include <nntpchan/sanitize.hpp>
#include <nntpchan/storage.hpp>
#include <sstream> #include <sstream>
namespace nntpchan namespace nntpchan
{ {
ArticleStorage::ArticleStorage()
{
}
ArticleStorage::ArticleStorage(const fs::path & fpath) { ArticleStorage::ArticleStorage(const fs::path &fpath) { SetPath(fpath); }
SetPath(fpath);
}
ArticleStorage::~ArticleStorage() ArticleStorage::~ArticleStorage() {}
{
}
void ArticleStorage::SetPath(const fs::path & fpath) void ArticleStorage::SetPath(const fs::path &fpath)
{ {
basedir = fpath; basedir = fpath;
fs::create_directories(basedir); fs::create_directories(basedir);
assert(init_skiplist("posts_skiplist")); assert(init_skiplist("posts_skiplist"));
} }
bool ArticleStorage::init_skiplist(const std::string & subdir) const bool ArticleStorage::init_skiplist(const std::string &subdir) const
{ {
fs::path skiplist = basedir / fs::path(subdir); fs::path skiplist = basedir / fs::path(subdir);
fs::create_directories(skiplist); fs::create_directories(skiplist);
const auto subdirs = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; const auto subdirs = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
for (const auto & s : subdirs) for (const auto &s : subdirs)
fs::create_directories(skiplist / s); fs::create_directories(skiplist / s);
return true; return true;
} }
bool ArticleStorage::Accept(const std::string& msgid) const bool ArticleStorage::Accept(const std::string &msgid) const
{ {
if (!IsValidMessageID(msgid)) return false; if (!IsValidMessageID(msgid))
return false;
auto p = MessagePath(msgid); auto p = MessagePath(msgid);
return !fs::exists(p); return !fs::exists(p);
} }
fs::path ArticleStorage::MessagePath(const std::string & msgid) const fs::path ArticleStorage::MessagePath(const std::string &msgid) const { return basedir / msgid; }
{
return basedir / msgid; FileHandle_ptr ArticleStorage::OpenRead(const std::string &msgid) const { return OpenFile(MessagePath(msgid), eRead); }
}
FileHandle_ptr ArticleStorage::OpenWrite(const std::string &msgid) const
FileHandle_ptr ArticleStorage::OpenRead(const std::string & msgid) const {
{ return OpenFile(MessagePath(msgid), eWrite);
return OpenFile(MessagePath(msgid), eRead); }
}
bool ArticleStorage::LoadBoardPage(BoardPage &board, const std::string &newsgroup, uint32_t perpage,
FileHandle_ptr ArticleStorage::OpenWrite(const std::string & msgid) const uint32_t page) const
{ {
return OpenFile(MessagePath(msgid), eWrite); (void)board;
} (void)newsgroup;
(void)perpage;
(void)page;
return false;
}
bool ArticleStorage::FindThreadByHash(const std::string &hashhex, std::string &msgid) const
{
(void)hashhex;
(void)msgid;
return false;
}
bool ArticleStorage::LoadThread(Thread &thread, const std::string &rootmsgid) const
{
(void)thread;
(void)rootmsgid;
return false;
}
/** ensure symlinks are formed for this article by message id */
void ArticleStorage::EnsureSymlinks(const std::string &msgid) const { (void)msgid; }
} }

View File

@ -1,24 +1,27 @@
#include <nntpchan/template_engine.hpp>
#include <nntpchan/sanitize.hpp>
#include <mstch/mstch.hpp>
#include <iostream> #include <iostream>
#include <mstch/mstch.hpp>
#include <nntpchan/sanitize.hpp>
#include <nntpchan/template_engine.hpp>
#include <sstream> #include <sstream>
namespace nntpchan namespace nntpchan
{ {
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; }; template <class... Ts> struct overloaded : Ts...
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; {
using Ts::operator()...;
};
template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
namespace mustache = mstch; namespace mustache = mstch;
static mustache::map post_to_map(const nntpchan::model::Post & post) static mustache::map post_to_map(const nntpchan::model::Post &post)
{ {
mustache::map m; mustache::map m;
mustache::array attachments; mustache::array attachments;
mustache::map h; mustache::map h;
for (const auto & att : nntpchan::model::GetAttachments(post)) for (const auto &att : nntpchan::model::GetAttachments(post))
{ {
mustache::map a; mustache::map a;
a["filename"] = nntpchan::model::GetFilename(att); a["filename"] = nntpchan::model::GetFilename(att);
@ -27,10 +30,10 @@ namespace nntpchan
attachments.push_back(a); attachments.push_back(a);
} }
for (const auto & item : nntpchan::model::GetHeader(post)) for (const auto &item : nntpchan::model::GetHeader(post))
{ {
mustache::array vals; mustache::array vals;
for(const auto & v : item.second) for (const auto &v : item.second)
vals.push_back(v); vals.push_back(v);
h[item.first] = vals; h[item.first] = vals;
} }
@ -39,64 +42,60 @@ namespace nntpchan
m["message"] = nntpchan::model::GetBody(post); m["message"] = nntpchan::model::GetBody(post);
m["header"] = h; m["header"] = h;
return m; return m;
} }
static mustache::map thread_to_map(const nntpchan::model::Thread & t) static mustache::map thread_to_map(const nntpchan::model::Thread &t)
{ {
mustache::map thread; mustache::map thread;
mustache::array posts; mustache::array posts;
for(const auto & post : t) for (const auto &post : t)
{ {
posts.push_back(post_to_map(post)); posts.push_back(post_to_map(post));
} }
auto & opHeader = nntpchan::model::GetHeader(t[0]); auto &opHeader = nntpchan::model::GetHeader(t[0]);
thread["title"] = nntpchan::model::HeaderIFind(opHeader, "subject", "None")[0]; thread["title"] = nntpchan::model::HeaderIFind(opHeader, "subject", "None")[0];
thread["posts"] = posts; thread["posts"] = posts;
return thread; return thread;
} }
struct MustacheTemplateEngine : public TemplateEngine struct MustacheTemplateEngine : public TemplateEngine
{ {
struct Impl struct Impl
{ {
Impl(const std::map<std::string, std::string> & partials) : m_partials(partials) {} Impl(const std::map<std::string, std::string> &partials) : m_partials(partials) {}
bool ParseTemplate(const FileHandle_ptr & in) bool ParseTemplate(const FileHandle_ptr &in)
{ {
std::stringstream str; std::stringstream str;
std::string line; std::string line;
while(std::getline(*in, line)) while (std::getline(*in, line))
str << line << "\n"; str << line << "\n";
m_tmplString = str.str(); m_tmplString = str.str();
return in->eof(); return in->eof();
} }
bool RenderFile(const Args_t & args, const FileHandle_ptr & out) bool RenderFile(const Args_t &args, const FileHandle_ptr &out)
{ {
mustache::map obj; mustache::map obj;
for (const auto & item : args) for (const auto &item : args)
{ {
std::visit(overloaded { std::visit(overloaded{[&obj, item](const nntpchan::model::Model &m) {
[&obj, item](const nntpchan::model::Model & m) { std::visit(overloaded{[&obj, item](const nntpchan::model::BoardPage &p) {
std::visit(overloaded {
[&obj, item](const nntpchan::model::BoardPage & p) {
mustache::array threads; mustache::array threads;
for (const auto & thread : p) for (const auto &thread : p)
{ {
threads.push_back(thread_to_map(thread)); threads.push_back(thread_to_map(thread));
} }
obj[item.first] = threads; obj[item.first] = threads;
}, },
[&obj, item](const nntpchan::model::Thread & t) { [&obj, item](const nntpchan::model::Thread &t) {
obj[item.first] = thread_to_map(t); obj[item.first] = thread_to_map(t);
} }},
}, m); m);
} },
,[&obj, item](const std::string & str) { [&obj, item](const std::string &str) { obj[item.first] = str; }},
obj[item.first] = str; item.second);
}
}, item.second);
} }
std::string str = mustache::render(m_tmplString, obj); std::string str = mustache::render(m_tmplString, obj);
@ -106,27 +105,27 @@ namespace nntpchan
} }
std::string m_tmplString; std::string m_tmplString;
const std::map<std::string, std::string> & m_partials; const std::map<std::string, std::string> &m_partials;
}; };
virtual bool WriteTemplate(const fs::path & fpath, 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); auto templFile = OpenFile(fpath, eRead);
if(!templFile) if (!templFile)
{ {
std::clog << "no such template at " << fpath << std::endl; std::clog << "no such template at " << fpath << std::endl;
return false; return false;
} }
std::map<std::string, std::string> partials; std::map<std::string, std::string> partials;
if(!LoadPartials(fpath.parent_path(), partials)) if (!LoadPartials(fpath.parent_path(), partials))
{ {
std::clog << "failed to load partials" << std::endl; std::clog << "failed to load partials" << std::endl;
return false; return false;
} }
Impl impl(partials); Impl impl(partials);
if(impl.ParseTemplate(templFile)) if (impl.ParseTemplate(templFile))
{ {
return impl.RenderFile(args, out); return impl.RenderFile(args, out);
} }
@ -135,32 +134,33 @@ namespace nntpchan
return false; return false;
} }
bool LoadPartials(fs::path dir, std::map<std::string, std::string> & partials) bool LoadPartials(fs::path dir, std::map<std::string, std::string> &partials)
{ {
const auto partial_files = { "header", "footer" }; const auto partial_files = {"header", "footer"};
for(const auto & fname : partial_files) for (const auto &fname : partial_files)
{ {
auto file = OpenFile(dir / fs::path(fname + std::string(".html")), eRead); auto file = OpenFile(dir / fs::path(fname + std::string(".html")), eRead);
if(!file) { if (!file)
{
std::clog << "no such partial: " << fname << std::endl; std::clog << "no such partial: " << fname << std::endl;
return false; return false;
} }
std::string line; std::string line;
std::stringstream input; std::stringstream input;
while(std::getline(*file, line)) while (std::getline(*file, line))
input << line << "\n"; input << line << "\n";
partials[fname] = input.str(); partials[fname] = input.str();
} }
return true; return true;
} }
}; };
TemplateEngine * CreateTemplateEngine(const std::string & dialect) TemplateEngine *CreateTemplateEngine(const std::string &dialect)
{ {
auto d = ToLower(dialect); auto d = ToLower(dialect);
if(d == "mustache") if (d == "mustache")
return new MustacheTemplateEngine; return new MustacheTemplateEngine;
else else
return nullptr; return nullptr;
} }
} }

View File

@ -1,13 +1,11 @@
#include <nntpchan/exec_frontend.hpp>
#include <nntpchan/sanitize.hpp>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <nntpchan/exec_frontend.hpp>
#include <nntpchan/sanitize.hpp>
int main(int, char *[])
int main(int , char * [])
{ {
nntpchan::Frontend_ptr f (new nntpchan::ExecFrontend("./contrib/nntpchan.sh")); nntpchan::Frontend_ptr f(new nntpchan::ExecFrontend("./contrib/nntpchan.sh"));
assert(f->AcceptsMessage("<test@server>")); assert(f->AcceptsMessage("<test@server>"));
assert(f->AcceptsNewsgroup("overchan.test")); assert(f->AcceptsNewsgroup("overchan.test"));
assert(nntpchan::IsValidMessageID("<test@test>")); assert(nntpchan::IsValidMessageID("<test@test>"));

View File

@ -3,21 +3,18 @@
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <string>
#include <iostream> #include <iostream>
#include <sodium.h> #include <sodium.h>
#include <string>
static void print_help(const std::string & exename) static void print_help(const std::string &exename)
{ {
std::cout << "usage: " << exename << " [help|gen|check]" << std::endl; std::cout << "usage: " << exename << " [help|gen|check]" << std::endl;
} }
static void print_long_help() static void print_long_help() {}
{
} static void gen_passwd(const std::string &username, const std::string &passwd)
static void gen_passwd(const std::string & username, const std::string & passwd)
{ {
std::array<uint8_t, 8> random; std::array<uint8_t, 8> random;
randombytes_buf(random.data(), random.size()); randombytes_buf(random.data(), random.size());
@ -29,57 +26,69 @@ static void gen_passwd(const std::string & username, const std::string & passwd)
std::cout << username << ":" << hash << ":" << salt << std::endl; std::cout << username << ":" << hash << ":" << salt << std::endl;
} }
static bool check_cred(const std::string &cred, const std::string &passwd)
static bool check_cred(const std::string & cred, const std::string & passwd)
{ {
auto idx = cred.find(":"); auto idx = cred.find(":");
if(idx == std::string::npos || idx == 0) return false; if (idx == std::string::npos || idx == 0)
std::string part = cred.substr(idx+1); return false;
std::string part = cred.substr(idx + 1);
idx = part.find(":"); idx = part.find(":");
if(idx == std::string::npos || idx == 0) return false; if (idx == std::string::npos || idx == 0)
std::string salt = part.substr(idx+1); return false;
std::string salt = part.substr(idx + 1);
std::string hash = part.substr(0, idx); std::string hash = part.substr(0, idx);
std::vector<uint8_t> h; std::vector<uint8_t> h;
if(!nntpchan::B64Decode(hash, h)) return false; if (!nntpchan::B64Decode(hash, h))
return false;
nntpchan::SHA512Digest d; nntpchan::SHA512Digest d;
std::string l = passwd + salt; std::string l = passwd + salt;
nntpchan::SHA512((const uint8_t*)l.data(), l.size(), d); nntpchan::SHA512((const uint8_t *)l.data(), l.size(), d);
return std::memcmp(h.data(), d.data(), d.size()) == 0; return std::memcmp(h.data(), d.data(), d.size()) == 0;
} }
int main(int argc, char * argv[]) int main(int argc, char *argv[])
{ {
assert(sodium_init() == 0); assert(sodium_init() == 0);
if(argc == 1) { if (argc == 1)
{
print_help(argv[0]); print_help(argv[0]);
return 1; return 1;
} }
std::string cmd(argv[1]); std::string cmd(argv[1]);
if (cmd == "help") { if (cmd == "help")
{
print_long_help(); print_long_help();
return 0; return 0;
} }
if (cmd == "gen") { if (cmd == "gen")
if(argc == 4) { {
if (argc == 4)
{
gen_passwd(argv[2], argv[3]); gen_passwd(argv[2], argv[3]);
return 0; return 0;
} else { }
else
{
std::cout << "usage: " << argv[0] << " gen username password" << std::endl; std::cout << "usage: " << argv[0] << " gen username password" << std::endl;
return 1; return 1;
} }
} }
if(cmd == "check" ) { if (cmd == "check")
{
std::string cred; std::string cred;
std::cout << "credential: " ; std::cout << "credential: ";
if(!std::getline(std::cin, cred)) { if (!std::getline(std::cin, cred))
{
return 1; return 1;
} }
std::string passwd; std::string passwd;
std::cout << "password: "; std::cout << "password: ";
if(!std::getline(std::cin, passwd)) { if (!std::getline(std::cin, passwd))
{
return 1; return 1;
} }
if(check_cred(cred, passwd)) { if (check_cred(cred, passwd))
{
std::cout << "okay" << std::endl; std::cout << "okay" << std::endl;
return 0; return 0;
} }

View File

@ -1,11 +1,9 @@
#include <nntpchan/exec_frontend.hpp>
#include <nntpchan/sanitize.hpp>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <nntpchan/exec_frontend.hpp>
#include <nntpchan/sanitize.hpp>
int main(int, char *[])
int main(int , char * [])
{ {
nntpchan::Frontend_ptr f(new nntpchan::ExecFrontend("./contrib/nntpchan.sh")); nntpchan::Frontend_ptr f(new nntpchan::ExecFrontend("./contrib/nntpchan.sh"));
assert(nntpchan::IsValidMessageID("<a28a71493831188@web.oniichan.onion>")); assert(nntpchan::IsValidMessageID("<a28a71493831188@web.oniichan.onion>"));