46 if( m_unlock && m_state == State::Locked )
48 G_DEBUG(
"GStore::StoredFile::dtor: unlocking envelope [" << epath(State::Locked).basename() <<
"]" ) ;
49 FileOp::rename( epath(State::Locked) , epath(State::Normal) ) ;
68std::string GStore::StoredFile::location()
const
70 return cpath().str() ;
73G::Path GStore::StoredFile::cpath()
const
75 return m_store.contentPath( m_id ) ;
78G::Path GStore::StoredFile::epath( State state )
const
80 return m_store.envelopePath( m_id , state ) ;
83GStore::MessageStore::BodyType GStore::StoredFile::bodyType()
const
85 return m_env.body_type ;
88void GStore::StoredFile::close()
93std::string GStore::StoredFile::reopen()
95 std::string reason =
"error" ;
96 if( !readEnvelope(reason) || !openContent(reason) )
109 catch( std::exception & e )
120 G_DEBUG(
"GStore::FileStore::openContent: reading content [" << cpath().basename() <<
"]" ) ;
121 auto stream = std::make_unique<Stream>( cpath() ) ;
124 reason =
"cannot open content file" ;
127 stream->exceptions( std::ios_base::badbit ) ;
128 m_content = std::move( stream ) ;
131 catch( std::exception & e )
133 G_DEBUG(
"GStore::FileStore: exception: " << e.what() ) ;
139const std::string & GStore::StoredFile::eol()
const
141 static const std::string crlf(
"\r\n" ) ;
142 static const std::string lf(
"\n" ) ;
143 return m_env.crlf ? crlf : lf ;
148 G_DEBUG(
"GStore::StoredFile::lock: locking envelope [" << epath(m_state).basename() <<
"]" ) ;
149 const G::Path src = epath( m_state ) ;
150 const G::Path dst = epath( State::Locked ) ;
151 bool ok = FileOp::rename( src , dst ) ;
154 m_state = State::Locked ;
159 G_DEBUG(
"GStore::StoredFile::lock: failed to lock envelope "
166void GStore::StoredFile::editRecipients(
const G::StringArray & recipients )
168 editEnvelope( [&recipients](
Envelope &env_){env_.to_remote=recipients;} ) ;
174 G::Path envelope_path = epath(m_state) ;
175 std::ifstream envelope_stream ;
177 envelope_stream.seekg( envelope.endpos ) ;
180 edit_fn( envelope ) ;
183 const G::Path envelope_path_tmp = epath(m_state).
str().append(
".tmp") ;
184 G::ScopeExit file_cleanup( [envelope_path_tmp](){FileOp::remove(envelope_path_tmp);} ) ;
185 std::ofstream envelope_stream_tmp ;
186 envelope.endpos = writeEnvelopeImp( envelope , envelope_path_tmp , envelope_stream_tmp ) ;
187 envelope.crlf = true ;
197 envelope_stream.close() ;
198 envelope_stream_tmp.close() ;
199 if( envelope_stream_tmp.fail() )
200 throw EditError( envelope_path.
basename() ) ;
203 replaceEnvelope( envelope_path , envelope_path_tmp ) ;
204 file_cleanup.release() ;
209void GStore::StoredFile::replaceEnvelope(
const G::Path & envelope_path ,
const G::Path & envelope_path_tmp )
211 G_DEBUG(
"GStore::StoredFile::replaceEnvelope: renaming envelope "
212 "[" << envelope_path.
basename() <<
"] -> [" << envelope_path_tmp.
basename() <<
"]" ) ;
214 if( !FileOp::renameOnto( envelope_path_tmp , envelope_path ) )
218std::size_t GStore::StoredFile::writeEnvelopeImp(
const Envelope & envelope ,
const G::Path & envelope_path , std::ofstream & stream )
220 if( !FileOp::openOut( stream , envelope_path ) )
221 throw EditError(
"creating" , envelope_path.
basename() ) ;
225 throw EditError( envelope_path.
basename() ) ;
229void GStore::StoredFile::fail(
const std::string & reason ,
int reason_code )
231 if( FileOp::exists( epath(m_state) ) )
233 addReason( epath(m_state) , reason , reason_code ) ;
235 G::Path bad_path = epath( State::Bad ) ;
236 G_LOG_S(
"GStore::StoredFile::fail: failing envelope [" << epath(m_state).basename() <<
"] "
237 <<
"-> [" << bad_path.
basename() <<
"]" ) ;
239 FileOp::rename( epath(m_state) , bad_path ) ;
240 m_state = State::Bad ;
244 G_DEBUG(
"GStore::StoredFile::fail: cannot fail envelope [" << epath(m_state).basename() <<
"]" ) ;
247 static_cast<MessageStore&
>(m_store).updated() ;
250void GStore::StoredFile::addReason(
const G::Path & path ,
const std::string & reason ,
int reason_code )
const
252 std::ofstream stream ;
253 if( !FileOp::openAppend( stream , path ) )
254 G_ERROR(
"GStore::StoredFile::addReason: cannot re-open envelope file to append the failure reason: "
258 stream <<
FileStore::x() <<
"ReasonCode:" ;
if( reason_code ) stream <<
" " << reason_code ; stream << eol() ;
261void GStore::StoredFile::destroy()
263 G_LOG(
"GStore::StoredFile::destroy: deleting envelope [" << epath(m_state).basename() <<
"]" ) ;
264 if( !FileOp::remove( epath(m_state) ) )
265 G_WARNING(
"GStore::StoredFile::destroy: failed to delete envelope file "
266 <<
"[" << epath(m_state).basename() <<
"] (" <<
G::Process::strerror(FileOp::errno_()) <<
")" ) ;
268 G_LOG(
"GStore::StoredFile::destroy: deleting content [" << cpath().basename() <<
"]" ) ;
270 if( !FileOp::remove( cpath() ) )
271 G_WARNING(
"GStore::StoredFile::destroy: failed to delete content file "
275 static_cast<MessageStore&
>(m_store).updated() ;
278std::string GStore::StoredFile::from()
const
283std::string GStore::StoredFile::to( std::size_t i )
const
285 return i < m_env.to_remote.size() ? m_env.to_remote[i] : std::string() ;
288std::size_t GStore::StoredFile::toCount()
const
290 return m_env.to_remote.size() ;
293std::size_t GStore::StoredFile::contentSize()
const
295 std::streamoff size = m_content ? m_content->size() : 0U ;
296 if(
static_cast<std::make_unsigned<std::streamoff>::type
>(size) > std::numeric_limits<std::size_t>::max() )
297 throw SizeError(
"too big" ) ;
298 return static_cast<std::size_t
>(size) ;
301std::istream & GStore::StoredFile::contentStream()
303 if( m_content ==
nullptr )
304 m_content = std::make_unique<Stream>() ;
308std::string GStore::StoredFile::authentication()
const
310 return m_env.authentication ;
313std::string GStore::StoredFile::fromAuthIn()
const
315 return m_env.from_auth_in ;
318std::string GStore::StoredFile::forwardTo()
const
320 return m_env.forward_to ;
323std::string GStore::StoredFile::forwardToAddress()
const
325 return m_env.forward_to_address ;
328std::string GStore::StoredFile::clientAccountSelector()
const
330 return m_env.client_account_selector ;
333bool GStore::StoredFile::utf8Mailboxes()
const
335 return m_env.utf8_mailboxes ;
338std::string GStore::StoredFile::fromAuthOut()
const
340 return m_env.from_auth_out ;
345GStore::StoredFile::Stream::Stream() :
346 StreamBuf(&
G::File::read,&
G::File::write,&
G::File::close),
347 std::istream(static_cast<StreamBuf*>(this))
351GStore::StoredFile::Stream::Stream(
const G::Path & path ) :
352 StreamBuf(&
G::File::read,&
G::File::write,&
G::File::close),
353 std::istream(static_cast<StreamBuf*>(this))
361 int fd = FileOp::fdopen( path ) ;
364 StreamBuf::open( fd ) ;
369 clear( std::ios_base::failbit ) ;
373std::streamoff GStore::StoredFile::Stream::size()
const
377 auto old =
G::File::seek( fd , 0 , G::File::Seek::Current ) ;
379 auto new_ =
G::File::seek( fd , old , G::File::Seek::Start ) ;
380 if( old < 0 || old != new_ )
381 throw StoredFile::SizeError() ;
A structure containing the contents of an envelope file, with support for file reading,...
static void copyExtra(std::istream &, std::ostream &)
A convenience function to copy extra envelope lines from an envelope input stream to an output stream...
static std::size_t write(std::ostream &, const Envelope &)
Writes an envelope to a seekable stream.
A concrete implementation of the MessageStore interface dealing in paired flat files.
static std::string x()
Returns the prefix for envelope header lines.
static Envelope readEnvelope(const G::Path &, std::ifstream *=nullptr)
Used by FileStore sibling classes to read an envelope file.
A somewhat opaque identifer for a GStore::MessageStore message id.
A class which allows SMTP messages to be stored and retrieved.
bool openContent(std::string &reason)
Opens the content file. Returns false on error.
bool readEnvelope(std::string &reason)
Reads the envelope.
StoredFile(FileStore &store, const MessageId &, State=State::Normal)
Constructor.
bool lock()
Locks the file by renaming the envelope file.
void editEnvelope(std::function< void(Envelope &)>, std::istream *headers=nullptr)
Edits the envelope and updates it in the file store.
void noUnlock()
Disable unlocking in the destructor.
~StoredFile() override
Destructor.
MessageId id() const override
Override from GStore::StoredMessage.
static std::streamoff seek(int fd, std::streamoff offset, Seek) noexcept
Does ::lseek() or equivalent.
A Path object represents a file system path.
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
std::string str() const
Returns the path string.
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic 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.
void open(T file)
Installs the given file descriptor.
std::vector< std::string > StringArray
A std::vector of std::strings.