more nntpchan-daemon stuff
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#include "base64.hpp"
|
||||
#include <nntpchan/base64.hpp>
|
||||
|
||||
|
||||
// taken from i2pd
|
||||
|
@@ -1,17 +0,0 @@
|
||||
#ifndef NNTPCHAN_BASE64_HPP
|
||||
#define NNTPCHAN_BASE64_HPP
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
/** returns base64 encoded string */
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "buffer.hpp"
|
||||
#include <nntpchan/buffer.hpp>
|
||||
#include <cstring>
|
||||
|
||||
namespace nntpchan
|
||||
@@ -9,7 +9,7 @@ namespace nntpchan
|
||||
std::memcpy(buf, b, s);
|
||||
this->b = uv_buf_init(buf, s);
|
||||
w.data = this;
|
||||
};
|
||||
}
|
||||
|
||||
WriteBuffer::WriteBuffer(const std::string & s) : WriteBuffer(s.c_str(), s.size()) {}
|
||||
|
||||
|
@@ -1,19 +0,0 @@
|
||||
#ifndef NNTPCHAN_BUFFER_HPP
|
||||
#define NNTPCHAN_BUFFER_HPP
|
||||
#include <uv.h>
|
||||
#include <string>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
struct WriteBuffer
|
||||
{
|
||||
uv_write_t w;
|
||||
uv_buf_t b;
|
||||
|
||||
WriteBuffer(const std::string & s);
|
||||
WriteBuffer(const char * b, const size_t s);
|
||||
~WriteBuffer();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "crypto.hpp"
|
||||
#include <nntpchan/crypto.hpp>
|
||||
#include <sodium.h>
|
||||
#include <cassert>
|
||||
|
||||
|
@@ -1,22 +0,0 @@
|
||||
#ifndef NNTPCHAN_CRYPTO_HPP
|
||||
#define NNTPCHAN_CRYPTO_HPP
|
||||
|
||||
#include <sodium/crypto_hash.h>
|
||||
#include <array>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
typedef std::array<uint8_t, crypto_hash_BYTES> SHA512Digest;
|
||||
|
||||
void SHA512(const uint8_t * d, std::size_t l, SHA512Digest & h);
|
||||
|
||||
/** global crypto initializer */
|
||||
struct Crypto
|
||||
{
|
||||
Crypto();
|
||||
~Crypto();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "event.hpp"
|
||||
#include <nntpchan/event.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace nntpchan
|
||||
|
@@ -1,26 +0,0 @@
|
||||
#ifndef NNTPCHAN_EVENT_HPP
|
||||
#define NNTPCHAN_EVENT_HPP
|
||||
#include <uv.h>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
class Mainloop
|
||||
{
|
||||
public:
|
||||
|
||||
Mainloop();
|
||||
~Mainloop();
|
||||
|
||||
operator uv_loop_t * () const { return m_loop; }
|
||||
|
||||
void Run(uv_run_mode mode = UV_RUN_DEFAULT);
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
|
||||
uv_loop_t * m_loop;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "exec_frontend.hpp"
|
||||
#include <nntpchan/exec_frontend.hpp>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <errno.h>
|
||||
|
@@ -1,30 +0,0 @@
|
||||
#ifndef NNTPCHAN_EXEC_FRONTEND_HPP
|
||||
#define NNTPCHAN_EXEC_FRONTEND_HPP
|
||||
#include "frontend.hpp"
|
||||
#include <deque>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
class ExecFrontend : public Frontend
|
||||
{
|
||||
public:
|
||||
|
||||
ExecFrontend(const std::string & exe);
|
||||
|
||||
~ExecFrontend();
|
||||
|
||||
void ProcessNewMessage(const fs::path & fpath);
|
||||
bool AcceptsNewsgroup(const std::string & newsgroup);
|
||||
bool AcceptsMessage(const std::string & msgid);
|
||||
|
||||
private:
|
||||
|
||||
int Exec(std::deque<std::string> args);
|
||||
|
||||
private:
|
||||
std::string m_exec;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "file_handle.hpp"
|
||||
#include <nntpchan/file_handle.hpp>
|
||||
|
||||
|
||||
namespace nntpchan
|
||||
|
@@ -1,23 +0,0 @@
|
||||
#ifndef NNTPCHAN_FILE_HANDLE_HPP
|
||||
#define NNTPCHAN_FILE_HANDLE_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <experimental/filesystem>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
typedef std::unique_ptr<std::fstream> FileHandle_ptr;
|
||||
|
||||
enum FileMode
|
||||
{
|
||||
eRead,
|
||||
eWrite
|
||||
};
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
FileHandle_ptr OpenFile(const fs::path & fname, FileMode mode);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,29 +0,0 @@
|
||||
#ifndef NNTPCHAN_FRONTEND_HPP
|
||||
#define NNTPCHAN_FRONTEND_HPP
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <experimental/filesystem>
|
||||
namespace nntpchan
|
||||
{
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
/** @brief nntpchan frontend ui interface */
|
||||
class Frontend
|
||||
{
|
||||
public:
|
||||
|
||||
/** @brief process an inbound message stored at fpath that we have accepted. */
|
||||
virtual void ProcessNewMessage(const fs::path & fpath) = 0;
|
||||
|
||||
/** @brief return true if we take posts in a newsgroup */
|
||||
virtual bool AcceptsNewsgroup(const std::string & newsgroup) = 0;
|
||||
|
||||
/** @brief return true if we will accept a message given its message-id */
|
||||
virtual bool AcceptsMessage(const std::string & msgid) = 0;
|
||||
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<Frontend> Frontend_ptr;
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,6 +0,0 @@
|
||||
#ifndef NNTPCHAN_HTTP_HPP
|
||||
#define NNTPCHAN_HTTP_HPP
|
||||
|
||||
|
||||
|
||||
#endif
|
@@ -1,5 +0,0 @@
|
||||
#ifndef NNTPCHAN_HTTP_CLIENT_HPP
|
||||
#define NNTPCHAN_HTTP_CLIENT_HPP
|
||||
|
||||
|
||||
#endif
|
@@ -1,6 +0,0 @@
|
||||
#ifndef NNTPCHAN_HTTP_SERVER_HPP
|
||||
#define NNTPCHAN_HTTP_SERVER_HPP
|
||||
|
||||
|
||||
|
||||
#endif
|
@@ -1,186 +0,0 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) <2015> <carriez.md@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INI_HPP
|
||||
#define INI_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace INI {
|
||||
|
||||
struct Level
|
||||
{
|
||||
Level() : parent(NULL), depth(0) {}
|
||||
Level(Level* p) : parent(p), depth(0) {}
|
||||
|
||||
typedef std::map<std::string, std::string> value_map_t;
|
||||
typedef std::map<std::string, Level> section_map_t;
|
||||
typedef std::list<value_map_t::const_iterator> values_t;
|
||||
typedef std::list<section_map_t::const_iterator> sections_t;
|
||||
value_map_t values;
|
||||
section_map_t sections;
|
||||
values_t ordered_values; // original order in the ini file
|
||||
sections_t ordered_sections;
|
||||
Level* parent;
|
||||
size_t depth;
|
||||
|
||||
const std::string& operator[](const std::string& name) { return values[name]; }
|
||||
Level& operator()(const std::string& name) { return sections[name]; }
|
||||
};
|
||||
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser(const char* fn);
|
||||
Parser(std::istream& f) : f_(&f), ln_(0) { parse(top_); }
|
||||
Level& top() { return top_; }
|
||||
void dump(std::ostream& s) { dump(s, top(), ""); }
|
||||
|
||||
private:
|
||||
void dump(std::ostream& s, const Level& l, const std::string& sname);
|
||||
void parse(Level& l);
|
||||
void parseSLine(std::string& sname, size_t& depth);
|
||||
void err(const char* s);
|
||||
|
||||
private:
|
||||
Level top_;
|
||||
std::ifstream f0_;
|
||||
std::istream* f_;
|
||||
std::string line_;
|
||||
size_t ln_;
|
||||
};
|
||||
|
||||
inline void
|
||||
Parser::err(const char* s)
|
||||
{
|
||||
char buf[256];
|
||||
sprintf(buf, "%s on line #%ld", s, ln_);
|
||||
throw std::runtime_error(buf);
|
||||
}
|
||||
|
||||
inline std::string trim(const std::string& s)
|
||||
{
|
||||
char p[] = " \t\r\n";
|
||||
long sp = 0;
|
||||
long ep = s.length() - 1;
|
||||
for (; sp <= ep; ++sp)
|
||||
if (!strchr(p, s[sp])) break;
|
||||
for (; ep >= 0; --ep)
|
||||
if (!strchr(p, s[ep])) break;
|
||||
return s.substr(sp, ep-sp+1);
|
||||
}
|
||||
|
||||
inline
|
||||
Parser::Parser(const char* fn) : f0_(fn), f_(&f0_), ln_(0)
|
||||
{
|
||||
if (!f0_)
|
||||
throw std::runtime_error(std::string("failed to open file: ") + fn);
|
||||
|
||||
parse(top_);
|
||||
}
|
||||
|
||||
inline void
|
||||
Parser::parseSLine(std::string& sname, size_t& depth)
|
||||
{
|
||||
depth = 0;
|
||||
for (; depth < line_.length(); ++depth)
|
||||
if (line_[depth] != '[') break;
|
||||
|
||||
sname = line_.substr(depth, line_.length() - 2*depth);
|
||||
}
|
||||
|
||||
inline void
|
||||
Parser::parse(Level& l)
|
||||
{
|
||||
while (std::getline(*f_, line_)) {
|
||||
++ln_;
|
||||
if (line_[0] == '#' || line_[0] == ';') continue;
|
||||
line_ = trim(line_);
|
||||
if (line_.empty()) continue;
|
||||
if (line_[0] == '[') {
|
||||
size_t depth;
|
||||
std::string sname;
|
||||
parseSLine(sname, depth);
|
||||
Level* lp = NULL;
|
||||
Level* parent = &l;
|
||||
if (depth > l.depth + 1)
|
||||
err("section with wrong depth");
|
||||
if (l.depth == depth-1)
|
||||
lp = &l.sections[sname];
|
||||
else {
|
||||
lp = l.parent;
|
||||
size_t n = l.depth - depth;
|
||||
for (size_t i = 0; i < n; ++i) lp = lp->parent;
|
||||
parent = lp;
|
||||
lp = &lp->sections[sname];
|
||||
}
|
||||
if (lp->depth != 0)
|
||||
err("duplicate section name on the same level");
|
||||
if (!lp->parent) {
|
||||
lp->depth = depth;
|
||||
lp->parent = parent;
|
||||
}
|
||||
parent->ordered_sections.push_back(parent->sections.find(sname));
|
||||
parse(*lp);
|
||||
} else {
|
||||
size_t n = line_.find('=');
|
||||
if (n == std::string::npos)
|
||||
err("no '=' found");
|
||||
std::pair<Level::value_map_t::const_iterator, bool> res =
|
||||
l.values.insert(std::make_pair(trim(line_.substr(0, n)),
|
||||
trim(line_.substr(n+1, line_.length()-n-1))));
|
||||
if (!res.second)
|
||||
err("duplicated key found");
|
||||
l.ordered_values.push_back(res.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
Parser::dump(std::ostream& s, const Level& l, const std::string& sname)
|
||||
{
|
||||
if (!sname.empty()) s << '\n';
|
||||
for (size_t i = 0; i < l.depth; ++i) s << '[';
|
||||
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)
|
||||
s << (*it)->first << '=' << (*it)->second << std::endl;
|
||||
for (Level::sections_t::const_iterator it = l.ordered_sections.begin(); it != l.ordered_sections.end(); ++it) {
|
||||
assert((*it)->second.depth == l.depth+1);
|
||||
dump(s, (*it)->second, (*it)->first);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // INI_HPP
|
||||
|
@@ -1,12 +0,0 @@
|
||||
#ifndef NNTPCHAN_IO_HANDLE_HPP
|
||||
#define NNTPCHAN_IO_HANDLE_HPP
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
typedef std::unique_ptr<std::iostream> IOHandle_ptr;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "line.hpp"
|
||||
#include <nntpchan/line.hpp>
|
||||
|
||||
namespace nntpchan {
|
||||
|
||||
|
@@ -1,34 +0,0 @@
|
||||
#ifndef NNTPCHAN_LINE_HPP
|
||||
#define NNTPCHAN_LINE_HPP
|
||||
#include "server.hpp"
|
||||
#include <stdint.h>
|
||||
namespace nntpchan
|
||||
{
|
||||
|
||||
/** @brief a buffered line reader */
|
||||
class LineReader
|
||||
{
|
||||
public:
|
||||
|
||||
LineReader(size_t lineLimit);
|
||||
|
||||
/** @brief queue inbound data from connection */
|
||||
void Data(const char * data, ssize_t s);
|
||||
|
||||
/** implements IConnHandler */
|
||||
virtual bool ShouldClose();
|
||||
|
||||
protected:
|
||||
/** @brief handle a line from the client */
|
||||
virtual void HandleLine(const std::string & line) = 0;
|
||||
|
||||
|
||||
private:
|
||||
void OnLine(const char * d, const size_t l);
|
||||
std::string m_leftovers;
|
||||
bool m_close;
|
||||
const size_t lineLimit;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "mime.hpp"
|
||||
#include <nntpchan/mime.hpp>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
|
@@ -1,30 +0,0 @@
|
||||
#ifndef NNTPCHAN_MIME_HPP
|
||||
#define NNTPCHAN_MIME_HPP
|
||||
#include "file_handle.hpp"
|
||||
#include "io_handle.hpp"
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
|
||||
typedef std::map<std::string, std::string> RawHeader;
|
||||
|
||||
bool ReadHeader(const FileHandle_ptr & f, RawHeader & h);
|
||||
|
||||
|
||||
struct MimePart
|
||||
{
|
||||
virtual RawHeader & Header() = 0;
|
||||
virtual IOHandle_ptr OpenPart() = 0;
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<MimePart> MimePart_ptr;
|
||||
|
||||
typedef std::function<bool(MimePart_ptr)> PartReader;
|
||||
|
||||
bool ReadParts(const FileHandle_ptr & f, PartReader r);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,59 +0,0 @@
|
||||
#ifndef NNTPCHAN_MODEL_HPP
|
||||
#define NNTPCHAN_MODEL_HPP
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
namespace model
|
||||
{
|
||||
// MIME Header
|
||||
typedef std::map<std::string, std::set<std::string> > PostHeader;
|
||||
// text post contents
|
||||
typedef std::string PostBody;
|
||||
// single file attachment, (orig_filename, hexdigest, thumb_filename)
|
||||
typedef std::tuple<std::string, std::string, std::string> PostAttachment;
|
||||
// all attachments on a post
|
||||
typedef std::vector<PostAttachment> Attachments;
|
||||
// a post (header, Post Text, Attachments)
|
||||
typedef std::tuple<PostHeader, PostBody, Attachments> Post;
|
||||
// a thread (many posts in post order)
|
||||
typedef std::vector<Post> Thread;
|
||||
|
||||
|
||||
static inline std::string & GetFilename(PostAttachment & att)
|
||||
{
|
||||
return std::get<0>(att);
|
||||
}
|
||||
|
||||
static inline std::string & GetHexDigest(PostAttachment & att)
|
||||
{
|
||||
return std::get<1>(att);
|
||||
}
|
||||
|
||||
static inline std::string & GetThumbnail(PostAttachment & att)
|
||||
{
|
||||
return std::get<2>(att);
|
||||
}
|
||||
|
||||
static inline PostHeader & GetHeader(Post & post)
|
||||
{
|
||||
return std::get<0>(post);
|
||||
}
|
||||
|
||||
static inline PostBody & GetBody(Post & post)
|
||||
{
|
||||
return std::get<1>(post);
|
||||
}
|
||||
|
||||
static inline Attachments & GetAttachments(Post & post)
|
||||
{
|
||||
return std::get<2>(post);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,4 +1,4 @@
|
||||
#include "net.hpp"
|
||||
#include <nntpchan/net.hpp>
|
||||
#include <uv.h>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
@@ -1,23 +0,0 @@
|
||||
#ifndef NNTPCHAN_NET_HPP
|
||||
#define NNTPCHAN_NET_HPP
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <string>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
struct NetAddr
|
||||
{
|
||||
NetAddr();
|
||||
|
||||
sockaddr_in6 addr;
|
||||
operator sockaddr * () { return (sockaddr *) &addr; }
|
||||
operator const sockaddr * () const { return (sockaddr *) &addr; }
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
NetAddr ParseAddr(const std::string & addr);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,6 +1,6 @@
|
||||
#include "nntp_auth.hpp"
|
||||
#include "crypto.hpp"
|
||||
#include "base64.hpp"
|
||||
#include <nntpchan/nntp_auth.hpp>
|
||||
#include <nntpchan/crypto.hpp>
|
||||
#include <nntpchan/base64.hpp>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
@@ -1,61 +0,0 @@
|
||||
#ifndef NNTPCHAN_NNTP_AUTH_HPP
|
||||
#define NNTPCHAN_NNTP_AUTH_HPP
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include "line.hpp"
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
/** @brief nntp credential db interface */
|
||||
class NNTPCredentialDB
|
||||
{
|
||||
public:
|
||||
/** @brief open connection to database, return false on error otherwise return true */
|
||||
virtual bool Open() = 0;
|
||||
/** @brief close connection to database */
|
||||
virtual void Close() = 0;
|
||||
/** @brief return true if username password combo is correct */
|
||||
virtual bool CheckLogin(const std::string & user, const std::string & passwd) = 0;
|
||||
virtual ~NNTPCredentialDB() {}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<NNTPCredentialDB> CredDB_ptr;
|
||||
|
||||
/** @brief nntp credential db using hashed+salted passwords */
|
||||
class HashedCredDB : public NNTPCredentialDB, public LineReader
|
||||
{
|
||||
public:
|
||||
HashedCredDB();
|
||||
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);
|
||||
void HandleLine(const std::string & line);
|
||||
private:
|
||||
bool ProcessLine(const std::string & line);
|
||||
|
||||
std::mutex m_access;
|
||||
std::string m_user, m_passwd;
|
||||
bool m_found;
|
||||
/** return true if we have a line that matches this username / password combo */
|
||||
std::istream * m_instream;
|
||||
};
|
||||
|
||||
class HashedFileDB : public HashedCredDB
|
||||
{
|
||||
public:
|
||||
HashedFileDB(const std::string & fname);
|
||||
~HashedFileDB();
|
||||
bool Open();
|
||||
void Close();
|
||||
private:
|
||||
std::string m_fname;
|
||||
std::ifstream f;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
#include "nntp_handler.hpp"
|
||||
#include "sanitize.hpp"
|
||||
#include <nntpchan/nntp_handler.hpp>
|
||||
#include <nntpchan/sanitize.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
@@ -1,62 +0,0 @@
|
||||
#ifndef NNTPCHAN_NNTP_HANDLER_HPP
|
||||
#define NNTPCHAN_NNTP_HANDLER_HPP
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include "line.hpp"
|
||||
#include "nntp_auth.hpp"
|
||||
#include "storage.hpp"
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
class NNTPServerHandler : public LineReader, public IConnHandler
|
||||
{
|
||||
public:
|
||||
NNTPServerHandler(const std::string & storage);
|
||||
~NNTPServerHandler();
|
||||
|
||||
virtual bool ShouldClose();
|
||||
|
||||
void SetAuth(CredDB_ptr creds);
|
||||
|
||||
virtual void OnData(const char *, ssize_t);
|
||||
|
||||
void Greet();
|
||||
|
||||
protected:
|
||||
void HandleLine(const std::string & line);
|
||||
void HandleCommand(const std::deque<std::string> & command);
|
||||
private:
|
||||
|
||||
enum State {
|
||||
eStateReadCommand,
|
||||
eStateStoreArticle,
|
||||
eStateQuit
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
void EnterState(State st);
|
||||
|
||||
void ArticleObtained();
|
||||
|
||||
// handle quit command, this queues a reply
|
||||
void Quit();
|
||||
|
||||
// switch nntp modes, this queues a reply
|
||||
void SwitchMode(const std::string & mode);
|
||||
|
||||
bool PostingAllowed();
|
||||
|
||||
private:
|
||||
std::string m_articleName;
|
||||
FileHandle_ptr m_article;
|
||||
CredDB_ptr m_auth;
|
||||
ArticleStorage m_store;
|
||||
std::string m_mode;
|
||||
bool m_authed;
|
||||
State m_state;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@@ -1,8 +1,8 @@
|
||||
|
||||
#include "nntp_server.hpp"
|
||||
#include "nntp_auth.hpp"
|
||||
#include "nntp_handler.hpp"
|
||||
#include "net.hpp"
|
||||
#include <nntpchan/nntp_server.hpp>
|
||||
#include <nntpchan/nntp_auth.hpp>
|
||||
#include <nntpchan/nntp_handler.hpp>
|
||||
#include <nntpchan/net.hpp>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
@@ -1,62 +0,0 @@
|
||||
#ifndef NNTPCHAN_NNTP_SERVER_HPP
|
||||
#define NNTPCHAN_NNTP_SERVER_HPP
|
||||
#include <uv.h>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
#include "frontend.hpp"
|
||||
#include "server.hpp"
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
|
||||
class NNTPServer : public Server
|
||||
{
|
||||
public:
|
||||
|
||||
NNTPServer(uv_loop_t * loop);
|
||||
|
||||
virtual ~NNTPServer();
|
||||
|
||||
void SetStoragePath(const std::string & path);
|
||||
|
||||
void SetLoginDB(const std::string path);
|
||||
|
||||
void SetInstanceName(const std::string & name);
|
||||
|
||||
std::string InstanceName() const;
|
||||
|
||||
void Close();
|
||||
|
||||
virtual IServerConn * CreateConn(uv_stream_t * s);
|
||||
|
||||
virtual void OnAcceptError(int status);
|
||||
|
||||
void SetFrontend(Frontend * f);
|
||||
|
||||
private:
|
||||
|
||||
std::string m_logindbpath;
|
||||
std::string m_storagePath;
|
||||
std::string m_servername;
|
||||
|
||||
Frontend_ptr m_frontend;
|
||||
|
||||
};
|
||||
|
||||
class NNTPServerConn : public IServerConn
|
||||
{
|
||||
public:
|
||||
|
||||
NNTPServerConn(uv_loop_t * l, uv_stream_t * s, Server * parent, IConnHandler * h) : IServerConn(l, s, parent, h) {}
|
||||
|
||||
virtual bool IsTimedOut() { return false; };
|
||||
|
||||
/** @brief send next queued reply */
|
||||
virtual void SendNextReply();
|
||||
|
||||
virtual void Greet();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,11 +1,19 @@
|
||||
#include "sanitize.hpp"
|
||||
#include <nntpchan/sanitize.hpp>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <cctype>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
std::string NNTPSanitize(const std::string & str)
|
||||
|
||||
std::string NNTPSanitizeLine(const std::string & str)
|
||||
{
|
||||
if(str == ".") return " .";
|
||||
std::string sane;
|
||||
sane += str;
|
||||
const char ch = ' ';
|
||||
std::replace_if(sane.begin(), sane.end(), [](unsigned char ch) -> bool { return iscntrl(ch); } , ch);
|
||||
return sane;
|
||||
}
|
||||
|
||||
std::string ToLower(const std::string & str)
|
||||
@@ -21,4 +29,11 @@ namespace nntpchan
|
||||
{
|
||||
return std::regex_search(msgid, re_ValidMessageID) == 1;
|
||||
}
|
||||
|
||||
static const std::regex re_ValidNewsgroup("^[a-zA-Z][a-zA-Z0-9.]{1,128}$");
|
||||
|
||||
bool IsValidNewsgroup(const std::string & msgid)
|
||||
{
|
||||
return std::regex_search(msgid, re_ValidNewsgroup) == 1;
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
#ifndef NNTPCHAN_SANITIZE_HPP
|
||||
#define NNTPCHAN_SANITIZE_HPP
|
||||
#include <string>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
std::string NNTPSanitize(const std::string & str);
|
||||
std::string ToLower(const std::string & str);
|
||||
bool IsValidMessageID(const std::string & msgid);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,6 +1,6 @@
|
||||
#include "buffer.hpp"
|
||||
#include "server.hpp"
|
||||
#include "net.hpp"
|
||||
#include <nntpchan/buffer.hpp>
|
||||
#include <nntpchan/server.hpp>
|
||||
#include <nntpchan/net.hpp>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
|
@@ -1,98 +0,0 @@
|
||||
#ifndef NNTPCHAN_SERVER_HPP
|
||||
#define NNTPCHAN_SERVER_HPP
|
||||
#include <uv.h>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
|
||||
class Server;
|
||||
|
||||
|
||||
struct IConnHandler
|
||||
{
|
||||
|
||||
virtual ~IConnHandler() {};
|
||||
|
||||
/** got inbound data */
|
||||
virtual void OnData(const char * data, ssize_t s) = 0;
|
||||
|
||||
/** get next line of data to send */
|
||||
std::string GetNextLine();
|
||||
|
||||
/** return true if we have a line to send */
|
||||
bool HasNextLine();
|
||||
|
||||
/** return true if we should close this connection otherwise return false */
|
||||
virtual bool ShouldClose() = 0;
|
||||
|
||||
/** queue a data send */
|
||||
void QueueLine(const std::string & line);
|
||||
|
||||
virtual void Greet() = 0;
|
||||
|
||||
|
||||
private:
|
||||
std::deque<std::string> m_sendlines;
|
||||
};
|
||||
|
||||
/** server connection handler interface */
|
||||
struct IServerConn
|
||||
{
|
||||
IServerConn(uv_loop_t * l, uv_stream_t * s, Server * parent, IConnHandler * h);
|
||||
virtual ~IServerConn();
|
||||
virtual void Close();
|
||||
virtual void Greet() = 0;
|
||||
virtual void SendNextReply() = 0;
|
||||
virtual bool IsTimedOut() = 0;
|
||||
void SendString(const std::string & str);
|
||||
Server * Parent() { return m_parent; };
|
||||
IConnHandler * GetHandler() { return m_handler; };
|
||||
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
|
||||
{
|
||||
public:
|
||||
Server(uv_loop_t * loop);
|
||||
/** called after socket close, NEVER call directly */
|
||||
virtual ~Server() {}
|
||||
/** create connection handler from open stream */
|
||||
virtual IServerConn * CreateConn(uv_stream_t * s) = 0;
|
||||
/** close all sockets and stop */
|
||||
void Close();
|
||||
/** bind to address */
|
||||
void Bind(const std::string & addr);
|
||||
|
||||
typedef std::function<void(IServerConn *)> ConnVisitor;
|
||||
|
||||
/** visit all open connections */
|
||||
void VisitConns(ConnVisitor v);
|
||||
|
||||
/** remove connection from server, called after proper close */
|
||||
void RemoveConn(IServerConn * conn);
|
||||
|
||||
protected:
|
||||
uv_loop_t * GetLoop() { return m_loop; }
|
||||
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);
|
||||
std::deque<IServerConn *> m_conns;
|
||||
uv_tcp_t m_server;
|
||||
uv_loop_t * m_loop;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@@ -1,11 +0,0 @@
|
||||
#ifndef NNTPCHAN_SHA1_HPP
|
||||
#define NNTPCHAN_SHA1_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
std::string sha1_hex(const std::string & data);
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,8 +1,8 @@
|
||||
#include "staticfile_frontend.hpp"
|
||||
#include "file_handle.hpp"
|
||||
#include "sanitize.hpp"
|
||||
#include "mime.hpp"
|
||||
#include "sha1.hpp"
|
||||
#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 <iostream>
|
||||
#include <set>
|
||||
@@ -36,70 +36,89 @@ namespace nntpchan
|
||||
|
||||
// read body
|
||||
|
||||
// render templates
|
||||
std::map<std::string, std::any> thread_args;
|
||||
|
||||
auto findMsgidFunc = [](const std::pair<std::string, std::string> & item) -> bool {
|
||||
auto lower = ToLower(item.first);
|
||||
return (lower == "message-id") || (lower == "messageid");
|
||||
};
|
||||
|
||||
auto msgid = std::find_if(header.begin(), header.end(), findMsgidFunc);
|
||||
|
||||
if(!IsValidMessageID(msgid->second))
|
||||
{
|
||||
std::clog << "invalid message-id: " << msgid->second << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string msgid_hash = sha1_hex(msgid->second);
|
||||
|
||||
fs::path threadFilePath = m_OutDir / fs::path("thread-" + msgid_hash + ".html");
|
||||
if(m_TemplateEngine)
|
||||
{
|
||||
std::map<std::string, std::any> thread_args;
|
||||
|
||||
auto findMsgidFunc = [](const std::pair<std::string, std::string> & item) -> bool {
|
||||
auto lower = ToLower(item.first);
|
||||
return (lower == "message-id") || (lower == "messageid");
|
||||
};
|
||||
|
||||
auto msgid = std::find_if(header.begin(), header.end(), findMsgidFunc);
|
||||
|
||||
std::string msgid_hash = sha1_hex(msgid->second);
|
||||
|
||||
fs::path threadFilePath = m_OutDir / fs::path("thread-" + msgid_hash + ".html");
|
||||
FileHandle_ptr out = OpenFile(threadFilePath, eWrite);
|
||||
if(!m_TemplateEngine->WriteTemplate("thread.mustache", thread_args, out))
|
||||
{
|
||||
std::clog << "failed to write " << threadFilePath << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::set<std::string> newsgroups_list;
|
||||
|
||||
auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> & item) -> bool
|
||||
{
|
||||
return ToLower(item.first) == "newsgroups";
|
||||
};
|
||||
|
||||
std::set<std::string> newsgroups_list;
|
||||
|
||||
auto findNewsgroupsFunc = [](const std::pair<std::string, std::string> & item) -> bool
|
||||
auto group = std::find_if(header.begin(), header.end(), findNewsgroupsFunc);
|
||||
if(group == std::end(header))
|
||||
{
|
||||
std::clog << "no newsgroups header" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::istringstream input(group->second);
|
||||
|
||||
std::string newsgroup;
|
||||
while(std::getline(input, newsgroup, ' '))
|
||||
{
|
||||
if(IsValidNewsgroup(newsgroup))
|
||||
newsgroups_list.insert(newsgroup);
|
||||
}
|
||||
|
||||
for(const auto & name : newsgroups_list)
|
||||
{
|
||||
auto board = GetThreadsPaginated(name, 10, m_Pages);
|
||||
uint32_t pageno = 0;
|
||||
for(Threads_t threads : board)
|
||||
{
|
||||
return ToLower(item.first) == "newsgroups";
|
||||
};
|
||||
|
||||
auto group = std::find_if(header.begin(), header.end(), findNewsgroupsFunc);
|
||||
if(group == std::end(header))
|
||||
{
|
||||
std::clog << "no newsgroups header" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::istringstream input(group->second);
|
||||
|
||||
std::string newsgroup;
|
||||
while(std::getline(input, newsgroup, ' '))
|
||||
{
|
||||
newsgroups_list.insert(NNTPSanitize(newsgroup));
|
||||
}
|
||||
|
||||
for(const auto & name : newsgroups_list)
|
||||
{
|
||||
auto board = GetThreadsPaginated(name, 10, m_Pages);
|
||||
uint32_t pageno = 0;
|
||||
for(Threads_t threads : board)
|
||||
std::map<std::string, std::any> board_args;
|
||||
board_args["group"] = std::make_any<std::string>(name);
|
||||
board_args["pageno"] = std::make_any<uint32_t>(pageno);
|
||||
board_args["threads"] = std::make_any<Threads_t>(threads);
|
||||
|
||||
fs::path boardPageFilename(newsgroup + "-" + std::to_string(pageno) + ".html");
|
||||
if(m_TemplateEngine)
|
||||
{
|
||||
std::map<std::string, std::any> board_args;
|
||||
board_args["group"] = std::make_any<std::string>(name);
|
||||
board_args["pageno"] = std::make_any<uint32_t>(pageno);
|
||||
board_args["threads"] = std::make_any<Threads_t>(threads);
|
||||
|
||||
fs::path boardPageFilename(newsgroup + "-" + std::to_string(pageno) + ".html");
|
||||
out = OpenFile(m_OutDir / boardPageFilename, eWrite);
|
||||
FileHandle_ptr out = OpenFile(m_OutDir / boardPageFilename, eWrite);
|
||||
m_TemplateEngine->WriteTemplate("board.mustache", board_args, out);
|
||||
|
||||
++pageno;
|
||||
}
|
||||
|
||||
++pageno;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool StaticFileFrontend::AcceptsNewsgroup(const std::string & newsgroup)
|
||||
{
|
||||
return IsValidNewsgroup(newsgroup);
|
||||
}
|
||||
|
||||
bool StaticFileFrontend::AcceptsMessage(const std::string & msgid)
|
||||
{
|
||||
return IsValidMessageID(msgid);
|
||||
}
|
||||
|
||||
StaticFileFrontend::BoardPage_t StaticFileFrontend::GetThreadsPaginated(const std::string & group, uint32_t perpage, uint32_t pages)
|
||||
{
|
||||
|
@@ -1,44 +0,0 @@
|
||||
#ifndef NNTPCHAN_STATICFILE_FRONTEND_HPP
|
||||
#define NNTPCHAN_STATICFILE_FRONTEND_HPP
|
||||
#include "frontend.hpp"
|
||||
#include "template_engine.hpp"
|
||||
#include "model.hpp"
|
||||
#include <experimental/filesystem>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
class StaticFileFrontend : public Frontend
|
||||
{
|
||||
public:
|
||||
|
||||
StaticFileFrontend(TemplateEngine * tmpl, const std::string & templateDir, const std::string & outDir, uint32_t pages);
|
||||
|
||||
~StaticFileFrontend();
|
||||
|
||||
void ProcessNewMessage(const fs::path & fpath);
|
||||
bool AcceptsNewsgroup(const std::string & newsgroup) { (void) newsgroup; return true; }
|
||||
bool AcceptsMessage(const std::string & msgid) { (void) msgid; return true; }
|
||||
|
||||
private:
|
||||
|
||||
typedef nntpchan::model::Thread Thread_t;
|
||||
|
||||
typedef std::vector<Thread_t> Threads_t;
|
||||
|
||||
typedef std::vector<Threads_t> BoardPage_t;
|
||||
|
||||
BoardPage_t GetThreadsPaginated(const std::string & group, uint32_t perpage, uint32_t pages);
|
||||
|
||||
private:
|
||||
|
||||
TemplateEngine_ptr m_TemplateEngine;
|
||||
fs::path m_TemplateDir;
|
||||
fs::path m_OutDir;
|
||||
uint32_t m_Pages;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
#include "storage.hpp"
|
||||
#include "sanitize.hpp"
|
||||
#include <nntpchan/storage.hpp>
|
||||
#include <nntpchan/sanitize.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace nntpchan
|
||||
|
@@ -1,40 +0,0 @@
|
||||
#ifndef NNTPCHAN_STORAGE_HPP
|
||||
#define NNTPCHAN_STORAGE_HPP
|
||||
|
||||
#include <experimental/filesystem>
|
||||
#include <string>
|
||||
#include "file_handle.hpp"
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
|
||||
class ArticleStorage
|
||||
{
|
||||
public:
|
||||
ArticleStorage();
|
||||
ArticleStorage(const fs::path & fpath);
|
||||
~ArticleStorage();
|
||||
|
||||
void SetPath(const fs::path & fpath);
|
||||
|
||||
FileHandle_ptr OpenWrite(const std::string & msgid);
|
||||
FileHandle_ptr OpenRead(const std::string & msgid);
|
||||
|
||||
/**
|
||||
return true if we should accept a new message give its message id
|
||||
*/
|
||||
bool Accept(const std::string & msgid);
|
||||
|
||||
private:
|
||||
|
||||
fs::path MessagePath(const std::string & msgid);
|
||||
|
||||
fs::path basedir;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@@ -1,5 +1,6 @@
|
||||
#include "template_engine.hpp"
|
||||
#include "sanitize.hpp"
|
||||
#include <nntpchan/template_engine.hpp>
|
||||
#include <nntpchan/sanitize.hpp>
|
||||
#include <iostream>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
@@ -8,19 +9,33 @@ namespace nntpchan
|
||||
{
|
||||
struct Impl
|
||||
{
|
||||
bool RenderFile(const std::string & fname, const Args_t & args, const FileHandle_ptr & out)
|
||||
|
||||
bool ParseTemplate(const FileHandle_ptr & in)
|
||||
{
|
||||
auto file = OpenFile(fname, eRead);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenderFile(const Args_t & args, const FileHandle_ptr & out)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
virtual bool WriteTemplate(const std::string & fname, const Args_t & args, const FileHandle_ptr & out)
|
||||
virtual bool WriteTemplate(const fs::path & fpath, const Args_t & args, const FileHandle_ptr & out)
|
||||
{
|
||||
auto templFile = OpenFile(fpath, eRead);
|
||||
if(!templFile)
|
||||
{
|
||||
std::clog << "no such template at " << fpath << std::endl;
|
||||
return false;
|
||||
}
|
||||
auto impl = std::make_unique<Impl>();
|
||||
return impl->RenderFile(fname, args, out);
|
||||
if(impl->ParseTemplate(templFile))
|
||||
return impl->RenderFile(args, out);
|
||||
|
||||
std::clog << "failed to parse template " << fpath << std::endl;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -1,23 +0,0 @@
|
||||
#ifndef NNTPCHAN_TEMPLATE_ENGINE_HPP
|
||||
#define NNTPCHAN_TEMPLATE_ENGINE_HPP
|
||||
#include "file_handle.hpp"
|
||||
#include <any>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace nntpchan
|
||||
{
|
||||
|
||||
struct TemplateEngine
|
||||
{
|
||||
using Args_t = std::map<std::string, std::any>;
|
||||
virtual bool WriteTemplate(const std::string & template_fname, const Args_t & args, const FileHandle_ptr & out) = 0;
|
||||
};
|
||||
|
||||
TemplateEngine * CreateTemplateEngine(const std::string & dialect);
|
||||
|
||||
typedef std::unique_ptr<TemplateEngine> TemplateEngine_ptr;
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user