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