35 std::size_t size = 0U ;
39 bool overflow = false ;
40 bool invalid = false ;
41 std::size_t n = G::Str::toUnsigned<std::size_t>( token.
data() , token.
data()+token.
size() , overflow , invalid ) ;
42 if( !overflow && !invalid )
43 size = n , ok = true ;
66 return {
"invalid mail-from command"} ;
69 if( result.error.empty() )
71 if( !parseMailStringValue(line,
"SMTPUTF8="_sv,result).empty() )
72 result.error =
"invalid mail-from parameter" ;
74 result.auth = parseMailStringValue( line ,
"AUTH="_sv , result , Conversion::ValidXtext ) ;
75 result.body = parseMailStringValue( line ,
"BODY="_sv , result , Conversion::Upper ) ;
76 result.size = parseMailNumericValue( line ,
"SIZE="_sv , result ) ;
77 result.smtputf8 = parseMailBoolean( line ,
"SMTPUTF8"_sv , result ) ;
78 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: error=[" <<
G::Str::printable(result.error) <<
"]" ) ;
79 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: address=[" <<
G::Str::printable(result.address) <<
"]" ) ;
80 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: size=" << result.size ) ;
81 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: auth=[" <<
G::Str::printable(result.auth) <<
"]" ) ;
82 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: smtputf8=" << (result.smtputf8?
"1":
"0") ) ;
91 return {
"invalid rcpt-to command"} ;
93 return parseAddressPart( line , config ) ;
106 if( line.find(
'\0') != std::string::npos ||
107 line.find_first_of(
"\r\n",0,2U) != std::string::npos )
109 return {
"invalid character in mailbox name"} ;
113 std::size_t startpos = line.find(
':' ) ;
114 if( startpos == std::string::npos )
115 return {
"missing colon"} ;
119 AddressCommand result ;
120 if( startpos < line.size() && ( line[startpos] ==
' ' || line[startpos] ==
'\t' ) )
121 result.invalid_spaces = true ;
122 startpos = line.find_first_not_of(
" \t" , startpos , 2U ) ;
123 if( startpos == std::string::npos ) startpos = line.size() ;
124 if( startpos < line.size() && line[startpos] !=
'<' )
125 result.invalid_nobrackets = true ;
128 if( result.invalid_spaces && !config.allow_spaces )
130 result.error =
"invalid space after colon" ;
133 if( result.invalid_nobrackets && !config.allow_nobrackets )
135 result.error =
"missing angle brackets in mailbox name" ;
140 std::size_t endpos = 0U ;
141 if( result.invalid_nobrackets )
143 endpos = line.find_first_of(
" \t"_sv , startpos ) ;
144 if( endpos == std::string::npos ) endpos = line.size() ;
145 G_ASSERT( startpos < line.size() && endpos <= line.size() && endpos > startpos ) ;
147 else if( (startpos+2U) > line.size() || line.find(
'>',startpos+1U) == std::string::npos )
149 result.error =
"invalid angle brackets in mailbox name" ;
155 if( line.at(startpos+1U) ==
'@' )
159 startpos = line.find(
':' , startpos+1U ) ;
160 if( startpos == std::string::npos || (startpos+2U) >= line.size() )
161 return {
"invalid source route in mailbox name"} ;
165 if( line.at(startpos+1U) ==
'"' )
167 for( std::size_t i = startpos+2U ; endpos == 0U && i < line.size() ; i++ )
169 if( line[i] ==
'\\' )
171 else if( line[i] ==
'"' )
172 endpos = line.find(
'>' , i ) ;
174 if( endpos == std::string::npos )
175 return {
"invalid quoting"} ;
179 endpos = line.find(
'>' , startpos+1U ) ;
180 G_ASSERT( endpos != std::string::npos ) ;
182 if( (endpos+1U) < line.size() && line.at(endpos+1U) !=
' ' )
183 return {
"invalid angle brackets"} ;
185 G_ASSERT( startpos < line.size() && endpos < line.size() && endpos > startpos ) ;
186 G_ASSERT( line.at(startpos) ==
'<' || line.at(startpos) ==
':' ) ;
187 G_ASSERT( line.at(endpos) ==
'>' ) ;
190 std::string_view address =
191 result.invalid_nobrackets ?
192 std::string_view( line.data()+startpos , endpos-startpos ) :
193 std::string_view( line.data()+startpos+1U , endpos-startpos-1U ) ;
196 if( address_style == AddressStyle::Invalid )
197 return {
"invalid character in mailbox name"} ;
199 result.utf8_mailbox_part = address_style == AddressStyle::Utf8Both || address_style == AddressStyle::Utf8Mailbox ;
200 result.utf8_domain_part = address_style == AddressStyle::Utf8Both || address_style == AddressStyle::Utf8Domain ;
201 result.raw_address = G::sv_to_string( address ) ;
202 result.address = result.utf8_domain_part ? encodeDomain(address) : result.raw_address ;
203 result.address_style = address_style ;
204 result.tailpos = result.invalid_nobrackets ? endpos : (endpos+1U) ;
208std::string GSmtp::ServerParser::encodeDomain( std::string_view address )
210 std::size_t at_pos = address.rfind(
'@' ) ;
215 G::sv_to_string( address ) :
216 G::sv_to_string(user).append(1U,
'@').append(
G::Idn::encode(domain)) ;
219std::size_t GSmtp::ServerParser::parseMailNumericValue( std::string_view line , std::string_view key_eq , AddressCommand & out )
221 std::size_t result = 0U ;
222 if( out.error.empty() && out.tailpos != std::string::npos && out.tailpos < line.size() )
224 std::string str = parseMailStringValue( line , key_eq , out ) ;
231std::string GSmtp::ServerParser::parseMailStringValue( std::string_view line , std::string_view key_eq , AddressCommand & out , Conversion conversion )
234 if( out.error.empty() && out.tailpos != std::string::npos && out.tailpos < line.size() )
236 std::string_view tail = G::sv_substr_noexcept( std::string_view(line) , out.tailpos ) ;
238 for( ; word ; ++word )
240 if(
G::Str::ifind( word() , key_eq ) == 0U && word().size() > key_eq.size() )
242 result = G::sv_to_string( word().substr(key_eq.size() ) ) ;
246 if( conversion == Conversion::ValidXtext )
248 else if( conversion == Conversion::Upper )
254bool GSmtp::ServerParser::parseMailBoolean( std::string_view line , std::string_view key , AddressCommand & out )
256 bool result = false ;
257 if( out.error.empty() && out.tailpos != std::string::npos && out.tailpos < line.size() )
259 std::string_view tail = G::sv_substr_noexcept( line , out.tailpos ) ;
261 for( ; word && !result ; ++word )
273 std::string line = line_in ;
276 if( line.size() > 9U )
279 std::string tail = line.substr( line.size() - 9U ) ;
282 line = line.substr( 0U , line.size()-9U ) ;
286 std::size_t pos = line.find_first_of(
" \t" ) ;
287 if( pos != std::string::npos )
288 to = line.substr(pos) ;
294 std::size_t pos = line.find_first_not_of(
" \t" ) ;
295 if( pos == std::string::npos )
298 pos = line.find_first_of(
" \t" , pos ) ;
299 if( pos == std::string::npos )
302 std::string smtp_peer_name = line.substr( pos + 1U ) ;
304 return smtp_peer_name ;
static AddressCommand parseRcptTo(std::string_view, const Config &)
Parses a RCPT-TO command.
static std::string parseVrfy(const std::string &)
Parses a VRFY command.
static AddressCommand parseMailFrom(std::string_view, const Config &)
Parses a MAIL-FROM command.
static std::pair< bool, bool > parseBdatLast(std::string_view)
Parses a BDAT LAST command.
static std::pair< std::size_t, bool > parseBdatSize(std::string_view)
Parses a BDAT command.
static std::string parseHeloPeerName(const std::string &)
Parses the peer name from an HELO/EHLO command.
static AddressStyle addressStyle(std::string_view address)
Parses an address to determine whether it has ASCII or UTF-8 parts.
static std::string & trimRight(std::string &s, std::string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
static std::string & trim(std::string &s, std::string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
static bool isULong(std::string_view s) noexcept
Returns true if the string can be converted into an unsigned long without throwing an exception.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static std::string upper(std::string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
static std::size_t ifind(std::string_view s, std::string_view key)
Returns the position of the key in 's' using a seven-bit case-insensitive search.
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.
static std::string_view tailView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static unsigned long toULong(std::string_view s, Limited)
Converts string 's' to an unsigned long.
static std::string_view headView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like head() but returning a view into the input string.
static std::string trimmed(const std::string &s, std::string_view ws)
Returns a trim()med version of s.
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
StringTokenT< T > & next() noexcept
Moves to the next token.
const char_type * data() const noexcept
Returns the current token pointer.
std::size_t size() const noexcept
Returns the current token size.
static std::string encode(std::string_view)
Encodes the given string.
static std::string decode(std::string_view)
Decodes the given string.
A configuration structure for GSmtp::ServerParser.
Overload discrimiator for G::Str::toUWhatever() requesting a range-limited result.