// OpenSMTPD filter protocol documentation: // https://man7.org/linux/man-pages/man7/smtpd-filters.7.html #include #include #include #include using namespace std; using namespace util; 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; } } } 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) { 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& header, const vector& 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); } } 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); } }