gsmtpclient.cpp
Go to the documentation of this file.
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 
36 const std::string & GSmtp::Client::crlf()
37 {
38  static const std::string s( "\015\012" ) ;
39  return s ;
40 }
41 
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 
56 {
57  m_protocol.doneSignal().disconnect() ;
58  m_protocol.preprocessorSignal().disconnect() ;
59  m_processor->doneSignal().disconnect() ;
60 }
61 
63 {
64  return m_message_done_signal ;
65 }
66 
68 {
69  G_ASSERT( !store.empty() ) ;
70  G_ASSERT( !connected() ) ; // ie. immediately after construction
71  m_store = &store ;
72 }
73 
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 
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 
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 
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 
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 
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 
153 {
154  if( m_secure_tunnel )
155  {
156  sslConnect() ;
157  }
158  else
159  {
160  doOnConnect() ;
161  }
162 }
163 
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 
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 
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 
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 
246 void GSmtp::Client::messageDestroy()
247 {
248  if( m_message.get() != NULL )
249  {
250  m_message->destroy() ;
251  m_message <<= 0 ;
252  }
253 }
254 
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 
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 
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 
283 {
284  m_protocol.sendDone() ;
285 }
286 
287 // ==
288 
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 
void connect(Slot0 slot)
Definition: gslot.h:147
#define G_LOG_S(expr)
Definition: glog.h:103
void sendMessage(std::auto_ptr< StoredMessage > message)
Starts sending the given message.
Definition: gsmtpclient.cpp:74
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:507
Network classes.
virtual void onConnect()
Final override from GNet::SimpleClient.
virtual bool onReceive(const std::string &)
Final override from GNet::Client.
virtual bool empty() const =0
Returns true if the message store is empty.
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
static void splitIntoFields(const std::string &in, Strings &out, const std::string &seperators, char escape= '\0', bool discard_bogus_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:765
G::Signal1< std::string > & messageDoneSignal()
Returns a signal that indicates that sendMessage() has completed or failed.
Definition: gsmtpclient.cpp:62
The Address class encapsulates an IP transport address.
Definition: gaddress.h:48
Slot0 slot(T &object, void(T::*fn)())
Part of the slot/signal system.
Definition: gslot.h:156
A class that holds a host/service name pair and optionally the results of a name-to-address lookup...
Definition: gresolverinfo.h:48
A structure containing GSmtp::ClientProtocol configuration parameters.
Config(std::string, unsigned int, GNet::Address, ClientProtocol::Config, unsigned int, unsigned int, bool)
virtual ~Client()
Destructor.
Definition: gsmtpclient.cpp:55
virtual void onSecure(const std::string &)
Final override from GNet::SocketProtocol.
G::Signal2< std::string, int > & doneSignal()
Returns a signal that is raised once the protocol has finished with a given message.
Client(const GNet::ResolverInfo &remote, const GAuth::Secrets &secrets, Config config)
Constructor.
Definition: gsmtpclient.cpp:42
#define G_ASSERT(test)
Definition: gassert.h:30
A class which allows SMTP messages (envelope+content) to be stored and retrieved. ...
Definition: gmessagestore.h:45
A simple interface to a store of secrets as used in authentication.
Definition: gsecrets.h:44
A factory for message processors.
#define G_LOG(expr)
Definition: glog.h:98
#define G_DEBUG(expr)
Definition: glog.h:95
A structure containing GSmtp::Client configuration parameters.
Definition: gsmtpclient.h:61
A class which acts as an SMTP client, extracting messages from a message store and forwarding them to...
Definition: gsmtpclient.h:55
virtual void onDelete(const std::string &, bool)
Final override from GNet::HeapClient.
virtual void onSendComplete()
Final override from GNet::BufferedClient.
void sendMessagesFrom(MessageStore &store)
Sends all messages from the given message store once connected.
Definition: gsmtpclient.cpp:67
void connect(Slot2< P1, P2 > slot)
Definition: gslot.h:295
G::Signal0 & preprocessorSignal()
Returns a signal that is raised when the protocol needs to do message preprocessing.