35 AdminServer & server ,
const std::string & remote_address ,
37 bool with_terminate ) :
39 m_es(esu.bind(this)) ,
41 m_prompt(
"E-MailRelay> ") ,
43 m_remote_address(remote_address) ,
45 m_info_commands(info_commands) ,
46 m_config_commands(config_commands) ,
47 m_with_terminate(with_terminate)
49 G_LOG_S(
"GSmtp::AdminServerPeer: admin connection from " << peer_info.m_address.
displayString() ) ;
56 m_client_ptr.deletedSignal().disconnect() ;
59void GSmtp::AdminServerPeer::clientDone(
const std::string & s )
64 sendLine(
"error: " + s ) ;
67void GSmtp::AdminServerPeer::onDelete(
const std::string & reason )
69 G_LOG_S(
"GSmtp::AdminServerPeer: admin connection closed: " << reason << (reason.empty()?
"":
": ")
70 << peerAddress().second.displayString() ) ;
73void GSmtp::AdminServerPeer::onSecure(
const std::string & ,
const std::string & ,
const std::string & )
77bool GSmtp::AdminServerPeer::onReceive(
const char * line_data , std::size_t line_size , std::size_t ,
80 std::string line( line_data , line_size ) ;
81 if( is(line,
"flush") )
85 else if( is(line,
"forward") )
89 else if( is(line,
"help") )
93 else if( is(line,
"status") )
97 else if( is(line,
"notify") )
101 else if( is(line,
"list") )
103 sendList( spooled() ) ;
105 else if( is(line,
"failures") )
107 sendList( failures() ) ;
109 else if( is(line,
"unfail-all") )
114 else if( is(line,
"pid") )
118 else if( is(line,
"quit") )
122 else if( is(line,
"terminate") && m_with_terminate )
124 G_LOG_S(
"GSmtp::AdminServerPeer::onReceive: received a terminate command from "
125 << peerAddress().second.displayString() ) ;
129 else if( is(line,
"info") && !m_info_commands.empty() )
131 std::string arg = argument( line ) ;
132 if( arg.empty() || !find(arg,m_info_commands).first )
135 sendLine( find(arg,m_info_commands).second ) ;
137 else if( is(line,
"config") && !m_config_commands.empty() )
139 std::string arg = argument( line ) ;
141 sendLine(
G::Str::join( eol() , m_config_commands ,
"=[" ,
"]" ) ) ;
142 else if( !find(arg,m_config_commands).first )
145 sendLine( find(arg,m_config_commands).second ) ;
147 else if( line.find_first_not_of(
" \r\n\t") != std::string::npos )
149 sendLine(
"error: unrecognised command" ) ;
158std::string GSmtp::AdminServerPeer::eol()
const
160 std::string eol = lineBuffer().eol() ;
161 return eol.empty() ? std::string(
"\r\n") : eol ;
164bool GSmtp::AdminServerPeer::is(
const std::string & line_in ,
const std::string & key )
171std::string GSmtp::AdminServerPeer::argument(
const std::string & line_in )
175 return parts.size() > 1U ? parts.at(1U) : std::string() ;
178std::pair<bool,std::string> GSmtp::AdminServerPeer::find(
const std::string & line ,
const G::StringMap & map )
180 for(
const auto & item : map )
182 if( is(line,item.first) )
183 return std::make_pair(
true,item.second) ;
185 return std::make_pair(
false,std::string()) ;
188void GSmtp::AdminServerPeer::help()
190 std::set<std::string> commands ;
191 commands.insert(
"flush" ) ;
192 commands.insert(
"forward" ) ;
193 commands.insert(
"help" ) ;
194 commands.insert(
"status" ) ;
195 commands.insert(
"list" ) ;
196 commands.insert(
"failures" ) ;
197 commands.insert(
"unfail-all" ) ;
198 commands.insert(
"notify" ) ;
199 commands.insert(
"pid" ) ;
200 commands.insert(
"quit" ) ;
201 if( !m_info_commands.empty() ) commands.insert(
"info" ) ;
202 if( !m_config_commands.empty() ) commands.insert(
"config" ) ;
203 if( m_with_terminate ) commands.insert(
"terminate" ) ;
204 sendLine( std::string(
"commands: ") +
G::Str::join(
", ",commands) ) ;
207void GSmtp::AdminServerPeer::flush()
209 G_DEBUG(
"GSmtp::AdminServerPeer: flush: \"" << m_remote_address <<
"\"" ) ;
210 if( m_client_ptr.busy() )
212 sendLine(
"error: still working" ) ;
214 else if( m_remote_address.empty() )
216 sendLine(
"error: no remote server configured: use --forward-to" ) ;
218 else if( m_server.store().empty() )
220 sendLine(
"error: no messages to send" ) ;
224 m_client_ptr.reset( std::make_unique<GSmtp::Client>(
GNet::ExceptionSink(m_client_ptr,m_es.esrc()) ,
225 m_server.ff() ,
GNet::Location(m_remote_address) , m_server.clientSecrets() , m_server.clientConfig() ) ) ;
227 m_client_ptr->sendMessagesFrom( m_server.store() ) ;
232void GSmtp::AdminServerPeer::forward()
234 if( m_remote_address.empty() )
236 sendLine(
"error: no remote server configured: use --forward-to" ) ;
245void GSmtp::AdminServerPeer::sendLine( std::string line ,
bool with_prompt )
248 line.append(
"\n" ) ;
251 line.append( m_prompt ) ;
256 const std::string & s2 ,
const std::string &s3 )
262 s.append( 2U ,
' ' ) ;
267void GSmtp::AdminServerPeer::send_(
const std::string & s )
271 G_DEBUG(
"GSmtp::AdminServerPeer::send: flow control asserted: cannot send" ) ;
276 m_blocked = ! send( s ) ;
280void GSmtp::AdminServerPeer::onSendComplete()
285void GSmtp::AdminServerPeer::status()
287 std::ostringstream ss ;
290 std::string eolstr = eol() ;
292 std::string report = ss.str() ;
298 sendLine(
"no info" ) ;
302std::shared_ptr<GSmtp::MessageStore::Iterator> GSmtp::AdminServerPeer::spooled()
304 return m_server.store().iterator(
false) ;
307std::shared_ptr<GSmtp::MessageStore::Iterator> GSmtp::AdminServerPeer::failures()
309 return m_server.store().failures() ;
312void GSmtp::AdminServerPeer::sendList( std::shared_ptr<MessageStore::Iterator> iter )
314 std::ostringstream ss ;
315 for(
bool first =
true ;; first = false )
317 std::unique_ptr<StoredMessage> message( ++iter ) ;
318 if( message ==
nullptr ) break ;
319 if( !first ) ss << eol() ;
320 ss << message->id().str() ;
323 std::string result = ss.str() ;
325 sendLine(
"<none>" ) ;
327 sendLine( ss.str() ) ;
330void GSmtp::AdminServerPeer::unfailAll()
332 return m_server.store().unfailAll() ;
346 const G::StringArray & interfaces ,
unsigned int port ,
bool allow_remote ,
347 const std::string & remote_address ,
unsigned int connection_timeout ,
349 bool with_terminate ) :
350 GNet::MultiServer(es,interfaces,port,
"admin",server_peer_config,server_config) ,
351 m_forward_timer(*this,&
AdminServer::onForwardTimeout,es) ,
354 m_forward_request(forward_request) ,
355 m_client_config(client_config) ,
356 m_client_secrets(client_secrets) ,
357 m_allow_remote(allow_remote) ,
358 m_remote_address(remote_address) ,
359 m_connection_timeout(connection_timeout) ,
360 m_info_commands(info_commands) ,
361 m_config_commands(config_commands) ,
362 m_with_terminate(with_terminate)
374 std::unique_ptr<GNet::ServerPeer> ptr ;
380 G_WARNING(
"GSmtp::Server: configured to reject non-local admin connection: " << reason ) ;
384 ptr = std::make_unique<AdminServerPeer>( esu , peer_info , *
this , m_remote_address ,
385 m_info_commands , m_config_commands , m_with_terminate ) ;
388 catch( std::exception & e )
390 G_WARNING(
"GSmtp::AdminServer: new connection error: " << e.what() ) ;
397 m_forward_timer.startTimer( 0 ) ;
400void GSmtp::AdminServer::onForwardTimeout()
404 m_forward_request.emit( std::string(
"admin") ) ;
406 catch( std::exception & e )
408 G_WARNING(
"GSmtp::AdminServer: exception: " << e.what() ) ;
418 const std::string & s2 ,
const std::string & s3 )
422 using List = std::vector<std::weak_ptr<GNet::ServerPeer> > ;
423 List list = peers() ;
424 for(
auto & wptr : list )
426 if( wptr.expired() ) continue ;
427 std::shared_ptr<GNet::ServerPeer> ptr = wptr.lock() ;
429 G_DEBUG(
"GSmtp::AdminServer::notify: " << peer <<
": " << s0 <<
": " << s1 ) ;
430 peer->
notify( s0 , s1 , s2 , s3 ) ;
447 return m_client_secrets ;
452 return m_connection_timeout ;
457 return m_client_config ;
462 bool result = false ;
465 using List = std::vector<std::weak_ptr<GNet::ServerPeer> > ;
466 List list =
const_cast<AdminServer*
>(
this)->peers() ;
467 for(
auto & wptr : list )
469 if( wptr.expired() )
continue ;
470 std::shared_ptr<GNet::ServerPeer> ptr = wptr.lock() ;
An interface used by GAuth::SaslClient to obtain a client id and its authentication secret.
std::string displayString(bool with_scope_id=false) const
Returns a string which represents the transport address.
G::Slot::Signal< const std::string & > & deletedSignal()
A signal that is triggered after deleteSignal() once the client has been deleted and the ClientPtr is...
An exception class that is detected by GNet::EventHandlerList and results in onException() being call...
static bool exists()
Returns true if an instance exists.
virtual void quit(const std::string &reason)=0
Causes run() to return (once the call stack has unwound).
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
A potential ExceptionSink that is realised by bind()ing an exception source pointer.
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
static bool isLocal(const Address &, std::string &reason)
Returns true if the given address appears to be 'local', or a helpful error message if not.
A class that represents the remote target for out-going client connections.
void report(std::ostream &stream, const std::string &line_prefix=std::string(), const std::string &eol=std::string("\n")) const
Reports itself onto a stream.
static Monitor * instance()
Returns the singleton pointer. Returns nullptr if none.
A structure that GNet::Server uses to configure its ServerPeer objects.
A structure used in GNet::Server::newPeer().
A derivation of ServerPeer for the administration interface.
bool notifying() const
Returns true if the remote user has asked for notifications.
void notify(const std::string &s0, const std::string &s1, const std::string &s2, const std::string &s4)
Called when something happens which the admin user might be interested in.
~AdminServerPeer() override
Destructor.
AdminServerPeer(GNet::ExceptionSinkUnbound, const GNet::ServerPeerInfo &, AdminServer &, const std::string &remote, const G::StringMap &info_commands, const G::StringMap &config_commands, bool with_terminate)
Constructor.
A server class which implements the emailrelay administration interface.
void report() const
Generates helpful diagnostics.
MessageStore & store()
Returns a reference to the message store, as passed in to the constructor.
AdminServer(GNet::ExceptionSink, MessageStore &store, FilterFactory &ff, G::Slot::Signal< const std::string & > &forward_request, const GNet::ServerPeerConfig &server_peer_config, const GNet::ServerConfig &server_config, const GSmtp::Client::Config &client_config, const GAuth::SaslClientSecrets &client_secrets, const G::StringArray &interfaces, unsigned int port, bool allow_remote, const std::string &remote_address, unsigned int connection_timeout, const G::StringMap &info_commands, const G::StringMap &config_commands, bool with_terminate)
Constructor.
void forward()
Called to trigger asynchronous forwarding.
FilterFactory & ff()
Returns a reference to the filter factory, as passed in to the constructor.
~AdminServer() override
Destructor.
GSmtp::Client::Config clientConfig() const
Returns the client configuration.
void notify(const std::string &s0, const std::string &s1, const std::string &s2, const std::string &s3)
Called when something happens which the admin users might be interested in.
unsigned int connectionTimeout() const
Returns the connection timeout, as passed in to the constructor.
const GAuth::SaslClientSecrets & clientSecrets() const
Returns a reference to the client secrets object, as passed in to the constructor.
std::unique_ptr< GNet::ServerPeer > newPeer(GNet::ExceptionSinkUnbound, GNet::ServerPeerInfo, GNet::MultiServer::ServerInfo) override
Override from GNet::MultiServer.
bool notifying() const
Returns true if the remote user has asked for notifications.
A factory interface for GSmtp::Filter message processors.
A class which allows SMTP messages to be stored and retrieved.
Handles a connection from a remote SMTP client.
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
static bool imatch(char, char)
Returns true if the two characters are the same, ignoring Latin-1 case.
static string_view ws()
Returns a string of standard whitespace characters.
static std::set< std::string > keySet(const StringMap &string_map)
Extracts the keys from a map of strings.
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
static std::string unique(const std::string &s, char c, char r)
Returns a string with repeated 'c' characters replaced by one 'r' character.
static std::string & trimRight(std::string &s, string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
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 unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
std::vector< std::string > StringArray
A std::vector of std::strings.
std::map< std::string, std::string > StringMap
A std::map of std::strings.
A structure used in GNet::MultiServer::newPeer().
A configuration structure for GNet::Server.
A structure containing GSmtp::Client configuration parameters.