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 // gsmtpclient.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gsmtp.h"
24 #include "glocal.h"
25 #include "gfile.h"
26 #include "gstr.h"
27 #include "gmemory.h"
28 #include "gtimer.h"
29 #include "gsmtpclient.h"
30 #include "gresolver.h"
31 #include "gprocessorfactory.h"
32 #include "gresolver.h"
33 #include "gassert.h"
34 #include "glog.h"
35 
crlf()36 const std::string & GSmtp::Client::crlf()
37 {
38 	static const std::string s( "\015\012" ) ;
39 	return s ;
40 }
41 
Client(const GNet::ResolverInfo & remote,const GAuth::Secrets & secrets,Config config)42 GSmtp::Client::Client( const GNet::ResolverInfo & remote , const GAuth::Secrets & secrets , Config config ) :
43 	GNet::Client(remote,config.connection_timeout,0U, // the protocol does the response timeout-ing
44 		config.secure_connection_timeout,crlf(),config.local_address,false) ,
45 	m_store(NULL) ,
46 	m_processor(ProcessorFactory::newProcessor(config.processor_address,config.processor_timeout)) ,
47 	m_protocol(*this,secrets,config.client_protocol_config) ,
48 	m_secure_tunnel(config.secure_tunnel)
49 {
50 	m_protocol.doneSignal().connect( G::slot(*this,&Client::protocolDone) ) ;
51 	m_protocol.preprocessorSignal().connect( G::slot(*this,&Client::preprocessorStart) ) ;
52 	m_processor->doneSignal().connect( G::slot(*this,&Client::preprocessorDone) ) ;
53 }
54 
~Client()55 GSmtp::Client::~Client()
56 {
57 	m_protocol.doneSignal().disconnect() ;
58 	m_protocol.preprocessorSignal().disconnect() ;
59 	m_processor->doneSignal().disconnect() ;
60 }
61 
messageDoneSignal()62 G::Signal1<std::string> & GSmtp::Client::messageDoneSignal()
63 {
64 	return m_message_done_signal ;
65 }
66 
sendMessagesFrom(MessageStore & store)67 void GSmtp::Client::sendMessagesFrom( MessageStore & store )
68 {
69 	G_ASSERT( !store.empty() ) ;
70 	G_ASSERT( !connected() ) ; // ie. immediately after construction
71 	m_store = &store ;
72 }
73 
sendMessage(std::auto_ptr<StoredMessage> message)74 void GSmtp::Client::sendMessage( std::auto_ptr<StoredMessage> message )
75 {
76 	G_ASSERT( m_message.get() == NULL ) ;
77 	m_message = message ;
78 	if( connected() )
79 	{
80 		start( *m_message.get() ) ;
81 	}
82 }
83 
protocolSend(const std::string & line,size_t offset,bool go_secure)84 bool GSmtp::Client::protocolSend( const std::string & line , size_t offset , bool go_secure )
85 {
86 	bool rc = send( line , offset ) ; // BufferedClient::send()
87 	if( go_secure )
88 		sslConnect() ;
89 	return rc ;
90 }
91 
preprocessorStart()92 void GSmtp::Client::preprocessorStart()
93 {
94 	G_ASSERT( m_message.get() != NULL ) ;
95 	if( m_message.get() )
96 		m_processor->start( m_message->location() ) ;
97 }
98 
preprocessorDone(bool ok)99 void GSmtp::Client::preprocessorDone( bool ok )
100 {
101 	G_ASSERT( m_message.get() != NULL ) ;
102 
103 	// (different cancelled/repoll semantics on the client-side)
104 	bool ignore_this = !ok && m_processor->cancelled() && !m_processor->repoll() ;
105 	bool break_after = !ok && m_processor->cancelled() && m_processor->repoll() ;
106 
107 	if( ok || break_after )
108 		m_message->sync() ; // re-read it after the preprocessing
109 
110 	if( break_after )
111 	{
112 		G_DEBUG( "GSmtp::Client::preprocessorDone: making this the last message" ) ;
113 		m_iter.last() ; // so next next() returns nothing
114 	}
115 
116 	// pass the event on to the protocol
117 	m_protocol.preprocessorDone( ok || break_after ,
118 		ok || ignore_this || break_after ? std::string() : m_processor->text() ) ;
119 }
120 
onSecure(const std::string & certificate)121 void GSmtp::Client::onSecure( const std::string & certificate )
122 {
123 	if( m_secure_tunnel )
124 	{
125 		doOnConnect() ;
126 	}
127 	else
128 	{
129 		m_protocol.secure() ;
130 	}
131 }
132 
logCertificate(const std::string & certificate)133 void GSmtp::Client::logCertificate( const std::string & certificate )
134 {
135 	if( !certificate.empty() )
136 	{
137 		static std::string previous ;
138 		if( certificate != previous )
139 		{
140 			previous = certificate ;
141 			G::Strings lines ;
142 			G::Str::splitIntoFields( certificate , lines , "\n" ) ;
143 			for( G::Strings::iterator p = lines.begin() ; p != lines.end() ; ++p )
144 			{
145 				if( !(*p).empty() )
146 					G_LOG( "GSmtp::Client: certificate: " << (*p) ) ;
147 			}
148 		}
149 	}
150 }
151 
onConnect()152 void GSmtp::Client::onConnect()
153 {
154 	if( m_secure_tunnel )
155 	{
156 		sslConnect() ;
157 	}
158 	else
159 	{
160 		doOnConnect() ;
161 	}
162 }
163 
doOnConnect()164 void GSmtp::Client::doOnConnect()
165 {
166 	if( m_store != NULL )
167 	{
168 		m_iter = m_store->iterator(true) ;
169 		if( !sendNext() )
170 		{
171 			G_DEBUG( "GSmtp::Client::onConnect: deleting" ) ;
172 			doDelete( std::string() ) ;
173 		}
174 	}
175 	else
176 	{
177 		G_ASSERT( m_message.get() != NULL ) ;
178 		start( *m_message.get() ) ;
179 	}
180 }
181 
sendNext()182 bool GSmtp::Client::sendNext()
183 {
184 	m_message <<= 0 ;
185 
186 	// fetch the next message from the store, or return false if none
187 	{
188 		std::auto_ptr<StoredMessage> message( m_iter.next() ) ;
189 		if( message.get() == NULL )
190 		{
191 			G_LOG_S( "GSmtp::Client: no more messages to send" ) ;
192 			return false ;
193 		}
194 		m_message = message ;
195 	}
196 
197 	start( *m_message.get() ) ;
198 	return true ;
199 }
200 
start(StoredMessage & message)201 void GSmtp::Client::start( StoredMessage & message )
202 {
203 	eventSignal().emit( "sending" , message.name() ) ;
204 
205 	// prepare the remote server name -- use the dns canonical name if available
206 	std::string server_name = resolverInfo().name() ;
207 	if( server_name.empty() )
208 		server_name = resolverInfo().host() ;
209 
210 	std::auto_ptr<std::istream> content_stream( message.extractContentStream() ) ;
211 	m_protocol.start( message.from() , message.to() , message.eightBit() ,
212 		message.authentication() , server_name , content_stream ) ;
213 }
214 
protocolDone(std::string reason,int reason_code)215 void GSmtp::Client::protocolDone( std::string reason , int reason_code )
216 {
217 	G_DEBUG( "GSmtp::Client::protocolDone: \"" << reason << "\"" ) ;
218 	if( ! reason.empty() )
219 		reason = std::string("smtp client failure: ") + reason ;
220 
221 	if( reason.empty() )
222 	{
223 		if( reason_code != 1 ) // TODO magic number
224 			messageDestroy() ;
225 	}
226 	else
227 	{
228 		m_processor->abort() ;
229 		messageFail( reason , reason_code ) ;
230 	}
231 
232 	if( m_store != NULL )
233 	{
234 		if( !sendNext() )
235 		{
236 			G_DEBUG( "GSmtp::Client::protocolDone: deleting" ) ;
237 			doDelete( std::string() ) ;
238 		}
239 	}
240 	else
241 	{
242 		messageDoneSignal().emit( reason ) ;
243 	}
244 }
245 
messageDestroy()246 void GSmtp::Client::messageDestroy()
247 {
248 	if( m_message.get() != NULL )
249 	{
250 		m_message->destroy() ;
251 		m_message <<= 0 ;
252 	}
253 }
254 
messageFail(const std::string & reason,int reason_code)255 void GSmtp::Client::messageFail( const std::string & reason , int reason_code )
256 {
257 	if( m_message.get() != NULL )
258 	{
259 		m_message->fail( reason , reason_code ) ;
260 		m_message <<= 0 ;
261 	}
262 }
263 
onReceive(const std::string & line)264 bool GSmtp::Client::onReceive( const std::string & line )
265 {
266 	G_DEBUG( "GSmtp::Client::onReceive: [" << G::Str::printable(line) << "]" ) ;
267 	bool done = m_protocol.apply( line ) ;
268 	return !done ; // if the protocol is done don't apply() any more
269 }
270 
onDelete(const std::string & error,bool)271 void GSmtp::Client::onDelete( const std::string & error , bool )
272 {
273 	G_DEBUG( "GSmtp::Client::onDelete: error [" << error << "]" ) ;
274 	if( ! error.empty() )
275 	{
276 		G_LOG( "GSmtp::Client: smtp client error: \"" << error << "\"" ) ; // was warning
277 		messageFail( error , 0 ) ; // if not already failed or destroyed
278 	}
279 	m_message <<= 0 ;
280 }
281 
onSendComplete()282 void GSmtp::Client::onSendComplete()
283 {
284 	m_protocol.sendDone() ;
285 }
286 
287 // ==
288 
Config(std::string processor_address_,unsigned int processor_timeout_,GNet::Address address,ClientProtocol::Config protocol_config,unsigned int connection_timeout_,unsigned int secure_connection_timeout_,bool secure_tunnel_)289 GSmtp::Client::Config::Config( std::string processor_address_ , unsigned int processor_timeout_ ,
290 	GNet::Address address , ClientProtocol::Config protocol_config , unsigned int connection_timeout_ ,
291 	unsigned int secure_connection_timeout_ , bool secure_tunnel_ ) :
292 		processor_address(processor_address_) ,
293 		processor_timeout(processor_timeout_) ,
294 		local_address(address) ,
295 		client_protocol_config(protocol_config) ,
296 		connection_timeout(connection_timeout_) ,
297 		secure_connection_timeout(secure_connection_timeout_) ,
298 		secure_tunnel(secure_tunnel_)
299 {
300 }
301 
302 /// \file gsmtpclient.cpp
303