42 const AdminServer::Config & config ) ;
43 ~AdminServerImp()
override ;
45 void report( const
std::
string & group = {} )
const ;
47 FilterFactoryBase & ff() ;
50 void emitCommand( AdminServer::Command ,
unsigned int ) ;
51 bool notifying()
const ;
52 void notify(
const std::string & s0 ,
const std::string & s1 ,
const std::string & s2 ,
const std::string & s3 ) ;
55 AdminServerImp(
const AdminServerImp & ) = delete ;
56 AdminServerImp( AdminServerImp && ) = delete ;
57 AdminServerImp & operator=(
const AdminServerImp & ) = delete ;
58 AdminServerImp & operator=( AdminServerImp && ) = delete ;
65 void onCommandTimeout() ;
69 FilterFactoryBase & m_ff ;
71 AdminServer::Config m_config ;
74 AdminServer::Command m_command ;
75 unsigned int m_command_arg ;
81 AdminServerImp & server_imp ,
const std::string & remote_address ,
83 bool with_terminate ) :
85 m_es(esu.bind(this)) ,
86 m_server_imp(server_imp) ,
87 m_prompt(
"E-MailRelay> ") ,
89 m_remote_address(remote_address) ,
91 m_info_commands(info_commands) ,
92 m_with_terminate(with_terminate) ,
96 G_LOG_S(
"GSmtp::AdminServerPeer: admin connection from " <<
peerAddress().displayString() ) ;
103 m_client_ptr.deletedSignal().disconnect() ;
106void GSmtp::AdminServerPeer::clientDone(
const std::string & s )
108 G_DEBUG(
"GSmtp::AdminServerPeer::clientDone: [" << s <<
"]" ) ;
112 sendLine( std::move(std::string(
"error: ").append(s)) ) ;
115void GSmtp::AdminServerPeer::onDelete(
const std::string & reason )
117 G_LOG_S(
"GSmtp::AdminServerPeer: admin connection closed: " << reason << (reason.empty()?
"":
": ")
118 << peerAddress().displayString() ) ;
121void GSmtp::AdminServerPeer::onSecure(
const std::string & ,
const std::string & ,
const std::string & )
125bool GSmtp::AdminServerPeer::onReceive(
const char * line_data , std::size_t line_size , std::size_t ,
130 if( is(t(),
"flush") )
134 else if( is(t(),
"forward") )
138 else if( is(t(),
"help") )
142 else if( is(t(),
"status") )
146 else if( is(t(),
"notify") )
149 setIdleTimeout( 0U ) ;
151 else if( is(t(),
"list") )
153 sendMessageIds( m_server_imp.store().ids() ) ;
155 else if( is(t(),
"failures") )
157 sendMessageIds( m_server_imp.store().failures() ) ;
159 else if( is(t(),
"unfail-all") )
161 m_server_imp.store().unfailAll() ;
162 sendLine( std::string() ) ;
164 else if( is(t(),
"pid") )
168 else if( is(t(),
"quit") )
172 else if( is(t(),
"terminate") && m_with_terminate )
174 G_LOG_S(
"GSmtp::AdminServerPeer::onReceive: received a terminate command from "
175 << peerAddress().displayString() ) ;
179 else if( is(t(),
"info") && !m_info_commands.empty() )
182 if( arg.empty() || !find(arg,m_info_commands).first )
183 sendLine( std::move(std::string(
"usage: info {").append(
G::Str::join(
"|",
G::Str::keys(m_info_commands))).append(1U,
'}')) ) ;
185 sendLine( find(arg,m_info_commands).second ) ;
187 else if( is(t(),
"dnsbl") )
195 m_server_imp.emitCommand( AdminServer::Command::dnsbl , start ? 0U :
196 ( arg.empty() ? std::numeric_limits<unsigned int>::max() :
G::Str::toUInt(arg,
"0") ) ) ;
200 sendLine(
"usage: dnsbl {start|stop <timeout>}" ) ;
203 else if( is(t(),
"smtp") )
209 m_server_imp.emitCommand( AdminServer::Command::smtp_enable , 0U ) ;
214 m_server_imp.emitCommand( AdminServer::Command::smtp_enable , 1U ) ;
218 sendLine(
"usage: smtp {disable|enable}" ) ;
221 else if( line.find_first_not_of(
" \r\n\t") != std::string::npos )
223 sendLine(
"error: unrecognised command" ) ;
225 if( m_error_limit && m_error_count >= m_error_limit )
230 sendLine( std::string() ) ;
235std::string GSmtp::AdminServerPeer::eol()
const
237 std::string eol = lineBuffer().eol() ;
238 return eol.empty() ? std::string(
"\r\n") : eol ;
248 for(
const auto & item : map )
250 if( is(line,item.first) )
251 return { true , item.second } ;
253 return { false , {} } ;
256void GSmtp::AdminServerPeer::help()
258 sendLine( std::move(std::string(
"commands: ")
260 .append(
"failures, " )
262 .append(
"forward, " )
264 .append(
"info, " , m_info_commands.empty() ? 0U : 6U )
266 .append(
"notify, " )
270 .append(
"status, " )
271 .append(
"terminate, " , m_with_terminate ? 11U : 0U )
272 .append(
"unfail-all" )) ) ;
275void GSmtp::AdminServerPeer::flush()
277 G_DEBUG(
"GSmtp::AdminServerPeer: flush: \"" << m_remote_address <<
"\"" ) ;
278 if( m_client_ptr.busy() )
280 sendLine(
"error: still working" ) ;
282 else if( m_remote_address.empty() )
284 sendLine(
"error: no remote server configured: use --forward-to" ) ;
286 else if( m_server_imp.store().empty() )
288 sendLine(
"error: no messages to send" ) ;
292 m_client_ptr.reset( std::make_unique<GSmtp::Forward>(
GNet::ExceptionSink(m_client_ptr,m_es.esrc()) ,
293 m_server_imp.store() , m_server_imp.ff() ,
GNet::Location(m_remote_address) ,
294 m_server_imp.clientSecrets() , m_server_imp.clientConfig() ) ) ;
299void GSmtp::AdminServerPeer::forward()
301 if( m_remote_address.empty() )
303 sendLine(
"error: no remote server configured: use --forward-to" ) ;
308 m_server_imp.emitCommand( AdminServer::Command::forward , 0U ) ;
312void GSmtp::AdminServerPeer::sendLine( std::string && line )
315 line.append(
"\n" ) ;
317 line.append( m_prompt ) ;
327 s.append( 2U ,
' ' ) ;
332void GSmtp::AdminServerPeer::sendImp(
const std::string & s )
336 G_DEBUG(
"GSmtp::AdminServerPeer::send: flow control asserted: cannot send" ) ;
341 m_blocked = !send( s ) ;
345void GSmtp::AdminServerPeer::onSendComplete()
350void GSmtp::AdminServerPeer::status()
352 std::ostringstream ss ;
355 const std::string eolstr = eol() ;
357 std::string
report = ss.str() ;
359 sendLine( std::move(
report) ) ;
363 sendLine(
"no info" ) ;
367void GSmtp::AdminServerPeer::sendMessageIds(
const std::vector<GStore::MessageId> & ids )
369 std::ostringstream ss ;
371 for(
const auto &
id : ids )
373 if( !first ) ss << eol() ;
378 std::string result = ss.str() ;
380 sendLine(
"<none>" ) ;
382 sendLine( ss.str() ) ;
395 m_imp(
std::make_unique<AdminServerImp>(es,store,ff,client_secrets,interfaces,config))
409 return m_imp->commandSignal() ;
414 m_imp->report( group ) ;
420 return m_imp->store() ;
434 return m_imp->clientSecrets() ;
441 return m_imp->emitCommand( command , arg ) ;
447 return m_imp->notifying() ;
450void GSmtp::AdminServer::notify(
const std::string & s0 ,
const std::string & s1 ,
const std::string & s2 ,
const std::string & s3 )
452 m_imp->notify( s0 , s1 , s2 , s3 ) ;
459 const G::StringArray & interfaces ,
const AdminServer::Config & config ) :
460 GNet::MultiServer(es,interfaces,config.port,
"admin",config.net_server_peer_config,config.net_server_config) ,
463 m_client_secrets(client_secrets) ,
465 m_command_timer(*this,&AdminServerImp::onCommandTimeout,es) ,
466 m_command(AdminServer::Command::forward) ,
471GSmtp::AdminServerImp::~AdminServerImp()
479 std::unique_ptr<GNet::ServerPeer> ptr ;
483 if( !m_config.allow_remote && !peer_info.m_address.isLocal(reason) )
485 G_WARNING(
"GSmtp::Server: configured to reject non-local admin connection: " << reason ) ;
489 ptr = std::make_unique<AdminServerPeer>( esu , std::move(peer_info) , *
this ,
490 m_config.remote_address , m_config.info_commands ,
491 m_config.with_terminate ) ;
494 catch( std::exception & e )
496 G_WARNING(
"GSmtp::AdminServer: new connection error: " << e.what() ) ;
501void GSmtp::AdminServerImp::emitCommand( AdminServer::Command command ,
unsigned int arg )
503 m_command = command ;
504 m_command_arg = arg ;
505 m_command_timer.startTimer( 0 ) ;
508void GSmtp::AdminServerImp::onCommandTimeout()
512 m_command_signal.emit( m_command , m_command_arg ) ;
514 catch( std::exception & e )
516 G_WARNING(
"GSmtp::AdminServer: exception: " << e.what() ) ;
522 return m_command_signal ;
527 serverReport( group ) ;
530void GSmtp::AdminServerImp::notify(
const std::string & s0 ,
const std::string & s1 ,
const std::string & s2 ,
const std::string & s3 )
534 using List = std::vector<std::weak_ptr<GNet::ServerPeer>> ;
535 List list = peers() ;
536 for(
auto & wptr : list )
538 if( wptr.expired() ) continue ;
539 std::shared_ptr<GNet::ServerPeer> ptr = wptr.lock() ;
540 AdminServerPeer * peer =
static_cast<AdminServerPeer*
>( ptr.get() ) ;
541 peer->notify( s0 , s1 , s2 , s3 ) ;
558 return m_client_secrets ;
563 return m_config.smtp_client_config ;
566bool GSmtp::AdminServerImp::notifying()
const
568 bool result = false ;
571 using List = std::vector<std::weak_ptr<GNet::ServerPeer>> ;
572 List list =
const_cast<AdminServerImp*
>(
this)->peers() ;
573 for(
auto & wptr : list )
575 if( wptr.expired() )
continue ;
576 std::shared_ptr<GNet::ServerPeer> ptr = wptr.lock() ;
577 AdminServerPeer * peer =
static_cast<AdminServerPeer*
>( ptr.get() ) ;
578 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 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.
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(ExceptionSinkUnbound, 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::ExceptionSinkUnbound, 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.
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.
AdminServer(GNet::ExceptionSink, GStore::MessageStore &store, FilterFactoryBase &, const GAuth::SaslClientSecrets &client_secrets, const G::StringArray &interfaces, const Config &config)
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 unsigned int toUInt(string_view s)
Converts string 's' to an unsigned int.
static StringArray keys(const StringMap &string_map)
Extracts the keys from a map of strings.
static unsigned int replaceAll(std::string &s, string_view from, string_view to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
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 join(string_view sep, const StringArray &strings)
Concatenates an array of strings with separators.
static bool isUInt(string_view s) noexcept
Returns true if the string can be converted into an unsigned integer without throwing an exception.
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 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,...
A class like c++17's std::string_view.
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.