E-MailRelay

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