35 Filter::Type filter_type ,
const Filter::Config & filter_config ,
const std::string & spec ) :
38 m_filter_type(filter_type) ,
39 m_filter_config(filter_config) ,
42 m_timer(*this,&
MxFilter::onTimeout,es)
45 m_mxlookup_config = parseSpec( m_spec , m_mxlookup_nameservers ) ;
53 m_lookup->doneSignal().disconnect() ;
58 G::Path envelope_path = m_store.envelopePath( message_id , storestate() ) ;
60 auto forward_to = parseForwardTo( envelope.forward_to ) ;
61 if( !forward_to.address.empty() )
64 G_LOG_MORE(
"GFilters::MxFilter::start: " << prefix() <<
" copying forward-to to forward-to-address: " << forward_to.address ) ;
67 msg.editEnvelope( [forward_to](
GStore::Envelope &env_){env_.forward_to_address=forward_to.address;} ) ;
68 m_timer.startTimer( 0U ) ;
69 m_result = Result::ok ;
71 else if( forward_to.domain.empty() )
73 G_LOG_MORE(
"GFilters::MxFilter::start: " << prefix() <<
" no forward-to domain" ) ;
74 m_timer.startTimer( 0U ) ;
75 m_result = Result::ok ;
79 G_LOG(
"GFilters::MxFilter::start: " << prefix() <<
" looking up [" << forward_to.domain <<
"]" ) ;
81 if( m_lookup ) m_lookup->doneSignal().disconnect() ;
82 m_lookup = std::make_unique<MxLookup>( m_es , m_mxlookup_config , m_mxlookup_nameservers ) ;
83 m_lookup->doneSignal().connect(
G::Slot::slot(*
this,&MxFilter::lookupDone) ) ;
85 m_lookup->start( message_id , forward_to.domain , forward_to.port ) ;
86 if( m_filter_config.timeout )
87 m_timer.startTimer( m_filter_config.timeout ) ;
89 m_timer.cancelTimer() ;
93std::string GFilters::MxFilter::id()
const
98bool GFilters::MxFilter::quiet()
const
103bool GFilters::MxFilter::special()
const
108GSmtp::Filter::Result GFilters::MxFilter::result()
const
113std::string GFilters::MxFilter::response()
const
115 return { m_result == Result::fail ?
"failed" :
"" } ;
118int GFilters::MxFilter::responseCode()
const
123std::string GFilters::MxFilter::reason()
const
130 return m_done_signal ;
133void GFilters::MxFilter::cancel()
139void GFilters::MxFilter::onTimeout()
141 G_DEBUG(
"GFilters::MxFilter::onTimeout: response=[" << response() <<
"] special=" << m_special ) ;
142 m_done_signal.emit(
static_cast<int>(m_result) ) ;
145void GFilters::MxFilter::lookupDone(
GStore::MessageId message_id , std::string address , std::string error )
147 G_ASSERT( address.empty() == !error.empty() ) ;
153 G_LOG(
"GFilters::MxFilter::start: " << prefix() <<
": [" << message_id.
str() <<
"]: "
154 <<
"setting forward-to-address [" << address <<
"]"
155 << (error.empty()?
"":
" (") << error << (error.empty()?
"":
")") ) ;
160 msg.editEnvelope( [address](
GStore::Envelope &env_){env_.forward_to_address=address;} ) ;
162 m_result = error.empty() ? Result::ok : Result::fail ;
163 m_timer.startTimer( 0U ) ;
166GStore::FileStore::State GFilters::MxFilter::storestate()
const
168 return m_filter_type == GSmtp::Filter::Type::server ?
169 GStore::FileStore::State::New :
170 GStore::FileStore::State::Locked ;
173GFilters::MxLookup::Config GFilters::MxFilter::parseSpec( std::string_view spec , std::vector<GNet::Address> & nameservers_out )
175 MxLookup::Config config ;
178 std::string_view s = t() ;
179 if( s.find(
"nst=") == 0U && s.size() > 4U &&
G::Str::isUInt(s.substr(4U)) )
181 else if( s.find(
"rt=") == 0U && s.size() > 3U &&
G::Str::isUInt(s.substr(3U)) )
188 G_WARNING_ONCE(
"GFilters::MxFilter::parseSpec: invalid mx filter configuration: ignoring [" <<
G::Str::printable(s) <<
"]" ) ;
193std::string GFilters::MxFilter::addressLiteral( std::string_view s ,
unsigned int port )
196 if( s.size() > 2U && s[0] ==
'[' && s[s.size()-1U] ==
']' )
198 if( port == 0U ) port = 25U ;
199 s = s.substr( 1U , s.size()-2U ) ;
201 if( ipv6 ) s = s.substr( 5U ) ;
205 if( address.family() == ( ipv6 ? GNet::Address::Family::ipv6 : GNet::Address::Family::ipv4 ) )
206 return address.displayString() ;
212GFilters::MxFilter::ParserResult GFilters::MxFilter::parseForwardTo(
const std::string & forward_to )
219 std::size_t pos = no_user.rfind(
':' ) ;
223 auto domain = with_port ? head : no_user ;
225 return { G::sv_to_string(domain) , port , addressLiteral(domain,port) } ;
228std::string GFilters::MxFilter::prefix()
const
230 return G::sv_to_string(strtype(m_filter_type)).append(
" [").append(
id()).append(1U,
']') ;
A concrete GSmtp::Filter class for message routing: if the message's 'forward-to' envelope field is s...
~MxFilter() override
Destructor.
MxFilter(GNet::EventState es, GStore::FileStore &, Filter::Type, const Filter::Config &, const std::string &spec)
Constructor.
static bool enabled()
Returns true if implemented.
static bool validString(std::string_view display_string, std::string *reason=nullptr)
Returns true if the transport-address display string is valid.
static bool validStrings(std::string_view ip, std::string_view port_string, std::string *reason=nullptr)
Returns true if the combined network-address string and port string is valid.
static Address parse(std::string_view display_string)
Factory function for any address family.
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
A structure containing the contents of an envelope file, with support for file reading,...
A concrete implementation of the MessageStore interface dealing in paired flat files.
static Envelope readEnvelope(const G::Path &, std::ifstream *=nullptr)
Used by FileStore sibling classes to read an envelope file.
A somewhat opaque identifer for a GStore::MessageStore message id.
std::string str() const
Returns the id string.
A concete class implementing the GStore::StoredMessage interface for separate envelope and content fi...
A general-purpose exception class derived from std::exception and containing an error message.
A Path object represents a file system path.
static bool isUInt(std::string_view s) noexcept
Returns true if the string can be converted into an unsigned integer without throwing an exception.
static bool headMatch(std::string_view in, std::string_view head) noexcept
Returns true if the string has the given start (or head is empty).
static bool isNumeric(std::string_view s, bool allow_minus_sign=false) noexcept
Returns true if every character is a decimal digit.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static std::size_t ifind(std::string_view s, std::string_view key)
Returns the position of the key in 's' using a seven-bit case-insensitive search.
static std::string_view tailView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static unsigned int toUInt(std::string_view s)
Converts string 's' to an unsigned int.
static std::string_view headView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like head() but returning a view into the input string.
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
An interval between two G::SystemTime values or two G::TimerTime values.
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
A configuration structure for GFilters::MxLookup.