gadminserver.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gadminserver.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gprocess.h"
24 #include "geventloop.h"
25 #include "gsmtp.h"
26 #include "gadminserver.h"
27 #include "gmessagestore.h"
28 #include "gstoredmessage.h"
29 #include "gnullprocessor.h"
30 #include "gexecutableprocessor.h"
31 #include "glocal.h"
32 #include "gmonitor.h"
33 #include "gslot.h"
34 #include "gstr.h"
35 #include "gmemory.h"
36 #include <algorithm> // std::find()
37 
39  const GNet::Address & local_address , const std::string & remote_address ,
40  const G::StringMap & extra_commands , bool with_terminate ) :
41  BufferedServerPeer(peer_info,crlf()) ,
42  m_server(server) ,
43  m_local_address(local_address) ,
44  m_remote_address(remote_address) ,
45  m_notifying(false) ,
46  m_extra_commands(extra_commands) ,
47  m_with_terminate(with_terminate)
48 {
49  G_LOG_S( "GSmtp::AdminServerPeer: admin connection from " << peer_info.m_address.displayString() ) ;
50  m_client.doneSignal().connect( G::slot(*this,&AdminServerPeer::clientDone) ) ;
51  // dont prompt() here -- it confuses the poke program
52 }
53 
55 {
56  // AdminServerPeer objects are destroyed from within the AdminServer::dtor body
57  // via the GNet::Server::serverCleanup() mechanism -- this allows this
58  // AdminServerPeer dtor to call the AdminServer safely
59 
60  m_server.unregister( this ) ;
61  m_client.doneSignal().disconnect() ;
62 }
63 
64 void GSmtp::AdminServerPeer::clientDone( std::string s , bool )
65 {
66  m_client.reset() ;
67  if( s.empty() )
68  sendLine( "OK" ) ;
69  else
70  sendLine( std::string("error: ") + s ) ;
71  prompt() ;
72 }
73 
75 {
76  // never gets here
77 }
78 
79 void GSmtp::AdminServerPeer::onDelete( const std::string & reason )
80 {
81  G_LOG_S( "GSmtp::AdminServerPeer: admin connection closed: " << reason << (reason.empty()?"":": ")
82  << peerAddress().second.displayString() ) ;
83 }
84 
85 void GSmtp::AdminServerPeer::onSecure( const std::string & )
86 {
87 }
88 
89 bool GSmtp::AdminServerPeer::onReceive( const std::string & line )
90 {
91  if( is(line,"flush") )
92  {
93  if( flush() )
94  prompt() ;
95  }
96  else if( is(line,"help") )
97  {
98  help() ;
99  prompt() ;
100  }
101  else if( is(line,"info") )
102  {
103  info() ;
104  prompt() ;
105  }
106  else if( is(line,"notify") )
107  {
108  m_notifying = true ;
109  prompt() ;
110  }
111  else if( is(line,"list") )
112  {
113  list(spooled()) ;
114  prompt() ;
115  }
116  else if( is(line,"failures") )
117  {
118  list(failures()) ;
119  prompt() ;
120  }
121  else if( is(line,"unfail-all") )
122  {
123  unfailAll() ;
124  prompt() ;
125  }
126  else if( is(line,"pid") )
127  {
128  pid() ;
129  prompt() ;
130  }
131  else if( is(line,"quit") )
132  {
133  doDelete() ;
134  return false ;
135  }
136  else if( is(line,"terminate") && m_with_terminate )
137  {
139  GNet::EventLoop::instance().quit("admin terminate request") ;
140  }
141  else if( find(line,m_extra_commands).first )
142  {
143  sendLine( find(line,m_extra_commands).second ) ;
144  prompt() ;
145  }
146  else if( line.find_first_not_of(" \r\n\t") != std::string::npos )
147  {
148  sendLine( "error: unrecognised command" ) ;
149  prompt() ;
150  }
151  else
152  {
153  prompt() ;
154  }
155  return true ;
156 }
157 
158 const std::string & GSmtp::AdminServerPeer::crlf()
159 {
160  static const std::string s( "\015\012" ) ;
161  return s ;
162 }
163 
164 bool GSmtp::AdminServerPeer::is( const std::string & line_in , const std::string & key )
165 {
166  std::string line( line_in ) ;
167  G::Str::trim( line , " \t" ) ;
168  G::Str::toLower( line ) ;
169  return line.find(key) == 0U ;
170 }
171 
172 std::pair<bool,std::string> GSmtp::AdminServerPeer::find( const std::string & line , const G::StringMap & map )
173 {
174  for( G::StringMap::const_iterator p = map.begin() ; p != map.end() ; ++p )
175  {
176  if( is(line,(*p).first) )
177  return std::make_pair(true,(*p).second) ;
178  }
179  return std::make_pair(false,std::string()) ;
180 }
181 
182 void GSmtp::AdminServerPeer::help()
183 {
184  G::Strings commands ;
185  commands.push_back( "flush" ) ;
186  commands.push_back( "help" ) ;
187  commands.push_back( "info" ) ;
188  commands.push_back( "list" ) ;
189  commands.push_back( "failures" ) ;
190  commands.push_back( "unfail-all" ) ;
191  commands.push_back( "notify" ) ;
192  commands.push_back( "pid" ) ;
193  commands.push_back( "quit" ) ;
194  if( m_with_terminate ) commands.push_back( "terminate" ) ;
195  G::Strings extras = G::Str::keys( m_extra_commands ) ;
196  commands.splice( commands.end() , extras ) ;
197  commands.sort() ;
198  sendLine( std::string("commands: ") + G::Str::join(commands,", ") ) ;
199 }
200 
201 bool GSmtp::AdminServerPeer::flush()
202 {
203  G_DEBUG( "GSmtp::AdminServerPeer: flush: \"" << m_remote_address << "\"" ) ;
204 
205  bool do_prompt = false ;
206  if( m_client.busy() )
207  {
208  sendLine( "error: still working" ) ;
209  }
210  else if( m_remote_address.empty() )
211  {
212  sendLine( "error: no remote server configured: use --forward-to" ) ;
213  do_prompt = true ;
214  }
215  else if( m_server.store().empty() )
216  {
217  sendLine( "error: no messages to send" ) ;
218  do_prompt = true ;
219  }
220  else
221  {
222  m_client.reset( new Client(GNet::ResolverInfo(m_remote_address),m_server.secrets(),m_server.clientConfig()) ) ;
223  m_client->sendMessagesFrom( m_server.store() ) ; // once connected
224  }
225  return do_prompt ;
226 }
227 
228 void GSmtp::AdminServerPeer::prompt()
229 {
230  send( "E-MailRelay> " ) ;
231 }
232 
233 void GSmtp::AdminServerPeer::sendLine( std::string line )
234 {
235  line.append( "\n" ) ;
236  G::Str::replaceAll( line , "\n" , crlf() ) ;
237  send( line ) ;
238 }
239 
240 void GSmtp::AdminServerPeer::notify( const std::string & s0 , const std::string & s1 , const std::string & s2 )
241 {
242  if( m_notifying )
243  sendLine( std::string(1U,'\n') + "EVENT: " + s0 + ": " + s1 + ": " + s2 ) ;
244 }
245 
246 void GSmtp::AdminServerPeer::info()
247 {
248  std::ostringstream ss ;
250  {
251  GNet::Monitor::instance()->report( ss , "" , std::string(1U,'\n') ) ;
252  send( ss.str() ) ;
253  }
254  else
255  {
256  sendLine( "no info" ) ;
257  }
258 }
259 
260 void GSmtp::AdminServerPeer::pid()
261 {
262  sendLine( G::Process::Id().str() ) ;
263 }
264 
265 GSmtp::MessageStore::Iterator GSmtp::AdminServerPeer::spooled()
266 {
267  return m_server.store().iterator(false) ;
268 }
269 
270 GSmtp::MessageStore::Iterator GSmtp::AdminServerPeer::failures()
271 {
272  return m_server.store().failures() ;
273 }
274 
275 void GSmtp::AdminServerPeer::list( MessageStore::Iterator iter )
276 {
277  std::ostringstream ss ;
278  for(;;)
279  {
280  std::auto_ptr<StoredMessage> message( iter.next() ) ;
281  if( message.get() == NULL ) break ;
282  ss << message->name() << "\n" ;
283  }
284 
285  std::string result = ss.str() ;
286  if( result.size() == 0U )
287  sendLine( "<none>" ) ;
288  else
289  send( ss.str() ) ;
290 }
291 
292 void GSmtp::AdminServerPeer::unfailAll()
293 {
294  return m_server.store().unfailAll() ;
295 }
296 
297 // ===
298 
300  const GAuth::Secrets & secrets , const GNet::MultiServer::AddressList & listening_addresses ,
301  bool allow_remote , const GNet::Address & local_address , const std::string & remote_address ,
302  unsigned int connection_timeout , const G::StringMap & extra_commands , bool with_terminate ) :
303  GNet::MultiServer( listening_addresses , false ) ,
304  m_store(store) ,
305  m_client_config(client_config) ,
306  m_secrets(secrets) ,
307  m_local_address(local_address) ,
308  m_allow_remote(allow_remote) ,
309  m_remote_address(remote_address) ,
310  m_connection_timeout(connection_timeout) ,
311  m_extra_commands(extra_commands) ,
312  m_with_terminate(with_terminate)
313 {
314 }
315 
317 {
318  // early cleanup so peers can call unregister() safely
319  serverCleanup() ; // GNet::MultiServer
320 }
321 
323 {
324  try
325  {
326  std::string reason ;
327  if( ! m_allow_remote && ! GNet::Local::isLocal(peer_info.m_address,reason) )
328  {
329  G_WARNING( "GSmtp::Server: configured to reject non-local admin connection: " << reason ) ;
330  return NULL ;
331  }
332 
333  AdminServerPeer * peer = new AdminServerPeer( peer_info , *this , m_local_address , m_remote_address ,
334  m_extra_commands , m_with_terminate ) ;
335  m_peers.push_back( peer ) ;
336  return peer ;
337  }
338  catch( std::exception & e ) // newPeer()
339  {
340  G_WARNING( "GSmtp::AdminServer: exception from new connection: " << e.what() ) ;
341  return NULL ;
342  }
343 }
344 
346 {
347  serverReport( "admin" ) ;
348 }
349 
350 void GSmtp::AdminServer::notify( const std::string & s0 , const std::string & s1 , const std::string & s2 )
351 {
352  for( PeerList::iterator p = m_peers.begin() ; p != m_peers.end() ; ++p )
353  {
354  G_DEBUG( "GSmtp::AdminServer::notify: " << (*p) << ": " << s0 << ": " << s1 ) ;
355  (*p)->notify( s0 , s1 , s2 ) ;
356  }
357 }
358 
360 {
361  G_DEBUG( "GSmtp::AdminServer::unregister: server=" << this << ": peer=" << peer ) ;
362  PeerList::iterator p = std::find( m_peers.begin() , m_peers.end() , peer ) ;
363  if( p != m_peers.end() )
364  m_peers.erase( p ) ;
365 }
366 
368 {
369  return m_store ;
370 }
371 
373 {
374  return m_secrets ;
375 }
376 
378 {
379  return m_connection_timeout ;
380 }
381 
383 {
384  return m_client_config ;
385 }
386 
#define G_LOG_S(expr)
Definition: glog.h:103
An abstract base class for the GNet::Server's connection to a remote client.
Definition: gserver.h:191
A server class which implements the emailrelay administration interface.
Definition: gadminserver.h:112
virtual void onDelete(const std::string &)
Final override from GNet::ServerPeer.
Network classes.
void notify(const std::string &s0, const std::string &s1, const std::string &s2)
Called when something happens which the admin user might be interested in.
void unregister(AdminServerPeer *)
Called from the AdminServerPeer destructor.
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
The Address class encapsulates an IP transport address.
Definition: gaddress.h:48
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.
Definition: gmonitor.cpp:145
static bool isLocal(const Address &)
Returns true if the given address appears to be local.
Definition: glocal.cpp:82
void report() const
Generates helpful diagnostics.
Slot0 slot(T &object, void(T::*fn)())
Part of the slot/signal system.
Definition: gslot.h:156
A class that holds a host/service name pair and optionally the results of a name-to-address lookup...
Definition: gresolverinfo.h:48
AdminServer(MessageStore &store, const GSmtp::Client::Config &client_config, const GAuth::Secrets &client_secrets, const GNet::MultiServer::AddressList &listening_addresses, bool allow_remote, const GNet::Address &local_address, const std::string &remote_address, unsigned int connection_timeout, const G::StringMap &extra_commands, bool with_terminate)
Constructor.
virtual bool onReceive(const std::string &)
Final override from GNet::BufferedServerPeer.
static void trim(std::string &s, const std::string &ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:133
static void toLower(std::string &s)
Replaces all uppercase characters in string 's' by lowercase characters.
Definition: gstr.cpp:404
static Strings keys(const StringMap &string_map)
Extracts the keys from a map of strings.
Definition: gstr.cpp:822
std::string displayString(bool with_port=true, bool with_scope_id=false) const
Returns a string which represents the address for debugging and diagnostics purposes.
A class which allows SMTP messages (envelope+content) to be stored and retrieved. ...
Definition: gmessagestore.h:45
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurences of sub-string 'from' with 'to'...
Definition: gstr.cpp:78
A simple interface to a store of secrets as used in authentication.
Definition: gsecrets.h:44
const GAuth::Secrets & secrets() const
Returns a reference to the secrets object, as passed in to the constructor.
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
Definition: gmonitor.cpp:105
G::Signal2< std::string, bool > & doneSignal()
Returns a signal which indicates that client processing is complete and the client instance has delet...
Definition: gclientptr.h:217
MessageStore & store()
Returns a reference to the message store, as passed in to the constructor.
virtual ~AdminServerPeer()
Destructor.
void notify(const std::string &s0, const std::string &s1, const std::string &s2)
Called when something happens.
#define G_DEBUG(expr)
Definition: glog.h:95
A structure containing GSmtp::Client configuration parameters.
Definition: gsmtpclient.h:61
A derivation of ServerPeer for the administration interface.
Definition: gadminserver.h:51
virtual GNet::ServerPeer * newPeer(GNet::Server::PeerInfo)
Final override from GNet::MultiServer.
Process-id class.
Definition: gprocess.h:52
virtual void onSendComplete()
Final override from GNet::BufferedServerPeer.
std::map< std::string, std::string > StringMap
A std::map of std::strings.
Definition: gstrings.h:49
AdminServerPeer(GNet::Server::PeerInfo, AdminServer &, const GNet::Address &local, const std::string &remote, const G::StringMap &extra_commands, bool with_terminate)
Constructor.
std::list< Address > AddressList
Definition: gmultiserver.h:105
virtual ~AdminServer()
Destructor.
static bool exists()
Returns true if an instance exists.
Definition: geventloop.cpp:51
GSmtp::Client::Config clientConfig() const
Returns the client configuration.
void connect(Slot2< P1, P2 > slot)
Definition: gslot.h:295
A structure used in GNet::Server::newPeer().
Definition: gserver.h:91
unsigned int connectionTimeout() const
Returns the connection timeout, as passed in to the constructor.
static std::string join(const Strings &strings, const std::string &sep)
Concatenates a set of strings.
Definition: gstr.cpp:799
virtual void onSecure(const std::string &)
Final override from GNet::SocketProtocolSink.
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
Definition: geventloop.cpp:43
virtual void quit(std::string reason)=0
Causes run() to return (once the call stack has unwound).
#define G_WARNING(expr)
Definition: glog.h:107
An iterator class for GSmtp::MessageStore.
Definition: gmessagestore.h:62