39 m_nofilter_timer(*this,&
Client::onNoFilterTimeout,es) ,
40 m_filter(ff.newFilter(es,
Filter::Type::client,config.filter_config,config.filter_spec)) ,
41 m_protocol(es,*this,secrets,config.sasl_client_config,config.client_protocol_config,config.secure_tunnel) ,
43 m_filter_special(false)
45 G_ASSERT( m_filter.get() !=
nullptr ) ;
48 m_filter->doneSignal().connect(
G::Slot::slot(*
this,&Client::filterDone) ) ;
53 m_filter->doneSignal().disconnect() ;
54 m_protocol.filterSignal().disconnect() ;
55 m_protocol.doneSignal().disconnect() ;
62 .set_stream_socket_config( smtp_config.stream_socket_config )
63 .set_line_buffer_config( GNet::LineBuffer::Config::smtp() )
64 .set_bind_local_address( smtp_config.bind_local_address )
65 .set_local_address( smtp_config.local_address )
66 .set_connection_timeout( smtp_config.connection_timeout )
67 .set_socket_protocol_config(
69 .set_client_tls_profile( smtp_config.client_tls_profile )
70 .set_secure_connection_timeout( smtp_config.secure_connection_timeout ) ) ;
77 return m_message_done_signal ;
82 G_ASSERT( message.get() !=
nullptr ) ;
83 m_message = std::move( message ) ;
88bool GSmtp::Client::ready()
const
90 return m_config.secure_tunnel ? ( connected() && m_secure ) : connected() ;
93void GSmtp::Client::onConnect()
95 if( m_config.secure_tunnel )
101void GSmtp::Client::onSecure(
const std::string & ,
const std::string & ,
const std::string & )
104 if( m_config.secure_tunnel )
107 m_protocol.secure() ;
110void GSmtp::Client::start()
112 G_LOG_S(
"GSmtp::Client::start: smtp connection to " << peerAddress().displayString() ) ;
115 eventSignal().emit(
"sending" , std::string(message()->
id().str()) , std::string() ) ;
116 if( this_.deleted() ) return ;
118 m_protocol.start( std::weak_ptr<GStore::StoredMessage>(message()) ) ;
121std::shared_ptr<GStore::StoredMessage> GSmtp::Client::message()
123 G_ASSERT( m_message !=
nullptr ) ;
127bool GSmtp::Client::protocolSend(
G::string_view line , std::size_t offset ,
bool go_secure )
129 offset = std::min( offset , line.size() ) ;
131 bool rc = data.empty() ? true : send( data ) ;
137void GSmtp::Client::filterStart()
139 if( !message()->forwardTo().empty() )
142 m_nofilter_timer.startTimer( 0U ) ;
146 G_LOG_MORE(
"GSmtp::Client::filterStart: client-filter [" << m_filter->id() <<
"]: [" << message()->
id().str() <<
"]" ) ;
148 m_filter_special = false ;
149 m_filter->start( message()->
id() ) ;
153void GSmtp::Client::onNoFilterTimeout()
155 m_protocol.filterDone( Filter::Result::ok , {} , {} ) ;
158void GSmtp::Client::filterDone(
int filter_result )
160 G_ASSERT(
static_cast<int>(m_filter->result()) == filter_result ) ;
162 const bool ok = filter_result == 0 ;
163 const bool abandon = filter_result == 1 ;
164 m_filter_special = m_filter->special() ;
166 G_LOG_IF( !m_filter->quiet() ,
"GSmtp::Client::filterDone: client-filter "
167 "[" << m_filter->id() <<
"]: [" << message()->
id().str() <<
"]: "
168 << m_filter->str(Filter::Type::client) ) ;
170 std::string reopen_error ;
172 reopen_error = message()->reopen() ;
175 if( ok && reopen_error.empty() )
177 m_protocol.filterDone( Filter::Result::ok , {} , {} ) ;
181 m_protocol.filterDone( Filter::Result::abandon , {} , {} ) ;
183 else if( !reopen_error.empty() )
185 m_protocol.filterDone( Filter::Result::fail ,
"failed" , reopen_error ) ;
189 m_protocol.filterDone( Filter::Result::fail , m_filter->response() , m_filter->reason() ) ;
193void GSmtp::Client::protocolDone(
const ClientProtocol::DoneInfo & info )
195 G_ASSERT( info.response_code >= -2 ) ;
196 G_DEBUG(
"GSmtp::Client::protocolDone: \"" << info.response <<
"\"" ) ;
198 std::string reason = info.reason.empty() ? info.response : info.reason ;
199 std::string short_reason = ( info.response.empty() || info.reason.empty() ) ? info.response : info.reason ;
200 std::string message_id = message()->id().str() ;
202 if( info.response_code == -1 )
205 short_reason =
"abandoned" ;
207 else if( info.response_code == -2 )
209 messageFail( 550 , reason ) ;
210 short_reason =
"rejected" ;
212 else if( info.response.empty() )
217 else if( info.rejects.empty() )
220 G_ASSERT( !reason.empty() ) ;
222 messageFail( info.response_code , reason ) ;
228 message()->editRecipients( info.rejects ) ;
229 messageFail( info.response_code , reason ) ;
233 eventSignal().emit(
"sent" , message_id , short_reason ) ;
234 if( this_.deleted() ) return ;
237 messageDoneSignal().emit( { std::max(0,info.response_code) , info.response , m_filter_special } ) ;
242 m_protocol.finish() ;
246void GSmtp::Client::messageDestroy()
248 message()->destroy() ;
252void GSmtp::Client::messageFail(
int response_code ,
const std::string & reason )
254 message()->fail( reason , response_code ) ;
258bool GSmtp::Client::onReceive(
const char * line_data , std::size_t line_size , std::size_t , std::size_t ,
char )
260 std::string line( line_data , line_size ) ;
264 bool done = m_protocol.apply( line ) ;
265 if( this_.deleted() )
return false ;
276void GSmtp::Client::onDelete(
const std::string & error )
278 G_DEBUG(
"GSmtp::Client::onDelete: error [" << error <<
"]" ) ;
281 if( m_message && hasConnected() )
282 messageFail( 0 , error ) ;
287void GSmtp::Client::onSendComplete()
289 m_protocol.sendComplete() ;
294GSmtp::Client::Config::Config() :
295 local_address(
GNet::Address::defaultAddress())
An interface used by GAuth::SaslClient to obtain a client id and its authentication secret.
An exception class that is detected by GNet::EventHandlerList and results in onException() being call...
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
A class that represents the remote target for out-going client connections.
G::Slot::Signal< const DoneInfo & > & doneSignal() noexcept
Returns a signal that is raised once the protocol has finished with a given message.
G::Slot::Signal & filterSignal() noexcept
Returns a signal that is raised when the protocol needs to do message filtering.
A class which acts as an SMTP client, sending messages to a remote SMTP server.
G::Slot::Signal< const MessageDoneInfo & > & messageDoneSignal() noexcept
Returns a signal that indicates that sendMessage() has completed or failed.
void quitAndFinish()
Finishes a sendMessage() sequence.
void sendMessage(std::unique_ptr< GStore::StoredMessage > message)
Starts sending the given message.
Client(GNet::ExceptionSink, FilterFactoryBase &, const GNet::Location &remote, const GAuth::SaslClientSecrets &, const Config &config)
Constructor.
~Client() override
Destructor.
A factory interface for making GSmtp::Filter message processors.
An interface for processing a message file through a filter.
An object to represent a nested execution context.
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 ...
A class like c++17's std::string_view.
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
A structure containing GNet::Client configuration parameters.
A configuration structure for GNet::SocketProtocol.
A structure containing GSmtp::Client configuration parameters.