Compare commits
2 Commits
Author | SHA1 | Date |
---|---|---|
Thor | a4abb2ac91 | 4 years ago |
Thor | 4ca867ceab | 4 years ago |
31 changed files with 910 additions and 488 deletions
@ -0,0 +1,80 @@ |
||||
{ |
||||
"folders": [ |
||||
{ |
||||
"path": "." |
||||
} |
||||
], |
||||
"settings": { |
||||
"files.associations": { |
||||
"__debug": "cpp", |
||||
"__locale": "cpp", |
||||
"algorithm": "cpp", |
||||
"cstddef": "cpp", |
||||
"functional": "cpp", |
||||
"initializer_list": "cpp", |
||||
"ios": "cpp", |
||||
"iterator": "cpp", |
||||
"limits": "cpp", |
||||
"list": "cpp", |
||||
"locale": "cpp", |
||||
"memory": "cpp", |
||||
"streambuf": "cpp", |
||||
"string": "cpp", |
||||
"type_traits": "cpp", |
||||
"utility": "cpp", |
||||
"map": "cpp", |
||||
"variant": "cpp", |
||||
"iosfwd": "cpp", |
||||
"vector": "cpp", |
||||
"array": "cpp", |
||||
"atomic": "cpp", |
||||
"bit": "cpp", |
||||
"*.tcc": "cpp", |
||||
"bitset": "cpp", |
||||
"cctype": "cpp", |
||||
"chrono": "cpp", |
||||
"clocale": "cpp", |
||||
"cmath": "cpp", |
||||
"codecvt": "cpp", |
||||
"complex": "cpp", |
||||
"condition_variable": "cpp", |
||||
"cstdarg": "cpp", |
||||
"cstdint": "cpp", |
||||
"cstdio": "cpp", |
||||
"cstdlib": "cpp", |
||||
"cstring": "cpp", |
||||
"ctime": "cpp", |
||||
"cwchar": "cpp", |
||||
"cwctype": "cpp", |
||||
"deque": "cpp", |
||||
"set": "cpp", |
||||
"unordered_map": "cpp", |
||||
"exception": "cpp", |
||||
"memory_resource": "cpp", |
||||
"numeric": "cpp", |
||||
"optional": "cpp", |
||||
"random": "cpp", |
||||
"ratio": "cpp", |
||||
"string_view": "cpp", |
||||
"system_error": "cpp", |
||||
"tuple": "cpp", |
||||
"slist": "cpp", |
||||
"fstream": "cpp", |
||||
"iomanip": "cpp", |
||||
"iostream": "cpp", |
||||
"istream": "cpp", |
||||
"mutex": "cpp", |
||||
"new": "cpp", |
||||
"ostream": "cpp", |
||||
"sstream": "cpp", |
||||
"stdexcept": "cpp", |
||||
"thread": "cpp", |
||||
"cinttypes": "cpp", |
||||
"typeindex": "cpp", |
||||
"typeinfo": "cpp", |
||||
"any": "cpp", |
||||
"unordered_set": "cpp" |
||||
}, |
||||
"C_Cpp.loggingLevel": "Error" |
||||
} |
||||
} |
@ -0,0 +1,56 @@ |
||||
#ifndef _LOG_ |
||||
#define _LOG_ |
||||
|
||||
#include <log/common.hpp> |
||||
#include <log/internal.hpp> |
||||
|
||||
#include <util.hpp> |
||||
|
||||
namespace log { |
||||
void setLevel(Level toLevel); |
||||
|
||||
// Grouping by indentation
|
||||
void open(); |
||||
void close(); |
||||
|
||||
template <typename... Ts> |
||||
void log(Level atLevel, const char * format, const Ts & ... args) { |
||||
if(atLevel >= internal::level) { |
||||
std::cerr << "[" << internal::LEVEL_LABELS.at(atLevel) << "] "; |
||||
internal::printFormatted(util::lpad(format, internal::group).c_str(), args...); |
||||
std::cerr << std::endl; |
||||
} |
||||
} |
||||
|
||||
// Library debug
|
||||
template <typename... Ts> |
||||
void ldebug(const Ts & ... args) { |
||||
log(LDEBUG, args...); |
||||
} |
||||
|
||||
template <typename... Ts> |
||||
void debug(const Ts & ... args) { |
||||
log(DEBUG, args...); |
||||
} |
||||
|
||||
template <typename... Ts> |
||||
void info(const Ts & ... args) { |
||||
log(INFO, args...); |
||||
} |
||||
|
||||
template <typename... Ts> |
||||
void warning(const Ts &... args) { |
||||
log(WARNING, args...); |
||||
} |
||||
|
||||
template <typename... Ts> |
||||
void error(const Ts & ... args) { |
||||
log(ERROR, args...); |
||||
} |
||||
template <typename... Ts> |
||||
void critical(const Ts & ... args) { |
||||
log(CRITICAL, args...); |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,15 @@ |
||||
#ifndef _LOG_COMMON_ |
||||
#define _LOG_COMMON_ |
||||
|
||||
namespace log { |
||||
using Level = int; |
||||
|
||||
const Level LDEBUG = 50; |
||||
const Level DEBUG = 100; |
||||
const Level INFO = 200; |
||||
const Level WARNING = 300; |
||||
const Level ERROR = 400; |
||||
const Level CRITICAL = 500; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,48 @@ |
||||
#ifndef _LOG_INTERNAL_ |
||||
#define _LOG_INTERNAL_ |
||||
|
||||
// Internal formatting functions
|
||||
|
||||
#include <log/common.hpp> |
||||
|
||||
#include <unordered_map> |
||||
#include <iostream> |
||||
|
||||
namespace log { |
||||
namespace internal { |
||||
extern Level level; |
||||
extern int group; |
||||
|
||||
const std::unordered_map<Level, std::string> LEVEL_LABELS { |
||||
{ LDEBUG, "LDB" }, |
||||
{ DEBUG, "DEB" }, |
||||
{ INFO, "INF" }, |
||||
{ WARNING, "WAR" }, |
||||
{ ERROR, "ERR" }, |
||||
{ CRITICAL, "CRI" } |
||||
}; |
||||
|
||||
void printFormatted(const char * format); |
||||
|
||||
template<typename T, typename... Ts> |
||||
void printFormatted(const char * format, const T & first, const Ts & ... rest) { |
||||
// Iterate characters in (rest of) format string
|
||||
for (; *format != '\0'; ++format) { |
||||
// Check for placeholder character
|
||||
if (*format == '%') { |
||||
// Output first parameter
|
||||
std::cerr << first; |
||||
|
||||
// Tail recurseion with rest of string and parameters
|
||||
printFormatted(format + 1, rest...); |
||||
return; |
||||
} |
||||
|
||||
// If not found, output unformatted character
|
||||
std::cerr << *format; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#endif |
@ -1,34 +0,0 @@ |
||||
#ifndef _MAIL_ |
||||
#define _MAIL_ |
||||
|
||||
#include <protocol/protocol.hpp> |
||||
|
||||
#include <functional> |
||||
#include <vector> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
|
||||
namespace mail { |
||||
namespace status { |
||||
const int SPAM = 0; |
||||
const int HAM = 1; |
||||
const int UNSURE = 2; |
||||
const int ERROR = 3; |
||||
} |
||||
|
||||
struct message_t { |
||||
std::vector<std::string> header; |
||||
std::vector<std::string> body; |
||||
}; |
||||
|
||||
struct session_t { |
||||
bool has_header = 0; |
||||
protocol::token::string_t token; |
||||
message_t message; |
||||
int status = status::UNSURE; |
||||
}; |
||||
|
||||
using session_map_t = std::unordered_map<protocol::session_key_t, session_t>; |
||||
} |
||||
|
||||
#endif |
@ -1,68 +0,0 @@ |
||||
#ifndef _PROTOCOL_ |
||||
#define _PROTOCOL_ |
||||
|
||||
#include <protocol/token.hpp> |
||||
|
||||
#include <string> |
||||
#include <map> |
||||
#include <functional> |
||||
#include <iostream> |
||||
|
||||
namespace protocol { |
||||
struct message_t { |
||||
std::string input; |
||||
token::pattern_t pattern; |
||||
}; |
||||
|
||||
class protocol_t; |
||||
using handler_t = std::function<void(protocol_t&, const message_t&)>; |
||||
using handler_map_t = std::map<token::pattern_t, handler_t>; |
||||
|
||||
using session_key_t = token::string_t; |
||||
|
||||
session_key_t session_key_from(const token::list_t& tokens); |
||||
session_key_t session_key_from(const message_t& message); |
||||
|
||||
token::string_t session_token_from(const token::list_t& tokens); |
||||
token::string_t session_token_from(const message_t& message); |
||||
|
||||
class protocol_t { |
||||
public: |
||||
void set_handlers(const handler_map_t handlers); |
||||
|
||||
void absorb(const std::string& input); |
||||
void send(const token::list_t& tokens); |
||||
|
||||
// TODO: Move these methods to a separate session_t class!
|
||||
void send_result(const session_key_t& session_key, const token::string_t& session_token, token::list_t tokens); |
||||
void proceed(const session_key_t& session_key, const token::string_t& session_token); |
||||
void junk(const session_key_t& session_key, const token::string_t& session_token); |
||||
void fail(const session_key_t& session_key, const token::string_t& session_token); |
||||
void submit_message(const session_key_t& session_key, const token::string_t& session_token, const std::vector<std::string>& header, const std::vector<std::string>& body); |
||||
|
||||
protected: |
||||
virtual void emit(const std::string& output) { }; |
||||
|
||||
private: |
||||
handler_map_t handlers; |
||||
size_t max_pattern_length; |
||||
std::string version; |
||||
|
||||
void send_data_line(const session_key_t& session_key, const token::string_t& session_token, const std::string& data_line); |
||||
}; |
||||
|
||||
class ios_protocol_t : public protocol_t { |
||||
public: |
||||
ios_protocol_t(std::istream& in, std::ostream& out); |
||||
void run(); |
||||
|
||||
protected: |
||||
virtual void emit(const std::string& output); |
||||
|
||||
private: |
||||
std::istream& in; |
||||
std::ostream& out; |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -1,33 +0,0 @@ |
||||
#ifndef _TOKEN_ |
||||
#define _TOKEN_ |
||||
|
||||
#include <vector> |
||||
#include <map> |
||||
#include <string> |
||||
#include <iostream> |
||||
|
||||
namespace protocol::token { |
||||
using string_t = std::string; |
||||
using list_t = std::vector<string_t>; |
||||
|
||||
std::string compose(const list_t& tokens); |
||||
list_t decompose(const std::string& input, size_t max_tokens = -1); |
||||
|
||||
class pattern_t : public std::map<size_t, string_t> { |
||||
private: |
||||
bool wildcard; |
||||
|
||||
public: |
||||
pattern_t(); |
||||
pattern_t(list_t tokens, bool wildcard = false); |
||||
pattern_t(std::string input, bool wildcard = true); |
||||
|
||||
list_t token_list() const; |
||||
|
||||
friend std::ostream& operator<<(typeof(std::cout)& out, const pattern_t& pattern); |
||||
friend bool operator<(const pattern_t& l, const pattern_t& r); |
||||
friend bool operator==(const pattern_t& l, const pattern_t& r); |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,22 @@ |
||||
#ifndef _SMTPD_IOS_PROTOCOL_ |
||||
#define _SMTPD_IOS_PROTOCOL_ |
||||
|
||||
#include <smtpd/protocol.hpp> |
||||
#include <smtpd/types.hpp> |
||||
|
||||
namespace smtpd { |
||||
class IOSProtocol : public Protocol { |
||||
public: |
||||
IOSProtocol(std::istream & in, std::ostream & out); |
||||
void run(); |
||||
|
||||
protected: |
||||
virtual void emit(std::string output); |
||||
|
||||
private: |
||||
std::istream & in; |
||||
std::ostream & out; |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,17 @@ |
||||
#ifndef _SMTPD_MAIL_ |
||||
#define _SMTPD_MAIL_ |
||||
|
||||
#include <smtpd/types.hpp> |
||||
|
||||
#include <vector> |
||||
#include <string> |
||||
|
||||
namespace smtpd { |
||||
struct Mail { |
||||
bool hasHeader = false; |
||||
std::vector<std::string> header; |
||||
std::vector<std::string> body; |
||||
}; |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,23 @@ |
||||
#ifndef _SMTPD_MESSAGE_ |
||||
#define _SMTPD_MESSAGE_ |
||||
|
||||
#include <smtpd/token.hpp> |
||||
|
||||
namespace smtpd { |
||||
class Message { |
||||
public: |
||||
Message(std::string input, TokenPattern pattern); |
||||
|
||||
// Raw protocol string
|
||||
const std::string input; |
||||
|
||||
// TokenPattern that matched this message
|
||||
const TokenPattern pattern; |
||||
|
||||
// Retrieve message metadata
|
||||
SessionID sessionID() const; |
||||
RequestToken requestToken() const; |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,47 @@ |
||||
#ifndef _SMTPD_PROTOCOL_ |
||||
#define _SMTPD_PROTOCOL_ |
||||
|
||||
#include <smtpd/session.hpp> |
||||
#include <smtpd/message.hpp> |
||||
#include <smtpd/token.hpp> |
||||
#include <smtpd/types.hpp> |
||||
|
||||
#include <string> |
||||
#include <memory> |
||||
#include <functional> |
||||
#include <map> |
||||
#include <any> |
||||
|
||||
namespace smtpd { |
||||
class Protocol; |
||||
class Request; |
||||
|
||||
using ProtocolListener = std::function<void(Protocol & protocol, const Message & message)>; |
||||
using SessionMap = std::unordered_map<SessionID, Session>; |
||||
|
||||
class Protocol { |
||||
public: |
||||
std::string version; |
||||
|
||||
Protocol(); |
||||
|
||||
void addListener(const TokenPattern & pattern, ProtocolListener listener); |
||||
void send(const TokenPattern & pattern); |
||||
|
||||
SessionMap sessions; |
||||
|
||||
protected: |
||||
virtual void emit(std::string output) = 0; |
||||
void absorb(std::string input); |
||||
|
||||
private: |
||||
std::multimap<TokenPattern, ProtocolListener> listeners; |
||||
|
||||
static void configListener(Protocol & protocol, const Message & message); |
||||
static void sessionListener(Protocol & protocol, const Message & message); |
||||
static void requestListener(Protocol & protocol, const Message & message); |
||||
}; |
||||
|
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,27 @@ |
||||
#include <smtpd/mail.hpp> |
||||
#include <smtpd/types.hpp> |
||||
#include <smtpd/token.hpp> |
||||
|
||||
#include <any> |
||||
#include <functional> |
||||
|
||||
namespace smtpd { |
||||
class Request { |
||||
public: |
||||
void proceed(); |
||||
void junk(); |
||||
void fail(std::string response = "451 Aborted"); |
||||
void commit(); |
||||
|
||||
void sendFilterResult(TokenPattern pattern); |
||||
void submitMail(const Mail & mail); |
||||
|
||||
std::any payload; |
||||
|
||||
std::function<void(Token command, TokenPattern arguments)> send; |
||||
|
||||
private: |
||||
void submitMailSection(const std::vector<std::string> & lines, bool isBody); |
||||
void submitDataLine(std::string line, bool inBody); |
||||
}; |
||||
} |
@ -0,0 +1,15 @@ |
||||
#include <smtpd/request.hpp> |
||||
#include <smtpd/types.hpp> |
||||
|
||||
#include <functional> |
||||
|
||||
namespace smtpd { |
||||
using RequestMap = std::unordered_map<RequestToken, Request>; |
||||
|
||||
class Session { |
||||
public: |
||||
RequestMap requests; |
||||
|
||||
std::function<void(Token command, Token requestID, TokenPattern arguments)> send; |
||||
}; |
||||
} |
@ -0,0 +1,14 @@ |
||||
#ifndef _SMTPD_ |
||||
#define _SMTPD_ |
||||
|
||||
/* In this file, and the library in general, headers are always in reverse order
|
||||
* of dependence. This helps weed out missing includes to some degree. */ |
||||
|
||||
#include <smtpd/iosprotocol.hpp> |
||||
#include <smtpd/protocol.hpp> |
||||
#include <smtpd/message.hpp> |
||||
#include <smtpd/mail.hpp> |
||||
#include <smtpd/token.hpp> |
||||
#include <smtpd/types.hpp> |
||||
|
||||
#endif |
@ -0,0 +1,32 @@ |
||||
#ifndef _SMTPD_TOKEN_ |
||||
#define _SMTPD_TOKEN_ |
||||
|
||||
#include <smtpd/types.hpp> |
||||
|
||||
#include <sparsevector.hpp> |
||||
|
||||
#include <vector> |
||||
#include <map> |
||||
#include <string> |
||||
#include <iostream> |
||||
#include <memory> |
||||
|
||||
namespace smtpd { |
||||
class TokenPattern : public sparse_vector<Token> { |
||||
public: |
||||
TokenPattern(); |
||||
TokenPattern(const TokenPattern & pattern); |
||||
TokenPattern(const std::vector<std::string> & input, bool wildcard = false); |
||||
TokenPattern(const std::string & input, bool wildcard = false); |
||||
TokenPattern(std::initializer_list<Token> list); |
||||
operator std::string() const; |
||||
|
||||
private: |
||||
bool wildcard; |
||||
}; |
||||
|
||||
bool operator<(const TokenPattern & left, const TokenPattern & right); |
||||
std::ostream & operator<<(std::ostream & os, const TokenPattern & pattern); |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,17 @@ |
||||
#ifndef _SMTPD_TYPES_ |
||||
#define _SMTPD_TYPES_ |
||||
|
||||
#include <vector> |
||||
#include <string> |
||||
#include <memory> |
||||
#include <functional> |
||||
|
||||
namespace smtpd { |
||||
using Token = std::string; |
||||
|
||||
using SessionID = std::string; |
||||
using RequestToken = std::string; |
||||
} |
||||
|
||||
|
||||
#endif |
@ -0,0 +1,132 @@ |
||||
#include <vector> |
||||
#include <map> |
||||
#include <iterator> |
||||
|
||||
template <class T> |
||||
class sparse_vector { |
||||
std::map<size_t, T> map; |
||||
size_t _size = 0; |
||||
|
||||
public: |
||||
const T empty; |
||||
|
||||
template <class U> |
||||
class iterator : public std::iterator<std::input_iterator_tag, U> { |
||||
sparse_vector<U> & vector; |
||||
size_t index; |
||||
|
||||
public: |
||||
iterator(sparse_vector<U> & vector, size_t index) : vector(vector), index(index) {} |
||||
iterator(const iterator<U> & from) : vector(from.vector), index(from.index) {} |
||||
|
||||
// Prefix increment operator
|
||||
iterator<U> & operator++() { |
||||
++index; |
||||
return *this; |
||||
} |
||||
|
||||
// Postfix increment operator
|
||||
iterator<U> operator++(int) { |
||||
iterator<U> tmp(*this); |
||||
operator++(); |
||||
return tmp; |
||||
} |
||||
|
||||
bool operator==(const iterator<U> & rhs) const { |
||||
return index == rhs.index; |
||||
} |
||||
|
||||
bool operator!=(const iterator<U> & rhs) const { |
||||
return index != rhs.index; |
||||
} |
||||
|
||||
U & operator*() const { |
||||
return vector.at(index); |
||||
} |
||||
}; |
||||
|
||||
sparse_vector() {} |
||||
|
||||
// Construct from element list given as arguments
|
||||
sparse_vector(std::initializer_list<T> list) { |
||||
size_t index = 0; |
||||
for(auto element : list) { |
||||
map[index] = element; |
||||
} |
||||
} |
||||
|
||||
// Copy constructor
|
||||
sparse_vector(const sparse_vector<T> & svector) { |
||||
map = svector.map; |
||||
_size = svector._size; |
||||
} |
||||
|
||||
// Move constructor
|
||||
sparse_vector(sparse_vector<T> && svector) { |
||||
map = std::move(svector.map); |
||||
_size = svector._size; |
||||
svector._size = 0; |
||||
} |
||||
|
||||
// Construct from regular vector
|
||||
sparse_vector(const std::vector<T> vector) { |
||||
for(size_t index = 0; index < vector.size(); ++index) { |
||||
map[index] = vector.at(index); |
||||
} |
||||
_size = vector.size(); |
||||
} |
||||
|
||||
// Move assignment operator
|
||||
sparse_vector<T> & operator=(sparse_vector<T> && svector) { |
||||
map = std::move(svector.map); |
||||
size = svector._size; |
||||
svector._size = 0; |
||||
return *this; |
||||
} |
||||
|
||||
size_t size() const { |
||||
return _size; |
||||
} |
||||
|
||||
const T & at(size_t index) const { |
||||
return map.at(index); |
||||
} |
||||
|
||||
bool has(size_t index) const { |
||||
return map.count(index) == 1; |
||||
} |
||||
|
||||
void push_back(const T & element) { |
||||
map[_size++] = element; |
||||
} |
||||
|
||||
template <class Range> |
||||
void push_back(Range range) { |
||||
for(T & element : range) { |
||||
push_back(element); |
||||
} |
||||
} |
||||
|
||||
T pop_back() { |
||||
--_size; |
||||
if(has(_size)) { |
||||
T element = map->at(_size); |
||||
map.erase(_size); |
||||
return element; |
||||
} |
||||
|
||||
return empty; |
||||
} |
||||
|
||||
iterator<T> begin() const { |
||||
return iterator<T>(*this, 0); |
||||
} |
||||
|
||||
iterator<T> end() const { |
||||
return iterator<T>(*this, size()); |
||||
} |
||||
|
||||
T & operator[](size_t index) { |
||||
return map[index]; |
||||
} |
||||
}; |
@ -0,0 +1,13 @@ |
||||
filter|0.5|1576146008.006099|smtp-in|data|a9e57d0a1a1eca0d|faec7151cc9fa53b |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|From: <test@test.com> |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|To: <thj@thj.no> |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|Subject: Hello, World! |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b| |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|This is a message to you. |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|.. |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|It contains nothing important. |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b| |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|Regards, |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|Mr. Test |
||||
filter|0.5|1591207508.350020|smtp-in|data-line|a9e57d0a1a1eca0d|faec7151cc9fa53b|. |
||||
filter|0.5|1591207508.350020|smtp-in|commit|a9e57d0a1a1eca0d|faec7151cc9fa53b |
@ -0,0 +1,29 @@ |
||||
#include <log.hpp> |
||||
|
||||
#include <string> |
||||
#include <iostream> |
||||
|
||||
using namespace std; |
||||
|
||||
namespace log::internal { |
||||
Level level = INFO; |
||||
int group = 0; |
||||
|
||||
void printFormatted(const char * format) { |
||||
cerr << format; |
||||
} |
||||
} |
||||
|
||||
namespace log { |
||||
void open() { |
||||
++internal::group; |
||||
} |
||||
|
||||
void close() { |
||||
--internal::group; |
||||
} |
||||
|
||||
void setLevel(Level toLevel) { |
||||
internal::level = toLevel; |
||||
} |
||||
} |
@ -1,139 +0,0 @@ |
||||
// OpenSMTPD filter protocol documentation:
|
||||
// https://man7.org/linux/man-pages/man7/smtpd-filters.7.html
|
||||
|
||||
#include <util.hpp> |
||||
|
||||
#include <protocol/protocol.hpp> |
||||
|
||||
#include <iostream> |
||||
#include <string> |
||||
|
||||
using namespace std; |
||||
using namespace util; |
||||
|
||||
const bool debug = false; |
||||
|
||||
namespace protocol { |
||||
void protocol_t::set_handlers(const handler_map_t handlers) { |
||||
size_t length = 0; |
||||
for(auto&& [pattern, handler] : handlers) { |
||||
for(auto&& [index, token] : pattern) { |
||||
if(index + 1 > length) { |
||||
length = index + 1; |
||||
} |
||||
} |
||||
} |
||||
max_pattern_length = length; |
||||
debug && cerr << "Maximum handler pattern length is " << length << endl; |
||||
|
||||
this->handlers = handlers; |
||||
} |
||||
|
||||
void protocol_t::send(const token::list_t& tokens) { |
||||
debug && cerr << "Sent pattern " << protocol::token::pattern_t(tokens) << endl; |
||||
this->emit(token::compose(tokens)); |
||||
} |
||||
|
||||
void protocol_t::absorb(const std::string& input) { |
||||
auto tokens = token::decompose(input, debug ? -1 : max_pattern_length); |
||||
auto pattern = token::pattern_t(tokens, false); |
||||
debug && cerr << "Received pattern " << pattern << endl; |
||||
|
||||
if(tokens.size() >= 2 && tokens.at(0) == "filter") { |
||||
version = tokens.at(1); |
||||
} |
||||
|
||||
auto element = handlers.find(pattern); |
||||
if(element == handlers.end()) { |
||||
debug && cerr << "Unknown pattern " << pattern << endl; |
||||
} else { |
||||
element->second(*this, {input, element->first}); |
||||
} |
||||
} |
||||
|
||||
void protocol_t::send_result(const session_key_t& session_key, const token::string_t& session_token, token::list_t tokens) { |
||||
token::list_t result{"filter-result"}; |
||||
if(version < "0.5") { |
||||
result.insert(result.end(), {session_token, session_key}); |
||||
} else { |
||||
result.insert(result.end(), {session_key, session_token}); |
||||
} |
||||
for(auto& token : tokens) { |
||||
result.push_back(token); |
||||
} |
||||
send(result); |
||||
} |
||||
|
||||
void protocol_t::proceed(const session_key_t& session_key, const token::string_t& session_token) { |
||||
send_result(session_key, session_token, {"proceed"}); |
||||
} |
||||
|
||||
void protocol_t::junk(const session_key_t& session_key, const token::string_t& session_token) { |
||||
send_result(session_key, session_token, {"junk"}); |
||||
} |
||||
|
||||
void protocol_t::fail(const session_key_t& session_key, const token::string_t& session_token) { |
||||
send_result(session_key, session_token, {"disconnect", "451 Aborted"}); |
||||
} |
||||
|
||||
void protocol_t::send_data_line(const session_key_t& session_key, const token::string_t& session_token, const string& data_line) { |
||||
if(version < "0.5") { |
||||
send({"filter-dataline", session_token, session_key, data_line}); |
||||
} else { |
||||
send({"filter-dataline", session_key, session_token, data_line}); |
||||
} |
||||
} |
||||
|
||||
void protocol_t::submit_message(const session_key_t& session_key, const token::string_t& session_token, const vector<string>& header, const vector<string>& body) { |
||||
for(auto& data_line : header) { |
||||
send_data_line(session_key, session_token, data_line); |
||||
} |
||||
|
||||
send_data_line(session_key, session_token, ""); |
||||
|
||||
for(auto& data_line : body) { |
||||
if(data_line == ".") { |
||||
send_data_line(session_key, session_token, ".."); |
||||
continue; |
||||
} |
||||
|
||||
send_data_line(session_key, session_token, data_line); |
||||
} |
||||
|
||||
send_data_line(session_key, session_token, "."); |
||||
} |
||||
|
||||
ios_protocol_t::ios_protocol_t(std::istream& in, std::ostream& out) : |
||||
in(in), out(out) { } |
||||
|
||||
void ios_protocol_t::emit(const std::string& output) { |
||||
out << output << endl; |
||||
} |
||||
|
||||
void ios_protocol_t::run() { |
||||
while(!in.eof()) { |
||||
string line; |
||||
std::getline(in, line); |
||||
absorb(line); |
||||
} |
||||
cerr << "EOF reached" << endl; |
||||
} |
||||
|
||||
session_key_t session_key_from(const token::list_t& tokens) { |
||||
return tokens.at(5); |
||||
} |
||||
|
||||
session_key_t session_key_from(const message_t& message) { |
||||
auto tokens = token::decompose(message.input, 6); |
||||
return session_key_from(tokens); |
||||
} |
||||
|
||||
token::string_t session_token_from(const token::list_t& tokens) { |
||||
return tokens.at(6); |
||||
} |
||||
|
||||
token::string_t session_token_from(const message_t& message) { |
||||
auto tokens = token::decompose(message.input, 7); |
||||
return session_token_from(tokens); |
||||
} |
||||
} |
@ -1,111 +0,0 @@ |
||||
#include <protocol/token.hpp> |
||||
#include <util.hpp> |
||||
|
||||
using namespace std; |
||||
using namespace util; |
||||
|
||||
const bool debug = false; |
||||
|
||||
namespace protocol::token { |
||||
|
||||
std::string compose(const list_t& tokens) { |
||||
return join(tokens, '|'); |
||||
} |
||||
|
||||
list_t decompose(const std::string& input, size_t max_tokens) { |
||||
return split(input, '|', max_tokens); |
||||
} |
||||
|
||||
pattern_t::pattern_t() {} |
||||
|
||||
pattern_t::pattern_t(list_t tokens, bool wildcard) : wildcard(wildcard) { |
||||
for(auto index = 0; index < tokens.size(); ++index) { |
||||
auto token = tokens[index]; |
||||
if(!wildcard || token != "*") { |
||||
this->emplace(index, tokens[index]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
pattern_t::pattern_t(string input, bool wildcard) : pattern_t(split(input, '|'), wildcard) {} |
||||
|
||||
list_t pattern_t::token_list() const { |
||||
list_t output; |
||||
for(auto element = begin(); element != end(); ++element) { |
||||
output.push_back(element->second); |
||||
} |
||||
return output; |
||||
} |
||||
|
||||
ostream& operator<<(typeof(cout)& out, const pattern_t& pattern) { |
||||
return out << join(pattern.token_list(), '|') << "<wildcard=" << pattern.wildcard << ">"; |
||||
} |
||||
|
||||
bool operator<(const pattern_t& l, const pattern_t& r) { |
||||
debug && cerr << endl; |
||||
debug && cerr << "Checking if " << l << " < " << r << endl; |
||||
|
||||
if(l == r) { |
||||
debug && cerr << "Not less because left equals right" << endl; |
||||
return false; |
||||
} |
||||
|
||||
for(auto le = l.begin(); le != l.end(); ++le) { |
||||
if(r.count(le->first) == 0) { |
||||
debug && cerr << "Not less because right is missing " << le->first << " -> " << le->second << endl; |
||||
return false; |
||||
} |
||||
|
||||
if(le->second < r.at(le->first)) { |
||||
debug && cerr << "Not less because " << le->second << " < " << r.at(le->first) << endl; |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
bool operator==(const pattern_t& l, const pattern_t& r) { |
||||
debug && cerr << "Checking if " << l << " == " << r << endl; |
||||
|
||||
const pattern_t *subset, *superset; |
||||
if(r.wildcard == l.wildcard) { |
||||
debug && cerr << "Left and right are both (not) wildcards" << endl; |
||||
if(l.size() != r.size()) { |
||||
debug && cerr << "Not equal because sizes are unequal" << endl; |
||||
return false; |
||||
} else { |
||||
subset = &l; |
||||
superset = &r; |
||||
} |
||||
} else if(l.wildcard) { |
||||
debug && cerr << "Left is wildcard" << endl; |
||||
subset = &l; |
||||
superset = &r; |
||||
} else { |
||||
debug && cerr << "Right is wildcard" << endl; |
||||
subset = &r; |
||||
superset = &l; |
||||
} |
||||
|
||||
for(auto le : *subset) { |
||||
if(superset->count(le.first) == 0) { |
||||
debug && cerr << "Not equal because " << le.first << " -> " << le.second << " is missing in superset" << endl; |
||||
return false; |
||||
} |
||||
|
||||
auto rv = superset->at(le.first); |
||||
|
||||
if(rv != le.second) { |
||||
debug && cerr << "Not equal because " << le.first << " -> " << le.second << " != " << rv << " in superset" << endl; |
||||
return false; |
||||
} |
||||
|
||||
debug && cerr << le.first << " -> " << le.second << " == " << rv << " in superset" << endl; |
||||
} |
||||
|
||||
debug && cerr << "Patterns match" << endl; |
||||
|
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,24 @@ |
||||
#include <smtpd/iosprotocol.hpp> |
||||
#include <log.hpp> |
||||
|
||||
#include <iostream> |
||||
|
||||
using namespace std; |
||||
|
||||
namespace smtpd { |
||||
IOSProtocol::IOSProtocol(std::istream & in, std::ostream & out) : |
||||
in(in), out(out) { } |
||||
|
||||
void IOSProtocol::emit(string output) { |
||||
out << output << endl; |
||||
} |
||||
|
||||
void IOSProtocol::run() { |
||||
while(!in.eof()) { |
||||
string line; |
||||
std::getline(in, line); |
||||
absorb(line); |
||||
} |
||||
cerr << "EOF reached" << endl; |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
#include <smtpd/message.hpp> |
||||
#include <smtpd/token.hpp> |
||||
|
||||
using namespace std; |
||||
|
||||
namespace smtpd { |
||||
Message::Message(string input, TokenPattern pattern) : |
||||
input(input), pattern(pattern) { } |
||||
|
||||
SessionID Message::sessionID() const { |
||||
return pattern.at(5); |
||||
} |
||||
|
||||
RequestToken Message::requestToken() const { |
||||
return pattern.at(6); |
||||
} |
||||
} |
@ -0,0 +1,108 @@ |
||||
// OpenSMTPD filter protocol documentation:
|
||||
// https://man7.org/linux/man-pages/man7/smtpd-filters.7.html
|
||||
|
||||
#include <util.hpp> |
||||
#include <log.hpp> |
||||
|
||||
#include <smtpd/protocol.hpp> |
||||
#include <smtpd/message.hpp> |
||||
#include <smtpd/types.hpp> |
||||
|
||||
#include <iostream> |
||||
#include <set> |
||||
|
||||
using namespace std; |
||||
using namespace util; |
||||
|
||||
namespace smtpd { |
||||
Protocol::Protocol() { |
||||
addListener({"config|ready"}, Protocol::configListener); |
||||
addListener({"filter|"}, Protocol::sessionListener); |
||||
addListener({"filter|*|*|smtp-in"}, Protocol::requestListener); |
||||
} |
||||
|
||||
void Protocol::addListener(const TokenPattern & pattern, ProtocolListener listener) { |
||||
log::ldebug("Adding passed-as-arguments listener for pattern %", pattern); |
||||
listeners.insert({pattern, listener}); |
||||
listeners.insert(std::pair<TokenPattern, ProtocolListener>{pattern, listener}); |
||||
} |
||||
|
||||
void Protocol::send(const TokenPattern & pattern) { |
||||
log::ldebug("Sending pattern: %", pattern); |
||||
this->emit(pattern); |
||||
} |
||||
|
||||
void Protocol::absorb(string input) { |
||||
TokenPattern pattern(input); |
||||
log::ldebug("Received pattern: %", pattern); |
||||
|
||||
if(pattern.size() >= 2 && pattern.at(0) == "filter") { |
||||
version = pattern.at(1); |
||||
} |
||||
|
||||
auto range = listeners.equal_range(pattern); |
||||
if(range.first == range.second) { |
||||
log::error("Unsupported message: %", pattern); |
||||
return; |
||||
} |
||||
|
||||
for_each(range.first, range.second, [this, input](const auto & listenerPair) { |
||||
log::ldebug("Matched listener for: %", toString(listenerPair.first)); |
||||
listenerPair.second(*this, Message{input, listenerPair.first}); |
||||
}); |
||||
} |
||||
|
||||
void Protocol::configListener(Protocol & protocol, const Message & message) { |
||||
// Find unique SMTPD filters in listener map
|
||||
set<TokenPattern> uniqueFilters; |
||||
for(auto&& [pattern, listener] : protocol.listeners) { |
||||
log::ldebug("Found listener for: %", pattern); |
||||
if(pattern.at(0) == "filter" && pattern.has(3) && pattern.has(4) > 0) { |
||||
uniqueFilters.insert({"register", "filter", pattern.at(3), pattern.at(4)}); |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
// Register filters with SMTPD
|
||||
for(TokenPattern message : uniqueFilters) { |
||||
protocol.send(message); |
||||
} |
||||
|
||||
protocol.send({"register", "ready"}); |
||||
} |
||||
|
||||
void Protocol::sessionListener(Protocol & protocol, const Message & message) { |
||||
auto sessionID = message.sessionID(); |
||||
if(protocol.sessions.count(sessionID) == 0) { |
||||
auto & session = protocol.sessions[sessionID]; |
||||
session.send = [&protocol, &sessionID](Token command, RequestToken requestToken, TokenPattern arguments) { |
||||
TokenPattern pattern = {command}; |
||||
|
||||
if(protocol.version < "0.5") { |
||||
pattern.push_back(requestToken); |
||||
pattern.push_back(sessionID); |
||||
} else { |
||||
pattern.push_back(sessionID); |
||||
pattern.push_back(requestToken); |
||||
} |
||||
|
||||
for(auto & argument : arguments) { |
||||
pattern.push_back(argument); |
||||
} |
||||
|
||||
protocol.send(pattern); |
||||
}; |
||||
} |
||||
} |
||||
|
||||
void Protocol::requestListener(Protocol & protocol, const Message & message) { |
||||
Session & session = protocol.sessions.at(message.sessionID()); |
||||
auto requestToken = message.requestToken(); |
||||
if(session.requests.count(requestToken) == 0) { |
||||
auto & request = session.requests[requestToken]; |
||||
request.send = [&session, requestToken](Token command, TokenPattern arguments) { |
||||
session.send(command, requestToken, arguments); |
||||
}; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
#include <smtpd/request.hpp> |
||||
|
||||
#include <vector> |
||||
#include <string> |
||||
|
||||
using namespace std; |
||||
|
||||
namespace smtpd { |
||||
void Request::proceed() { |
||||
send("filter-result", {"proceed"}); |
||||
} |
||||
|
||||
void Request::junk() { |
||||
send("filter-result", {"junk"}); |
||||
} |
||||
|
||||
void Request::fail(string response) { |
||||
send("filter-result", {"disconnect", response}); |
||||
} |
||||
|
||||
void Request::submitMail(const Mail & mail) { |
||||
submitMailSection(mail.header, false); |
||||
submitMailSection(mail.body, true); |
||||
} |
||||
|
||||
void Request::submitMailSection(const vector<string> & lines, bool isBody) { |
||||
for(auto line : lines) { |
||||
submitDataLine(line, isBody); |
||||
} |
||||
submitDataLine(isBody ? "." : "", false); |
||||
} |
||||
|
||||
void Request::submitDataLine(string line, bool inBody) { |
||||
send("filter-dataline", {inBody && line == "." ? ".." : line}); |
||||
} |
||||
} |
@ -0,0 +1,5 @@ |
||||
#include <smtpd/session.hpp> |
||||
|
||||
namespace smtpd { |
||||
|
||||
} |
@ -0,0 +1,35 @@ |
||||
#include <smtpd/token.hpp> |
||||
#include <util.hpp> |
||||
#include <log.hpp> |
||||
|
||||
using namespace std; |
||||
|
||||
namespace smtpd { |
||||
TokenPattern::TokenPattern() : |
||||
sparse_vector<Token>() {} |
||||
|
||||
TokenPattern::TokenPattern(const TokenPattern & pattern) : |
||||
sparse_vector<Token>(pattern), wildcard(pattern.wildcard) {}; |
||||
|
||||
TokenPattern::TokenPattern(const std::vector<std::string> & vector, bool wildcard) : |
||||
sparse_vector<Token>(vector), wildcard(wildcard) {} |
||||
|
||||
TokenPattern::TokenPattern(const string & input, bool wildcard) : |
||||
TokenPattern(util::split(input, '|'), wildcard) {} |
||||
|
||||
TokenPattern::TokenPattern(std::initializer_list<Token> list) : |
||||
sparse_vector<Token>(list) {} |
||||
|
||||
|
||||
TokenPattern::operator std::string() const { |
||||
return util::join(*this, '|'); |
||||
} |
||||
|
||||
bool operator<(const TokenPattern& left, const TokenPattern& right) { |
||||
return true; |
||||
} |
||||
|
||||
ostream & operator<<(ostream & out, const TokenPattern & pattern) { |
||||
return out; |
||||
} |
||||
} |
@ -1,36 +1,41 @@ |
||||
#include <util.hpp> |
||||
|
||||
std::vector<std::string> util::split(const std::string input, const std::string::value_type separator, size_t max_tokens) { |
||||
std::vector<std::string> tokens; |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
for(auto index = 0, last = -1; index != max_tokens; ++index) { |
||||
auto current = input.find(separator, last + 1); |
||||
auto eol = current == std::string::npos; |
||||
using namespace std; |
||||
|
||||
if(eol || index == max_tokens) { |
||||
current = input.length(); |
||||
} |
||||
namespace util { |
||||
const vector<string> split(const string input, const string::value_type separator, const size_t max_tokens) { |
||||
vector<string> tokens; |
||||
|
||||
for(size_t index = 0, last = -1; index != max_tokens; ++index) { |
||||
auto current = input.find(separator, last + 1); |
||||
auto eol = current == string::npos; |
||||
|
||||
auto length = current - last - 1; |
||||
auto token = input.substr(last + 1, length); |
||||
tokens.push_back(token); |
||||
last = current; |
||||
if(eol || index == max_tokens) { |
||||
current = input.length(); |
||||
} |
||||
|
||||
if(eol) { |
||||
break; |
||||
auto length = current - last - 1; |
||||
auto token = input.substr(last + 1, length); |
||||
tokens.push_back(token); |
||||
last = current; |
||||
|
||||
if(eol) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return tokens; |
||||
} |
||||
return tokens; |
||||
} |
||||
|
||||
std::string util::join(const std::vector<std::string> strings, const std::string::value_type separator) { |
||||
std::string output; |
||||
for(size_t index = 0; index < strings.size(); ++index) { |
||||
output.append(strings.at(index)); |
||||
if(index + 1 < strings.size()) { |
||||
output.push_back(separator); |
||||
const string lpad(const string input, const int length, const string str) { |
||||
string output; |
||||
for(int i = 0; i < length; ++i) { |
||||
output += str; |
||||
} |
||||
output += input; |
||||
return output.c_str(); |
||||
} |
||||
return output; |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue