commit
2dff81806a
11 changed files with 448 additions and 0 deletions
@ -0,0 +1,4 @@ |
||||
build/ |
||||
.vscode/ |
||||
|
||||
*.swp |
@ -0,0 +1,30 @@ |
||||
# Makefile inspired by:
|
||||
# https://spin.atomicobject.com/2016/08/26/makefile-c-projects/
|
||||
|
||||
EXEC := bogofilter-smtpd
|
||||
|
||||
CXX=/usr/local/Cellar/gcc/9.3.0_1/bin/g++-9
|
||||
CXXFLAGS=-std=gnu++17 -Iinclude
|
||||
CPPFLAGS=-MMD -MP
|
||||
|
||||
SRCDIR := src
|
||||
BUILDDIR := build
|
||||
|
||||
SRCS := $(shell find src -name '*.cpp')
|
||||
OBJS := $(SRCS:%=$(BUILDDIR)/%.o)
|
||||
DEPS := $(OBJS:.o=.d)
|
||||
|
||||
$(BUILDDIR)/$(EXEC) : $(OBJS) |
||||
$(CXX) -o $@ $^
|
||||
|
||||
run : $(BUILDDIR)/$(EXEC) |
||||
$<
|
||||
|
||||
clean : |
||||
rm -R $(BUILDDIR)
|
||||
|
||||
$(BUILDDIR)/%.cpp.o: %.cpp |
||||
mkdir -p $(@D)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
-include $(DEPS) |
@ -0,0 +1,20 @@ |
||||
#ifndef _MAIL_ |
||||
#define _MAIL_ |
||||
|
||||
#include <protocol/protocol.hpp> |
||||
|
||||
#include <functional> |
||||
#include <vector> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
|
||||
namespace mail { |
||||
struct message_t { |
||||
std::vector<std::string> header; |
||||
std::vector<std::string> body; |
||||
}; |
||||
|
||||
using session_map_t = std::unordered_map<protocol::session_t, message_t>; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,69 @@ |
||||
#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>; |
||||
|
||||
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); |
||||
|
||||
protected: |
||||
virtual void emit(const std::string& output) { }; |
||||
|
||||
private: |
||||
handler_map_t handlers; |
||||
size_t max_pattern_length; |
||||
}; |
||||
|
||||
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; |
||||
}; |
||||
|
||||
struct session_t { |
||||
std::string session; |
||||
std::string token; |
||||
}; |
||||
|
||||
bool operator==(const session_t& l, const session_t& r); |
||||
|
||||
session_t session_from(token::list_t tokens); |
||||
session_t session_from(message_t message); |
||||
} |
||||
|
||||
namespace std { |
||||
template<> struct hash<protocol::session_t> { |
||||
std::size_t operator()(protocol::session_t const& session) const noexcept { |
||||
auto session_hash = std::hash<std::string>{}(session.session); |
||||
auto token_hash = std::hash<std::string>{}(session.token); |
||||
return session_hash ^ (token_hash << 1); |
||||
} |
||||
}; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,33 @@ |
||||
#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,12 @@ |
||||
#ifndef _UTIL_ |
||||
#define _UTIL_ |
||||
|
||||
#include <vector> |
||||
#include <string> |
||||
|
||||
namespace util { |
||||
std::vector<std::string> split(const std::string input, const std::string::value_type separator, size_t max_tokens = -1); |
||||
std::string join(const std::vector<std::string> strings, const std::string::value_type separator); |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,50 @@ |
||||
#include <protocol/protocol.hpp> |
||||
#include <mail.hpp> |
||||
|
||||
#include <string> |
||||
#include <vector> |
||||
#include <map> |
||||
#include <unordered_map> |
||||
#include <iostream> |
||||
#include <functional> |
||||
|
||||
const bool debug = true; |
||||
|
||||
using namespace std; |
||||
|
||||
mail::session_map_t mail_messages; |
||||
|
||||
void on_ready(protocol::protocol_t& protocol, protocol::message_t message) { |
||||
debug && cerr << "Ready" << endl; |
||||
protocol.send({"register", "filter", "smtp-in", "data"}); |
||||
protocol.send({"register", "filter", "smtp-in", "data-line"}); |
||||
protocol.send({"register", "filter", "smtp-in", "commit"}); |
||||
protocol.send({"register", "ready"}); |
||||
} |
||||
|
||||
void on_data(protocol::protocol_t& protocol, protocol::message_t message) { |
||||
mail_messages.emplace(protocol::session_from(message), mail::message_t{}); |
||||
} |
||||
|
||||
void on_dataline(protocol::protocol_t& protocol, protocol::message_t message) { |
||||
debug && cerr << "Dataline" << endl; |
||||
} |
||||
|
||||
void on_commit(protocol::protocol_t& protocol, protocol::message_t message) { |
||||
|
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
protocol::ios_protocol_t protocol(cin, cout); |
||||
|
||||
protocol::handler_map_t handlers; |
||||
handlers.emplace("config|ready", on_ready); |
||||
handlers.emplace("filter|*|*|smtp-in|data", on_data); |
||||
handlers.emplace("filter|*|*|smtp-in|data-line", on_dataline); |
||||
handlers.emplace("filter|*|*|smtp-in|commit", on_commit); |
||||
protocol.set_handlers(handlers); |
||||
|
||||
protocol.run(); |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,2 @@ |
||||
#include <mail.hpp> |
||||
|
@ -0,0 +1,77 @@ |
||||
#include <util.hpp> |
||||
|
||||
#include <protocol/protocol.hpp> |
||||
|
||||
#include <iostream> |
||||
#include <string> |
||||
|
||||
using namespace std; |
||||
using namespace util; |
||||
|
||||
//config|smtpd-version|6.6.1
|
||||
//config|smtp-session-timeout|300
|
||||
//config|subsystem|smtp-in
|
||||
|
||||
const bool debug = true; |
||||
|
||||
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; |
||||
} |
||||
} |
||||
} |
||||
debug && cerr << "Maximum handler pattern length is " << length << endl; |
||||
|
||||
this->handlers = handlers; |
||||
max_pattern_length = length; |
||||
} |
||||
|
||||
void protocol_t::send(const token::list_t& tokens) { |
||||
this->emit(token::compose(tokens)); |
||||
} |
||||
|
||||
void protocol_t::absorb(const std::string& input) { |
||||
auto tokens = token::decompose(input, max_pattern_length); |
||||
auto pattern = token::pattern_t(tokens, false); |
||||
debug && cout << "Received pattern " << pattern << endl; |
||||
|
||||
auto element = handlers.find(pattern); |
||||
if(element != handlers.end()) { |
||||
element->second(*this, {input, element->first}); |
||||
} |
||||
} |
||||
|
||||
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); |
||||
} |
||||
} |
||||
|
||||
bool operator==(const session_t& l, const session_t& r) { |
||||
return l.session == r.session && l.token == r.token; |
||||
} |
||||
|
||||
session_t session_from(token::list_t tokens) { |
||||
auto session_id = tokens.at(5); |
||||
auto session_token = tokens.at(6); |
||||
return {session_id, session_token}; |
||||
} |
||||
|
||||
session_t session_from(message_t message) { |
||||
auto tokens = token::decompose(message.input); |
||||
return session_from(tokens); |
||||
} |
||||
} |
@ -0,0 +1,115 @@ |
||||
#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 << "Checking if " << l << " < " << r << endl; |
||||
|
||||
if(l == r) { |
||||
debug && cerr << "Not less because left equals right" << endl; |
||||
return false; |
||||
} |
||||
|
||||
if(l.size() < r.size()) { |
||||
debug && cerr << "Less because right has more elements" << endl; |
||||
return true; |
||||
} |
||||
|
||||
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,36 @@ |
||||
#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; |
||||
|
||||
for(auto index = 0, last = -1; index != max_tokens; ++index) { |
||||
auto current = input.find(separator, last + 1); |
||||
auto eol = current == std::string::npos; |
||||
|
||||
if(eol || index == max_tokens) { |
||||
current = input.length(); |
||||
} |
||||
|
||||
auto length = current - last - 1; |
||||
auto token = input.substr(last + 1, length); |
||||
tokens.push_back(token); |
||||
last = current; |
||||
|
||||
if(eol) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
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); |
||||
} |
||||
} |
||||
return output; |
||||
} |
Loading…
Reference in new issue