From 2f122529b0b82f7964c38fa2bab11546f0a50e6e Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Wed, 3 May 2017 08:09:23 -0400 Subject: [PATCH] more --- contrib/backends/nntpchan-daemon/.gitignore | 1 + contrib/backends/nntpchan-daemon/Makefile | 19 +- contrib/backends/nntpchan-daemon/src/http.hpp | 6 + .../nntpchan-daemon/src/http_client.hpp | 5 + .../nntpchan-daemon/src/http_server.hpp | 6 + contrib/backends/nntpchan-daemon/src/line.cpp | 43 +-- contrib/backends/nntpchan-daemon/src/line.hpp | 27 +- .../backends/nntpchan-daemon/src/mustach.c | 257 ++++++++++++++++++ .../backends/nntpchan-daemon/src/mustach.h | 112 ++++++++ .../nntpchan-daemon/src/nntp_auth.cpp | 7 +- .../nntpchan-daemon/src/nntp_auth.hpp | 7 +- .../nntpchan-daemon/src/nntp_handler.cpp | 1 + .../nntpchan-daemon/src/nntp_server.cpp | 14 +- .../backends/nntpchan-daemon/src/server.cpp | 61 +++++ .../backends/nntpchan-daemon/src/server.hpp | 93 +++++++ 15 files changed, 604 insertions(+), 55 deletions(-) create mode 100644 contrib/backends/nntpchan-daemon/src/http.hpp create mode 100644 contrib/backends/nntpchan-daemon/src/http_client.hpp create mode 100644 contrib/backends/nntpchan-daemon/src/http_server.hpp create mode 100644 contrib/backends/nntpchan-daemon/src/mustach.c create mode 100644 contrib/backends/nntpchan-daemon/src/mustach.h create mode 100644 contrib/backends/nntpchan-daemon/src/server.cpp create mode 100644 contrib/backends/nntpchan-daemon/src/server.hpp diff --git a/contrib/backends/nntpchan-daemon/.gitignore b/contrib/backends/nntpchan-daemon/.gitignore index eeeecc2..37ae6dc 100644 --- a/contrib/backends/nntpchan-daemon/.gitignore +++ b/contrib/backends/nntpchan-daemon/.gitignore @@ -1,4 +1,5 @@ *.o +*.a nntpd nntpchan-tool test diff --git a/contrib/backends/nntpchan-daemon/Makefile b/contrib/backends/nntpchan-daemon/Makefile index 772bdc5..f8a9491 100644 --- a/contrib/backends/nntpchan-daemon/Makefile +++ b/contrib/backends/nntpchan-daemon/Makefile @@ -17,18 +17,21 @@ LD_FLAGS := $(shell pkg-config --libs $(PKGS)) INC_FLAGS := $(shell pkg-config --cflags $(PKGS)) -I $(REPO)/src CXXFLAGS := -std=c++11 -Wall -Wextra $(INC_FLAGS) -g - +LIB = libnntpchan.a all: $(EXE) $(TOOL) -$(EXE): $(OBJECTS) - $(CXX) -o $(EXE) $(OBJECTS) $(CXXFLAGS) nntpd.cpp $(LD_FLAGS) +$(LIB): $(OBJECTS) + $(AR) -q $(LIB) $(OBJECTS) -$(TOOL): $(OBJECTS) - $(CXX) -o $(TOOL) $(OBJECTS) $(CXXFLAGS) tool.cpp $(LD_FLAGS) +$(EXE): $(LIB) + $(CXX) -o $(EXE) $(CXXFLAGS) nntpd.cpp $(LIB) $(LD_FLAGS) -build-test: $(OBJECTS) - $(CXX) -o test $(OBJECTS) $(CXXFLAGS) test.cpp $(LD_FLAGS) +$(TOOL): $(LIB) + $(CXX) -o $(TOOL) $(CXXFLAGS) tool.cpp $(LIB) $(LD_FLAGS) + +build-test: $(LIB) + $(CXX) -o test $(CXXFLAGS) test.cpp $(LIB) $(LD_FLAGS) test: build-test ./test @@ -37,4 +40,4 @@ test: build-test $(CXX) $(CXXFLAGS) -c -o $@ clean: - rm -f $(OBJECTS) $(EXE) $(TOOL) test + rm -f $(OBJECTS) $(LIB) $(EXE) $(TOOL) test diff --git a/contrib/backends/nntpchan-daemon/src/http.hpp b/contrib/backends/nntpchan-daemon/src/http.hpp new file mode 100644 index 0000000..f3923ee --- /dev/null +++ b/contrib/backends/nntpchan-daemon/src/http.hpp @@ -0,0 +1,6 @@ +#ifndef NNTPCHAN_HTTP_HPP +#define NNTPCHAN_HTTP_HPP + + + +#endif diff --git a/contrib/backends/nntpchan-daemon/src/http_client.hpp b/contrib/backends/nntpchan-daemon/src/http_client.hpp new file mode 100644 index 0000000..368a5fd --- /dev/null +++ b/contrib/backends/nntpchan-daemon/src/http_client.hpp @@ -0,0 +1,5 @@ +#ifndef NNTPCHAN_HTTP_CLIENT_HPP +#define NNTPCHAN_HTTP_CLIENT_HPP + + +#endif diff --git a/contrib/backends/nntpchan-daemon/src/http_server.hpp b/contrib/backends/nntpchan-daemon/src/http_server.hpp new file mode 100644 index 0000000..6c1fb4c --- /dev/null +++ b/contrib/backends/nntpchan-daemon/src/http_server.hpp @@ -0,0 +1,6 @@ +#ifndef NNTPCHAN_HTTP_SERVER_HPP +#define NNTPCHAN_HTTP_SERVER_HPP + + + +#endif diff --git a/contrib/backends/nntpchan-daemon/src/line.cpp b/contrib/backends/nntpchan-daemon/src/line.cpp index f637349..ea87bd9 100644 --- a/contrib/backends/nntpchan-daemon/src/line.cpp +++ b/contrib/backends/nntpchan-daemon/src/line.cpp @@ -3,20 +3,35 @@ namespace nntpchan { + LineReader::LineReader(size_t limit) : m_close(false), lineLimit(limit) {} + void LineReader::OnData(const char * d, ssize_t l) { if(l <= 0) return; + // process leftovers + std::string current = m_leftovers + std::string(d, l); + if(current.size() > lineLimit) { + m_close = true; + return; + } std::size_t idx = 0; + ssize_t begin = l; + const char * data = current.c_str(); while(l-- > 0) { - char c = d[idx++]; + char c = data[idx++]; if(c == '\n') { - OnLine(d, idx-1); - d += idx; - } else if (c == '\r' && d[idx] == '\n') { - OnLine(d, idx-1); - d += idx + 1; + OnLine(data, idx-1); + data += idx; + } else if (c == '\r' && data[idx] == '\n') { + OnLine(data, idx-1); + data += idx + 1; } } + if (idx < begin) + { + // leftovers + m_leftovers = std::string(data, begin-idx); + } } void LineReader::OnLine(const char *d, const size_t l) @@ -24,21 +39,9 @@ namespace nntpchan { std::string line(d, l); HandleLine(line); } - - bool LineReader::HasNextLine() - { - return m_sendlines.size() > 0; - } - std::string LineReader::GetNextLine() + bool LineReader::ShouldClose() { - std::string line = m_sendlines[0]; - m_sendlines.pop_front(); - return line; - } - - void LineReader::QueueLine(const std::string & line) - { - m_sendlines.push_back(line); + return m_close; } } diff --git a/contrib/backends/nntpchan-daemon/src/line.hpp b/contrib/backends/nntpchan-daemon/src/line.hpp index d838371..1592280 100644 --- a/contrib/backends/nntpchan-daemon/src/line.hpp +++ b/contrib/backends/nntpchan-daemon/src/line.hpp @@ -1,34 +1,33 @@ #ifndef NNTPCHAN_LINE_HPP #define NNTPCHAN_LINE_HPP -#include -#include +#include "server.hpp" +#include namespace nntpchan { /** @brief a buffered line reader */ - class LineReader + class LineReader : public IConnHandler { public: - + + LineReader(size_t lineLimit); /** @brief queue inbound data from connection */ - void OnData(const char * data, ssize_t s); + virtual void OnData(const char * data, ssize_t s); - /** @brief do we have line to send to the client? */ - bool HasNextLine(); - /** @brief get the next line to send to the client, does not check if it exists */ - std::string GetNextLine(); + /** implements IConnHandler */ + virtual bool ShouldClose(); protected: /** @brief handle a line from the client */ virtual void HandleLine(const std::string & line) = 0; - /** @brief queue the next line to send to the client */ - void QueueLine(const std::string & line); - + + private: void OnLine(const char * d, const size_t l); - // lines to send - std::deque m_sendlines; + std::string m_leftovers; + bool m_close; + const size_t lineLimit; }; } diff --git a/contrib/backends/nntpchan-daemon/src/mustach.c b/contrib/backends/nntpchan-daemon/src/mustach.c new file mode 100644 index 0000000..03f3cb1 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/src/mustach.c @@ -0,0 +1,257 @@ +/* + Author: José Bollo + Author: José Bollo + + https://gitlab.com/jobol/mustach + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include "mustach.h" + +#define NAME_LENGTH_MAX 1024 +#define DEPTH_MAX 256 + +static int getpartial(struct mustach_itf *itf, void *closure, const char *name, char **result) +{ + int rc; + FILE *file; + size_t size; + + *result = NULL; + file = open_memstream(result, &size); + if (file == NULL) + rc = MUSTACH_ERROR_SYSTEM; + else { + rc = itf->put(closure, name, 0, file); + if (rc == 0) + /* adds terminating null */ + rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0; + fclose(file); + if (rc < 0) { + free(*result); + *result = NULL; + } + } + return rc; +} + +static int process(const char *template, struct mustach_itf *itf, void *closure, FILE *file, const char *opstr, const char *clstr) +{ + char name[NAME_LENGTH_MAX + 1], *partial, c; + const char *beg, *term; + struct { const char *name, *again; size_t length; int emit, entered; } stack[DEPTH_MAX]; + size_t oplen, cllen, len, l; + int depth, rc, emit; + + emit = 1; + oplen = strlen(opstr); + cllen = strlen(clstr); + depth = 0; + for(;;) { + beg = strstr(template, opstr); + if (beg == NULL) { + /* no more mustach */ + if (emit) + fwrite(template, strlen(template), 1, file); + return depth ? MUSTACH_ERROR_UNEXPECTED_END : 0; + } + if (emit) + fwrite(template, (size_t)(beg - template), 1, file); + beg += oplen; + term = strstr(beg, clstr); + if (term == NULL) + return MUSTACH_ERROR_UNEXPECTED_END; + template = term + cllen; + len = (size_t)(term - beg); + c = *beg; + switch(c) { + case '!': + case '=': + break; + case '{': + for (l = 0 ; clstr[l] == '}' ; l++); + if (clstr[l]) { + if (!len || beg[len-1] != '}') + return MUSTACH_ERROR_BAD_UNESCAPE_TAG; + len--; + } else { + if (term[l] != '}') + return MUSTACH_ERROR_BAD_UNESCAPE_TAG; + template++; + } + c = '&'; + case '^': + case '#': + case '/': + case '&': + case '>': +#if !defined(NO_EXTENSION_FOR_MUSTACH) && !defined(NO_COLON_EXTENSION_FOR_MUSTACH) + case ':': +#endif + beg++; len--; + default: + while (len && isspace(beg[0])) { beg++; len--; } + while (len && isspace(beg[len-1])) len--; + if (len == 0) + return MUSTACH_ERROR_EMPTY_TAG; + if (len > NAME_LENGTH_MAX) + return MUSTACH_ERROR_TAG_TOO_LONG; + memcpy(name, beg, len); + name[len] = 0; + break; + } + switch(c) { + case '!': + /* comment */ + /* nothing to do */ + break; + case '=': + /* defines separators */ + if (len < 5 || beg[len - 1] != '=') + return MUSTACH_ERROR_BAD_SEPARATORS; + beg++; + len -= 2; + for (l = 0; l < len && !isspace(beg[l]) ; l++); + if (l == len) + return MUSTACH_ERROR_BAD_SEPARATORS; + opstr = strndupa(beg, l); + while (l < len && isspace(beg[l])) l++; + if (l == len) + return MUSTACH_ERROR_BAD_SEPARATORS; + clstr = strndupa(beg + l, len - l); + oplen = strlen(opstr); + cllen = strlen(clstr); + break; + case '^': + case '#': + /* begin section */ + if (depth == DEPTH_MAX) + return MUSTACH_ERROR_TOO_DEPTH; + rc = emit; + if (rc) { + rc = itf->enter(closure, name); + if (rc < 0) + return rc; + } + stack[depth].name = beg; + stack[depth].again = template; + stack[depth].length = len; + stack[depth].emit = emit; + stack[depth].entered = rc; + if ((c == '#') == (rc == 0)) + emit = 0; + depth++; + break; + case '/': + /* end section */ + if (depth-- == 0 || len != stack[depth].length || memcmp(stack[depth].name, name, len)) + return MUSTACH_ERROR_CLOSING; + rc = emit && stack[depth].entered ? itf->next(closure) : 0; + if (rc < 0) + return rc; + if (rc) { + template = stack[depth++].again; + } else { + emit = stack[depth].emit; + if (emit && stack[depth].entered) + itf->leave(closure); + } + break; + case '>': + /* partials */ + if (emit) { + rc = getpartial(itf, closure, name, &partial); + if (rc == 0) { + rc = process(partial, itf, closure, file, opstr, clstr); + free(partial); + } + if (rc < 0) + return rc; + } + break; + default: + /* replacement */ + if (emit) { + rc = itf->put(closure, name, c != '&', file); + if (rc < 0) + return rc; + } + break; + } + } +} + +int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file) +{ + int rc = itf->start ? itf->start(closure) : 0; + if (rc == 0) + rc = process(template, itf, closure, file, "{{", "}}"); + return rc; +} + +int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd) +{ + int rc; + FILE *file; + + file = fdopen(fd, "w"); + if (file == NULL) { + rc = MUSTACH_ERROR_SYSTEM; + errno = ENOMEM; + } else { + rc = fmustach(template, itf, closure, file); + fclose(file); + } + return rc; +} + +int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size) +{ + int rc; + FILE *file; + size_t s; + + *result = NULL; + if (size == NULL) + size = &s; + file = open_memstream(result, size); + if (file == NULL) { + rc = MUSTACH_ERROR_SYSTEM; + errno = ENOMEM; + } else { + rc = fmustach(template, itf, closure, file); + if (rc == 0) + /* adds terminating null */ + rc = fputc(0, file) ? MUSTACH_ERROR_SYSTEM : 0; + fclose(file); + if (rc >= 0) + /* removes terminating null of the length */ + (*size)--; + else { + free(*result); + *result = NULL; + *size = 0; + } + } + return rc; +} + diff --git a/contrib/backends/nntpchan-daemon/src/mustach.h b/contrib/backends/nntpchan-daemon/src/mustach.h new file mode 100644 index 0000000..8196679 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/src/mustach.h @@ -0,0 +1,112 @@ +/* + Author: José Bollo + Author: José Bollo + + https://gitlab.com/jobol/mustach + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef _mustach_h_included_ +#define _mustach_h_included_ + +/** + * mustach_itf - interface for callbacks + * + * All of this function should return a negative value to stop + * the mustache processing. The returned negative value will be + * then returned to the caller of mustach as is. + * + * The functions enter and next should return 0 or 1. + * + * All other functions should normally return 0. + * + * @start: Starts the mustach processing of the closure + * 'start' is optional (can be NULL) + * + * @put: Writes the value of 'name' to 'file' with 'escape' or not + * + * @enter: Enters the section of 'name' if possible. + * Musts return 1 if entered or 0 if not entered. + * When 1 is returned, the function 'leave' will always be called. + * Conversely 'leave' is never called when enter returns 0 or + * a negative value. + * When 1 is returned, the function must activate the first + * item of the section. + * + * @next: Activates the next item of the section if it exists. + * Musts return 1 when the next item is activated. + * Musts return 0 when there is no item to activate. + * + * @leave: Leaves the last entered section + */ +struct mustach_itf { + int (*start)(void *closure); + int (*put)(void *closure, const char *name, int escape, FILE *file); + int (*enter)(void *closure, const char *name); + int (*next)(void *closure); + int (*leave)(void *closure); +}; + +#define MUSTACH_OK 0 +#define MUSTACH_ERROR_SYSTEM -1 +#define MUSTACH_ERROR_UNEXPECTED_END -2 +#define MUSTACH_ERROR_EMPTY_TAG -3 +#define MUSTACH_ERROR_TAG_TOO_LONG -4 +#define MUSTACH_ERROR_BAD_SEPARATORS -5 +#define MUSTACH_ERROR_TOO_DEPTH -6 +#define MUSTACH_ERROR_CLOSING -7 +#define MUSTACH_ERROR_BAD_UNESCAPE_TAG -8 + +/** + * fmustach - Renders the mustache 'template' in 'file' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @file: the file where to write the result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int fmustach(const char *template, struct mustach_itf *itf, void *closure, FILE *file); + +/** + * fmustach - Renders the mustache 'template' in 'fd' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @fd: the file descriptor number where to write the result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int fdmustach(const char *template, struct mustach_itf *itf, void *closure, int fd); + +/** + * fmustach - Renders the mustache 'template' in 'result' for 'itf' and 'closure'. + * + * @template: the template string to instanciate + * @itf: the interface to the functions that mustach calls + * @closure: the closure to pass to functions called + * @result: the pointer receiving the result when 0 is returned + * @size: the size of the returned result + * + * Returns 0 in case of success, -1 with errno set in case of system error + * a other negative value in case of error. + */ +extern int mustach(const char *template, struct mustach_itf *itf, void *closure, char **result, size_t *size); + +#endif + diff --git a/contrib/backends/nntpchan-daemon/src/nntp_auth.cpp b/contrib/backends/nntpchan-daemon/src/nntp_auth.cpp index 67f10a7..1f9f6d7 100644 --- a/contrib/backends/nntpchan-daemon/src/nntp_auth.cpp +++ b/contrib/backends/nntpchan-daemon/src/nntp_auth.cpp @@ -7,6 +7,8 @@ namespace nntpchan { + HashedCredDB::HashedCredDB() : LineReader(1024) {} + bool HashedCredDB::CheckLogin(const std::string & user, const std::string & passwd) { std::unique_lock lock(m_access); @@ -58,7 +60,7 @@ namespace nntpchan { m_instream = s; } - + std::string HashedCredDB::Hash(const std::string & data, const std::string & salt) { SHA512Digest h; @@ -71,7 +73,7 @@ namespace nntpchan m_fname(fname), f(nullptr) { - + } HashedFileDB::~HashedFileDB() @@ -95,4 +97,3 @@ namespace nntpchan return false; } } - diff --git a/contrib/backends/nntpchan-daemon/src/nntp_auth.hpp b/contrib/backends/nntpchan-daemon/src/nntp_auth.hpp index dae6eb2..05890c1 100644 --- a/contrib/backends/nntpchan-daemon/src/nntp_auth.hpp +++ b/contrib/backends/nntpchan-daemon/src/nntp_auth.hpp @@ -20,20 +20,21 @@ namespace nntpchan virtual bool CheckLogin(const std::string & user, const std::string & passwd) = 0; virtual ~NNTPCredentialDB() {} }; - + /** @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; diff --git a/contrib/backends/nntpchan-daemon/src/nntp_handler.cpp b/contrib/backends/nntpchan-daemon/src/nntp_handler.cpp index f1c5af3..a53bbeb 100644 --- a/contrib/backends/nntpchan-daemon/src/nntp_handler.cpp +++ b/contrib/backends/nntpchan-daemon/src/nntp_handler.cpp @@ -8,6 +8,7 @@ namespace nntpchan { NNTPServerHandler::NNTPServerHandler(const std::string & storage) : + LineReader(1024), m_auth(nullptr), m_store(storage), m_authed(false), diff --git a/contrib/backends/nntpchan-daemon/src/nntp_server.cpp b/contrib/backends/nntpchan-daemon/src/nntp_server.cpp index 2f40f88..d3d29b2 100644 --- a/contrib/backends/nntpchan-daemon/src/nntp_server.cpp +++ b/contrib/backends/nntpchan-daemon/src/nntp_server.cpp @@ -38,7 +38,7 @@ namespace nntpchan NNTPServer * self = (NNTPServer *) s->data; self->OnAccept(s, status); }; - + assert(uv_listen(*this, 5, cb) == 0); } @@ -53,7 +53,7 @@ namespace nntpchan std::ifstream i; i.open(m_logindbpath); if(i.is_open()) creds = new HashedFileDB(m_logindbpath); - + NNTPServerConn * conn = new NNTPServerConn(m_loop, s, m_storagePath, creds); conn->Greet(); } @@ -63,7 +63,7 @@ namespace nntpchan { m_logindbpath = path; } - + void NNTPServer::SetStoragePath(const std::string & path) { @@ -75,7 +75,7 @@ namespace nntpchan if(m_frontend) delete m_frontend; m_frontend = f; } - + NNTPServerConn::NNTPServerConn(uv_loop_t * l, uv_stream_t * s, const std::string & storage, NNTPCredentialDB * creds) : m_handler(storage) { @@ -110,7 +110,7 @@ namespace nntpchan } }); } - + void NNTPServerConn::SendNextReply() { if(m_handler.HasNextLine()) { @@ -125,7 +125,7 @@ namespace nntpchan m_handler.Greet(); SendNextReply(); } - + void NNTPServerConn::SendString(const std::string & str) { WriteBuffer * b = new WriteBuffer(str); @@ -145,5 +145,5 @@ namespace nntpchan delete self; s->data = nullptr; }); - } + } } diff --git a/contrib/backends/nntpchan-daemon/src/server.cpp b/contrib/backends/nntpchan-daemon/src/server.cpp new file mode 100644 index 0000000..ef43935 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/src/server.cpp @@ -0,0 +1,61 @@ +#include "server.hpp" +#include "net.hpp" +#include + +namespace nntpchan +{ + Server::Server(uv_loop_t * loop) + { + m_loop = loop; + uv_tcp_init(m_loop, &m_server); + m_server.data = this; + } + + void Server::Close() + { + uv_close((uv_handle_t*)&m_server, [](uv_handle_t * s) { + Server * self = (Server*)s->data; + if (self) delete self; + s->data = nullptr; + }); + } + + void Server::Bind(const std::string & addr) + { + auto saddr = ParseAddr(addr); + assert(uv_tcp_bind(*this, saddr, 0) == 0); + auto cb = [] (uv_stream_t * s, int status) { + Server * self = (Server *) s->data; + self->OnAccept(s, status); + }; + assert(uv_listen(*this, 5, cb) == 0); + } + + void Server::OnAccept(uv_stream_t * s, int status) + { + if(status < 0) { + OnAcceptError(status); + return; + } + IServerConn * conn = CreateConn(s); + assert(conn); + conn->Greet(); + } + + void IConnHandler::QueueLine(const std::string & line) + { + m_sendlines.push_back(line); + } + + bool IConnHandler::HasNextLine() + { + return m_sendlines.size() > 0; + } + + std::string IConnHandler::GetNextLine() + { + std::string line = m_sendlines[0]; + m_sendlines.pop_front(); + return line; + } +} diff --git a/contrib/backends/nntpchan-daemon/src/server.hpp b/contrib/backends/nntpchan-daemon/src/server.hpp new file mode 100644 index 0000000..d68d980 --- /dev/null +++ b/contrib/backends/nntpchan-daemon/src/server.hpp @@ -0,0 +1,93 @@ +#ifndef NNTPCHAN_SERVER_HPP +#define NNTPCHAN_SERVER_HPP +#include +#include +#include +#include +#include + +namespace nntpchan +{ + + class Server; + + + struct 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); + + private: + std::deque m_sendlines; + }; + + /** server connection handler interface */ + struct IServerConn + { + IServerConn(uv_loop_t * l, uv_stream_t * s, Server * parent, IConnHandler * h); + virtual ~IServerConn() = 0; + virtual void Close() = 0; + virtual void Greet() = 0; + virtual void SendNextReply() = 0; + virtual bool IsTimedOut() = 0; + Server * Parent() { return m_parent; }; + IConnHandler * GetHandler() { return m_handler; }; + operator uv_stream_t * () { return m_stream; }; + uv_loop_t * GetLoop() { return m_loop; }; + private: + uv_stream_t * m_stream; + 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 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::vector m_conns; + uv_tcp_t m_server; + uv_loop_t * m_loop; + }; +} + + +#endif