48 if( m_unlock && m_state == State::Locked )
50 G_DEBUG(
"GStore::StoredFile::dtor: unlocking envelope [" << epath(State::Locked).basename() <<
"]" ) ;
51 FileOp::rename( epath(State::Locked) , epath(State::Normal) ) ;
70std::string GStore::StoredFile::location()
const
72 return cpath().str() ;
75G::Path GStore::StoredFile::cpath()
const
77 return m_store.contentPath( m_id ) ;
80G::Path GStore::StoredFile::epath( State state )
const
82 return m_store.envelopePath( m_id , state ) ;
85GStore::MessageStore::BodyType GStore::StoredFile::bodyType()
const
87 return m_env.body_type ;
90void GStore::StoredFile::close()
95std::string GStore::StoredFile::reopen()
97 std::string reason =
"error" ;
98 if( !readEnvelope(reason) || !openContent(reason) )
101 return std::string() ;
111 catch( std::exception & e )
122 G_DEBUG(
"GStore::FileStore::openContent: reading content [" << cpath().basename() <<
"]" ) ;
123 auto stream = std::make_unique<Stream>( cpath() ) ;
126 reason =
"cannot open content file" ;
129 stream->exceptions( std::ios_base::badbit ) ;
130 m_content = std::move( stream ) ;
133 catch( std::exception & e )
135 G_DEBUG(
"GStore::FileStore: exception: " << e.what() ) ;
141const std::string & GStore::StoredFile::eol()
const
143 static const std::string crlf(
"\r\n" ) ;
144 static const std::string lf(
"\n" ) ;
145 return m_env.crlf ? crlf : lf ;
150 G_DEBUG(
"GStore::StoredFile::lock: locking envelope [" << epath(m_state).basename() <<
"]" ) ;
151 const G::Path src = epath( m_state ) ;
152 const G::Path dst = epath( State::Locked ) ;
153 bool ok = FileOp::rename( src , dst ) ;
156 m_state = State::Locked ;
161 G_DEBUG(
"GStore::StoredFile::lock: failed to lock envelope "
168void GStore::StoredFile::editRecipients(
const G::StringArray & recipients )
170 editEnvelope( [&recipients](
Envelope &env_){env_.to_remote=recipients;} ) ;
176 G::Path envelope_path = epath(m_state) ;
177 std::ifstream envelope_stream ;
179 envelope_stream.seekg( envelope.endpos ) ;
182 edit_fn( envelope ) ;
185 const G::Path envelope_path_tmp = epath(m_state).
str().append(
".tmp") ;
186 G::ScopeExit file_cleanup( [envelope_path_tmp](){FileOp::remove(envelope_path_tmp);} ) ;
187 std::ofstream envelope_stream_tmp ;
188 envelope.endpos = writeEnvelopeImp( envelope , envelope_path_tmp , envelope_stream_tmp ) ;
189 envelope.crlf = true ;
199 envelope_stream.close() ;
200 envelope_stream_tmp.close() ;
201 if( envelope_stream_tmp.fail() )
202 throw EditError( envelope_path.
basename() ) ;
205 replaceEnvelope( envelope_path , envelope_path_tmp ) ;
206 file_cleanup.release() ;
211void GStore::StoredFile::replaceEnvelope(
const G::Path & envelope_path ,
const G::Path & envelope_path_tmp )
213 G_DEBUG(
"GStore::StoredFile::replaceEnvelope: renaming envelope "
214 "[" << envelope_path.
basename() <<
"] -> [" << envelope_path_tmp.
basename() <<
"]" ) ;
216 if( !FileOp::renameOnto( envelope_path_tmp , envelope_path ) )
220std::size_t GStore::StoredFile::writeEnvelopeImp(
const Envelope & envelope ,
const G::Path & envelope_path , std::ofstream & stream )
222 if( !FileOp::openOut( stream , envelope_path ) )
223 throw EditError(
"creating" , envelope_path.
basename() ) ;
227 throw EditError( envelope_path.
basename() ) ;
231void GStore::StoredFile::fail(
const std::string & reason ,
int reason_code )
233 if( FileOp::exists( epath(m_state) ) )
235 addReason( epath(m_state) , reason , reason_code ) ;
237 G::Path bad_path = epath( State::Bad ) ;
238 G_LOG_S(
"GStore::StoredFile::fail: failing envelope [" << epath(m_state).basename() <<
"] "
239 <<
"-> [" << bad_path.
basename() <<
"]" ) ;
241 FileOp::rename( epath(m_state) , bad_path ) ;
242 m_state = State::Bad ;
246 G_DEBUG(
"GStore::StoredFile::fail: cannot fail envelope [" << epath(m_state).basename() <<
"]" ) ;
249 static_cast<MessageStore&
>(m_store).updated() ;
252void GStore::StoredFile::addReason(
const G::Path & path ,
const std::string & reason ,
int reason_code )
const
254 std::ofstream stream ;
255 if( !FileOp::openAppend( stream , path ) )
256 G_ERROR(
"GStore::StoredFile::addReason: cannot re-open envelope file to append the failure reason: "
260 stream <<
FileStore::x() <<
"ReasonCode:" ;
if( reason_code ) stream <<
" " << reason_code ; stream << eol() ;
263void GStore::StoredFile::destroy()
265 G_LOG(
"GStore::StoredFile::destroy: deleting envelope [" << epath(m_state).basename() <<
"]" ) ;
266 if( !FileOp::remove( epath(m_state) ) )
267 G_WARNING(
"GStore::StoredFile::destroy: failed to delete envelope file "
268 <<
"[" << epath(m_state).basename() <<
"] (" <<
G::Process::strerror(FileOp::errno_()) <<
")" ) ;
270 G_LOG(
"GStore::StoredFile::destroy: deleting content [" << cpath().basename() <<
"]" ) ;
272 if( !FileOp::remove( cpath() ) )
273 G_WARNING(
"GStore::StoredFile::destroy: failed to delete content file "
277 static_cast<MessageStore&
>(m_store).updated() ;
280std::string GStore::StoredFile::from()
const
285std::string GStore::StoredFile::to( std::size_t i )
const
287 return i < m_env.to_remote.size() ? m_env.to_remote[i] : std::string() ;
290std::size_t GStore::StoredFile::toCount()
const
292 return m_env.to_remote.size() ;
295std::size_t GStore::StoredFile::contentSize()
const
297 std::streamoff size = m_content ? m_content->size() : 0U ;
298 if(
static_cast<std::make_unsigned<std::streamoff>::type
>(size) > std::numeric_limits<std::size_t>::max() )
299 throw SizeError(
"too big" ) ;
300 return static_cast<std::size_t
>(size) ;
303std::istream & GStore::StoredFile::contentStream()
305 if( m_content ==
nullptr )
306 m_content = std::make_unique<Stream>() ;
310std::string GStore::StoredFile::authentication()
const
312 return m_env.authentication ;
315std::string GStore::StoredFile::fromAuthIn()
const
317 return m_env.from_auth_in ;
320std::string GStore::StoredFile::forwardTo()
const
322 return m_env.forward_to ;
325std::string GStore::StoredFile::forwardToAddress()
const
327 return m_env.forward_to_address ;
330std::string GStore::StoredFile::clientAccountSelector()
const
332 return m_env.client_account_selector ;
335bool GStore::StoredFile::utf8Mailboxes()
const
337 return m_env.utf8_mailboxes ;
340std::string GStore::StoredFile::fromAuthOut()
const
342 return m_env.from_auth_out ;
347GStore::StoredFile::Stream::Stream() :
348 StreamBuf(&
G::File::read,&
G::File::write,&
G::File::close),
349 std::istream(static_cast<StreamBuf*>(this))
353GStore::StoredFile::Stream::Stream(
const G::Path & path ) :
354 StreamBuf(&
G::File::read,&
G::File::write,&
G::File::close),
355 std::istream(static_cast<StreamBuf*>(this))
363 int fd = FileOp::fdopen( path.
cstr() ) ;
366 StreamBuf::open( fd ) ;
371 clear( std::ios_base::failbit ) ;
375std::streamoff GStore::StoredFile::Stream::size()
const
379 auto old =
G::File::seek( fd , 0 , G::File::Seek::Current ) ;
381 auto new_ =
G::File::seek( fd , old , G::File::Seek::Start ) ;
382 if( old < 0 || old != new_ )
383 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.
const char * cstr() const noexcept
Returns the path string.
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.