E-MailRelay
gsmtpclientreply.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 gsmtpclientreply.cpp
19///
20
21#include "gdef.h"
22#include "gsmtpclientreply.h"
23#include "gstr.h"
24#include "gstringarray.h"
25#include "gexception.h"
26#include "glog.h"
27#include "gassert.h"
28
30= default ;
31
32#ifndef G_LIB_SMALL
34{
35 ClientReply reply ;
36 reply.m_value = 250 ;
37 reply.m_done_code = 250 ;
38 reply.m_text = "OK" ;
39 G_ASSERT( reply.positive() ) ;
40 return reply ;
41}
42#endif
43
44GSmtp::ClientReply GSmtp::ClientReply::internal( Value v , int done_code )
45{
46 G_ASSERT( static_cast<int>(v) >= 1 && static_cast<int>(v) < 100 ) ;
47 ClientReply reply ;
48 reply.m_value = static_cast<int>( v ) ;
49 reply.m_done_code = done_code ;
50 return reply ;
51}
52
54{
55 return internal( Value::Internal_secure , 0 ) ;
56}
57
59{
60 return internal( Value::Internal_start , 0 ) ;
61}
62
64{
65 return internal( Value::Internal_filter_ok , 0 ) ;
66}
67
69{
70 return internal( Value::Internal_filter_abandon , -1 ) ;
71}
72
74 const std::string & filter_reason )
75{
76 ClientReply reply ;
77 reply.m_value = static_cast<int>( Value::Internal_filter_error ) ;
78 reply.m_done_code = -2 ;
79 reply.m_text = response ;
80 reply.m_filter_reason = filter_reason ;
81 return reply ;
82}
83
85{
86 G_ASSERT( complete(lines) ) ;
87 if( !complete(lines) )
88 throw G::Exception( "invalid client response" ) ;
89
90 m_value = G::Str::toInt( lines.at(lines.size()-1U).substr(0U,3U) ) ;
91 m_done_code = m_value ;
92 G_ASSERT( m_value >= 100 && m_value < 600 ) ;
93
94 for( const auto & line : lines )
95 {
96 if( line.length() > 4U )
97 {
98 std::string s = line.substr( 4U ) ;
99 G::Str::trimLeft( s , " \t" ) ;
100 G::Str::replace( s , '\t' , ' ' ) ;
101 G::Str::replace( s , '\n' , ' ' ) ;
102 G::Str::removeAll( s , '\r' ) ;
103 if( !m_text.empty() && !s.empty() ) m_text.append(1U,sep) ;
104 m_text.append( s ) ;
105 }
106 }
107}
108
110{
111 if( lines.empty() )
112 return false ;
113
114 std::string digits ;
115 for( std::size_t i = 0U ; i < lines.size() ; i++ )
116 {
117 if( !validLine(lines[i],digits,i,lines.size()) )
118 return false ;
119 }
120 return true ;
121}
122
124{
125 if( lines.empty() ) return false ;
126 const std::string & last = lines[lines.size()-1U] ;
127 return valid(lines) && ( last.size() == 3U || last.at(3U) == ' ' ) ;
128}
129
130bool GSmtp::ClientReply::validLine( const std::string & line , std::string & digits ,
131 std::size_t index , std::size_t )
132{
133 if( index == 0U && line.length() >= 3U )
134 digits = line.substr( 0U , 3U ) ;
135 else if( index == 0U )
136 return false ;
137
138 return
139 line.length() >= 3U &&
140 isDigit(line.at(0U)) &&
141 line.at(0U) >= '1' && line.at(0U) <= '5' &&
142 isDigit(line.at(1U)) &&
143 isDigit(line.at(2U)) &&
144 digits == line.substr(0U,3U) &&
145 ( line.length() == 3U || ( line.at(3U) == ' ' || line.at(3U) == '-' ) ) ;
146}
147
148bool GSmtp::ClientReply::isDigit( char c )
149{
150 return c >= '0' && c <= '9' ;
151}
152
154{
155 return m_value >= 100 && m_value < 400 ;
156}
157
159{
160 return m_value >= 200 && m_value < 300 ;
161}
162
164{
165 return m_value ;
166}
167
168bool GSmtp::ClientReply::is( Value v ) const
169{
170 return m_value == static_cast<int>(v) ;
171}
172
174{
175 return m_done_code ;
176}
177
179{
180 return positiveCompletion() ? std::string() : ( m_text.empty() ? std::string("error") : m_text ) ;
181}
182
183std::string GSmtp::ClientReply::reason() const
184{
185 return m_filter_reason ;
186}
187
188std::string GSmtp::ClientReply::text() const
189{
190 return m_text ;
191}
192
Encapsulates SMTP replies from a remote client, or replies from a client filter, or the result of a T...
std::string errorText() const
Returns the empty string if positiveCompletion() or non-empty text() or "error".
bool positiveCompletion() const
Returns true if value() is between 200 and 299.
static ClientReply secure()
Factory function for Internal_secure.
static ClientReply filterAbandon()
Factory function for Internal_filter_abandon.
int value() const
Returns the numeric value of the reply.
std::string text() const
Returns the text of the reply, with some whitespace normalisation and no tabs.
static ClientReply start()
Factory function for Internal_start.
bool positive() const
Returns true if value() is between 100 and 399.
std::string reason() const
Returns the filter-reason text from a filterError() reply or the empty string.
static ClientReply filterOk()
Factory function for Internal_filter_ok.
static ClientReply ok()
Factory function returning a generic 'Ok' reply object with a value() of 250.
ClientReply(const G::StringArray &text, char sep='\n')
Constructor taking lines of text from the remote SMTP client.
static bool complete(const G::StringArray &)
Returns true if the reply text is valid() and complete.
bool is(Value v) const
Returns true if the value() is as given.
static ClientReply filterError(const std::string &response, const std::string &filter_reason)
Factory function for Internal_filter_error.
int doneCode() const
Returns -1 for filterAbandon() or -2 for filterError() or zero if less than 100 or value().
static bool valid(const G::StringArray &)
Returns true if the reply text is syntactivally valid but possibly incomplete.
A general-purpose exception class derived from std::exception and containing an error message.
Definition: gexception.h:64
static int toInt(std::string_view s)
Converts string 's' to an int.
Definition: gstr.cpp:538
static void removeAll(std::string &, char)
Removes all occurrences of the character from the string. See also only().
Definition: gstr.cpp:262
static std::string & trimLeft(std::string &s, std::string_view ws, std::size_t limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:288
static bool replace(std::string &s, std::string_view from, std::string_view to, std::size_t *pos_p=nullptr)
A std::string_view overload.
Definition: gstr.cpp:226
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstringarray.h:30