E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gsmtpclient.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 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 // gsmtpclient.cpp
19 //
20
21 #include "gdef.h"
22 #include "gsmtp.h"
23 #include "glocal.h"
24 #include "gfile.h"
25 #include "gstr.h"
26 #include "gtimer.h"
27 #include "gsmtpclient.h"
28 #include "gresolver.h"
29 #include "gfilterfactory.h"
30 #include "gresolver.h"
31 #include "gassert.h"
32 #include "glog.h"
33
34 GSmtp::Client::Client( const GNet::Location & remote , const GAuth::Secrets & secrets , Config config ) :
35 GNet::Client(remote,config.connection_timeout,0U, // the protocol does the response timeout-ing
36 config.secure_connection_timeout,GNet::LineBufferConfig::smtp(),config.bind_local_address,
37 config.local_address) ,
38 m_store(nullptr) ,
39 m_filter(FilterFactory::newFilter(*this,false,config.filter_address,config.filter_timeout)) ,
40 m_protocol(*this,*this,secrets,config.client_protocol_config,config.secure_tunnel) ,
41 m_secure_tunnel(config.secure_tunnel) ,
42 m_message_count(0U)
43 {
44 m_protocol.doneSignal().connect( G::Slot::slot(*this,&Client::protocolDone) ) ;
45 m_protocol.filterSignal().connect( G::Slot::slot(*this,&Client::filterStart) ) ;
46 m_filter->doneSignal().connect( G::Slot::slot(*this,&Client::filterDone) ) ;
47 }
48
49 GSmtp::Client::~Client()
50 {
51 m_protocol.doneSignal().disconnect() ;
52 m_protocol.filterSignal().disconnect() ;
53 m_filter->doneSignal().disconnect() ;
54 }
55
56 G::Slot::Signal1<std::string> & GSmtp::Client::messageDoneSignal()
57 {
58 return m_message_done_signal ;
59 }
60
61 void GSmtp::Client::sendMessagesFrom( MessageStore & store )
62 {
63 G_ASSERT( !connected() ) ; // ie. immediately after construction
64 m_store = &store ;
65 }
66
67 void GSmtp::Client::sendMessage( unique_ptr<StoredMessage> message )
68 {
69 G_ASSERT( m_message.get() == nullptr ) ;
70 m_message.reset( message.release() ) ;
71 if( connected() )
72 {
73 start( *m_message.get() ) ;
74 }
75 }
76
77 bool GSmtp::Client::protocolSend( const std::string & line , size_t offset , bool go_secure )
78 {
79 bool rc = send( line , offset ) ; // BufferedClient::send()
80 if( go_secure )
81 secureConnect() ; // GNet::SocketProtocol
82 return rc ;
83 }
84
85 void GSmtp::Client::filterStart()
86 {
87 G_ASSERT( m_message.get() != nullptr ) ;
88 if( m_message.get() )
89 {
90 if( m_filter->id() != "none" )
91 {
92 G_LOG( "GSmtp::Client::filterStart: client filter: [" << m_filter->id() << "]" ) ;
93 }
94 m_filter->start( m_message->location() ) ;
95 }
96 }
97
98 void GSmtp::Client::filterDone( int filter_result )
99 {
100 G_ASSERT( m_message.get() != nullptr ) ;
101
102 const bool ok = filter_result == 0 ;
103 const bool abandon = filter_result == 1 ;
104 const bool stop_scanning = m_filter->special() ;
105 G_ASSERT( m_filter->reason().empty() == (ok || abandon) ) ;
106
107 if( ok )
108 {
109 // re-read the envelope after the filtering
110 m_message->sync( !m_filter->simple() ) ;
111 }
112
113 if( stop_scanning )
114 {
115 G_DEBUG( "GSmtp::Client::filterDone: making this the last message" ) ;
116 m_iter.last() ; // so next next() returns nothing
117 }
118
119 if( m_filter->id() != "none" )
120 {
121 G_LOG( "GSmtp::Client::filterDone: client filter done: " << m_filter->str(false) ) ;
122 }
123
124 // pass the event on to the client protocol
125 if( ok )
126 {
127 m_protocol.filterDone( true , std::string() , std::string() ) ;
128 }
129 else if( abandon )
130 {
131 m_protocol.filterDone( false , std::string() , std::string() ) ; // protocolDone(-1)
132 }
133 else
134 {
135 m_protocol.filterDone( false , m_filter->response() , m_filter->reason() ) ; // protocolDone(-2)
136 }
137 }
138
139 void GSmtp::Client::onSecure( const std::string & certificate )
140 {
141 if( m_secure_tunnel )
142 {
143 doOnConnect() ;
144 }
145 else
146 {
147 m_protocol.secure() ;
148 }
149 }
150
151 void GSmtp::Client::logCertificate( const std::string & certificate )
152 {
153 if( !certificate.empty() )
154 {
155 static std::string previous ;
156 if( certificate != previous )
157 {
158 previous = certificate ;
159 G::StringArray lines ;
160 lines.reserve( 30U ) ;
161 G::Str::splitIntoFields( certificate , lines , "\n" ) ;
162 for( G::StringArray::iterator p = lines.begin() ; p != lines.end() ; ++p )
163 {
164 if( !(*p).empty() )
165 G_LOG( "GSmtp::Client: certificate: " << (*p) ) ;
166 }
167 }
168 }
169 }
170
171 void GSmtp::Client::onConnect()
172 {
173 if( m_secure_tunnel )
174 {
175 secureConnect() ; // GNet::SocketProtocol
176 }
177 else
178 {
179 doOnConnect() ;
180 }
181 }
182
183 void GSmtp::Client::doOnConnect()
184 {
185 G_LOG_S( "GSmtp::Client::doOnConnect: smtp connection to " << peerAddress().second.displayString() ) ;
186 if( m_store != nullptr )
187 {
188 // initialise the message iterator
189 m_iter = m_store->iterator( true ) ;
190
191 // start sending the first message
192 bool started = sendNext() ;
193 if( !started )
194 {
195 G_DEBUG( "GSmtp::Client::doOnConnect: deleting" ) ;
196 doDelete( std::string() ) ;
197 }
198 }
199 else
200 {
201 G_ASSERT( m_message.get() != nullptr ) ;
202 start( *m_message.get() ) ;
203 }
204 }
205
206 bool GSmtp::Client::sendNext()
207 {
208 m_message.reset() ;
209
210 // fetch the next message from the store, or return false if none
211 {
212 unique_ptr<StoredMessage> message( m_iter.next() ) ;
213 if( message.get() == nullptr )
214 {
215 if( m_message_count != 0U )
216 G_LOG( "GSmtp::Client: no more messages to send" ) ;
217 m_message_count = 0U ;
218 return false ;
219 }
220 m_message.reset( message.release() ) ;
221 }
222
223 start( *m_message.get() ) ;
224 return true ;
225 }
226
227 void GSmtp::Client::start( StoredMessage & message )
228 {
229 m_message_count++ ;
230 eventSignal().emit( "sending" , message.name() ) ;
231
232 m_protocol.start( message.from() , message.to() ,
233 message.eightBit() , message.fromAuthOut() ,
234 unique_ptr<std::istream>(message.extractContentStream()) ) ;
235 }
236
237 void GSmtp::Client::protocolDone( int response_code , std::string response , std::string reason )
238 {
239 G_DEBUG( "GSmtp::Client::protocolDone: \"" << response << "\"" ) ;
240 if( ! response.empty() )
241 response = "smtp client failure: " + response ;
242
243 if( response_code == -1 )
244 {
245 // we called protocol.filterDone(false,""), so abandon
246 // this message if eg. already deleted
247 }
248 else if( response_code == -2 )
249 {
250 // we called protocol.filterDone(false,"..."), so fail this
251 // message (550 => "action not taken, eg. for policy reasons")
252 messageFail( 550 , reason.empty() ? response : reason ) ;
253 }
254 else if( response.empty() )
255 {
256 // forwarded ok, so delete our copy
257 messageDestroy() ;
258 }
259 else
260 {
261 // eg. rejected by the server, so fail the message
262 m_filter->cancel() ;
263 messageFail( response_code , reason.empty() ? response : reason ) ;
264 }
265
266 if( m_store != nullptr )
267 {
268 if( !sendNext() )
269 {
270 m_protocol.finish() ; // send quit
271 socket().shutdown() ;
272 finish() ; // GNet::HeapClient
273 }
274 }
275 else
276 {
277 messageDoneSignal().emit( response ) ;
278 }
279 }
280
281 void GSmtp::Client::messageDestroy()
282 {
283 if( m_message.get() != nullptr )
284 {
285 m_message->destroy() ;
286 m_message.reset() ;
287 }
288 }
289
290 void GSmtp::Client::messageFail( int response_code , const std::string & reason )
291 {
292 if( m_message.get() != nullptr )
293 {
294 m_message->fail( reason , response_code ) ;
295 m_message.reset() ;
296 }
297 }
298
299 bool GSmtp::Client::onReceive( const char * line_data , size_t line_size , size_t )
300 {
301 std::string line( line_data , line_size ) ;
302 G_DEBUG( "GSmtp::Client::onReceive: [" << G::Str::printable(line) << "]" ) ;
303 bool done = m_protocol.apply( line ) ;
304 if( done )
305 doDelete( std::string() ) ;
306 return !done ; // discard line-buffer input if done
307 }
308
309 void GSmtp::Client::onDelete( const std::string & error )
310 {
311 G_DEBUG( "GSmtp::Client::onDelete: error [" << error << "]" ) ;
312 if( ! error.empty() )
313 {
314 G_LOG( "GSmtp::Client: smtp client error: " << error ) ; // was warning
315 messageFail( 0 , error ) ; // if not already failed or destroyed
316 }
317 m_message.reset() ;
318 }
319
320 void GSmtp::Client::onSendComplete()
321 {
322 m_protocol.sendDone() ;
323 }
324
325 // ==
326
327 GSmtp::Client::Config::Config( std::string filter_address_ , unsigned int filter_timeout_ ,
328 bool bind_local_address , const GNet::Address & local_address ,
329 const ClientProtocol::Config & protocol_config , unsigned int connection_timeout_ ,
330 unsigned int secure_connection_timeout_ , bool secure_tunnel_ ) :
331 filter_address(filter_address_) ,
332 filter_timeout(filter_timeout_) ,
333 bind_local_address(bind_local_address) ,
334 local_address(local_address) ,
335 client_protocol_config(protocol_config) ,
336 connection_timeout(connection_timeout_) ,
337 secure_connection_timeout(secure_connection_timeout_) ,
338 secure_tunnel(secure_tunnel_)
339 {
340 }
341
342 /// \file gsmtpclient.cpp
GSmtp::Client::onDelete
virtual void onDelete(const std::string &) override
Override from GNet::HeapClient.
Definition: gsmtpclient.cpp:309
G::Str::printable
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:701
gfilterfactory.h
GNet
Network classes.
Definition: gaddress.h:29
GSmtp::Client::onSecure
virtual void onSecure(const std::string &) override
Override from GNet::SocketProtocol.
Definition: gsmtpclient.cpp:139
glog.h
GSmtp::ClientProtocol::filterSignal
G::Slot::Signal0 & filterSignal()
Returns a signal that is raised when the protocol needs to do message filtering.
Definition: gclientprotocol.cpp:683
GSmtp::Client::sendMessage
void sendMessage(unique_ptr< StoredMessage > message)
Starts sending the given message.
Definition: gsmtpclient.cpp:67
GNet::Address
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:54
GSmtp::Client::messageDoneSignal
G::Slot::Signal1< std::string > & messageDoneSignal()
Returns a signal that indicates that sendMessage() has completed or failed.
Definition: gsmtpclient.cpp:56
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
GSmtp::ClientProtocol::doneSignal
G::Slot::Signal3< int, std::string, std::string > & doneSignal()
Returns a signal that is raised once the protocol has finished with a given message.
Definition: gclientprotocol.cpp:678
GSmtp::ClientProtocol::Config
A structure containing GSmtp::ClientProtocol configuration parameters.
Definition: gclientprotocol.h:183
GSmtp::Client::onConnect
virtual void onConnect() override
Override from GNet::SimpleClient.
Definition: gsmtpclient.cpp:171
GSmtp::Client::~Client
virtual ~Client()
Destructor.
Definition: gsmtpclient.cpp:49
GNet::Location
A class that holds a host/service name pair and the preferred address family (if any), and also the results of a name-to-address lookup, ie.
Definition: glocation.h:50
GSmtp::Client::Client
Client(const GNet::Location &remote, const GAuth::Secrets &secrets, Config config)
Constructor.
Definition: gsmtpclient.cpp:34
GSmtp::FilterFactory
A factory for message processors.
Definition: gfilterfactory.h:39
gstr.h
gassert.h
GSmtp::MessageStore
A class which allows SMTP messages to be stored and retrieved.
Definition: gmessagestore.h:43
GAuth::Secrets
A simple interface for a store of secrets used in authentication.
Definition: gsecrets.h:40
gtimer.h
G::Slot::slot
Slot0 slot(T &object, void(T::*fn)())
A slot factory function overloaded for a zero-parameter callback.
Definition: gslot.h:209
GSmtp::Client::onReceive
virtual bool onReceive(const char *, size_t, size_t) override
Override from GNet::Client.
Definition: gsmtpclient.cpp:299
GSmtp::Client::onSendComplete
virtual void onSendComplete() override
Override from GNet::BufferedClient.
Definition: gsmtpclient.cpp:320
gfile.h
GSmtp::Client::Config
A structure containing GSmtp::Client configuration parameters.
Definition: gsmtpclient.h:55
GSmtp::Client
A class which acts as an SMTP client, extracting messages from a message store and forwarding them to...
Definition: gsmtpclient.h:52
glocal.h
GSmtp::Client::sendMessagesFrom
void sendMessagesFrom(MessageStore &store)
Sends all messages from the given message store once connected.
Definition: gsmtpclient.cpp:61
gsmtp.h
gsmtpclient.h
G::Str::splitIntoFields
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &separators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:975
G::Slot::Signal1< std::string >
gresolver.h