43 const AdminServer::Config & config ) ;
44 ~AdminServerImp()
override ;
46 void report( const
std::
string & group = {} )
const ;
48 FilterFactoryBase & ff() ;
51 void emitCommand( AdminServer::Command ,
unsigned int ) ;
52 bool notifying()
const ;
53 void notify(
const std::string & s0 ,
const std::string & s1 ,
const std::string & s2 ,
const std::string & s3 ) ;
56 AdminServerImp(
const AdminServerImp & ) = delete ;
57 AdminServerImp( AdminServerImp && ) = delete ;
58 AdminServerImp & operator=(
const AdminServerImp & ) = delete ;
59 AdminServerImp & operator=( AdminServerImp && ) = delete ;
66 void onCommandTimeout() ;
70 FilterFactoryBase & m_ff ;
72 AdminServer::Config m_config ;
75 AdminServer::Command m_command {AdminServer::Command::forward} ;
76 unsigned int m_command_arg {0U} ;
82 AdminServerImp & server_imp ,
const std::string & remote_address ,
84 bool with_terminate ) :
86 m_es(esbind(esu,this)) ,
87 m_server_imp(server_imp) ,
88 m_prompt(
"E-MailRelay> ") ,
89 m_remote_address(remote_address) ,
90 m_info_commands(info_commands) ,
91 m_with_terminate(with_terminate)
93 G_LOG_S(
"GSmtp::AdminServerPeer: admin connection from " <<
peerAddress().displayString() ) ;
100 m_client_ptr.deletedSignal().disconnect() ;
103void GSmtp::AdminServerPeer::clientDone(
const std::string & s )
105 G_DEBUG(
"GSmtp::AdminServerPeer::clientDone: [" << s <<
"]" ) ;
109 sendLine( std::move(std::string(
"error: ").append(s)) ) ;
112void GSmtp::AdminServerPeer::onDelete(
const std::string & reason )
114 G_LOG_S(
"GSmtp::AdminServerPeer: admin connection closed: " << reason << (reason.empty()?
"":
": ")
115 << peerAddress().displayString() ) ;
118void GSmtp::AdminServerPeer::onSecure(
const std::string & ,
const std::string & ,
const std::string & )
122bool GSmtp::AdminServerPeer::onReceive(
const char * line_data , std::size_t line_size , std::size_t ,
125 std::string_view line( line_data , line_size ) ;
127 if( is(t(),
"flush") )
131 else if( is(t(),
"forward") )
135 else if( is(t(),
"help") )
139 else if( is(t(),
"status") )
143 else if( is(t(),
"notify") )
146 setIdleTimeout( 0U ) ;
148 else if( is(t(),
"list") )
150 sendMessageIds( m_server_imp.store().ids() ) ;
152 else if( is(t(),
"failures") )
154 sendMessageIds( m_server_imp.store().failures() ) ;
156 else if( is(t(),
"unfail-all") )
158 m_server_imp.store().unfailAll() ;
159 sendLine( std::string() ) ;
161 else if( is(t(),
"pid") )
165 else if( is(t(),
"quit") )
169 else if( is(t(),
"terminate") && m_with_terminate )
171 G_LOG_S(
"GSmtp::AdminServerPeer::onReceive: received a terminate command from "
172 << peerAddress().displayString() ) ;
176 else if( is(t(),
"info") && !m_info_commands.empty() )
178 std::string_view arg = (++t)() ;
179 if( arg.empty() || !find(arg,m_info_commands).first )
180 sendLine( std::move(std::string(
"usage: info {").append(
G::Str::join(
"|",
G::Str::keys(m_info_commands))).append(1U,
'}')) ) ;
182 sendLine( find(arg,m_info_commands).second ) ;
184 else if( is(t(),
"dnsbl") )
186 std::string_view action = (++t)() ;
187 std::string_view arg = (++t)() ;
192 m_server_imp.emitCommand( AdminServer::Command::dnsbl , start ? 0U :
193 ( arg.empty() ? std::numeric_limits<unsigned int>::max() :
G::Str::toUInt(arg,
"0") ) ) ;
197 sendLine(
"usage: dnsbl {start|stop <timeout>}" ) ;
200 else if( is(t(),
"smtp") )
202 std::string_view arg = (++t)() ;
206 m_server_imp.emitCommand( AdminServer::Command::smtp_enable , 0U ) ;
211 m_server_imp.emitCommand( AdminServer::Command::smtp_enable , 1U ) ;
215 sendLine(
"usage: smtp {disable|enable}" ) ;
218 else if( line.find_first_not_of(
" \r\n\t") != std::string::npos )
220 sendLine(
"error: unrecognised command" ) ;
222 if( m_error_limit && m_error_count >= m_error_limit )
227 sendLine( std::string() ) ;
232std::string GSmtp::AdminServerPeer::eol()
const
234 std::string eol = lineBuffer().eol() ;
235 return eol.empty() ? std::string(
"\r\n") : eol ;
238bool GSmtp::AdminServerPeer::is( std::string_view token , std::string_view key )
243std::pair<bool,std::string> GSmtp::AdminServerPeer::find( std::string_view line ,
const G::StringMap & map )
245 for(
const auto & item : map )
247 if( is(line,item.first) )
248 return { true , item.second } ;
250 return { false , {} } ;
253void GSmtp::AdminServerPeer::help()
255 sendLine( std::move(std::string(
"commands: ")
257 .append(
"failures, " )
259 .append(
"forward, " )
261 .append(
"info, " , m_info_commands.empty() ? 0U : 6U )
263 .append(
"notify, " )
267 .append(
"status, " )
268 .append(
"terminate, " , m_with_terminate ? 11U : 0U )
269 .append(
"unfail-all" )) ) ;
272void GSmtp::AdminServerPeer::flush()
274 G_DEBUG(
"GSmtp::AdminServerPeer: flush: \"" << m_remote_address <<
"\"" ) ;
275 if( m_client_ptr.busy() )
277 sendLine(
"error: still working" ) ;
279 else if( m_remote_address.empty() )
281 sendLine(
"error: no remote server configured: use --forward-to" ) ;
283 else if( m_server_imp.store().empty() )
285 sendLine(
"error: no messages to send" ) ;
289 m_client_ptr.reset( std::make_unique<GSmtp::Forward>( m_es.eh(m_client_ptr) ,
290 m_server_imp.store() , m_server_imp.ff() ,
GNet::Location(m_remote_address) ,
291 m_server_imp.clientSecrets() , m_server_imp.clientConfig() ) ) ;
296void GSmtp::AdminServerPeer::forward()
298 if( m_remote_address.empty() )
300 sendLine(
"error: no remote server configured: use --forward-to" ) ;
305 m_server_imp.emitCommand( AdminServer::Command::forward , 0U ) ;
309void GSmtp::AdminServerPeer::sendLine( std::string && line )
312 line.append(
"\n" ) ;
314 line.append( m_prompt ) ;
324 s.append( 2U ,
' ' ) ;
329void GSmtp::AdminServerPeer::sendImp(
const std::string & s )
333 G_DEBUG(
"GSmtp::AdminServerPeer::send: flow control asserted: cannot send" ) ;
338 m_blocked = !send( s ) ;
342void GSmtp::AdminServerPeer::onSendComplete()
347void GSmtp::AdminServerPeer::status()
349 std::ostringstream ss ;
352 const std::string eolstr = eol() ;
354 std::string
report = ss.str() ;
356 sendLine( std::move(
report) ) ;
360 sendLine(
"no info" ) ;
364void GSmtp::AdminServerPeer::sendMessageIds(
const std::vector<GStore::MessageId> & ids )
366 std::ostringstream ss ;
368 for(
const auto &
id : ids )
370 if( !first ) ss << eol() ;
375 std::string result = ss.str() ;
377 sendLine(
"<none>" ) ;
379 sendLine( ss.str() ) ;
392 m_imp(
std::make_unique<AdminServerImp>(es,store,ff,client_secrets,interfaces,config))
406 return m_imp->commandSignal() ;
411 m_imp->report( group ) ;
417 return m_imp->store() ;
431 return m_imp->clientSecrets() ;
438 m_imp->emitCommand( command , arg ) ;
444 return m_imp->notifying() ;
447void GSmtp::AdminServer::notify(
const std::string & s0 ,
const std::string & s1 ,
const std::string & s2 ,
const std::string & s3 )
449 m_imp->notify( s0 , s1 , s2 , s3 ) ;
456 const G::StringArray & interfaces ,
const AdminServer::Config & config ) :
457 GNet::MultiServer(es,interfaces,config.port,
"admin",config.net_server_peer_config,config.net_server_config) ,
460 m_client_secrets(client_secrets) ,
462 m_command_timer(*this,&AdminServerImp::onCommandTimeout,es)
466GSmtp::AdminServerImp::~AdminServerImp()
474 std::unique_ptr<GNet::ServerPeer> ptr ;
478 if( !m_config.allow_remote && !peer_info.m_address.isLocal(reason) )
480 G_WARNING(
"GSmtp::Server: configured to reject non-local admin connection: " << reason ) ;
484 ptr = std::make_unique<AdminServerPeer>( esu , std::move(peer_info) , *
this ,
485 m_config.remote_address , m_config.info_commands ,
486 m_config.with_terminate ) ;
489 catch( std::exception & e )
491 G_WARNING(
"GSmtp::AdminServer: new connection error: " << e.what() ) ;
496void GSmtp::AdminServerImp::emitCommand( AdminServer::Command command ,
unsigned int arg )
498 m_command = command ;
499 m_command_arg = arg ;
500 m_command_timer.startTimer( 0 ) ;
503void GSmtp::AdminServerImp::onCommandTimeout()
507 m_command_signal.emit( m_command , m_command_arg ) ;
509 catch( std::exception & e )
511 G_WARNING(
"GSmtp::AdminServer: exception: " << e.what() ) ;
517 return m_command_signal ;
522 serverReport( group ) ;
525void GSmtp::AdminServerImp::notify(
const std::string & s0 ,
const std::string & s1 ,
const std::string & s2 ,
const std::string & s3 )
529 using List = std::vector<std::weak_ptr<GNet::ServerPeer>> ;
530 List list = peers() ;
531 for(
auto & wptr : list )
533 if( wptr.expired() ) continue ;
534 std::shared_ptr<GNet::ServerPeer> ptr = wptr.lock() ;
535 AdminServerPeer * peer =
static_cast<AdminServerPeer*
>( ptr.get() ) ;
536 peer->notify( s0 , s1 , s2 , s3 ) ;
553 return m_client_secrets ;
558 return m_config.smtp_client_config ;
561bool GSmtp::AdminServerImp::notifying()
const
563 bool result = false ;
566 using List = std::vector<std::weak_ptr<GNet::ServerPeer>> ;
567 List list =
const_cast<AdminServerImp*
>(
this)->peers() ;
568 for(
auto & wptr : list )
570 if( wptr.expired() )
continue ;
571 std::shared_ptr<GNet::ServerPeer> ptr = wptr.lock() ;
572 AdminServerPeer * peer =
static_cast<AdminServerPeer*
>( ptr.get() ) ;
573 if( peer->notifying() )
An interface used by GAuth::SaslClient to obtain a client id and its authentication secret.
G::Slot::Signal< const std::string & > & deletedSignal() noexcept
A signal that is triggered after deleteSignal() once the client has been deleted and the ClientPtr is...
An exception class that is caught separately by GNet::EventEmitter and GNet::TimerList so that onExce...
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.
The EventStateUnbound class is used as a device to force factory methods to plumb-in an ExceptionSour...
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
A class that represents the remote target for out-going client connections.
void report(std::ostream &stream, const std::string &line_prefix={}, 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 server that listens on more than one address using a facade pattern to multiple GNet::Server instan...
virtual std::unique_ptr< ServerPeer > newPeer(EventStateUnbound, ServerPeerInfo &&, ServerInfo)=0
A factory method which creates a ServerPeer-derived object.
A move-only structure used in GNet::Server::newPeer() and containing the new socket.
Address peerAddress() const override
Returns the peer address.
A timer class template in which the timeout is delivered to the specified method.
bool notifying() const
Returns true if the remote user has asked for notifications.
AdminServerPeer(GNet::EventStateUnbound, GNet::ServerPeerInfo &&, AdminServerImp &, const std::string &remote, const G::StringMap &info_commands, bool with_terminate)
Constructor.
~AdminServerPeer() override
Destructor.
void notify(const std::string &s0, const std::string &s1, const std::string &s2, const std::string &s3)
Called when something happens which the remote admin user might be interested in.
static bool enabled()
Returns true if the server is enabled.
~AdminServer()
Destructor.
G::Slot::Signal< Command, unsigned int > & commandSignal()
Returns a reference to a signal that is emit()ted when the remote user makes a request.
void report(const std::string &group={}) const
Generates helpful diagnostics.
AdminServer(GNet::EventState, GStore::MessageStore &store, FilterFactoryBase &, const GAuth::SaslClientSecrets &client_secrets, const G::StringArray &interfaces, const Config &config)
Constructor.
void emitCommand(Command, unsigned int)
Emits an asynchronous event on the commandSignal().
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.
GStore::MessageStore & store()
Returns a reference to the message store, as passed in to the constructor.
FilterFactoryBase & ff()
Returns a reference to the filter factory, 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.
bool notifying() const
Returns true if the remote user has asked for notifications.
A factory interface for making GSmtp::Filter message processors.
Handles a connection from a remote SMTP client.
A class which allows SMTP messages to be stored and retrieved.
A general-purpose exception class derived from std::exception and containing an error message.
static std::string & trimRight(std::string &s, std::string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
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 std::string join(std::string_view sep, const StringArray &strings)
Concatenates an array of strings with separators.
static StringArray keys(const StringMap &string_map)
Extracts the keys from a map of strings.
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
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 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 toUInt(std::string_view s)
Converts string 's' to an unsigned int.
static unsigned int replaceAll(std::string &s, std::string_view from, std::string_view to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
static std::string_view ws() noexcept
Returns a string of standard whitespace characters.
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
void report(const Server *, const std::string &group={})
Calls GPop::Server::report().
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::ServerPeer.
A structure containing GSmtp::Client configuration parameters.
A slot holder, with connect() and emit() methods.