- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
46 GSmtp::StoredFile::~StoredFile()
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
67 bool GSmtp::StoredFile::eightBit() const
68 {
69 return m_eight_bit ;
70 }
71
72 void GSmtp::StoredFile::sync()
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
262 bool GSmtp::StoredFile::lock()
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
313 void GSmtp::StoredFile::unfail()
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
354 void GSmtp::StoredFile::destroy()
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
376 const G::Strings & GSmtp::StoredFile::to() const
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
398 size_t GSmtp::StoredFile::remoteRecipientCount() const
399 {
400 return m_to_remote.size() ;
401 }
402
403 size_t GSmtp::StoredFile::errorCount() const
404 {
405 return m_errors ;
406 }
407
408 std::string GSmtp::StoredFile::authentication() const
409 {
410 return m_authentication ;
411 }
412
GSmtp::StoredFile::readEnvelope
bool readEnvelope(std::string &reason, bool check_for_no_remote_recipients)
Reads the envelope.
Definition: gstoredfile.cpp:77
GSmtp::FileStore::format
static std::string format()
Returns an identifier for the storage format implemented by this class.
Definition: gfilestore.cpp:115
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.cpp:135
G_LOG_S
#define G_LOG_S(expr)
Definition: glog.h:103
GSmtp::StoredFile::destroy
virtual void destroy()
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:354
GSmtp::StoredFile::errorCount
virtual size_t errorCount() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:403
GSmtp::FileStore::x
static std::string x()
Returns the prefix for envelope header lines.
Definition: gfilestore.cpp:110
glog.h
G::Xtext::decode
static std::string decode(const std::string &)
Decodes the given string.
Definition: gxtext.cpp:79
G::Str::toUInt
static unsigned int toUInt(const std::string &s, bool limited=false)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:346
G::Path::basename
std::string basename() const
Returns the path, excluding drive/directory parts.
Definition: gpath.cpp:162
G::Strings
std::list< std::string > Strings
A std::list of std::strings.
Definition: gstrings.h:39
GSmtp::StoredFile::name
virtual std::string name() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:57
GSmtp::FileWriter
Used by GSmtp::FileStore, GSmtp::NewFile and GSmtp::StoredFile to claim write permissions.
Definition: gfilestore.h:194
G::File::rename
static bool rename(const Path &from, const Path &to, const NoThrow &)
Renames the file. Returns false on error.
Definition: gfile.cpp:46
GSmtp::StoredFile::eightBit
virtual bool eightBit() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:67
GSmtp::StoredFile::sync
virtual void sync()
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:72
GSmtp::StoredFile::~StoredFile
virtual ~StoredFile()
Destructor.
Definition: gstoredfile.cpp:46
G_ASSERT
#define G_ASSERT(test)
Definition: gassert.h:30
gstr.h
GSmtp::StoredFile::extractContentStream
virtual std::auto_ptr< std::istream > extractContentStream()
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:381
gfilestore.h
gassert.h
GSmtp::StoredFile::to
virtual const G::Strings & to() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:376
GSmtp::StoredFile::openContent
bool openContent(std::string &reason)
Opens the content file.
Definition: gstoredfile.cpp:206
G_LOG
#define G_LOG(expr)
Definition: glog.h:98
gfile.h
G::File::exists
static bool exists(const Path &file)
Returns true if the file (directory, link, device etc.) exists.
Definition: gfile.cpp:130
G::Str::readLineFrom
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
GSmtp::StoredFile::StoredFile
StoredFile(FileStore &store, const G::Path &envelope_path)
Constructor.
Definition: gstoredfile.cpp:33
GSmtp::StoredFile::from
virtual const std::string & from() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:371
GSmtp::StoredFile::fail
virtual void fail(const std::string &reason, int reason_code)
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:297
G_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
G::File::remove
static bool remove(const Path &path, const NoThrow &)
Deletes the file or directory. Returns false on error.
Definition: gfile.cpp:29
GSmtp::StoredFile::authentication
virtual std::string authentication() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:408
gxtext.h
GSmtp::StoredFile::location
virtual std::string location() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:62
G::File::NoThrow
An overload discriminator class for File methods.
Definition: gfile.h:56
gstoredfile.h
GSmtp::StoredFile::unfail
virtual void unfail()
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:313
GSmtp::StoredFile::remoteRecipientCount
virtual size_t remoteRecipientCount() const
Final override from GSmtp::StoredMessage.
Definition: gstoredfile.cpp:398
GSmtp::FileStore
A concrete implementation of the MessageStore interface dealing in paired flat files and with an opti...
Definition: gfilestore.h:62
gsmtp.h
GSmtp::FileStore::knownFormat
static bool knownFormat(const std::string &format)
Returns true if the storage format string is recognised and supported for reading.
Definition: gfilestore.cpp:120
GSmtp::FileReader
Used by GSmtp::FileStore, GSmtp::NewFile and GSmtp::StoredFile to claim read permissions for reading ...
Definition: gfilestore.h:161
gmemory.h
GSmtp::StoredFile::lock
bool lock()
Locks the file by renaming the envelope file.
Definition: gstoredfile.cpp:262
G::Path
A Path object represents a file system path.
Definition: gpath.h:44
G::Path::removeExtension
void removeExtension()
Modifies the path by removing any extension.
Definition: gpath.cpp:291
G_WARNING
#define G_WARNING(expr)
Definition: glog.h:107