Bogofilter adapter for OpenSMTPD in C++
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

108 lines
3.8 KiB

// 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);
};
}
}
}