E-MailRelay
gnewfile.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2023 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/// \file gnewfile.cpp
19///
20
21#include "gdef.h"
22#include "gfilestore.h"
23#include "gnewfile.h"
24#include "gprocess.h"
25#include "groot.h"
26#include "gfile.h"
27#include "gstr.h"
28#include "gxtext.h"
29#include "gassert.h"
30#include "glog.h"
31#include <functional>
32#include <limits>
33#include <algorithm>
34#include <iostream>
35#include <fstream>
36
37GStore::NewFile::NewFile( FileStore & store , const std::string & from ,
38 const MessageStore::SmtpInfo & smtp_info , const std::string & from_auth_out ,
39 std::size_t max_size ) :
40 m_store(store) ,
41 m_id(store.newId()) ,
42 m_committed(false) ,
43 m_saved(false) ,
44 m_size(0U) ,
45 m_max_size(max_size)
46{
47 m_env.from = from ;
48 m_env.from_auth_in = smtp_info.auth ;
49 m_env.from_auth_out = from_auth_out ;
50 m_env.body_type = Envelope::parseSmtpBodyType( smtp_info.body ) ;
51 m_env.utf8_mailboxes = smtp_info.utf8address ;
52
53 // ask the store for a content stream
54 G_LOG( "GStore::NewFile: new content file [" << cpath().basename() << "]" ) ;
55 m_content = FileStore::stream( cpath() ) ;
56}
57
59{
60 try
61 {
62 cleanup() ;
63 }
64 catch(...) // dtor
65 {
66 }
67}
68
69void GStore::NewFile::cleanup()
70{
71 m_content.reset() ;
72 if( !m_committed )
73 {
74 G_DEBUG( "GStore::NewFile::cleanup: deleting envelope [" << epath(State::New).basename() << "]" ) ;
75 FileOp::remove( epath(State::New) ) ;
76
77 G_DEBUG( "GStore::NewFile::cleanup: deleting content [" << cpath().basename() << "]" ) ;
78 FileOp::remove( cpath() ) ;
79
80 static_cast<MessageStore&>(m_store).updated() ;
81 }
82}
83
84void GStore::NewFile::prepare( const std::string & session_auth_id ,
85 const std::string & peer_socket_address , const std::string & peer_certificate )
86{
87 // flush and close the content file
88 G_ASSERT( m_content != nullptr ) ;
89 m_content->close() ;
90 if( m_content->fail() )
91 throw FileError( "cannot write content file " + cpath().str() ) ;
92 m_content.reset() ;
93
94 // save the envelope
95 m_env.authentication = session_auth_id ;
96 m_env.client_socket_address = peer_socket_address ;
97 m_env.client_certificate = peer_certificate ;
98 saveEnvelope( m_env , epath(State::New) ) ;
99
100 static_cast<MessageStore&>(m_store).updated() ;
101}
102
103void GStore::NewFile::commit( bool throw_on_error )
104{
105 m_committed = true ;
106 m_saved = FileOp::rename( epath(State::New) , epath(State::Normal) ) ;
107 if( !m_saved && throw_on_error )
108 throw FileError( "cannot rename envelope file to " + epath(State::Normal).str() ) ;
109 static_cast<MessageStore&>(m_store).updated() ;
110}
111
112void GStore::NewFile::addTo( const std::string & to , bool local , bool utf8address )
113{
114 if( local )
115 {
116 m_env.to_local.push_back( to ) ;
117 }
118 else
119 {
120 m_env.to_remote.push_back( to ) ;
121 if( utf8address )
122 m_env.utf8_mailboxes = true ;
123 }
124}
125
126GStore::NewMessage::Status GStore::NewFile::addContent( const char * data , std::size_t data_size )
127{
128 std::size_t old_size = m_size ;
129 std::size_t new_size = m_size + data_size ;
130 if( new_size < m_size )
131 new_size = std::numeric_limits<std::size_t>::max() ;
132
133 m_size = new_size ;
134
135 // truncate to m_max_size bytes
136 if( m_max_size && new_size >= m_max_size )
137 data_size = std::max(m_max_size,old_size) - old_size ;
138
139 if( data_size )
140 {
141 std::ostream & stream = *m_content ;
142 stream.write( data , data_size ) ; // NOLINT narrowing
143 }
144
145 if( m_content->fail() )
146 return NewMessage::Status::Error ;
147 else if( m_max_size && m_size >= m_max_size )
148 return NewMessage::Status::TooBig ;
149 else
150 return NewMessage::Status::Ok ;
151}
152
153std::size_t GStore::NewFile::contentSize() const
154{
155 // wrt addContent() -- counts beyond max_size -- not valid if stream.fail()
156 return m_size ;
157}
158
159void GStore::NewFile::saveEnvelope( Envelope & env , const G::Path & path )
160{
161 G_LOG( "GStore::NewFile: new envelope file [" << path.basename() << "]" ) ;
162 std::unique_ptr<std::ofstream> envelope_stream = FileStore::stream( path ) ;
163 env.endpos = GStore::Envelope::write( *envelope_stream , env ) ;
164 env.crlf = true ;
165 envelope_stream->close() ;
166 if( envelope_stream->fail() )
167 throw FileError( "cannot write envelope file" , path.str() ) ;
168}
169
170GStore::MessageId GStore::NewFile::id() const
171{
172 return m_id ;
173}
174
175std::string GStore::NewFile::location() const
176{
177 return cpath().str() ;
178}
179
180G::Path GStore::NewFile::cpath() const
181{
182 return m_store.contentPath( m_id ) ;
183}
184
185G::Path GStore::NewFile::epath( State state ) const
186{
187 return state == State::Normal ?
188 m_store.envelopePath(m_id) :
189 G::Path( m_store.envelopePath(m_id).str() + ".new" ) ;
190}
191
static std::size_t write(std::ostream &, const Envelope &)
Writes an envelope to a seekable stream.
Definition: genvelope.cpp:59
static MessageStore::BodyType parseSmtpBodyType(const std::string &, MessageStore::BodyType default_=MessageStore::BodyType::Unknown)
Parses an SMTP MAIL-FROM BODY= parameter.
Definition: genvelope.cpp:175
A concrete implementation of the MessageStore interface dealing in paired flat files.
Definition: gfilestore.h:56
static std::unique_ptr< std::ofstream > stream(const G::Path &path)
Opens an output stream to a message file using the appropriate effective userid and umask.
Definition: gfilestore.cpp:198
A somewhat opaque identifer for a GStore::MessageStore message id.
Definition: gmessagestore.h:43
A class which allows SMTP messages to be stored and retrieved.
Definition: gmessagestore.h:73
NewFile(FileStore &store, const std::string &from, const MessageStore::SmtpInfo &, const std::string &from_auth_out, std::size_t max_size)
Constructor.
Definition: gnewfile.cpp:37
~NewFile() override
Destructor.
Definition: gnewfile.cpp:58
A Path object represents a file system path.
Definition: gpath.h:73
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:346
std::string str() const
Returns the path string.
Definition: gpath.h:224
Information on the SMTP options used when submitted.
Definition: gmessagestore.h:76