- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
38 GSmtp::AdminServerPeer::AdminServerPeer( GNet::Server::PeerInfo peer_info , AdminServer & server ,
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
54 GSmtp::AdminServerPeer::~AdminServerPeer()
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
74 void GSmtp::AdminServerPeer::onSendComplete()
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 {
138 if( GNet::EventLoop::exists() )
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 ;
249 if( GNet::Monitor::instance() )
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
299 GSmtp::AdminServer::AdminServer( MessageStore & store , const GSmtp::Client::Config & client_config ,
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
316 GSmtp::AdminServer::~AdminServer()
317 {
318 // early cleanup so peers can call unregister() safely
319 serverCleanup() ; // GNet::MultiServer
320 }
321
322 GNet::ServerPeer * GSmtp::AdminServer::newPeer( GNet::Server::PeerInfo peer_info )
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
345 void GSmtp::AdminServer::report() const
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
359 void GSmtp::AdminServer::unregister( AdminServerPeer * peer )
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
367 GSmtp::MessageStore & GSmtp::AdminServer::store()
368 {
369 return m_store ;
370 }
371
372 const GAuth::Secrets & GSmtp::AdminServer::secrets() const
373 {
374 return m_secrets ;
375 }
376
377 unsigned int GSmtp::AdminServer::connectionTimeout() const
378 {
379 return m_connection_timeout ;
380 }
381
382 GSmtp::Client::Config GSmtp::AdminServer::clientConfig() const
383 {
384 return m_client_config ;
385 }
386
G_LOG_S
#define G_LOG_S(expr)
Definition: glog.h:103
GNet::ServerPeer
An abstract base class for the GNet::Server's connection to a remote client.
Definition: gserver.h:191
GSmtp::AdminServer
A server class which implements the emailrelay administration interface.
Definition: gadminserver.h:112
gadminserver.h
GSmtp::AdminServerPeer::onDelete
virtual void onDelete(const std::string &)
Final override from GNet::ServerPeer.
Definition: gadminserver.cpp:79
GNet
Network classes.
GSmtp::AdminServer::notify
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.
Definition: gadminserver.cpp:350
GSmtp::AdminServer::unregister
void unregister(AdminServerPeer *)
Called from the AdminServerPeer destructor.
Definition: gadminserver.cpp:359
G::Strings
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
GNet::Address
The Address class encapsulates an IP transport address.
Definition: gaddress.h:48
GNet::Monitor::report
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
GNet::Local::isLocal
static bool isLocal(const Address &)
Returns true if the given address appears to be local.
Definition: glocal.cpp:82
GSmtp::AdminServer::report
void report() const
Generates helpful diagnostics.
Definition: gadminserver.cpp:345
GNet::Server::PeerInfo::m_address
Address m_address
Definition: gserver.h:94
G::slot
Slot0 slot(T &object, void(T::*fn)())
Part of the slot/signal system.
Definition: gslot.h:156
GNet::ResolverInfo
A class that holds a host/service name pair and optionally the results of a name-to-address lookup...
Definition: gresolverinfo.h:48
GSmtp::AdminServer::AdminServer
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.
Definition: gadminserver.cpp:299
GSmtp::AdminServerPeer::onReceive
virtual bool onReceive(const std::string &)
Final override from GNet::BufferedServerPeer.
Definition: gadminserver.cpp:89
gslot.h
geventloop.h
G::Str::trim
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
G::Str::toLower
static void toLower(std::string &s)
Replaces all uppercase characters in string 's' by lowercase characters.
Definition: gstr.cpp:404
G::Str::keys
static Strings keys(const StringMap &string_map)
Extracts the keys from a map of strings.
Definition: gstr.cpp:822
gexecutableprocessor.h
GNet::Address::displayString
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.
Definition: gaddress_ipv4.cpp:581
gstr.h
GSmtp::MessageStore
A class which allows SMTP messages (envelope+content) to be stored and retrieved. ...
Definition: gmessagestore.h:45
G::Str::replaceAll
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
GAuth::Secrets
A simple interface to a store of secrets as used in authentication.
Definition: gsecrets.h:44
GSmtp::AdminServer::secrets
const GAuth::Secrets & secrets() const
Returns a reference to the secrets object, as passed in to the constructor.
Definition: gadminserver.cpp:372
GNet::Monitor::instance
static Monitor * instance()
Returns the singleton pointer. Returns null if none.
Definition: gmonitor.cpp:105
gprocess.h
gmessagestore.h
GNet::ClientPtr::doneSignal
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
GSmtp::AdminServer::store
MessageStore & store()
Returns a reference to the message store, as passed in to the constructor.
Definition: gadminserver.cpp:367
GSmtp::AdminServerPeer::~AdminServerPeer
virtual ~AdminServerPeer()
Destructor.
Definition: gadminserver.cpp:54
GSmtp::AdminServerPeer::notify
void notify(const std::string &s0, const std::string &s1, const std::string &s2)
Called when something happens.
Definition: gadminserver.cpp:240
G_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
GSmtp::Client::Config
A structure containing GSmtp::Client configuration parameters.
Definition: gsmtpclient.h:61
GSmtp::AdminServerPeer
A derivation of ServerPeer for the administration interface.
Definition: gadminserver.h:51
gmonitor.h
GSmtp::AdminServer::newPeer
virtual GNet::ServerPeer * newPeer(GNet::Server::PeerInfo)
Final override from GNet::MultiServer.
Definition: gadminserver.cpp:322
G::Process::Id
Process-id class.
Definition: gprocess.h:52
GSmtp::AdminServerPeer::onSendComplete
virtual void onSendComplete()
Final override from GNet::BufferedServerPeer.
Definition: gadminserver.cpp:74
glocal.h
G::StringMap
std::map< std::string, std::string > StringMap
A std::map of std::strings.
Definition: gstrings.h:49
GSmtp::AdminServerPeer::AdminServerPeer
AdminServerPeer(GNet::Server::PeerInfo, AdminServer &, const GNet::Address &local, const std::string &remote, const G::StringMap &extra_commands, bool with_terminate)
Constructor.
Definition: gadminserver.cpp:38
gstoredmessage.h
GNet::MultiServer::AddressList
std::list< Address > AddressList
Definition: gmultiserver.h:105
gsmtp.h
GSmtp::AdminServer::~AdminServer
virtual ~AdminServer()
Destructor.
Definition: gadminserver.cpp:316
GNet::EventLoop::exists
static bool exists()
Returns true if an instance exists.
Definition: geventloop.cpp:51
GSmtp::AdminServer::clientConfig
GSmtp::Client::Config clientConfig() const
Returns the client configuration.
Definition: gadminserver.cpp:382
gnullprocessor.h
G::Signal2::connect
void connect(Slot2< P1, P2 > slot)
Definition: gslot.h:295
gmemory.h
GNet::Server::PeerInfo
A structure used in GNet::Server::newPeer().
Definition: gserver.h:91
GSmtp::AdminServer::connectionTimeout
unsigned int connectionTimeout() const
Returns the connection timeout, as passed in to the constructor.
Definition: gadminserver.cpp:377
G::Str::join
static std::string join(const Strings &strings, const std::string &sep)
Concatenates a set of strings.
Definition: gstr.cpp:799
GSmtp::AdminServerPeer::onSecure
virtual void onSecure(const std::string &)
Final override from GNet::SocketProtocolSink.
Definition: gadminserver.cpp:85
GNet::EventLoop::instance
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
Definition: geventloop.cpp:43
GNet::EventLoop::quit
virtual void quit(std::string reason)=0
Causes run() to return (once the call stack has unwound).
G_WARNING
#define G_WARNING(expr)
Definition: glog.h:107
GSmtp::MessageStore::Iterator
An iterator class for GSmtp::MessageStore.
Definition: gmessagestore.h:62