gserverprotocol.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 // gserverprotocol.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gsmtp.h"
23 #include "gsaslserverfactory.h"
24 #include "gserverprotocol.h"
25 #include "gbase64.h"
26 #include "gssl.h"
27 #include "gdate.h"
28 #include "gtime.h"
29 #include "gdatetime.h"
30 #include "gstr.h"
31 #include "glog.h"
32 #include "gassert.h"
33 #include <string>
34 
36  const GAuth::Secrets & secrets , Text & text , GNet::Address peer_address ,
37  const std::string & peer_socket_name , Config config ) :
38  m_sender(sender) ,
39  m_verifier(verifier) ,
40  m_pmessage(pmessage) ,
41  m_text(text) ,
42  m_peer_address(peer_address) ,
43  m_peer_socket_name(peer_socket_name) ,
44  m_fsm(sStart,sEnd,s_Same,s_Any) ,
45  m_authenticated(false) ,
46  m_secure(false) ,
47  m_sasl(GAuth::SaslServerFactory::newSaslServer(secrets,false,true)) ,
48  m_with_vrfy(config.with_vrfy) ,
49  m_preprocessor_timeout(config.preprocessor_timeout) ,
50  m_bad_client_count(0U) ,
51  m_bad_client_limit(8U) ,
52  m_disconnect_on_overflow(config.disconnect_on_overflow)
53 {
54  m_pmessage.doneSignal().connect( G::slot(*this,&ServerProtocol::processDone) ) ;
55  verifier.doneSignal().connect( G::slot(*this,&ServerProtocol::verifyDone) ) ;
56 
57  // (dont send anything to the peer from this ctor -- the Sender object is not fuly constructed)
58 
59  m_fsm.addTransition( eQuit , s_Any , sEnd , &GSmtp::ServerProtocol::doQuit ) ;
60  m_fsm.addTransition( eUnknown , s_Any , s_Same , &GSmtp::ServerProtocol::doUnknown ) ;
61  m_fsm.addTransition( eRset , sStart , s_Same , &GSmtp::ServerProtocol::doNoop ) ;
62  m_fsm.addTransition( eRset , s_Any , sIdle , &GSmtp::ServerProtocol::doRset ) ;
63  m_fsm.addTransition( eNoop , s_Any , s_Same , &GSmtp::ServerProtocol::doNoop ) ;
64  m_fsm.addTransition( eHelp , s_Any , s_Same , &GSmtp::ServerProtocol::doHelp ) ;
65  m_fsm.addTransition( eExpn , s_Any , s_Same , &GSmtp::ServerProtocol::doExpn ) ;
66  m_fsm.addTransition( eVrfy , sStart , sVrfyStart , &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
67  m_fsm.addTransition( eVrfyReply , sVrfyStart , sStart , &GSmtp::ServerProtocol::doVrfyReply ) ;
68  m_fsm.addTransition( eVrfy , sIdle , sVrfyIdle , &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
69  m_fsm.addTransition( eVrfyReply , sVrfyIdle , sIdle , &GSmtp::ServerProtocol::doVrfyReply ) ;
70  m_fsm.addTransition( eVrfy , sGotMail , sVrfyGotMail, &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
71  m_fsm.addTransition( eVrfyReply , sVrfyGotMail, sGotMail , &GSmtp::ServerProtocol::doVrfyReply ) ;
72  m_fsm.addTransition( eVrfy , sGotRcpt , sVrfyGotRcpt, &GSmtp::ServerProtocol::doVrfy , s_Same ) ;
73  m_fsm.addTransition( eVrfyReply , sVrfyGotRcpt, sGotRcpt , &GSmtp::ServerProtocol::doVrfyReply ) ;
74  m_fsm.addTransition( eEhlo , s_Any , sIdle , &GSmtp::ServerProtocol::doEhlo , s_Same ) ;
75  m_fsm.addTransition( eHelo , s_Any , sIdle , &GSmtp::ServerProtocol::doHelo , s_Same ) ;
76  m_fsm.addTransition( eMail , sIdle , sGotMail , &GSmtp::ServerProtocol::doMail , sIdle ) ;
77  m_fsm.addTransition( eRcpt , sGotMail , sVrfyTo1 , &GSmtp::ServerProtocol::doRcpt , s_Same ) ;
78  m_fsm.addTransition( eVrfyReply , sVrfyTo1 , sGotRcpt , &GSmtp::ServerProtocol::doVrfyToReply , sGotMail ) ;
79  m_fsm.addTransition( eRcpt , sGotRcpt , sVrfyTo2 , &GSmtp::ServerProtocol::doRcpt , s_Same ) ;
80  m_fsm.addTransition( eVrfyReply , sVrfyTo2 , sGotRcpt , &GSmtp::ServerProtocol::doVrfyToReply ) ;
81  m_fsm.addTransition( eData , sGotMail , sIdle , &GSmtp::ServerProtocol::doNoRecipients ) ;
82  m_fsm.addTransition( eData , sGotRcpt , sData , &GSmtp::ServerProtocol::doData ) ;
83  m_fsm.addTransition( eContent , sData , sData , &GSmtp::ServerProtocol::doContent , sDiscarding ) ;
84  m_fsm.addTransition( eEot , sData , sProcessing , &GSmtp::ServerProtocol::doEot ) ;
85  m_fsm.addTransition( eDone , sProcessing , sIdle , &GSmtp::ServerProtocol::doComplete ) ;
86  m_fsm.addTransition( eTimeout , sProcessing , sIdle , &GSmtp::ServerProtocol::doComplete ) ;
87  m_fsm.addTransition( eContent , sDiscarding , sDiscarding , &GSmtp::ServerProtocol::doDiscard ) ;
88  m_fsm.addTransition( eEot , sDiscarding , sIdle , &GSmtp::ServerProtocol::doDiscarded ) ;
89 
90  #ifndef USE_NO_AUTH
91  if( m_sasl->active() )
92  {
93  m_fsm.addTransition( eAuth , sIdle , sAuth , &GSmtp::ServerProtocol::doAuth , sIdle ) ;
94  m_fsm.addTransition( eAuthData, sAuth , sAuth , &GSmtp::ServerProtocol::doAuthData , sIdle ) ;
95  }
96  #endif
97 
99  m_with_ssl = ssl != NULL && ssl->enabled(true) ;
100  if( m_with_ssl )
101  {
102  m_fsm.addTransition( eStartTls , sIdle , sStartingTls , &GSmtp::ServerProtocol::doStartTls , sIdle ) ;
103  m_fsm.addTransition( eSecure , sStartingTls , sIdle , &GSmtp::ServerProtocol::doSecure ) ;
104  }
105 }
106 
108 {
109  sendGreeting( m_text.greeting() ) ;
110 }
111 
113 {
114  m_pmessage.doneSignal().disconnect() ;
115  m_verifier.doneSignal().disconnect() ;
116 }
117 
118 void GSmtp::ServerProtocol::secure( const std::string & certificate )
119 {
120  State new_state = m_fsm.apply( *this , eSecure , certificate ) ;
121  if( new_state == s_Any )
122  throw ProtocolDone( "protocol error" ) ;
123 }
124 
125 void GSmtp::ServerProtocol::doSecure( const std::string & certificate , bool & )
126 {
127  G_DEBUG( "GSmtp::ServerProtocol::doSecure" ) ;
128  m_secure = true ;
129  m_certificate = certificate ;
130 }
131 
132 void GSmtp::ServerProtocol::doStartTls( const std::string & , bool & ok )
133 {
134  if( m_secure )
135  {
136  send( "503 command out of sequence" ) ;
137  ok = false ;
138  }
139  else
140  {
141  send( "220 ready to start tls" , true ) ;
142  }
143 }
144 
145 void GSmtp::ServerProtocol::sendGreeting( const std::string & text )
146 {
147  send( std::string("220 ") + text ) ;
148 }
149 
150 void GSmtp::ServerProtocol::apply( const std::string & line )
151 {
152  Event event = eUnknown ;
153  State state = m_fsm.state() ;
154  const std::string * event_data = &line ;
155  if( (state == sData || state == sDiscarding) && isEndOfText(line) )
156  {
157  event = eEot ;
158  }
159  else if( state == sData || state == sDiscarding )
160  {
161  event = eContent ;
162  }
163  else if( state == sAuth )
164  {
165  event = eAuthData ;
166  }
167  else
168  {
169  G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::printable(line) << "\"" ) ;
170  event = commandEvent( commandWord(line) ) ;
171  m_buffer = commandLine(line) ;
172  event_data = &m_buffer ;
173  }
174 
175  State new_state = m_fsm.apply( *this , event , *event_data ) ;
176  if( new_state == s_Any )
177  sendOutOfSequence( line ) ;
178 }
179 
180 void GSmtp::ServerProtocol::doContent( const std::string & line , bool & ok )
181 {
182  if( isEscaped(line) )
183  ok = m_pmessage.addText( line.substr(1U) ) ; // temporary string constructed, but rare
184  else
185  ok = m_pmessage.addText( line ) ;
186 
187  if( !ok && m_disconnect_on_overflow )
188  sendTooBig( true ) ;
189 }
190 
191 void GSmtp::ServerProtocol::doEot( const std::string & line , bool & )
192 {
193  G_LOG( "GSmtp::ServerProtocol: rx<<: [message content not logged]" ) ;
194  G_LOG( "GSmtp::ServerProtocol: rx<<: \"" << G::Str::printable(line) << "\"" ) ;
195  if( m_preprocessor_timeout != 0U )
196  {
197  G_DEBUG( "GSmtp::ServerProtocol: starting preprocessor timer: " << m_preprocessor_timeout ) ;
198  startTimer( m_preprocessor_timeout ) ;
199  }
200  m_pmessage.process( m_sasl->id() , m_peer_address.displayString(false) , m_peer_socket_name , m_certificate ) ;
201 }
202 
203 void GSmtp::ServerProtocol::processDone( bool success , unsigned long , std::string reason )
204 {
205  G_DEBUG( "GSmtp::ServerProtocol::processDone: " << success << ", \"" << reason << "\"" ) ;
206 
207  reason = success ? std::string() : ( reason.empty() ? std::string("error") : reason ) ; // just in case
208 
209  State new_state = m_fsm.apply( *this , eDone , reason ) ;
210  if( new_state == s_Any )
211  throw ProtocolDone( "protocol error" ) ;
212 }
213 
215 {
216  G_WARNING( "GSmtp::ServerProtocol::onTimeout: message processing timed out" ) ;
217  State new_state = m_fsm.apply( *this , eTimeout , "message processing timed out" ) ;
218  if( new_state == s_Any )
219  throw ProtocolDone( "protocol error" ) ;
220 }
221 
223 {
224  G_IGNORE_PARAMETER(std::exception,e) ;
225  G_DEBUG( "GSmtp::ServerProtocol::onTimeoutException: exception: " << e.what() ) ;
226  throw ;
227 }
228 
229 void GSmtp::ServerProtocol::doComplete( const std::string & reason , bool & )
230 {
231  reset() ;
232  sendCompletionReply( reason.empty() , reason ) ;
233 }
234 
235 void GSmtp::ServerProtocol::doQuit( const std::string & , bool & )
236 {
237  reset() ;
238  sendClosing() ; // never deletes this
239  throw ProtocolDone() ;
240 }
241 
242 void GSmtp::ServerProtocol::doDiscard( const std::string & , bool & )
243 {
244  if( m_disconnect_on_overflow )
245  {
246  reset() ;
247  sendClosing() ; // never deletes this
248  throw ProtocolDone() ;
249  }
250 }
251 
252 void GSmtp::ServerProtocol::doNoop( const std::string & , bool & )
253 {
254  sendOk() ;
255 }
256 
257 void GSmtp::ServerProtocol::doNothing( const std::string & , bool & )
258 {
259 }
260 
261 void GSmtp::ServerProtocol::doDiscarded( const std::string & , bool & )
262 {
263  reset() ;
264  sendTooBig() ;
265 }
266 
267 void GSmtp::ServerProtocol::doExpn( const std::string & , bool & )
268 {
269  sendNotImplemented() ;
270 }
271 
272 void GSmtp::ServerProtocol::doHelp( const std::string & , bool & )
273 {
274  sendNotImplemented() ;
275 }
276 
277 void GSmtp::ServerProtocol::doVrfy( const std::string & line , bool & predicate )
278 {
279  if( m_with_vrfy )
280  {
281  std::string to = parseToParameter( line ) ;
282  if( to.empty() )
283  {
284  predicate = false ;
285  sendNotVerified( to , false ) ;
286  }
287  else
288  {
289  verify( to , "" ) ;
290  }
291  }
292  else
293  {
294  predicate = false ;
295  sendNotImplemented() ;
296  }
297 }
298 
299 void GSmtp::ServerProtocol::verify( const std::string & to , const std::string & from )
300 {
301  std::string mechanism = m_sasl->active() ? m_sasl->mechanism() : std::string() ;
302  std::string id = m_sasl->active() ? m_sasl->id() : std::string() ;
303  if( m_sasl->active() && !m_authenticated )
304  mechanism = "NONE" ;
305  m_verifier.verify( to , from , m_peer_address , mechanism , id ) ;
306 }
307 
308 void GSmtp::ServerProtocol::verifyDone( std::string mbox , VerifierStatus status )
309 {
310  State new_state = m_fsm.apply( *this , eVrfyReply , status.str(mbox) ) ;
311  if( new_state == s_Any )
312  throw ProtocolDone( "protocol error" ) ;
313 }
314 
315 void GSmtp::ServerProtocol::doVrfyReply( const std::string & line , bool & )
316 {
317  std::string mbox ;
318  VerifierStatus rc = VerifierStatus::parse( line , mbox ) ;
319 
320  if( rc.is_valid && rc.is_local )
321  sendVerified( rc.full_name ) ; // 250
322  else if( rc.is_valid )
323  sendWillAccept( mbox ) ; // 252
324  else
325  sendNotVerified( mbox , rc.temporary ) ; // 550 or 450
326 }
327 
328 std::string GSmtp::ServerProtocol::parseToParameter( const std::string & line ) const
329 {
330  std::string to ;
331  size_t pos = line.find_first_of( " \t" ) ;
332  if( pos != std::string::npos )
333  to = line.substr(pos) ;
334 
335  G::Str::trim( to , " \t" ) ;
336  return to ;
337 }
338 
339 void GSmtp::ServerProtocol::doEhlo( const std::string & line , bool & predicate )
340 {
341  std::string smtp_peer_name = parsePeerName( line ) ;
342  if( smtp_peer_name.empty() )
343  {
344  predicate = false ;
345  sendMissingParameter() ;
346  }
347  else
348  {
349  m_smtp_peer_name = smtp_peer_name ;
350  reset() ;
351  sendEhloReply() ;
352  }
353 }
354 
355 void GSmtp::ServerProtocol::doHelo( const std::string & line , bool & predicate )
356 {
357  std::string smtp_peer_name = parsePeerName( line ) ;
358  if( smtp_peer_name.empty() )
359  {
360  predicate = false ;
361  sendMissingParameter() ;
362  }
363  else
364  {
365  m_smtp_peer_name = smtp_peer_name ;
366  reset() ;
367  sendHeloReply() ;
368  }
369 }
370 
371 bool GSmtp::ServerProtocol::sensitive() const
372 {
373  // true if the sasl implementation is sensitive and so needs to be encrypted
374  return m_sasl->active() && m_sasl->requiresEncryption() && !m_secure ;
375 }
376 
377 #ifndef USE_NO_AUTH
378 void GSmtp::ServerProtocol::doAuth( const std::string & line , bool & predicate )
379 {
380  G::StringArray word_array ;
381  G::Str::splitIntoTokens( line , word_array , " \t" ) ;
382 
383  std::string mechanism = word_array.size() > 1U ? word_array[1U] : std::string() ;
384  G::Str::toUpper( mechanism ) ;
385  std::string initial_response = word_array.size() > 2U ? word_array[2U] : std::string() ;
386  bool got_initial_response = word_array.size() > 2U ;
387 
388  G_DEBUG( "ServerProtocol::doAuth: [" << mechanism << "], [" << initial_response << "]" ) ;
389 
390  if( sensitive() )
391  {
392  G_WARNING( "GSmtp::ServerProtocol: rejecting authentication attempt without encryption" ) ;
393  predicate = false ; // => idle
394  send( "504 Unsupported authentication mechanism" ) ;
395  }
396  else if( m_authenticated )
397  {
398  G_WARNING( "GSmtp::ServerProtocol: too many AUTHs" ) ;
399  predicate = false ; // => idle
400  sendOutOfSequence(line) ; // see RFC2554 "Restrictions"
401  }
402  else if( ! m_sasl->init(mechanism) )
403  {
404  G_WARNING( "GSmtp::ServerProtocol: request for unsupported AUTH mechanism: " << mechanism ) ;
405  predicate = false ; // => idle
406  send( "504 Unsupported authentication mechanism" ) ;
407  }
408  else if( got_initial_response && ! G::Base64::valid(initial_response) )
409  {
410  G_WARNING( "GSmtp::ServerProtocol: invalid base64 encoding of AUTH parameter" ) ;
411  predicate = false ; // => idle
412  send( "501 Invalid argument" ) ;
413  }
414  else if( got_initial_response )
415  {
416  std::string s = initial_response == "=" ? std::string() : G::Base64::decode(initial_response) ;
417  bool done = false ;
418  std::string next_challenge = m_sasl->apply( s , done ) ;
419  if( done )
420  {
421  predicate = false ; // => idle
422  m_authenticated = m_sasl->authenticated() ;
423  sendAuthDone( m_sasl->authenticated() ) ;
424  }
425  else
426  {
427  sendChallenge( next_challenge ) ;
428  }
429  }
430  else
431  {
432  sendChallenge( m_sasl->initialChallenge() ) ;
433  }
434 }
435 #endif
436 
437 void GSmtp::ServerProtocol::sendAuthDone( bool ok )
438 {
439  if( ok )
440  send( "235 Authentication sucessful" ) ;
441  else
442  send( "535 Authentication failed" ) ;
443 }
444 
445 #ifndef USE_NO_AUTH
446 void GSmtp::ServerProtocol::doAuthData( const std::string & line , bool & predicate )
447 {
448  G_LOG( "GSmtp::ServerProtocol: rx<<: [authentication response not logged]" ) ;
449  if( line == "*" )
450  {
451  predicate = false ; // => idle
452  send( "501 authentication cancelled" ) ;
453  }
454  else if( ! G::Base64::valid(line) )
455  {
456  G_WARNING( "GSmtp::ServerProtocol: invalid base64 encoding of authentication response" ) ;
457  predicate = false ; // => idle
458  sendAuthDone( false ) ;
459  }
460  else
461  {
462  bool done = false ;
463  std::string next_challenge = m_sasl->apply( G::Base64::decode(line) , done ) ;
464  if( done )
465  {
466  predicate = false ; // => idle
467  m_authenticated = m_sasl->authenticated() ;
468  sendAuthDone( m_sasl->authenticated() ) ;
469  }
470  else
471  {
472  sendChallenge( next_challenge ) ;
473  }
474  }
475 }
476 #endif
477 
478 void GSmtp::ServerProtocol::sendChallenge( const std::string & s )
479 {
480  send( std::string("334 ") + G::Base64::encode(s,std::string()) ) ;
481 }
482 
483 void GSmtp::ServerProtocol::doMail( const std::string & line , bool & predicate )
484 {
485  if( m_sasl->active() && !m_authenticated && ( !m_sasl->trusted(m_peer_address) || sensitive() ) )
486  {
487  predicate = false ;
488  sendAuthRequired() ;
489  }
490  else
491  {
492  m_pmessage.clear() ;
493  std::pair<std::string,std::string> from_pair = parseFrom( line ) ;
494  bool ok = from_pair.second.empty() && m_pmessage.setFrom( from_pair.first ) ;
495  predicate = ok ;
496  if( ok )
497  {
498  sendMailReply() ;
499  }
500  else
501  {
502  sendBadFrom( from_pair.second ) ;
503  }
504  }
505 }
506 
507 void GSmtp::ServerProtocol::doRcpt( const std::string & line , bool & predicate )
508 {
509  std::pair<std::string,std::string> to_pair = parseTo( line ) ;
510  std::string reason = to_pair.second ;
511  bool ok = reason.empty() ;
512 
513  if( ok )
514  {
515  verify( to_pair.first , m_pmessage.from() ) ;
516  }
517  else
518  {
519  predicate = false ;
520  sendBadTo( reason , false ) ;
521  }
522 }
523 
524 void GSmtp::ServerProtocol::doVrfyToReply( const std::string & line , bool & predicate )
525 {
526  std::string to ;
527  VerifierStatus status = VerifierStatus::parse( line , to ) ;
528 
529  bool ok = m_pmessage.addTo( to , status ) ;
530  if( ok )
531  {
532  sendRcptReply() ;
533  }
534  else
535  {
536  predicate = false ;
537  sendBadTo( G::Str::printable(status.reason) , status.temporary ) ;
538  }
539 }
540 
541 void GSmtp::ServerProtocol::doUnknown( const std::string & line , bool & )
542 {
543  sendUnrecognised( line ) ;
544 }
545 
546 void GSmtp::ServerProtocol::reset()
547 {
548  cancelTimer() ;
549  m_pmessage.clear() ;
550  m_verifier.reset() ;
551  m_bad_client_count = 0U ;
552 }
553 
554 void GSmtp::ServerProtocol::doRset( const std::string & , bool & )
555 {
556  reset() ;
557  m_pmessage.reset() ;
558  sendRsetReply() ;
559 }
560 
561 void GSmtp::ServerProtocol::doNoRecipients( const std::string & , bool & )
562 {
563  sendNoRecipients() ;
564 }
565 
566 void GSmtp::ServerProtocol::doData( const std::string & , bool & )
567 {
568  std::string received_line = m_text.received(m_smtp_peer_name) ;
569  if( received_line.length() )
570  m_pmessage.addReceived( received_line ) ;
571 
572  sendDataReply() ;
573 }
574 
575 void GSmtp::ServerProtocol::sendOutOfSequence( const std::string & )
576 {
577  send( "503 command out of sequence -- use RSET to resynchronise" ) ;
578  badClientEvent() ;
579 }
580 
581 void GSmtp::ServerProtocol::sendMissingParameter()
582 {
583  send( "501 parameter required" ) ;
584 }
585 
586 bool GSmtp::ServerProtocol::isEndOfText( const std::string & line ) const
587 {
588  return line.length() == 1U && line[0U] == '.' ;
589 }
590 
591 bool GSmtp::ServerProtocol::isEscaped( const std::string & line ) const
592 {
593  return line.length() > 1U && line[0U] == '.' ;
594 }
595 
596 std::string GSmtp::ServerProtocol::commandWord( const std::string & line_in ) const
597 {
598  std::string line( line_in ) ;
599  G::Str::trimLeft( line , " \t" ) ;
600 
601  size_t pos = line.find_first_of( " \t" ) ;
602  std::string command = line.substr( 0U , pos ) ;
603 
604  G::Str::toUpper( command ) ;
605  return command ;
606 }
607 
608 std::string GSmtp::ServerProtocol::commandLine( const std::string & line_in ) const
609 {
610  std::string line( line_in ) ;
611  G::Str::trimLeft( line , " \t" ) ;
612  return line ;
613 }
614 
615 GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::commandEvent( const std::string & command ) const
616 {
617  if( command == "QUIT" ) return eQuit ;
618  if( command == "HELO" ) return eHelo ;
619  if( command == "EHLO" ) return eEhlo ;
620  if( command == "RSET" ) return eRset ;
621  if( command == "DATA" ) return eData ;
622  if( command == "RCPT" ) return eRcpt ;
623  if( command == "MAIL" ) return eMail ;
624  if( command == "VRFY" ) return eVrfy ;
625  if( command == "NOOP" ) return eNoop ;
626  if( command == "EXPN" ) return eExpn ;
627  if( command == "HELP" ) return eHelp ;
628  if( command == "STARTTLS" ) return eStartTls ;
629  if( m_sasl->active() && command == "AUTH" ) return eAuth ;
630  return eUnknown ;
631 }
632 
633 void GSmtp::ServerProtocol::sendClosing()
634 {
635  send( "221 closing connection" ) ;
636 }
637 
638 void GSmtp::ServerProtocol::sendVerified( const std::string & user )
639 {
640  send( std::string("250 ") + user ) ;
641 }
642 
643 void GSmtp::ServerProtocol::sendNotVerified( const std::string & user , bool temporary )
644 {
645  send( std::string() + (temporary?"450":"550") + " no such mailbox: " + G::Str::printable(user) ) ;
646 }
647 
648 void GSmtp::ServerProtocol::sendWillAccept( const std::string & user )
649 {
650  send( std::string("252 cannot verify but will accept: ") + G::Str::printable(user) ) ;
651 }
652 
653 void GSmtp::ServerProtocol::sendUnrecognised( const std::string & line )
654 {
655  send( "500 command unrecognized: \"" + G::Str::printable(line) + std::string("\"") ) ;
656  badClientEvent() ;
657 }
658 
659 void GSmtp::ServerProtocol::sendNotImplemented()
660 {
661  send( "502 command not implemented" ) ;
662 }
663 
664 void GSmtp::ServerProtocol::sendAuthRequired()
665 {
666  std::string more_help = sensitive() ? ": use starttls" : "" ;
667  send( std::string() + "530 authentication required" + more_help ) ;
668 }
669 
670 void GSmtp::ServerProtocol::sendNoRecipients()
671 {
672  send( "554 no valid recipients" ) ;
673 }
674 
675 void GSmtp::ServerProtocol::sendTooBig( bool disconnecting )
676 {
677  send( disconnecting ? "554 message too big, disconnecting" : "554 message too big" ) ;
678 }
679 
680 void GSmtp::ServerProtocol::sendDataReply()
681 {
682  send( "354 start mail input -- end with <CRLF>.<CRLF>" ) ;
683 }
684 
685 void GSmtp::ServerProtocol::sendRsetReply()
686 {
687  send( "250 state reset" ) ;
688 }
689 
690 void GSmtp::ServerProtocol::sendMailReply()
691 {
692  sendOk() ;
693 }
694 
695 void GSmtp::ServerProtocol::sendCompletionReply( bool ok , const std::string & reason )
696 {
697  if( ok )
698  sendOk() ;
699  else
700  send( std::string("452 message processing failed: ") + reason ) ;
701 }
702 
703 void GSmtp::ServerProtocol::sendRcptReply()
704 {
705  sendOk() ;
706 }
707 
708 void GSmtp::ServerProtocol::sendBadFrom( std::string reason )
709 {
710  std::string msg("553 mailbox name not allowed") ;
711  if( ! reason.empty() )
712  {
713  msg.append( ": " ) ;
714  msg.append( reason ) ;
715  }
716  send( msg ) ;
717 }
718 
719 void GSmtp::ServerProtocol::sendBadTo( const std::string & text , bool temporary )
720 {
721  send( std::string() + (temporary?"450":"550") + " mailbox unavailable: " + text ) ;
722 }
723 
724 void GSmtp::ServerProtocol::sendEhloReply()
725 {
726  std::ostringstream ss ;
727  ss << "250-" << m_text.hello(m_smtp_peer_name) << crlf() ;
728 
729  // dont advertise authentication if the sasl server implementation does not like plaintext dialogs
730  if( m_sasl->active() && !sensitive() )
731  ss << "250-AUTH " << m_sasl->mechanisms() << crlf() ;
732 
733  if( m_with_ssl && !m_secure )
734  ss << "250-STARTTLS" << crlf() ;
735 
736  if( m_with_vrfy )
737  ss << "250-VRFY" << crlf() ; // see RFC2821-3.5.2
738 
739  ss << "250 8BITMIME" ;
740  send( ss.str() ) ;
741 }
742 
743 void GSmtp::ServerProtocol::sendHeloReply()
744 {
745  sendOk() ;
746 }
747 
748 void GSmtp::ServerProtocol::sendOk()
749 {
750  send( "250 OK" ) ;
751 }
752 
753 const std::string & GSmtp::ServerProtocol::crlf()
754 {
755  static const std::string s( "\015\012" ) ;
756  return s ;
757 }
758 
759 void GSmtp::ServerProtocol::send( std::string line , bool go_secure )
760 {
761  G_LOG( "GSmtp::ServerProtocol: tx>>: \"" << G::Str::printable(line) << "\"" ) ;
762  line.append( crlf() ) ;
763  m_sender.protocolSend( line , go_secure ) ;
764 }
765 
766 std::pair<std::string,std::string> GSmtp::ServerProtocol::parseFrom( const std::string & line ) const
767 {
768  // eg. MAIL FROM:<me@localhost>
769  return parse( line ) ;
770 }
771 
772 std::pair<std::string,std::string> GSmtp::ServerProtocol::parseTo( const std::string & line ) const
773 {
774  // eg. RCPT TO:<@first.co.uk,@second.co.uk:you@final.co.uk>
775  // eg. RCPT TO:<Postmaster>
776  return parse( line ) ;
777 }
778 
779 std::pair<std::string,std::string> GSmtp::ServerProtocol::parse( const std::string & line ) const
780 {
781  size_t start = line.find( '<' ) ;
782  size_t end = line.find( '>' ) ;
783  if( start == std::string::npos || end == std::string::npos || end < start )
784  {
785  std::string reason( "missing or invalid angle brackets in mailbox name" ) ;
786  return std::make_pair(std::string(),reason) ;
787  }
788 
789  std::string s = line.substr( start + 1U , end - start - 1U ) ;
790  G::Str::trim( s , " \t" ) ;
791 
792  // strip source route
793  if( s.length() > 0U && s.at(0U) == '@' )
794  {
795  size_t colon_pos = s.find( ':' ) ;
796  if( colon_pos == std::string::npos )
797  {
798  std::string reason( "invalid mailbox name: no colon after leading at character" ) ;
799  return std::make_pair(std::string(),reason) ;
800  }
801  s = s.substr( colon_pos + 1U ) ;
802  }
803 
804  return std::make_pair(s,std::string()) ;
805 }
806 
807 std::string GSmtp::ServerProtocol::parsePeerName( const std::string & line ) const
808 {
809  size_t pos = line.find_first_of( " \t" ) ;
810  if( pos == std::string::npos )
811  return std::string() ;
812 
813  std::string smtp_peer_name = line.substr( pos + 1U ) ;
814  G::Str::trim( smtp_peer_name , " \t" ) ;
815  return smtp_peer_name ;
816 }
817 
818 void GSmtp::ServerProtocol::badClientEvent()
819 {
820  m_bad_client_count++ ;
821  if( m_bad_client_limit && m_bad_client_count >= m_bad_client_limit )
822  {
823  std::string reason = "too many protocol errors from the client" ;
824  G_DEBUG( "GSmtp::ServerProtocol::badClientEvent: " << reason << ": dropping the connection" ) ;
825  throw ProtocolDone( reason ) ;
826  }
827 }
828 
829 // ===
830 
831 GSmtp::ServerProtocolText::ServerProtocolText( const std::string & ident , const std::string & thishost ,
832  const GNet::Address & peer_address , const std::string & peer_socket_name ) :
833  m_ident(ident) ,
834  m_thishost(thishost) ,
835  m_peer_address(peer_address) ,
836  m_peer_socket_name(peer_socket_name)
837 {
838 }
839 
841 {
842  return m_thishost + " -- " + m_ident + " -- Service ready" ;
843 }
844 
845 std::string GSmtp::ServerProtocolText::hello( const std::string & ) const
846 {
847  return m_thishost + " says hello" ;
848 }
849 
850 std::string GSmtp::ServerProtocolText::received( const std::string & smtp_peer_name ) const
851 {
852  return receivedLine( smtp_peer_name , m_peer_address.displayString(false) , m_peer_socket_name , m_thishost ) ;
853 }
854 
855 std::string GSmtp::ServerProtocolText::shortened( std::string prefix , std::string s )
856 {
857  // strip off the windows sid and ad domain
858  std::string::size_type pos = s.find_last_of( "=\\" ) ;
859  return
860  s.empty() || pos == std::string::npos || (pos+1U) == s.length() ?
861  std::string() :
862  ( prefix + G::Str::printable(s.substr(pos+1U),'^') ) ;
863 }
864 
865 std::string GSmtp::ServerProtocolText::receivedLine( const std::string & smtp_peer_name ,
866  const std::string & peer_address , const std::string & peer_socket_name , const std::string & thishost )
867 {
870  const std::string zone = G::DateTime::offsetString(G::DateTime::offset(t)) ;
871  const G::Date date( tm ) ;
872  const G::Time time( tm ) ;
873 
874  std::ostringstream ss ;
875  ss
876  << "Received: from " << smtp_peer_name
877  << " ("
878  << "[" << peer_address << "]"
879  << shortened( "," , peer_socket_name )
880  << ") by " << thishost << " with ESMTP ; "
881  << date.weekdayName(true) << ", "
882  << date.monthday() << " "
883  << date.monthName(true) << " "
884  << date.yyyy() << " "
885  << time.hhmmss(":") << " "
886  << zone ;
887  return ss.str() ;
888 }
889 
890 // ===
891 
893 {
894 }
895 
896 // ===
897 
899 {
900 }
901 
902 // ===
903 
904 GSmtp::ServerProtocol::Config::Config( bool b , unsigned int i ) :
905  with_vrfy(b) ,
906  preprocessor_timeout(i) ,
907  disconnect_on_overflow(true) // (too harsh?)
908 {
909 }
910 
void secure(const std::string &certificate)
To be called when the transport protocol goes into secure mode.
ServerProtocolText(const std::string &ident, const std::string &thishost, const GNet::Address &peer_address, const std::string &peer_socket_name)
Constructor.
An interface used by ServerProtocol to send protocol replies.
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime.cpp:34
static Library * instance()
Returns a pointer to a library object, if any.
static bool valid(const std::string &)
Returns true if the string can be decoded.
Definition: gbase64.cpp:166
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:507
virtual void onTimeout()
Final override from GNet::AbstractTimer.
std::time_t EpochTime
Definition: gdatetime.h:41
A date (dd/mm/yyyy) class.
Definition: gdate.h:40
std::string monthName(bool brief=false) const
Returns the month as a string (in english).
Definition: gdate.cpp:170
virtual void onTimeoutException(std::exception &)
Final override from GNet::AbstractTimer.
An interface used by ServerProtocol to provide response text strings.
static Offset offset(EpochTime epoch_time)
Returns the offset between UTC and localtime as at 'epoch_time'.
Definition: gdatetime.cpp:71
virtual std::string received(const std::string &smtp_peer_name_from_helo) const
Final override from GSmtp::ServerProtocol::Text.
ServerProtocol(Sender &sender, Verifier &verifier, ProtocolMessage &pmessage, const GAuth::Secrets &secrets, Text &text, GNet::Address peer_address, const std::string &peer_socket_name, Config config)
Constructor.
static BrokenDownTime local(EpochTime epoch_time)
Converts from epoch time to local broken-down-time.
Definition: gdatetime.cpp:63
void connect(Slot3< P1, P2, P3 > slot)
Definition: gslot.h:369
The Address class encapsulates an IP transport address.
Definition: gaddress.h:48
int monthday() const
Returns the day of the month.
Definition: gdate.cpp:111
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:44
Slot0 slot(T &object, void(T::*fn)())
Part of the slot/signal system.
Definition: gslot.h:156
void apply(const std::string &line)
Called on receipt of a string from the client.
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
A simple time-of-day (hh/mm/ss) class.
Definition: gtime.h:39
static void splitIntoTokens(const std::string &in, Strings &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:714
static VerifierStatus parse(const std::string &str, std::string &to_ref)
Parses a str() string into a structure and a recipient 'to' address (by reference).
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
An asynchronous interface that verifies recipient 'to' addresses.
Definition: gverifier.h:44
virtual G::Signal2< std::string, VerifierStatus > & doneSignal()=0
Returns a signal that is emit()ed when the verify() request is complete.
static void trimLeft(std::string &s, const std::string &ws, size_type limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:111
void init()
Starts the protocol. Use only once after construction.
A simple interface to a store of secrets as used in authentication.
Definition: gsecrets.h:44
Low-level classes.
virtual ~ServerProtocol()
Destructor.
#define G_LOG(expr)
Definition: glog.h:98
std::string yyyy() const
Returns the year as a four-digit decimal string.
Definition: gdate.cpp:192
std::string weekdayName(bool brief=false) const
Returns an english string representation of the day of the week.
Definition: gdate.cpp:153
#define G_DEBUG(expr)
Definition: glog.h:95
virtual G::Signal3< bool, unsigned long, std::string > & doneSignal()=0
Returns a signal which is raised once process() has completed.
static std::string offsetString(Offset offset)
Converts the given utc/localtime offset into a five-character "+/-hhmm" string.
Definition: gdatetime.cpp:81
A structure containing configuration parameters for ServerProtocol.
An interface used by the ServerProtocol class to assemble and process an incoming message...
A RAII class for initialising the underlying ssl library.
Definition: gssl.h:147
bool enabled(bool for_serving=false) const
Returns true if this is a real and enabled ssl library.
void addTransition(Event event, State from, State to, Action action)
Adds a transition.
struct std::tm BrokenDownTime
Definition: gdatetime.h:43
std::string hhmmss(const char *sep=NULL) const
Returns a hhmmss string.
Definition: gtime.cpp:80
static std::string decode(const std::string &)
Decodes the given string.
Definition: gbase64.cpp:131
SASL authentication classes.
virtual std::string hello(const std::string &smtp_peer_name_from_helo) const
Final override from GSmtp::ServerProtocol::Text.
static std::string encode(const std::string &s, const std::string &line_break)
Encodes the given string.
Definition: gbase64.cpp:68
static std::string receivedLine(const std::string &smtp_peer_name_from_helo, const std::string &peer_address, const std::string &peer_socket_name, const std::string &thishost)
Returns a standard "Received:" line.
#define G_WARNING(expr)
Definition: glog.h:107
virtual std::string greeting() const
Final override from GSmtp::ServerProtocol::Text.
static void toUpper(std::string &s)
Replaces all lowercase characters in string 's' by uppercase characters.
Definition: gstr.cpp:422