32 static constexpr const char * cc =
33 "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
34 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" "\x7F" ;
36 bool invalid = mailbox.find_first_of( cc , 0U , 33U ) != std::string::npos ;
40 return MailboxStyle::Invalid ;
42 return MailboxStyle::Ascii ;
44 return MailboxStyle::Utf8 ;
50 std::size_t size = 0U ;
52 if( token && ++token )
54 bool overflow = false ;
55 bool invalid = false ;
56 std::size_t n = G::Str::toUnsigned<std::size_t>( token.
data() , token.
data()+token.
size() , overflow , invalid ) ;
57 if( !overflow && !invalid )
58 size = n , ok = true ;
68 if( token && ++token )
81 return {
"invalid mail-from command"} ;
84 if( result.error.empty() )
86 if( !parseMailStringValue(line,
"SMTPUTF8="_sv,result).empty() )
87 result.error =
"invalid mail-from parameter" ;
89 result.auth = parseMailStringValue( line ,
"AUTH="_sv , result , Conversion::ValidXtext ) ;
90 result.body = parseMailStringValue( line ,
"BODY="_sv , result , Conversion::Upper ) ;
91 result.size = parseMailNumericValue( line ,
"SIZE="_sv , result ) ;
92 result.smtputf8 = parseMailBoolean( line ,
"SMTPUTF8"_sv , result ) ;
93 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: error=[" <<
G::Str::printable(result.error) <<
"]" ) ;
94 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: address=[" <<
G::Str::printable(result.address) <<
"]" ) ;
95 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: size=" << result.size ) ;
96 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: auth=[" <<
G::Str::printable(result.auth) <<
"]" ) ;
97 G_DEBUG(
"GSmtp::ServerParser::parseMailFrom: smtputf8=" << (result.smtputf8?
"1":
"0") ) ;
106 return {
"invalid rcpt-to command"} ;
108 return parseAddressPart( line ) ;
121 if( line.find(
'\0') != std::string::npos ||
122 line.find_first_of(
"\r\n",0,2U) != std::string::npos )
124 return {
"invalid character in mailbox name"} ;
128 std::size_t startpos = line.find(
':' ) ;
129 if( startpos == std::string::npos )
130 return {
"missing colon"} ;
132 while( startpos < line.size() && line[startpos] ==
' ' )
134 if( (startpos+2U) > line.size() || line[startpos] !=
'<' || line.find(
'>',startpos+1U) == std::string::npos )
136 return {
"missing or invalid angle brackets in mailbox name"} ;
140 if( line[startpos+1U] ==
'@' )
144 startpos = line.find(
':' , startpos+1U ) ;
145 if( startpos == std::string::npos || (startpos+2U) >= line.size() )
146 return {
"invalid source route in mailbox name"} ;
150 std::size_t endpos = 0U ;
151 if( line.at(startpos+1U) ==
'"' )
153 for( std::size_t i = startpos+2U ; endpos == 0U && i < line.size() ; i++ )
155 if( line[i] ==
'\\' )
157 else if( line[i] ==
'"' )
158 endpos = line.find(
'>' , i ) ;
160 if( endpos == std::string::npos )
161 return {
"invalid quoting"} ;
165 endpos = line.find(
'>' , startpos+1U ) ;
166 G_ASSERT( endpos != std::string::npos ) ;
168 if( (endpos+1U) < line.size() && line.at(endpos+1U) !=
' ' )
169 return {
"invalid angle brackets"} ;
171 G_ASSERT( startpos != std::string::npos && endpos != std::string::npos ) ;
172 G_ASSERT( endpos > startpos ) ;
173 G_ASSERT( endpos < line.size() ) ;
174 G_ASSERT( line.at(startpos) ==
'<' || line.at(startpos) ==
':' ) ;
175 G_ASSERT( line.at(endpos) ==
'>' ) ;
177 std::string address = std::string( line.data()+startpos+1U , endpos-startpos-1U ) ;
179 auto style = mailboxStyle( address ) ;
180 if( style == MailboxStyle::Invalid )
181 return {
"invalid character in mailbox name"} ;
183 AddressCommand result ;
184 result.address = std::string( line.data()+startpos+1U , endpos-startpos-1U ) ;
185 result.utf8address = style == MailboxStyle::Utf8 ;
186 result.tailpos = endpos+1U ;
192 std::size_t result = 0U ;
193 if( out.error.empty() && out.tailpos != std::string::npos && out.tailpos < line.size() )
195 std::string str = parseMailStringValue( line , key_eq , out ) ;
202std::string GSmtp::ServerParser::parseMailStringValue(
G::string_view line ,
G::string_view key_eq , AddressCommand & out , Conversion conversion )
205 if( out.error.empty() && out.tailpos != std::string::npos && out.tailpos < line.size() )
209 for( ; word ; ++word )
211 if(
G::Str::ifind( word() , key_eq ) == 0U && word().size() > key_eq.size() )
213 result = G::sv_to_string( word().substr(key_eq.size() ) ) ;
217 if( conversion == Conversion::ValidXtext )
219 else if( conversion == Conversion::Upper )
227 bool result = false ;
228 if( out.error.empty() && out.tailpos != std::string::npos && out.tailpos < line.size() )
232 for( ; word && !result ; ++word )
244 std::string line = line_in ;
247 if( line.size() > 9U )
250 std::string tail = line.substr( line.size() - 9U ) ;
253 line = line.substr( 0U , line.size()-9U ) ;
257 std::size_t pos = line.find_first_of(
" \t" ) ;
258 if( pos != std::string::npos )
259 to = line.substr(pos) ;
265 std::size_t pos = line.find_first_not_of(
" \t" ) ;
266 if( pos == std::string::npos )
267 return std::string() ;
269 pos = line.find_first_of(
" \t" , pos ) ;
270 if( pos == std::string::npos )
271 return std::string() ;
273 std::string smtp_peer_name = line.substr( pos + 1U ) ;
275 return smtp_peer_name ;
static MailboxStyle mailboxStyle(const std::string &mailbox)
Classifies the given mailbox name.
static std::string parseVrfy(const std::string &)
Parses a VRFY command.
static std::pair< std::size_t, bool > parseBdatSize(G::string_view)
Parses a BDAT command.
static std::pair< bool, bool > parseBdatLast(G::string_view)
Parses a BDAT LAST command.
static AddressCommand parseMailFrom(G::string_view)
Parses a MAIL-FROM command.
static AddressCommand parseRcptTo(G::string_view)
Parses a RCPT-TO command.
static std::string parseHeloPeerName(const std::string &)
Parses the peer name from an HELO/EHLO command.
static bool isPrintableAscii(string_view s) noexcept
Returns true if every character is a 7-bit, non-control character (ie.
static std::size_t ifind(string_view s, string_view key)
Returns the position of the key in 's' using a seven-bit case-insensitive search.
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
static std::string & trimLeft(std::string &s, string_view ws, std::size_t limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
static bool isULong(string_view s) noexcept
Returns true if the string can be converted into an unsigned long without throwing an exception.
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.
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(string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
static unsigned long toULong(string_view s, Limited)
Converts string 's' to an unsigned long.
static std::string trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
static std::string & trim(std::string &s, string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
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(string_view)
Encodes the given string.
static std::string decode(string_view)
Decodes the given string.
A class like c++17's std::string_view.
Overload discrimiator for G::Str::toUWhatever() requesting a range-limited result.