Initial commit

master
Thor 4 years ago
commit 2dff81806a
  1. 4
      .gitignore
  2. 30
      Makefile
  3. 20
      include/mail.hpp
  4. 69
      include/protocol/protocol.hpp
  5. 33
      include/protocol/token.hpp
  6. 12
      include/util.hpp
  7. 50
      src/bogofilter-smtpd.cpp
  8. 2
      src/mail.cpp
  9. 77
      src/protocol/protocol.cpp
  10. 115
      src/protocol/token.cpp
  11. 36
      src/util.cpp

4
.gitignore vendored

@ -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…
Cancel
Save