36 GNet::
Client(es.logging(this),remote,normalise(config.net_client_config)) ,
37 GNet::EventLogging(es.logging()) ,
38 m_es(es.logging(this)) ,
40 m_nofilter_timer(*this,&
Client::onNoFilterTimeout,m_es) ,
41 m_filter(ff.newFilter(m_es,
Filter::Type::client,config.filter_config,config.filter_spec)) ,
42 m_protocol(m_es,*this,secrets,config.sasl_client_config,config.client_protocol_config,config.secure_tunnel)
44 G_ASSERT( m_filter.get() !=
nullptr ) ;
47 m_filter->doneSignal().connect(
G::Slot::slot(*
this,&Client::filterDone) ) ;
52 m_filter->doneSignal().disconnect() ;
53 m_protocol.filterSignal().disconnect() ;
54 m_protocol.doneSignal().disconnect() ;
59 return net_client_config.set_line_buffer_config( GNet::LineBuffer::Config::smtp() ) ;
64 return m_message_done_signal ;
69 G_ASSERT( message.get() !=
nullptr ) ;
70 m_message = std::move( message ) ;
71 m_event_logging_string = eventLoggingString( m_message.get() , m_config ) ;
76bool GSmtp::Client::ready()
const
78 return m_config.secure_tunnel ? ( connected() && m_secure ) : connected() ;
81void GSmtp::Client::onConnect()
83 if( m_config.client_protocol_config.ehlo.find(
'.') == std::string::npos )
84 m_protocol.reconfigure( localAddress().hostPartString() ) ;
86 if( m_config.secure_tunnel )
92void GSmtp::Client::onSecure(
const std::string & ,
const std::string & ,
const std::string & )
95 if( m_config.secure_tunnel )
101void GSmtp::Client::start()
103 G_LOG_S(
"GSmtp::Client::start: smtp connection to " << peerAddress().displayString() ) ;
106 eventSignal().emit(
"sending" , std::string(message()->
id().str()) , std::string() ) ;
107 if( this_.deleted() ) return ;
109 m_protocol.start( std::weak_ptr<GStore::StoredMessage>(message()) ) ;
112std::shared_ptr<GStore::StoredMessage> GSmtp::Client::message()
114 G_ASSERT( m_message !=
nullptr ) ;
118bool GSmtp::Client::protocolSend( std::string_view line , std::size_t offset ,
bool go_secure )
120 offset = std::min( offset , line.size() ) ;
121 std::string_view data( line.data()+offset , line.size()-offset ) ;
122 bool rc = data.empty() ? true : send( data ) ;
128void GSmtp::Client::filterStart()
130 if( !message()->forwardTo().empty() )
133 m_nofilter_timer.startTimer( 0U ) ;
137 G_LOG_MORE(
"GSmtp::Client::filterStart: client-filter [" << m_filter->id() <<
"]: [" << message()->
id().str() <<
"]" ) ;
139 m_filter_special = false ;
140 m_filter->start( message()->
id() ) ;
144void GSmtp::Client::onNoFilterTimeout()
146 m_protocol.filterDone( Filter::Result::ok , {} , {} ) ;
149void GSmtp::Client::filterDone(
int filter_result )
151 G_ASSERT(
static_cast<int>(m_filter->result()) == filter_result ) ;
153 const bool ok = filter_result == 0 ;
154 const bool abandon = filter_result == 1 ;
155 m_filter_special = m_filter->special() ;
157 G_LOG_IF( !m_filter->quiet() ,
"GSmtp::Client::filterDone: client-filter "
158 "[" << m_filter->id() <<
"]: [" << message()->
id().str() <<
"]: "
159 << m_filter->str(Filter::Type::client) ) ;
161 std::string reopen_error ;
163 reopen_error = message()->reopen() ;
166 if( ok && reopen_error.empty() )
168 m_protocol.filterDone( Filter::Result::ok , {} , {} ) ;
172 m_protocol.filterDone( Filter::Result::abandon , {} , {} ) ;
174 else if( !reopen_error.empty() )
176 m_protocol.filterDone( Filter::Result::fail ,
"failed" , reopen_error ) ;
180 m_protocol.filterDone( Filter::Result::fail , m_filter->response() , m_filter->reason() ) ;
184void GSmtp::Client::protocolDone(
const ClientProtocol::DoneInfo & info )
186 G_ASSERT( info.response_code >= -2 ) ;
187 G_DEBUG(
"GSmtp::Client::protocolDone: \"" << info.response <<
"\"" ) ;
189 std::string reason = info.reason.empty() ? info.response : info.reason ;
190 std::string short_reason = ( info.response.empty() || info.reason.empty() ) ? info.response : info.reason ;
191 std::string message_id = message()->id().str() ;
193 if( info.response_code == -1 )
196 short_reason =
"abandoned" ;
198 else if( info.response_code == -2 )
200 messageFail( 550 , reason ) ;
201 short_reason =
"rejected" ;
203 else if( info.response.empty() )
208 else if( info.rejects.empty() )
211 G_ASSERT( !reason.empty() ) ;
213 messageFail( info.response_code , reason ) ;
219 message()->editRecipients( info.rejects ) ;
220 messageFail( info.response_code , reason ) ;
223 m_event_logging_string.clear() ;
226 eventSignal().emit(
"sent" , message_id , short_reason ) ;
227 if( this_.deleted() ) return ;
230 messageDoneSignal().emit( { std::max(0,info.response_code) , info.response , m_filter_special } ) ;
235 m_protocol.finish() ;
239void GSmtp::Client::messageDestroy()
241 message()->destroy() ;
245void GSmtp::Client::messageFail(
int response_code ,
const std::string & reason )
247 message()->fail( reason , response_code ) ;
251bool GSmtp::Client::onReceive(
const char * line_data , std::size_t line_size , std::size_t , std::size_t ,
char )
253 std::string line( line_data , line_size ) ;
257 bool done = m_protocol.apply( line ) ;
258 if( this_.deleted() )
return false ;
269void GSmtp::Client::onDelete(
const std::string & error )
271 G_DEBUG(
"GSmtp::Client::onDelete: error [" << error <<
"]" ) ;
274 if( m_message && hasConnected() )
275 messageFail( 0 , error ) ;
280void GSmtp::Client::onSendComplete()
282 m_protocol.sendComplete() ;
287 if( !msg || !config.log_msgid )
return {} ;
288 return std::string(1U,
'(').append(
G::Str::tail(msg->
id().
str(),
"."_sv,
false)).append(
") ",2U) ;
291std::string_view GSmtp::Client::eventLoggingString() const noexcept
293 if( m_event_logging_string.empty() )
return {} ;
294 return m_event_logging_string ;
An interface used by GAuth::SaslClient to obtain a client id and its authentication secret.
An exception class that is caught separately by GNet::EventEmitter and GNet::TimerList so that onExce...
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
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() override
Destructor.
static std::string eventLoggingString(const GStore::StoredMessage *, const Config &)
Returns an event logging string for the given message.
Client(GNet::EventState, FilterFactoryBase &, const GNet::Location &remote, const GAuth::SaslClientSecrets &, const Config &config)
Constructor.
A factory interface for making GSmtp::Filter message processors.
An interface for processing a message file through a filter.
std::string str() const
Returns the id string.
An abstract interface for messages which have come from the store.
virtual MessageId id() const =0
Returns the message identifier.
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 ...
static std::string tail(std::string_view in, std::size_t pos, std::string_view default_={})
Returns the last part of the string after the given position.
Slot< Args... > slot(TSink &sink, void(TSink::*method)(Args...))
A factory function for Slot objects.
A structure containing GNet::Client configuration parameters.
A structure containing GSmtp::Client configuration parameters.