42 const std::string & sasl_config ,
const std::string & challenge_hostname )
44 bool with_apop = false ;
54 m_verifier(verifier) ,
57 m_sasl(newSaslServer(secrets,config.sasl_server_config,config.sasl_server_challenge_hostname)) ,
59 m_apply_data(nullptr) ,
61 m_fsm(State::Start,State::End,State::s_Same,State::s_Any) ,
62 m_with_starttls(false) ,
63 m_peer_address(peer_address) ,
65 m_client_error_count(0U) ,
66 m_session_esmtp(false) ,
71 m_fsm( Event::Quit , State::s_Any , State::End , &ServerProtocol::doQuit ) ;
72 m_fsm( Event::Unknown , State::Processing , State::s_Same , &ServerProtocol::doIgnore ) ;
73 m_fsm( Event::Unknown , State::s_Any , State::s_Same , &ServerProtocol::doUnknown ) ;
74 m_fsm( Event::Rset , State::Start , State::s_Same , &ServerProtocol::doRset ) ;
75 m_fsm( Event::Rset , State::s_Any , State::Idle , &ServerProtocol::doRset ) ;
76 m_fsm( Event::Noop , State::s_Any , State::s_Same , &ServerProtocol::doNoop ) ;
77 m_fsm( Event::Help , State::s_Any , State::s_Same , &ServerProtocol::doHelp ) ;
78 m_fsm( Event::Expn , State::s_Any , State::s_Same , &ServerProtocol::doExpn ) ;
79 m_fsm( Event::Vrfy , State::Start , State::VrfyStart , &ServerProtocol::doVrfy , State::s_Same ) ;
80 m_fsm( Event::VrfyReply , State::VrfyStart , State::Start , &ServerProtocol::doVrfyReply ) ;
81 m_fsm( Event::Vrfy , State::Idle , State::VrfyIdle , &ServerProtocol::doVrfy , State::s_Same ) ;
82 m_fsm( Event::VrfyReply , State::VrfyIdle , State::Idle , &ServerProtocol::doVrfyReply ) ;
83 m_fsm( Event::Vrfy , State::GotMail , State::VrfyGotMail, &ServerProtocol::doVrfy , State::s_Same ) ;
84 m_fsm( Event::VrfyReply , State::VrfyGotMail, State::GotMail , &ServerProtocol::doVrfyReply ) ;
85 m_fsm( Event::Vrfy , State::GotRcpt , State::VrfyGotRcpt, &ServerProtocol::doVrfy , State::s_Same ) ;
86 m_fsm( Event::VrfyReply , State::VrfyGotRcpt, State::GotRcpt , &ServerProtocol::doVrfyReply ) ;
87 m_fsm( Event::Ehlo , State::s_Any , State::Idle , &ServerProtocol::doEhlo , State::s_Same ) ;
88 m_fsm( Event::Helo , State::s_Any , State::Idle , &ServerProtocol::doHelo , State::s_Same ) ;
89 m_fsm( Event::Mail , State::Idle , State::GotMail , &ServerProtocol::doMail , State::Idle ) ;
90 m_fsm( Event::Rcpt , State::GotMail , State::RcptTo1 , &ServerProtocol::doRcpt , State::s_Same ) ;
91 m_fsm( Event::RcptReply , State::RcptTo1 , State::GotRcpt , &ServerProtocol::doRcptToReply , State::GotMail ) ;
92 m_fsm( Event::Rcpt , State::GotRcpt , State::RcptTo2 , &ServerProtocol::doRcpt , State::s_Same ) ;
93 m_fsm( Event::RcptReply , State::RcptTo2 , State::GotRcpt , &ServerProtocol::doRcptToReply ) ;
94 m_fsm( Event::DataFail , State::GotMail , State::MustReset , &ServerProtocol::doBadDataCommand ) ;
95 m_fsm( Event::DataFail , State::GotRcpt , State::MustReset , &ServerProtocol::doBadDataCommand ) ;
96 m_fsm( Event::Data , State::GotMail , State::Idle , &ServerProtocol::doNoRecipients ) ;
97 m_fsm( Event::Data , State::GotRcpt , State::Data , &ServerProtocol::doData ) ;
98 m_fsm( Event::DataContent , State::Data , State::Data , &ServerProtocol::doDataContent ) ;
99 m_fsm( Event::Bdat , State::Idle , State::MustReset , &ServerProtocol::doBdatOutOfSequence ) ;
100 m_fsm( Event::Bdat , State::GotMail , State::Idle , &ServerProtocol::doNoRecipients ) ;
101 m_fsm( Event::BdatLast , State::GotMail , State::Idle , &ServerProtocol::doNoRecipients ) ;
102 m_fsm( Event::BdatLastZero , State::GotMail , State::Idle , &ServerProtocol::doNoRecipients ) ;
103 m_fsm( Event::Bdat , State::GotRcpt , State::BdatData , &ServerProtocol::doBdatFirst , State::MustReset ) ;
104 m_fsm( Event::BdatLast , State::GotRcpt , State::BdatDataLast , &ServerProtocol::doBdatFirstLast , State::MustReset ) ;
105 m_fsm( Event::BdatLastZero , State::GotRcpt , State::BdatChecking , &ServerProtocol::doBdatFirstLastZero ) ;
106 m_fsm( Event::BdatContent , State::BdatData , State::BdatIdle , &ServerProtocol::doBdatContent , State::BdatData ) ;
107 m_fsm( Event::Bdat , State::BdatIdle , State::BdatData , &ServerProtocol::doBdatMore , State::MustReset ) ;
108 m_fsm( Event::BdatLast , State::BdatIdle , State::BdatDataLast , &ServerProtocol::doBdatMoreLast , State::MustReset ) ;
109 m_fsm( Event::BdatLastZero , State::BdatIdle , State::BdatChecking , &ServerProtocol::doBdatMoreLastZero ) ;
110 m_fsm( Event::BdatContent , State::BdatDataLast , State::BdatChecking , &ServerProtocol::doBdatContentLast , State::BdatDataLast ) ;
111 m_fsm( Event::BdatCheck , State::BdatChecking , State::BdatProcessing , &ServerProtocol::doBdatCheck , State::Idle ) ;
112 m_fsm( Event::Done , State::BdatProcessing , State::Idle , &ServerProtocol::doBdatComplete ) ;
113 m_fsm( Event::Eot , State::Data , State::Processing , &ServerProtocol::doEot , State::Idle ) ;
114 m_fsm( Event::Done , State::Processing , State::Idle , &ServerProtocol::doComplete ) ;
115 m_fsm( Event::Auth , State::Idle , State::Auth , &ServerProtocol::doAuth , State::Idle ) ;
116 m_fsm( Event::AuthData, State::Auth , State::Auth , &ServerProtocol::doAuthData , State::Idle ) ;
117 if( m_config.tls_starttls )
119 m_with_starttls = true ;
120 m_fsm( Event::StartTls , State::Idle , State::StartingTls , &ServerProtocol::doStartTls , State::Idle ) ;
121 m_fsm( Event::Secure , State::StartingTls , State::Idle , &ServerProtocol::doSecure ) ;
123 else if( m_config.tls_connection )
125 m_fsm.
reset( State::StartingTls ) ;
126 m_fsm( Event::Secure , State::StartingTls , State::Start , &ServerProtocol::doSecureGreeting ) ;
134 m_pm.processedSignal().disconnect() ;
135 m_verifier.doneSignal().disconnect() ;
140 return m_change_signal ;
149 m_fsm.state() == State::Processing ||
151 m_fsm.state() == State::VrfyStart ||
152 m_fsm.state() == State::VrfyIdle ||
153 m_fsm.state() == State::VrfyGotMail ||
154 m_fsm.state() == State::VrfyGotRcpt ||
155 m_fsm.state() == State::RcptTo1 ||
156 m_fsm.state() == State::RcptTo2 ;
160bool GSmtp::ServerProtocol::rcptState()
const
163 m_fsm.state() == State::RcptTo1 ||
164 m_fsm.state() == State::RcptTo2 ;
168bool GSmtp::ServerProtocol::sendFlush()
const
173 if( !m_session_esmtp || !m_config.with_pipelining )
183 Event e = m_fsm.event() ;
187 e == Event::RcptReply ||
197 m_fsm.state() == State::Data ||
198 m_fsm.state() == State::BdatData ||
199 m_fsm.state() == State::BdatDataLast ;
212 if( m_config.tls_connection )
213 m_sender->protocolSecure() ;
215 sendGreeting( m_text.greeting() , m_enabled ) ;
218void GSmtp::ServerProtocol::applyEvent( Event event , EventData event_data )
220 State new_state = m_fsm.apply( *
this , event , event_data ) ;
221 if( new_state == State::s_Any )
222 throw Done(
"protocol error" ) ;
226 const std::string & protocol ,
const std::string & cipher )
228 m_certificate = certificate ;
229 m_protocol = protocol ;
232 applyEvent( Event::Secure ) ;
235void GSmtp::ServerProtocol::doSecure( EventData ,
bool & )
237 G_DEBUG(
"GSmtp::ServerProtocol::doSecure" ) ;
241void GSmtp::ServerProtocol::doSecureGreeting( EventData ,
bool & )
244 sendGreeting( m_text.greeting() , m_enabled ) ;
247void GSmtp::ServerProtocol::doStartTls( EventData ,
bool & ok )
251 sendOutOfSequence() ;
263 G_ASSERT( std::get<2>(args) == 2U || ( inDataState() && std::get<2>(args) == 0U ) ) ;
264 G_DEBUG(
"GSmtp::ServerProtocol::apply: apply "
266 << (std::get<1>(args)>10U?
"...":
"") <<
"] "
267 "state=" <<
static_cast<int>(m_fsm.state()) <<
" "
268 "more=" << std::get<5>(args) <<
" "
269 "busy=" << inBusyState() ) ;
276 m_apply_data = &args ;
277 m_apply_more = std::get<5>( args ) ;
278 G::ScopeExit clear_on_return( [&](){ m_apply_data = nullptr ; } ) ;
281 G::ScopeExit emit_on_return( [&](){ m_change_signal.emit() ; } ) ;
286 EventData event_data( std::get<0>(args) , std::get<1>(args) ) ;
289 Event
event = Event::Unknown ;
290 State state = m_fsm.state() ;
291 if( state == State::Data && isEndOfText(args) )
295 else if( state == State::Data )
297 event = Event::DataContent ;
299 else if( state == State::BdatData || state == State::BdatDataLast )
301 event = Event::BdatContent ;
303 else if( state == State::Auth )
305 event = Event::AuthData ;
309 G_LOG(
"GSmtp::ServerProtocol: rx<<: \"" <<
G::Str::printable(str(event_data)) <<
"\"" ) ;
310 event = commandEvent( event_data ) ;
314 State new_state = m_fsm.apply( *
this , event , event_data ) ;
315 if( new_state == State::s_Any )
317 sendOutOfSequence() ;
322 return !inBusyState() ;
325void GSmtp::ServerProtocol::doDataContent( EventData ,
bool & )
327 G_ASSERT( m_apply_data !=
nullptr ) ;
328 if( m_apply_data ==
nullptr )
throw Done(
"protocol error" ) ;
330 const char * ptr = std::get<0>( *m_apply_data ) ;
331 std::size_t size = std::get<1>( *m_apply_data ) ;
332 std::size_t eolsize = std::get<2>( *m_apply_data ) ;
334 if( isEscaped(*m_apply_data) )
335 m_pm.addContent( ptr+1 , size+eolsize-1U ) ;
337 m_pm.addContent( ptr , size+eolsize ) ;
342void GSmtp::ServerProtocol::doEot( EventData ,
bool & ok )
344 G_LOG(
"GSmtp::ServerProtocol: rx<<: [message content not logged]" ) ;
345 G_LOG(
"GSmtp::ServerProtocol: rx<<: \".\"" ) ;
347 if( messageAddContentFailed() )
353 else if( messageAddContentTooBig() )
361 m_pm.process( m_sasl->id() , m_peer_address.hostPartString() , m_certificate ) ;
365void GSmtp::ServerProtocol::protocolMessageProcessed(
const ProtocolMessage::ProcessedInfo & info )
367 G_ASSERT( info.response.find_first_of(
"\r\t",0U,2U) == std::string::npos ) ;
368 G_ASSERT( info.success == info.response.empty() ) ;
369 G_ASSERT( info.response_code >= 0 ) ;
370 G_DEBUG(
"GSmtp::ServerProtocol::protocolMessageProcessed: ok=" << (info.success?1:0) <<
" msgid=" << info.id.str()
371 <<
" rc=" << info.response_code <<
" rsp=[" << info.response <<
"] reason=[" << info.reason <<
"]" ) ;
373 std::string response = info.response ;
378 .append(
G::Str::fromInt((info.response_code<400||info.response_code>=600)?452:info.response_code)) ;
380 applyEvent( Event::Done , {response.data(),response.size()} ) ;
381 m_change_signal.emit() ;
384void GSmtp::ServerProtocol::doComplete( EventData event_data ,
bool & )
387 sendCompletionReply( event_data.empty() , code(event_data) , str(event_data) ) ;
390void GSmtp::ServerProtocol::doQuit( EventData ,
bool & )
394 m_sender->protocolShutdown( m_config.shutdown_how_on_quit ) ;
398void GSmtp::ServerProtocol::doBadDataCommand( EventData ,
bool & )
400 sendBadDataOutOfSequence() ;
404void GSmtp::ServerProtocol::doBdatOutOfSequence( EventData ,
bool & )
406 sendOutOfSequence() ;
410void GSmtp::ServerProtocol::doBdatFirst( EventData event_data ,
bool & ok )
412 doBdatImp( event_data , ok ,
true ,
false ,
false ) ;
415void GSmtp::ServerProtocol::doBdatFirstLast( EventData event_data ,
bool & ok )
417 doBdatImp( event_data , ok ,
true ,
true ,
false ) ;
420void GSmtp::ServerProtocol::doBdatFirstLastZero( EventData event_data ,
bool & ok )
422 doBdatImp( event_data , ok ,
true ,
true ,
true ) ;
425void GSmtp::ServerProtocol::doBdatMore( EventData event_data ,
bool & ok )
427 doBdatImp( event_data , ok ,
false ,
false ,
false ) ;
430void GSmtp::ServerProtocol::doBdatMoreLast( EventData event_data ,
bool & ok )
432 doBdatImp( event_data , ok ,
false ,
true ,
false ) ;
435void GSmtp::ServerProtocol::doBdatMoreLastZero( EventData event_data ,
bool & ok )
437 doBdatImp( event_data , ok ,
false ,
true ,
true ) ;
440void GSmtp::ServerProtocol::doBdatImp(
G::string_view bdat_line ,
bool & ok ,
bool first ,
bool last ,
bool zero )
442 G_ASSERT( !zero || last ) ;
445 std::string received_line = m_text.received( m_session_peer_name , m_sasl->authenticated() ,
446 m_secure , m_protocol , m_cipher ) ;
447 if( received_line.length() )
448 m_pm.addReceived( received_line ) ;
453 if( messageAddContentFailed() )
459 else if( messageAddContentTooBig() )
467 applyEvent( Event::BdatCheck ) ;
472 auto pair = parseBdatSize( bdat_line ) ;
473 std::size_t bdat_size = pair.first ;
474 bool bdat_ok = pair.second ;
475 if( !bdat_ok || ( bdat_size == 0U && !last ) )
477 G_DEBUG(
"GSmtp::ServerProtocol::doBdatImp: bad bdat command: ok=" << bdat_ok <<
" size=" << bdat_size <<
" last=" << last ) ;
479 sendInvalidArgument() ;
483 m_bdat_arg = bdat_size ;
485 m_sender->protocolExpect( m_bdat_arg ) ;
490void GSmtp::ServerProtocol::doBdatContent( EventData ,
bool & complete )
492 G_ASSERT( m_apply_data !=
nullptr ) ;
493 if( m_apply_data ==
nullptr )
throw Done(
"protocol error" ) ;
495 const char * ptr = std::get<0>( *m_apply_data ) ;
496 std::size_t size = std::get<1>( *m_apply_data ) ;
497 std::size_t eolsize = std::get<2>( *m_apply_data ) ;
499 G_ASSERT( eolsize == 0U ) ;
500 G_ASSERT( (m_bdat_sum+size+eolsize) <= m_bdat_arg ) ;
502 std::size_t fullsize = size + eolsize ;
503 m_bdat_sum += fullsize ;
504 complete = m_bdat_sum >= m_bdat_arg ;
506 G_DEBUG(
"GSmtp::ServerProtocol: rx<<: [" << fullsize <<
" bytes (" << m_bdat_sum <<
"/" << m_bdat_arg <<
")]" ) ;
507 m_pm.addContent( ptr , fullsize ) ;
511 std::ostringstream ss ;
512 ss << m_bdat_sum <<
" bytes received" ;
517void GSmtp::ServerProtocol::doBdatContentLast( EventData ,
bool & complete )
519 G_ASSERT( m_apply_data !=
nullptr ) ;
520 if( m_apply_data ==
nullptr )
throw Done(
"protocol error" ) ;
522 const char * ptr = std::get<0>( *m_apply_data ) ;
523 std::size_t size = std::get<1>( *m_apply_data ) ;
524 std::size_t eolsize = std::get<2>( *m_apply_data ) ;
526 G_ASSERT( (m_bdat_sum+size+eolsize) <= m_bdat_arg ) ;
528 std::size_t fullsize = size + eolsize ;
529 m_bdat_sum += fullsize ;
530 complete = m_bdat_sum >= m_bdat_arg ;
532 G_DEBUG(
"GSmtp::ServerProtocol: rx<<: [" << fullsize <<
" bytes (" << m_bdat_sum <<
"/" << m_bdat_arg <<
")]" ) ;
533 m_pm.addContent( ptr , fullsize ) ;
537 applyEvent( Event::BdatCheck ) ;
541void GSmtp::ServerProtocol::doBdatCheck( EventData ,
bool & ok )
543 if( messageAddContentFailed() )
549 else if( messageAddContentTooBig() )
557 m_pm.process( m_sasl->id() , m_peer_address.hostPartString() , m_certificate ) ;
561bool GSmtp::ServerProtocol::messageAddContentFailed()
563 bool failed = m_pm.addContent(
nullptr , 0U ) == GStore::NewMessage::Status::Error ;
565 G_WARNING(
"GSmtp::ServerProtocol::messageAddContentFailed: failed to save message content" ) ;
569bool GSmtp::ServerProtocol::messageAddContentTooBig()
571 bool too_big = m_pm.addContent(
nullptr , 0U ) == GStore::NewMessage::Status::TooBig ;
573 G_WARNING(
"GSmtp::ServerProtocol::messageAddContentTooBig: message content too big" ) ;
577void GSmtp::ServerProtocol::doBdatComplete( EventData event_data ,
bool & )
580 sendCompletionReply( event_data.empty() , code(event_data) , str(event_data) ) ;
583void GSmtp::ServerProtocol::doIgnore( EventData ,
bool & )
587void GSmtp::ServerProtocol::doNoop( EventData ,
bool & )
593void GSmtp::ServerProtocol::doNothing( EventData ,
bool & )
598void GSmtp::ServerProtocol::doExpn( EventData ,
bool & )
600 sendNotImplemented() ;
603void GSmtp::ServerProtocol::doHelp( EventData ,
bool & )
605 sendNotImplemented() ;
608void GSmtp::ServerProtocol::doVrfy( EventData event_data ,
bool & predicate )
610 if( !m_config.with_vrfy )
615 else if( m_config.mail_requires_authentication &&
616 !m_sasl->authenticated() &&
617 !m_sasl->trusted(m_peer_address.wildcards(),m_peer_address.hostPartString()) )
620 sendAuthRequired( m_config.mail_requires_encryption && !m_secure && m_with_starttls ) ;
622 else if( m_config.mail_requires_encryption && !m_secure )
625 sendEncryptionRequired( m_with_starttls ) ;
629 std::string to = parseVrfy( str(event_data) ) ;
633 sendNotVerified(
"invalid mailbox" ,
false ) ;
637 verify( Verifier::Command::VRFY , to ) ;
642void GSmtp::ServerProtocol::verify( Verifier::Command command ,
const std::string & to ,
const std::string & from )
644 Verifier::Info info ;
645 info.client_ip = m_peer_address ;
646 info.mail_from_parameter = from ;
647 info.auth_mechanism = m_sasl->authenticated() ? m_sasl->mechanism() : std::string(
"NONE") ;
648 info.auth_extra = m_sasl->id() ;
649 m_verifier.verify( command , to , info ) ;
652void GSmtp::ServerProtocol::verifyDone( Verifier::Command command ,
const VerifierStatus & status )
654 G_DEBUG(
"GSmtp::ServerProtocol::verifyDone: verify done: [" << status.str() <<
"]" ) ;
656 throw Done(
"address verifier abort" ) ;
658 Event
event = command == Verifier::Command::RCPT ? Event::RcptReply : Event::VrfyReply ;
661 std::string status_str = status.str() ;
662 State new_state = m_fsm.apply( *
this , event , {status_str.data(),status_str.size()} ) ;
663 if( new_state == State::s_Any )
664 throw Done(
"protocol error" ) ;
666 m_change_signal.emit() ;
669void GSmtp::ServerProtocol::doVrfyReply( EventData event_data ,
bool & )
673 if( status.is_valid && status.is_local )
674 sendVerified( status.full_name ) ;
675 else if( status.is_valid )
676 sendWillAccept( status.recipient ) ;
678 sendNotVerified( status.response , status.temporary ) ;
681void GSmtp::ServerProtocol::doEhlo( EventData event_data ,
bool & predicate )
683 std::string smtp_peer_name = parseHeloPeerName( str(event_data) ) ;
684 if( smtp_peer_name.empty() )
687 sendMissingParameter() ;
691 m_session_esmtp = true ;
692 m_session_peer_name = smtp_peer_name ;
695 G_ASSERT( !m_sasl->authenticated() ) ;
697 ServerSend::Advertise advertise ;
698 advertise.hello = m_text.hello( m_session_peer_name ) ;
699 advertise.max_size = m_config.max_size ;
700 advertise.mechanisms = mechanisms() ;
701 advertise.starttls = m_with_starttls && !m_secure ;
702 advertise.vrfy = m_config.with_vrfy ;
703 advertise.chunking = advertise.binarymime = m_config.with_chunking ;
704 advertise.pipelining = m_config.with_pipelining ;
705 advertise.smtputf8 = m_config.with_smtputf8 ;
706 sendEhloReply( advertise ) ;
710void GSmtp::ServerProtocol::doHelo( EventData event_data ,
bool & predicate )
712 std::string smtp_peer_name = parseHeloPeerName( str(event_data) ) ;
713 if( smtp_peer_name.empty() )
716 sendMissingParameter() ;
720 m_session_peer_name = smtp_peer_name ;
726void GSmtp::ServerProtocol::doAuth( EventData event_data ,
bool & predicate )
730 std::string initial_response = G::sv_to_string( word.next()() ) ;
731 bool got_initial_response = !initial_response.empty() ;
733 G_DEBUG(
"ServerProtocol::doAuth: [" << mechanism <<
"], [" << initial_response <<
"]" ) ;
735 if( m_sasl->authenticated() )
737 G_WARNING(
"GSmtp::ServerProtocol: too many AUTH requests" ) ;
739 sendOutOfSequence() ;
742 else if( mechanisms().empty() && !m_secure && !mechanisms(
true).empty() )
744 G_WARNING(
"GSmtp::ServerProtocol: rejecting authentication attempt without encryption" ) ;
746 sendInsecureAuth( m_with_starttls ) ;
748 else if( mechanisms().empty() )
750 G_WARNING(
"GSmtp::ServerProtocol: client protocol error: AUTH requested but not advertised" ) ;
752 sendNotImplemented() ;
754 else if( !m_sasl->init(m_secure,mechanism) )
756 G_WARNING(
"GSmtp::ServerProtocol: request for unsupported server AUTH mechanism: " << mechanism ) ;
758 sendBadMechanism( m_sasl->preferredMechanism(m_secure) ) ;
760 else if( got_initial_response && m_sasl->mustChallenge() )
762 G_WARNING(
"GSmtp::ServerProtocol: unexpected initial-response with a server-first AUTH mechanism" ) ;
764 sendInvalidArgument() ;
766 else if( got_initial_response && ( initial_response !=
"=" && !
G::Base64::valid(initial_response) ) )
768 G_WARNING(
"GSmtp::ServerProtocol: invalid base64 encoding of AUTH parameter" ) ;
770 sendInvalidArgument() ;
772 else if( got_initial_response )
774 std::string s = initial_response ==
"=" ? std::string() :
G::Base64::decode(initial_response) ;
776 std::string next_challenge = m_sasl->apply( s , done ) ;
780 sendAuthDone( m_sasl->authenticated() ) ;
784 sendChallenge( next_challenge ) ;
789 sendChallenge( m_sasl->initialChallenge() ) ;
793void GSmtp::ServerProtocol::doAuthData( EventData event_data ,
bool & predicate )
795 G_LOG(
"GSmtp::ServerProtocol: rx<<: [authentication response not logged]" ) ;
796 if( event_data.size() == 1U && event_data[0] ==
'*' )
799 sendAuthenticationCancelled() ;
803 G_WARNING(
"GSmtp::ServerProtocol: invalid base64 encoding of authentication response" ) ;
805 sendAuthDone(
false ) ;
810 std::string next_challenge = m_sasl->apply(
G::Base64::decode(event_data) , done ) ;
814 sendAuthDone( m_sasl->authenticated() ) ;
818 sendChallenge( next_challenge ) ;
823void GSmtp::ServerProtocol::doMail( EventData event_data ,
bool & predicate )
832 else if( m_config.mail_requires_authentication &&
833 !m_sasl->authenticated() &&
834 !m_sasl->trusted(m_peer_address.wildcards(),m_peer_address.hostPartString()) )
836 G_LOG(
"GSmtp::ServerProtocol::doMail: server authentication enabled "
837 "but not a trusted address: " << m_peer_address.hostPartString() ) ;
839 sendAuthRequired( m_config.mail_requires_encryption && !m_secure && m_with_starttls ) ;
841 else if( m_config.mail_requires_encryption && !m_secure )
844 sendEncryptionRequired( m_with_starttls ) ;
848 auto mail_command = parseMailFrom( mail_line ) ;
849 if( !mail_command.error.empty() )
852 sendBadFrom( mail_command.error ) ;
854 else if( m_config.max_size && mail_command.size > m_config.max_size )
860 else if( mail_command.utf8address && !mail_command.smtputf8 && m_config.smtputf8_strict )
863 sendBadFrom(
"invalid character in mailbox name" ) ;
867 sendMailReply( mail_command.address ) ;
868 ProtocolMessage::FromInfo from_info ;
869 from_info.auth = mail_command.auth ;
870 from_info.body = mail_command.body ;
871 from_info.smtputf8 = mail_command.smtputf8 ;
872 from_info.utf8address = mail_command.utf8address ;
873 m_pm.setFrom( mail_command.address , from_info ) ;
878void GSmtp::ServerProtocol::doRcpt( EventData event_data ,
bool & predicate )
881 auto rcpt_command = parseRcptTo( rcpt_line ) ;
882 if( !rcpt_command.error.empty() )
885 sendBadTo( std::string() , rcpt_command.error ,
false ) ;
887 else if( rcpt_command.utf8address && !m_pm.fromInfo().smtputf8 && m_config.smtputf8_strict )
890 sendBadTo( std::string() ,
"invalid character in mailbox name" ,
false ) ;
894 verify( Verifier::Command::RCPT , rcpt_command.address , m_pm.from() ) ;
898void GSmtp::ServerProtocol::doRcptToReply( EventData event_data ,
bool & predicate )
904 bool ok = m_pm.addTo( ProtocolMessage::ToInfo(status) ) ;
905 G_ASSERT( status.is_valid || !ok ) ;
910 sendRcptReply( status.recipient , status.is_local ) ;
915 sendBadTo( status.recipient , status.response , status.temporary ) ;
919void GSmtp::ServerProtocol::doUnknown( EventData event_data ,
bool & )
921 sendUnrecognised( str(event_data) ) ;
925void GSmtp::ServerProtocol::clear()
933 m_verifier.cancel() ;
936void GSmtp::ServerProtocol::doRset( EventData ,
bool & )
944void GSmtp::ServerProtocol::doNoRecipients( EventData ,
bool & )
949void GSmtp::ServerProtocol::doData( EventData ,
bool & )
951 std::string received_line = m_text.received( m_session_peer_name , m_sasl->authenticated() ,
952 m_secure , m_protocol , m_cipher ) ;
954 if( received_line.length() )
955 m_pm.addReceived( received_line ) ;
960bool GSmtp::ServerProtocol::isEndOfText(
const ApplyArgsTuple & args )
const
962 std::size_t eolsize = std::get<2>( args ) ;
963 std::size_t linesize = std::get<3>( args ) ;
964 char c0 = std::get<4>( args ) ;
965 return linesize == 1U && eolsize == 2U && c0 ==
'.' ;
968bool GSmtp::ServerProtocol::isEscaped(
const ApplyArgsTuple & args )
const
970 std::size_t size = std::get<1>( args ) ;
971 std::size_t linesize = std::get<3>( args ) ;
972 char c0 = std::get<4>( args ) ;
973 return size > 1U && size == linesize && c0 ==
'.' ;
976GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::commandEvent(
G::string_view line )
const
991 if(
G::Str::imatch(word,
"STARTTLS"_sv) && m_with_starttls )
return Event::StartTls ;
993 if(
G::Str::imatch(word,
"BDAT"_sv) && m_config.with_chunking )
return bdatEvent(line) ;
994 return Event::Unknown ;
997GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::dataEvent(
G::string_view )
const
1001 return Event::DataFail ;
1003 return Event::Data ;
1006GSmtp::ServerProtocol::Event GSmtp::ServerProtocol::bdatEvent(
G::string_view line )
const
1008 bool last = parseBdatLast(line).second ? parseBdatLast(line).first : false ;
1009 std::size_t size = parseBdatSize(line).second ? parseBdatSize(line).first : 0L ;
1010 if( last && size == 0U )
1011 return Event::BdatLastZero ;
1013 return Event::BdatLast ;
1015 return Event::Bdat ;
1018void GSmtp::ServerProtocol::badClientEvent()
1020 m_client_error_count++ ;
1021 if( m_config.client_error_limit && m_client_error_count >= m_config.client_error_limit )
1023 std::string reason =
"too many protocol errors from the client" ;
1024 G_DEBUG(
"GSmtp::ServerProtocol::badClientEvent: " << reason <<
": dropping the connection" ) ;
1025 throw Done( reason ) ;
1029int GSmtp::ServerProtocol::code( EventData sv )
1034std::string GSmtp::ServerProtocol::str( EventData sv )
1041 return m_sasl->mechanisms( m_secure ) ;
1044G::StringArray GSmtp::ServerProtocol::mechanisms(
bool secure )
const
1046 return m_sasl->mechanisms( secure ) ;
1051GSmtp::ServerProtocol::Config::Config()
static std::unique_ptr< SaslServer > newSaslServer(const SaslServerSecrets &, bool allow_pop, const std::string &config, const std::string &challenge_domain)
A factory function for a SaslServer.
An interface used by GAuth::SaslServer to obtain authentication secrets.
The GNet::Address class encapsulates a TCP/UDP transport address.
An interface used by the ServerProtocol class to assemble and process an incoming message.
virtual ProcessedSignal & processedSignal()=0
Returns a signal which is raised once process() has completed.
An interface used by GSmtp::ServerProtocol to provide response text strings.
G::Slot::Signal & changeSignal() noexcept
A signal that is emitted at the end of apply() or whenever the protocol state might have changed by s...
bool inBusyState() const
Returns true if in a state where the protocol is waiting for an asynchronous filter of address-verifi...
bool apply(const ApplyArgsTuple &)
Called on receipt of a complete line of text from the client, or possibly a line fragment iff this ob...
void setSender(ServerSender &)
Sets the ServerSender interface, overriding the constructor parameter.
void init()
Starts the protocol.
~ServerProtocol() override
Destructor.
void secure(const std::string &certificate, const std::string &protocol, const std::string &cipher)
To be called when the transport protocol successfully goes into secure mode.
bool inDataState() const
Returns true if currently in a data-transfer state meaning that the next apply() does not need to con...
ServerProtocol(ServerSender &, Verifier &, ProtocolMessage &, const GAuth::SaslServerSecrets &secrets, Text &text, const GNet::Address &peer_address, const Config &config, bool enabled)
Constructor.
A simple mix-in class for GSmtp::ServerProtocol that sends protocol responses via a GSmtp::ServerSend...
void setSender(ServerSender *)
Sets the sender interface pointer.
An interface used by ServerProtocol to send protocol responses.
static VerifierStatus parse(const std::string &str)
Parses a str() string into a structure.
An asynchronous interface that verifies recipient 'to' addresses.
virtual G::Slot::Signal< Command, const VerifierStatus & > & doneSignal()=0
Returns a signal that is emit()ed when the verify() request is complete.
static std::string decode(string_view, bool throw_on_invalid=false, bool strict=true)
Decodes the given string.
static bool valid(string_view, bool strict=true)
Returns true if the string is a valid base64 encoding, possibly allowing for embedded newlines,...
A class that calls an exit function at the end of its scope.
State reset(State new_state)
Sets the current state. Returns the old state.
static string_view headView(string_view in, std::size_t pos, string_view default_={}) noexcept
Like head() but returning a view into the input string.
static bool replace(std::string &s, string_view from, string_view to, std::size_t *pos_p=nullptr)
A string_view overload.
static string_view tailView(string_view in, std::size_t pos, string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static int toInt(string_view s)
Converts string 's' to an int.
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
static std::string fromInt(int i)
Converts int 'i' to a string.
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 std::string upper(string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
A class like c++17's std::string_view.
bool enabled() noexcept
Returns true if pop code is built in.
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.
A configuration structure for GSmtp::ServerProtocol.
A slot holder, with connect() and emit() methods.