gstoredfile.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 // gstoredfile.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gsmtp.h"
23 #include "gfilestore.h"
24 #include "gstoredfile.h"
25 #include "gmemory.h"
26 #include "gxtext.h"
27 #include "gfile.h"
28 #include "gstr.h"
29 #include "glog.h"
30 #include "gassert.h"
31 #include <fstream>
32 
33 GSmtp::StoredFile::StoredFile( FileStore & store , const G::Path & envelope_path ) :
34  m_store(store) ,
35  m_envelope_path(envelope_path) ,
36  m_eight_bit(false) ,
37  m_errors(0U) ,
38  m_locked(false)
39 {
40  m_name = m_envelope_path.basename() ;
41  size_t pos = m_name.rfind(".envelope") ;
42  if( pos != std::string::npos ) m_name.erase( pos ) ;
43  G_DEBUG( "StoredFile: \"" << m_name << "\"" ) ;
44 }
45 
47 {
48  try
49  {
50  unlock() ;
51  }
52  catch(...) // dtor
53  {
54  }
55 }
56 
57 std::string GSmtp::StoredFile::name() const
58 {
59  return m_name ;
60 }
61 
62 std::string GSmtp::StoredFile::location() const
63 {
64  return contentPath().str() ;
65 }
66 
68 {
69  return m_eight_bit ;
70 }
71 
73 {
74  readEnvelopeCore( true ) ;
75 }
76 
77 bool GSmtp::StoredFile::readEnvelope( std::string & reason , bool check )
78 {
79  try
80  {
81  readEnvelopeCore( check ) ;
82  return true ;
83  }
84  catch( std::exception & e ) // invalid file in store
85  {
86  reason = e.what() ;
87  return false ;
88  }
89 }
90 
91 void GSmtp::StoredFile::readEnvelopeCore( bool check )
92 {
93  FileReader claim_reader ;
94  std::ifstream stream( m_envelope_path.str().c_str() , std::ios_base::binary | std::ios_base::in ) ;
95  if( ! stream.good() )
96  throw OpenError( m_envelope_path.str() ) ;
97 
98  readFormat( stream ) ;
99  readFlag( stream ) ;
100  readFrom( stream ) ;
101  readToList( stream ) ;
102  readAuthentication( stream ) ;
103  readClientSocketAddress( stream ) ;
104  if( m_format == FileStore::format() )
105  {
106  readClientSocketName( stream ) ;
107  readClientCertificate( stream ) ;
108  }
109  readEnd( stream ) ;
110 
111  if( check && m_to_remote.size() == 0U )
112  throw NoRecipients() ;
113 
114  if( ! stream.good() )
115  throw StreamError() ;
116 
117  readReasons( stream ) ;
118 }
119 
120 void GSmtp::StoredFile::readFormat( std::istream & stream )
121 {
122  std::string format_line = getline(stream) ;
123  m_format = value(format_line,"Format") ;
124  if( ! FileStore::knownFormat(m_format) )
125  throw InvalidFormat( m_format ) ;
126 }
127 
128 void GSmtp::StoredFile::readFlag( std::istream & stream )
129 {
130  std::string content_line = getline(stream) ;
131  m_eight_bit = value(content_line,"Content") == "8bit" ;
132 }
133 
134 void GSmtp::StoredFile::readFrom( std::istream & stream )
135 {
136  m_from = value(getline(stream),"From") ;
137  G_DEBUG( "GSmtp::StoredFile::readFrom: from \"" << m_from << "\"" ) ;
138 }
139 
140 void GSmtp::StoredFile::readToList( std::istream & stream )
141 {
142  m_to_local.clear() ;
143  m_to_remote.clear() ;
144 
145  std::string to_count_line = getline(stream) ;
146  unsigned int to_count = G::Str::toUInt( value(to_count_line,"ToCount") ) ;
147 
148  for( unsigned int i = 0U ; i < to_count ; i++ )
149  {
150  std::string to_line = getline(stream) ;
151  bool is_local = to_line.find(FileStore::x()+"To-Local") == 0U ;
152  bool is_remote = to_line.find(FileStore::x()+"To-Remote") == 0U ;
153  if( ! is_local && ! is_remote )
154  throw InvalidTo(to_line) ;
155 
156  G_DEBUG( "GSmtp::StoredFile::readToList: to "
157  "[" << (i+1U) << "/" << to_count << "] "
158  "(" << (is_local?"local":"remote") << ") "
159  << "\"" << value(to_line) << "\"" ) ;
160 
161  if( is_local )
162  m_to_local.push_back( value(to_line) ) ;
163  else
164  m_to_remote.push_back( value(to_line) ) ;
165  }
166 }
167 
168 void GSmtp::StoredFile::readAuthentication( std::istream & stream )
169 {
170  m_authentication = G::Xtext::decode(value(getline(stream),"Authentication")) ;
171 }
172 
173 void GSmtp::StoredFile::readClientSocketAddress( std::istream & stream )
174 {
175  m_client_socket_address = value(getline(stream),"Client") ;
176 }
177 
178 void GSmtp::StoredFile::readClientSocketName( std::istream & stream )
179 {
180  m_client_socket_name = G::Xtext::decode(value(getline(stream),"ClientName")) ;
181 }
182 
183 void GSmtp::StoredFile::readClientCertificate( std::istream & stream )
184 {
185  m_client_certificate = G::Xtext::decode(value(getline(stream),"ClientCertificate")) ;
186 }
187 
188 void GSmtp::StoredFile::readEnd( std::istream & stream )
189 {
190  std::string end = getline(stream) ;
191  if( end.find(FileStore::x()+"End") != 0U )
192  throw NoEnd() ;
193 }
194 
195 void GSmtp::StoredFile::readReasons( std::istream & stream )
196 {
197  m_errors = 0U ;
198  while( stream.good() )
199  {
200  std::string reason = getline(stream) ;
201  if( reason.find(FileStore::x()+"Reason") == 0U )
202  m_errors++ ;
203  }
204 }
205 
206 bool GSmtp::StoredFile::openContent( std::string & reason )
207 {
208  try
209  {
210  FileReader claim_reader ;
211  G::Path content_path = contentPath() ;
212  G_DEBUG( "GSmtp::FileStore::openContent: \"" << content_path << "\"" ) ;
213  std::auto_ptr<std::istream> stream( new std::ifstream(
214  content_path.str().c_str() , std::ios_base::in | std::ios_base::binary ) ) ;
215  if( !stream->good() )
216  {
217  reason = "cannot open content file" ;
218  return false ;
219  }
220 
221  G_DEBUG( "GSmtp::MessageStore: processing envelope \"" << m_envelope_path.basename() << "\"" ) ;
222  G_DEBUG( "GSmtp::MessageStore: processing content \"" << content_path.basename() << "\"" ) ;
223 
224  m_content = stream ;
225  return true ;
226  }
227  catch( std::exception & e ) // invalid file in store
228  {
229  G_DEBUG( "GSmtp::FileStore: exception: " << e.what() ) ;
230  reason = e.what() ;
231  return false ;
232  }
233 }
234 
235 std::string GSmtp::StoredFile::getline( std::istream & stream ) const
236 {
237  return G::Str::readLineFrom( stream , crlf() ) ;
238 }
239 
240 std::string GSmtp::StoredFile::value( const std::string & s , const std::string & key ) const
241 {
242  size_t pos = s.find(":") ;
243  if( pos == std::string::npos )
244  throw MessageStore::FormatError(key) ;
245 
246  if( !key.empty() )
247  {
248  size_t key_pos = s.find(key) ;
249  if( key_pos == std::string::npos || (key_pos+key.length()) != pos )
250  throw MessageStore::FormatError(key) ;
251  }
252 
253  return s.substr(pos+2U) ;
254 }
255 
256 const std::string & GSmtp::StoredFile::crlf()
257 {
258  static const std::string s( "\015\012" ) ;
259  return s ;
260 }
261 
263 {
264  const G::Path src = m_envelope_path ;
265  const G::Path dst( src.str() + ".busy" ) ;
266  bool ok = false ;
267  {
268  FileWriter claim_writer ;
269  ok = G::File::rename( src , dst , G::File::NoThrow() ) ;
270  }
271  if( ok )
272  {
273  G_LOG( "GSmtp::StoredMessage: locking file \"" << src.basename() << "\"" ) ;
274  m_envelope_path = dst ;
275  m_old_envelope_path = src ;
276  m_locked = true ;
277  }
278  m_store.updated() ;
279  return ok ;
280 }
281 
282 void GSmtp::StoredFile::unlock()
283 {
284  if( m_locked )
285  {
286  G_LOG( "GSmtp::StoredMessage: unlocking file \"" << m_envelope_path.basename() << "\"" ) ;
287  {
288  FileWriter claim_writer ;
289  G::File::rename( m_envelope_path , m_old_envelope_path ) ;
290  }
291  m_envelope_path = m_old_envelope_path ;
292  m_locked = false ;
293  m_store.updated() ;
294  }
295 }
296 
297 void GSmtp::StoredFile::fail( const std::string & reason , int reason_code )
298 {
299  if( G::File::exists(m_envelope_path) ) // client-side preprocessing may have removed it
300  {
301  addReason( m_envelope_path , reason , reason_code ) ;
302 
303  G::Path bad_path = badPath( m_envelope_path ) ;
304  G_LOG_S( "GSmtp::StoredMessage: failing file: "
305  << "\"" << m_envelope_path.basename() << "\" -> "
306  << "\"" << bad_path.basename() << "\"" ) ;
307 
308  FileWriter claim_writer ;
309  G::File::rename( m_envelope_path , bad_path , G::File::NoThrow() ) ;
310  }
311 }
312 
314 {
315  G_DEBUG( "GSmtp::StoredMessage: unfailing file: " << m_envelope_path ) ;
316  if( m_envelope_path.extension() == "bad" )
317  {
318  G::Path dst = m_envelope_path ;
319  dst.removeExtension() ;
320  bool ok = false ;
321  {
322  FileWriter claim_writer ;
323  ok = G::File::rename( m_envelope_path , dst , G::File::NoThrow() ) ;
324  }
325  if( ok )
326  {
327  G_LOG( "GSmtp::StoredMessage: unfailed file: "
328  << "\"" << m_envelope_path.basename() << "\" -> "
329  << "\"" << dst.basename() << "\"" ) ;
330  m_envelope_path = dst ;
331  }
332  else
333  {
334  G_WARNING( "GSmtp::StoredMessage: failed to unfail file: \"" << m_envelope_path << "\"" ) ;
335  }
336  }
337 }
338 
339 void GSmtp::StoredFile::addReason( const G::Path & path , const std::string & reason , int reason_code )
340 {
341  FileWriter claim_writer ;
342  std::ofstream file( path.str().c_str() ,
343  std::ios_base::binary | std::ios_base::app ) ; // "app", not "ate", for win32
344  file << FileStore::x() << "Reason: " << reason << crlf() ;
345  file << FileStore::x() << "ReasonCode:" ; if( reason_code ) file << " " << reason_code ; file << crlf() ;
346 }
347 
348 G::Path GSmtp::StoredFile::badPath( G::Path busy_path )
349 {
350  busy_path.removeExtension() ; // "foo.envelope.busy" -> "foo.envelope"
351  return G::Path( busy_path.str() + ".bad" ) ; // "foo.envelope.bad"
352 }
353 
355 {
356  G_LOG( "GSmtp::StoredMessage: deleting file: \"" << m_envelope_path.basename() << "\"" ) ;
357  {
358  FileWriter claim_writer ;
359  G::File::remove( m_envelope_path , G::File::NoThrow() ) ;
360  }
361 
362  G::Path content_path = contentPath() ;
363  G_LOG( "GSmtp::StoredMessage: deleting file: \"" << content_path.basename() << "\"" ) ;
364  m_content <<= 0 ; // close it first (but not much good if it's been extracted already)
365  {
366  FileWriter claim_writer ;
367  G::File::remove( content_path , G::File::NoThrow() ) ;
368  }
369 }
370 
371 const std::string & GSmtp::StoredFile::from() const
372 {
373  return m_from ;
374 }
375 
377 {
378  return m_to_remote ;
379 }
380 
381 std::auto_ptr<std::istream> GSmtp::StoredFile::extractContentStream()
382 {
383  G_ASSERT( m_content.get() != NULL ) ; // (a stronger assertion would be that we own it too)
384  return m_content ;
385 }
386 
387 G::Path GSmtp::StoredFile::contentPath() const
388 {
389  std::string s = m_envelope_path.str() ;
390  std::string e( ".envelope" ) ; // also works for ".envelope.bad" etc.
391  size_t pos = s.rfind( e ) ;
392  if( pos == std::string::npos ) throw InvalidFilename(s) ;
393  s.erase( pos ) ;
394  s.append( ".content" ) ;
395  return G::Path( s ) ;
396 }
397 
399 {
400  return m_to_remote.size() ;
401 }
402 
404 {
405  return m_errors ;
406 }
407 
409 {
410  return m_authentication ;
411 }
412 
bool readEnvelope(std::string &reason, bool check_for_no_remote_recipients)
Reads the envelope.
Definition: gstoredfile.cpp:77
static std::string format()
Returns an identifier for the storage format implemented by this class.
Definition: gfilestore.cpp:115
std::string str() const
Returns the path string.
Definition: gpath.cpp:135
#define G_LOG_S(expr)
Definition: glog.h:103
virtual void destroy()
Final override from GSmtp::StoredMessage.
virtual size_t errorCount() const
Final override from GSmtp::StoredMessage.
static std::string x()
Returns the prefix for envelope header lines.
Definition: gfilestore.cpp:110
static std::string decode(const std::string &)
Decodes the given string.
Definition: gxtext.cpp:79
static unsigned int toUInt(const std::string &s, bool limited=false)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:346
std::string basename() const
Returns the path, excluding drive/directory parts.
Definition: gpath.cpp:162
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
virtual std::string name() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:57
Used by GSmtp::FileStore, GSmtp::NewFile and GSmtp::StoredFile to claim write permissions.
Definition: gfilestore.h:194
static bool rename(const Path &from, const Path &to, const NoThrow &)
Renames the file. Returns false on error.
Definition: gfile.cpp:46
virtual bool eightBit() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:67
virtual void sync()
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:72
virtual ~StoredFile()
Destructor.
Definition: gstoredfile.cpp:46
#define G_ASSERT(test)
Definition: gassert.h:30
virtual std::auto_ptr< std::istream > extractContentStream()
Final override from GSmtp::StoredMessage.
virtual const G::Strings & to() const
Final override from GSmtp::StoredMessage.
bool openContent(std::string &reason)
Opens the content file.
#define G_LOG(expr)
Definition: glog.h:98
static bool exists(const Path &file)
Returns true if the file (directory, link, device etc.) exists.
Definition: gfile.cpp:130
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:536
StoredFile(FileStore &store, const G::Path &envelope_path)
Constructor.
Definition: gstoredfile.cpp:33
virtual const std::string & from() const
Final override from GSmtp::StoredMessage.
virtual void fail(const std::string &reason, int reason_code)
Final override from GSmtp::StoredMessage.
#define G_DEBUG(expr)
Definition: glog.h:95
static bool remove(const Path &path, const NoThrow &)
Deletes the file or directory. Returns false on error.
Definition: gfile.cpp:29
virtual std::string authentication() const
Final override from GSmtp::StoredMessage.
virtual std::string location() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:62
An overload discriminator class for File methods.
Definition: gfile.h:56
virtual void unfail()
Final override from GSmtp::StoredMessage.
virtual size_t remoteRecipientCount() const
Final override from GSmtp::StoredMessage.
A concrete implementation of the MessageStore interface dealing in paired flat files and with an opti...
Definition: gfilestore.h:62
static bool knownFormat(const std::string &format)
Returns true if the storage format string is recognised and supported for reading.
Definition: gfilestore.cpp:120
Used by GSmtp::FileStore, GSmtp::NewFile and GSmtp::StoredFile to claim read permissions for reading ...
Definition: gfilestore.h:161
bool lock()
Locks the file by renaming the envelope file.
A Path object represents a file system path.
Definition: gpath.h:44
void removeExtension()
Modifies the path by removing any extension.
Definition: gpath.cpp:291
#define G_WARNING(expr)
Definition: glog.h:107