E-MailRelay

genvelope.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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 genvelope.cpp
19///
20
21#include "gdef.h"
22#include "genvelope.h"
23#include "gfilestore.h"
24#include "gstr.h"
25#include "gxtext.h"
26
27 namespace GSmtp
28{
29 namespace EnvelopeImp
30 {
31 std::string folded( const std::string & ) ;
32 std::string xnormalise( const std::string & ) ;
33 std::string readLine( std::istream & , bool * = nullptr ) ;
34 std::string readValue( std::istream & , const std::string & , bool * = nullptr ) ;
35 std::string value( const std::string & ) ;
36 std::string readFormat( std::istream & stream , bool * ) ;
37 void readEightBitFlag( std::istream & , Envelope & ) ;
38 void readFrom( std::istream & , Envelope & ) ;
39 void readFromAuthIn( std::istream & , Envelope & ) ;
40 void readFromAuthOut( std::istream & , Envelope & ) ;
41 void readToList( std::istream & , Envelope & ) ;
42 void readAuthentication( std::istream & , Envelope & ) ;
43 void readClientSocketAddress( std::istream & , Envelope & ) ;
44 void readClientSocketName( std::istream & , Envelope & ) ;
45 void readClientCertificate( std::istream & , Envelope & ) ;
46 void readEnd( std::istream & , Envelope & ) ;
47 }
48}
49
50std::size_t GSmtp::Envelope::write( std::ostream & stream , const GSmtp::Envelope & e )
51{
52 namespace imp = GSmtp::EnvelopeImp ;
53 const std::string x( GSmtp::FileStore::x() ) ;
54 const char * crlf = "\r\n" ;
55
56 std::streampos pos = stream.tellp() ;
57 if( pos < 0 || stream.fail() )
58 return 0U ;
59
60 stream << x << "Format: " << GSmtp::FileStore::format() << crlf ;
61 stream << x << "Content: " << (e.m_eight_bit==1?"8bit":(e.m_eight_bit==0?"7bit":"unknown")) << crlf ;
62 stream << x << "From: " << e.m_from << crlf ;
63 stream << x << "ToCount: " << (e.m_to_local.size()+e.m_to_remote.size()) << crlf ;
64 {
65 auto to_p = e.m_to_local.begin() ;
66 for( ; to_p != e.m_to_local.end() ; ++to_p )
67 stream << x << "To-Local: " << *to_p << crlf ;
68 }
69 {
70 auto to_p = e.m_to_remote.begin() ;
71 for( ; to_p != e.m_to_remote.end() ; ++to_p )
72 stream << x << "To-Remote: " << *to_p << crlf ;
73 }
74 stream << x << "Authentication: " << G::Xtext::encode(e.m_authentication) << crlf ;
75 stream << x << "Client: " << e.m_client_socket_address << crlf ;
76 stream << x << "ClientCertificate: " << imp::folded(e.m_client_certificate) << crlf ;
77 stream << x << "MailFromAuthIn: " << imp::xnormalise(e.m_from_auth_in) << crlf ;
78 stream << x << "MailFromAuthOut: " << imp::xnormalise(e.m_from_auth_out) << crlf ;
79 stream << x << "End: 1" << crlf ;
80 stream.flush() ;
81 return stream.fail() ? std::size_t(0U) : static_cast<std::size_t>( stream.tellp() - pos ) ;
82}
83
84 void GSmtp::Envelope::copy( std::istream & in , std::ostream & out )
85{
86 std::string line ;
87 while( in.good() )
88 {
89 G::Str::readLineFrom( in , "\n" , line ) ;
90 if( in )
91 {
92 G::Str::trimRight( line , {"\r",1U} ) ;
93 G::Str::trimRight( line , G::Str::ws() ) ;
94 out << line << "\r\n" ;
95 }
96 }
97 if( in.bad() || (in.fail()&&!in.eof()) )
98 throw ReadError() ;
99 in.clear( std::ios_base::eofbit ) ; // clear failbit
100}
101
102 void GSmtp::Envelope::read( std::istream & stream , GSmtp::Envelope & e )
103{
104 namespace imp = GSmtp::EnvelopeImp ;
105 std::streampos oldpos = stream.tellg() ;
106 std::string format = imp::readFormat( stream , &e.m_crlf ) ;
107 imp::readEightBitFlag( stream , e ) ;
108 imp::readFrom( stream , e ) ;
109 imp::readToList( stream , e ) ;
110 imp::readAuthentication( stream , e ) ;
111 imp::readClientSocketAddress( stream , e ) ;
112 if( format == GSmtp::FileStore::format() )
113 {
114 imp::readClientCertificate( stream , e ) ;
115 imp::readFromAuthIn( stream , e ) ;
116 imp::readFromAuthOut( stream , e ) ;
117 }
118 else if( format == GSmtp::FileStore::format(-1) )
119 {
120 imp::readClientSocketName( stream , e ) ;
121 imp::readClientCertificate( stream , e ) ;
122 }
123 imp::readEnd( stream , e ) ;
124
125 if( stream.bad() )
126 throw ReadError() ;
127 else if( stream.fail() && stream.eof() )
128 stream.clear( std::ios_base::eofbit ) ; // clear failbit -- see tellg()
129
130 std::streampos newpos = stream.tellg() ;
131 if( newpos <= 0 || newpos < oldpos )
132 throw ReadError() ; // never gets here
133
134 e.m_endpos = static_cast<std::size_t>(newpos-oldpos) ;
135}
136
137std::string GSmtp::EnvelopeImp::folded( const std::string & s_in )
138{
139 std::string s = s_in ;
140 G::Str::trim( s , G::Str::ws() ) ;
141 G::Str::replaceAll( s , "\r" , "" ) ;
142 G::Str::replaceAll( s , "\n" , "\r\n " ) ; // RFC-2822 folding
143 return s ;
144}
145
146std::string GSmtp::EnvelopeImp::xnormalise( const std::string & s )
147{
148 return G::Xtext::encode( G::Xtext::decode(s) ) ;
149}
150
151std::string GSmtp::EnvelopeImp::readFormat( std::istream & stream , bool * crlf )
152{
153 std::string format = readValue( stream , "Format" , crlf ) ;
154 if( ! FileStore::knownFormat(format) )
155 throw Envelope::ReadError( "unknown format id" , format ) ;
156 return format ;
157}
158
159 void GSmtp::EnvelopeImp::readEightBitFlag( std::istream & stream , Envelope & e )
160{
161 std::string content = readValue( stream , "Content" ) ;
162 e.m_eight_bit = content == "8bit" ? 1 : ( content == "7bit" ? 0 : -1 ) ;
163}
164
165 void GSmtp::EnvelopeImp::readFrom( std::istream & stream , Envelope & e )
166{
167 e.m_from = readValue( stream , "From" ) ;
168 G_DEBUG( "GSmtp::EnvelopeImp::readFrom: from \"" << e.m_from << "\"" ) ;
169}
170
171 void GSmtp::EnvelopeImp::readFromAuthIn( std::istream & stream , Envelope & e )
172{
173 e.m_from_auth_in = readValue( stream , "MailFromAuthIn" ) ;
174 if( !e.m_from_auth_in.empty() && e.m_from_auth_in != "+" && !G::Xtext::valid(e.m_from_auth_in) )
175 throw Envelope::ReadError( "invalid mail-from-auth-in encoding" ) ;
176}
177
178 void GSmtp::EnvelopeImp::readFromAuthOut( std::istream & stream , Envelope & e )
179{
180 e.m_from_auth_out = readValue( stream , "MailFromAuthOut" ) ;
181 if( !e.m_from_auth_out.empty() && e.m_from_auth_out != "+" && !G::Xtext::valid(e.m_from_auth_out) )
182 throw Envelope::ReadError( "invalid mail-from-auth-out encoding" ) ;
183}
184
185 void GSmtp::EnvelopeImp::readToList( std::istream & stream , Envelope & e )
186{
187 e.m_to_local.clear() ;
188 e.m_to_remote.clear() ;
189
190 unsigned int to_count = G::Str::toUInt( readValue(stream,"ToCount") ) ;
191
192 for( unsigned int i = 0U ; i < to_count ; i++ )
193 {
194 std::string to_line = readLine( stream ) ;
195 bool is_local = to_line.find(FileStore::x()+"To-Local: ") == 0U ;
196 bool is_remote = to_line.find(FileStore::x()+"To-Remote: ") == 0U ;
197 if( ! is_local && ! is_remote )
198 throw Envelope::ReadError( "bad 'to' line" ) ;
199
200 if( is_local )
201 e.m_to_local.push_back( value(to_line) ) ;
202 else
203 e.m_to_remote.push_back( value(to_line) ) ;
204 }
205}
206
207 void GSmtp::EnvelopeImp::readAuthentication( std::istream & stream , Envelope & e )
208{
209 e.m_authentication = G::Xtext::decode( readValue(stream,"Authentication") ) ;
210}
211
212 void GSmtp::EnvelopeImp::readClientSocketAddress( std::istream & stream , Envelope & e )
213{
214 e.m_client_socket_address = readValue( stream , "Client" ) ;
215}
216
217 void GSmtp::EnvelopeImp::readClientSocketName( std::istream & stream , Envelope & )
218{
219 G::Xtext::decode( readValue(stream,"ClientName") ) ;
220}
221
222 void GSmtp::EnvelopeImp::readClientCertificate( std::istream & stream , Envelope & e )
223{
224 e.m_client_certificate = readValue( stream , "ClientCertificate" ) ;
225}
226
227 void GSmtp::EnvelopeImp::readEnd( std::istream & stream , Envelope & )
228{
229 std::string end = readLine( stream ) ;
230 if( end.find(FileStore::x()+"End") != 0U )
231 throw Envelope::ReadError( "no end line" ) ;
232}
233
234std::string GSmtp::EnvelopeImp::readValue( std::istream & stream , const std::string & expected_key , bool * crlf )
235{
236 std::string line = readLine( stream , crlf ) ;
237
238 std::string prefix = FileStore::x() + expected_key + ":" ;
239 if( line == prefix )
240 return std::string() ;
241
242 prefix.append( 1U , ' ' ) ;
243 std::size_t pos = line.find( prefix ) ;
244 if( pos != 0U )
245 throw Envelope::ReadError( "expected \"" + FileStore::x() + expected_key + ":\"" ) ;
246
247 // RFC-2822 unfolding
248 for(;;)
249 {
250 int c = stream.peek() ;
251 if( c == ' ' || c == '\t' )
252 {
253 std::string next_line = readLine( stream ) ;
254 if( next_line.empty() || (next_line[0]!=' '&&next_line[0]!='\t') ) // just in case
255 throw Envelope::ReadError() ;
256 next_line[0] = '\n' ;
257 line.append( next_line ) ;
258 }
259 else
260 break ;
261 }
262
263 return value( line ) ;
264}
265
266std::string GSmtp::EnvelopeImp::readLine( std::istream & stream , bool * crlf )
267{
268 std::string line = G::Str::readLineFrom( stream ) ;
269
270 if( crlf && !line.empty() )
271 *crlf = line.at(line.size()-1U) == '\r' ;
272
273 G::Str::trimRight( line , {"\r",1U} ) ;
274 return line ;
275}
276
277std::string GSmtp::EnvelopeImp::value( const std::string & line )
278{
279 return G::Str::trimmed( G::Str::tail( line , line.find(':') , std::string() ) , G::Str::ws() ) ;
280}
281
GSmtp::Envelope
A structure containing the contents of an envelope file, with support for file reading,...
Definition: genvelope.h:39
GSmtp::Envelope::read
static void read(std::istream &, Envelope &)
Reads an envelope from a stream.
Definition: genvelope.cpp:102
GSmtp::Envelope::write
static std::size_t write(std::ostream &, const Envelope &)
Writes an envelope to a stream.
Definition: genvelope.cpp:50
GSmtp::Envelope::copy
static void copy(std::istream &, std::ostream &)
A convenience function to copy lines from an input stream to an output stream.
Definition: genvelope.cpp:84
GSmtp::FileStore::knownFormat
static bool knownFormat(const std::string &format)
Returns true if the storage format string is recognised and supported for reading.
Definition: gfilestore.cpp:145
GSmtp::FileStore::x
static std::string x()
Returns the prefix for envelope header lines.
Definition: gfilestore.cpp:129
GSmtp::FileStore::format
static std::string format(int generation=0)
Returns an identifier for the storage format implemented by this class, or some older generation of i...
Definition: gfilestore.cpp:134
G::Str::ws
static string_view ws()
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1255
G::Str::tail
static std::string tail(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1287
G::Str::trimRight
static std::string & trimRight(std::string &s, string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:347
G::Str::replaceAll
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
Definition: gstr.cpp:287
G::Str::toUInt
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:604
G::Str::trimmed
static std::string trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
Definition: gstr.cpp:364
G::Str::readLineFrom
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:924
G::Str::trim
static std::string & trim(std::string &s, string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:359
G::Xtext::decode
static std::string decode(const std::string &)
Decodes the given string.
Definition: gxtext.cpp:117
G::Xtext::valid
static bool valid(const std::string &, bool strict=false)
Returns true if a valid encoding.
Definition: gxtext.cpp:75
G::Xtext::encode
static std::string encode(const std::string &)
Encodes the given string.
Definition: gxtext.cpp:95
genvelope.h
gfilestore.h
gstr.h
gxtext.h
GSmtp
SMTP and message-store classes.
Definition: gadminserver.h:39