33 m_content(std::make_unique<std::ifstream>()) ,
35 m_state(State::Normal)
37 G_ASSERT( envelope_path.
basename().find(
".envelope") != std::string::npos ) ;
41 m_state = State::Bad ;
47 G_DEBUG(
"GSmtp::StoredFile::ctor: id=[" << m_id.
str() <<
"]" ) ;
54 if( m_state == State::Locked )
58 G::File::rename( epath(State::Locked) , epath(State::Normal) , std::nothrow ) ;
71std::string GSmtp::StoredFile::location()
const
73 return cpath().str() ;
76G::Path GSmtp::StoredFile::cpath()
const
78 return m_store.contentPath( m_id ) ;
81G::Path GSmtp::StoredFile::epath( State state )
const
83 if( state == State::Locked )
84 return m_store.envelopePath(m_id).
str().append(
".busy") ;
85 else if( state == State::Bad )
86 return m_store.envelopePath(m_id).str().append(
".bad") ;
87 return m_store.envelopePath( m_id ) ;
90int GSmtp::StoredFile::eightBit()
const
92 return m_env.m_eight_bit ;
95void GSmtp::StoredFile::close()
100std::string GSmtp::StoredFile::reopen()
102 std::string reason =
"error" ;
103 if( !readEnvelope(reason,
true) || !openContent(reason) )
106 return std::string() ;
113 readEnvelopeCore( check_recipients ) ;
116 catch( std::exception & e )
123void GSmtp::StoredFile::readEnvelopeCore(
bool check_recipients )
125 std::ifstream stream ;
130 if( ! stream.good() )
131 throw ReadError( epath(m_state).str() ) ;
135 if( check_recipients && m_env.m_to_remote.empty() )
136 throw FormatError(
"no recipients" ) ;
138 if( ! stream.good() )
139 throw ReadError( epath(m_state).str() ) ;
146 G_DEBUG(
"GSmtp::FileStore::openContent: \"" << cpath() <<
"\"" ) ;
147 auto stream = std::make_unique<std::ifstream>() ;
152 if( !stream->good() )
154 reason =
"cannot open content file" ;
157 stream->exceptions( std::ios_base::badbit ) ;
158 m_content = std::unique_ptr<std::istream>( stream.release() ) ;
161 catch( std::exception & e )
163 G_DEBUG(
"GSmtp::FileStore: exception: " << e.what() ) ;
169const std::string & GSmtp::StoredFile::eol()
const
171 static const std::string crlf(
"\r\n" ) ;
172 static const std::string lf(
"\n" ) ;
173 return m_env.m_crlf ? crlf : lf ;
178 const G::Path src = epath( m_state ) ;
179 const G::Path dst = epath( State::Locked ) ;
187 G_LOG(
"GSmtp::StoredMessage: locking file \"" << src.
basename() <<
"\"" ) ;
188 m_state = State::Locked ;
196 G_ASSERT( !rejectees.empty() ) ;
199 env_copy.m_to_remote = rejectees ;
201 const G::Path path_in = epath( m_state ) ;
202 const G::Path path_out = epath(m_state).
str().append(
".tmp") ;
211 throw EditError( path_in.
str() ) ;
217 throw EditError( path_in.
str() ) ;
226 throw EditError( path_in.
str() ) ;
231 if( env_check.m_endpos != m_env.m_endpos )
232 G_WARNING(
"GSmtp::StoredFile::edit: unexpected change to envelope file detected: " << path_in ) ;
235 in.seekg( env_check.m_endpos ) ;
237 throw EditError( path_in.
str() ) ;
243 throw EditError( path_in.
str() ) ;
252 throw EditError( path_in.
str() ) ;
253 file_deleter.release() ;
255 m_env.m_crlf = true ;
256 m_env.m_endpos = endpos ;
257 m_env.m_to_remote = rejectees ;
262 bool exists = false ;
269 addReason( epath(m_state) , reason , reason_code ) ;
271 G::Path bad_path = epath( State::Bad ) ;
272 G_LOG_S(
"GSmtp::StoredMessage: failing file: "
273 <<
"\"" << epath(m_state).basename() <<
"\" -> "
274 <<
"\"" << bad_path.
basename() <<
"\"" ) ;
278 m_state = State::Bad ;
282void GSmtp::StoredFile::unfail()
284 G_DEBUG(
"GSmtp::StoredMessage: unfailing file: " << epath(m_state) ) ;
285 if( m_state == State::Bad )
287 const G::Path src = epath( m_state ) ;
288 const G::Path dst = epath( State::Normal ) ;
296 G_LOG(
"GSmtp::StoredMessage: unfailed file: "
297 <<
"\"" << src.
basename() <<
"\" -> "
298 <<
"\"" << dst.
basename() <<
"\"" ) ;
299 m_state = State::Normal ;
303 G_WARNING(
"GSmtp::StoredMessage: failed to unfail file: \"" << src <<
"\"" ) ;
308void GSmtp::StoredFile::addReason(
const G::Path & path ,
const std::string & reason ,
int reason_code )
const
312 FileWriter claim_writer ;
315 if( !file.is_open() )
316 G_ERROR(
"GSmtp::StoredFile::addReason: cannot re-open envelope file to append the failure reason: " << path ) ;
318 file <<
FileStore::x() <<
"ReasonCode:" ;
if( reason_code ) file <<
" " << reason_code ; file << eol() ;
321void GSmtp::StoredFile::destroy()
323 G_LOG(
"GSmtp::StoredMessage: deleting file: \"" << epath(m_state).basename() <<
"\"" ) ;
325 FileWriter claim_writer ;
329 G_LOG(
"GSmtp::StoredMessage: deleting file: \"" << cpath().basename() <<
"\"" ) ;
332 FileWriter claim_writer ;
337std::string GSmtp::StoredFile::from()
const
339 return m_env.m_from ;
342std::string GSmtp::StoredFile::to( std::size_t i )
const
344 return i < m_env.m_to_remote.size() ? m_env.m_to_remote[i] : std::string() ;
347std::size_t GSmtp::StoredFile::toCount()
const
349 return m_env.m_to_remote.size() ;
352std::istream & GSmtp::StoredFile::contentStream()
354 G_ASSERT( m_content !=
nullptr ) ;
355 if( m_content ==
nullptr )
356 m_content = std::make_unique<std::ifstream>() ;
361std::string GSmtp::StoredFile::authentication()
const
363 return m_env.m_authentication ;
366std::string GSmtp::StoredFile::fromAuthIn()
const
368 return m_env.m_from_auth_in ;
371std::string GSmtp::StoredFile::fromAuthOut()
const
373 return m_env.m_from_auth_out ;
A structure containing the contents of an envelope file, with support for file reading,...
static void read(std::istream &, Envelope &)
Reads an envelope from a stream.
static std::size_t write(std::ostream &, const Envelope &)
Writes an envelope to a stream.
static void copy(std::istream &, std::ostream &)
A convenience function to copy lines from an input stream to an output stream.
Used by GSmtp::FileStore, GSmtp::NewFile and GSmtp::StoredFile to claim read permissions for reading ...
A concrete implementation of the MessageStore interface dealing in paired flat files.
static std::string x()
Returns the prefix for envelope header lines.
Used by GSmtp::FileStore, GSmtp::NewFile and GSmtp::StoredFile to claim write permissions.
A somewhat opaque identifer for a MessageStore message.
std::string str() const
Returns the id string.
StoredFile(FileStore &store, const G::Path &envelope_path)
Constructor.
void fail(const std::string &reason, int reason_code) override
Override from GSmtp::StoredMessage.
bool lock()
Locks the file by renaming the envelope file.
MessageId id() const override
Override from GSmtp::StoredMessage.
~StoredFile() override
Destructor.
void edit(const G::StringArray &) override
Override from GSmtp::StoredMessage.
bool openContent(std::string &reason)
Opens the content file.
bool readEnvelope(std::string &reason, bool check_for_no_remote_recipients)
Reads the envelope.
An overload discriminator for G::File::open().
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
static bool rename(const Path &from, const Path &to, std::nothrow_t) noexcept
Renames the file.
static bool remove(const Path &path, std::nothrow_t) noexcept
Deletes the file or directory. Returns false on error.
static bool exists(const Path &file)
Returns true if the file (directory, device etc.) exists.
A Path object represents a file system path.
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Path withoutExtension() const
Returns a path without the basename extension, if any.
std::string str() const
Returns the path string.
A class that calls an exit function at the end of its scope.
static std::string toPrintableAscii(const std::string &in, char escape='\\')
Returns a 7-bit printable representation of the given input string.
static bool tailMatch(const std::string &in, const std::string &ending)
Returns true if the string has the given ending (or the given ending is empty).
std::vector< std::string > StringArray
A std::vector of std::strings.