#include #include #include #include #include #include #include #include #include const bool debug = false; using namespace std; mail::session_map_t sessions; void on_ready(protocol::protocol_t& protocol, protocol::message_t message) { 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"}); cerr << "Ready to accept messages from SMTPD" << endl; } void on_data(protocol::protocol_t& protocol, protocol::message_t message) { cerr << "Accepting new message from SMTPD" << endl; auto session_key = protocol::session_key_from(message); auto session_token = protocol::session_token_from(message); sessions.emplace(session_key, mail::session_t{}); protocol.proceed(session_key, session_token); } void on_data_line(protocol::protocol_t& protocol, protocol::message_t message) { debug && cerr << "Received data line from SMTPD" << endl; auto tokens = protocol::token::decompose(message.input); auto session_key = protocol::session_key_from(tokens); auto session_token = protocol::session_token_from(tokens); try { auto& session = sessions.at(session_key); auto& data_line = tokens.at(7); if(!session.has_header) { if(data_line == "") { session.has_header = true; return; } if(data_line.substr(0, 6) == "X-Spam" || data_line.substr(0, 10) == "X-Bogosity") { cerr << "Found and stripped incoming spam filter header:" << endl; cerr << data_line << endl; return; } if(data_line.substr(0, 5) == "From:" || data_line.substr(0, 8) == "Subject:") { cerr << data_line << endl; } session.message.header.push_back(data_line); return; } if(data_line == ".") { FILE* f = popen("bogofilter -vu 1>&2", "w"); for(string& line : session.message.header) { fprintf(f, "%s\n", line.c_str()); } fwrite("\n", 1, 1, f); for(string& line : session.message.body) { fprintf(f, "%s\n", line.c_str()); } int status = pclose(f); session.status = WEXITSTATUS(status); if(session.status == 0) { session.message.header.push_back("X-Spam: Yes"); } protocol.submit_message(session_key, session_token, session.message.header, session.message.body); return; } if(data_line == "..") { session.message.body.push_back("."); return; } session.message.body.push_back(data_line); } catch(out_of_range& e) { cerr << "Unknown session key from SMTPD" << endl; protocol.fail(session_key, session_token); } } void on_commit(protocol::protocol_t& protocol, protocol::message_t message) { debug && cerr << "Commit request from SMTPD" << endl; auto session_key = protocol::session_key_from(message); auto session_token = protocol::session_token_from(message); try { auto& session = sessions.at(session_key); protocol.proceed(session_key, session_token); } catch(out_of_range& e) { cerr << "Unknown session key from SMTPD" << endl; protocol.fail(session_key, session_token); } sessions.erase(session_key); } 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_data_line); handlers.emplace("filter|*|*|smtp-in|commit", on_commit); if(debug) { cerr << "Handler patterns:" << endl; for(auto&& [first, second] : handlers) { cerr << "\t" << first << endl; } } cerr << "Bogofilter-SMTPD started" << endl; protocol.set_handlers(handlers); protocol.run(); return 0; }