E-MailRelay
gpopserverprotocol.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 gpopserverprotocol.cpp
19///
20
21#include "gdef.h"
22#include "gpopserverprotocol.h"
23#include "gsaslserverfactory.h"
24#include "gstr.h"
25#include "gstringtoken.h"
26#include "gtest.h"
27#include "gbase64.h"
28#include "gassert.h"
29#include "glog.h"
30#include <sstream>
31#include <algorithm>
32
34 const GAuth::SaslServerSecrets & server_secrets , const std::string & sasl_server_config ,
35 const Text & text , const GNet::Address & peer_address , const Config & config ) :
36 m_text(text) ,
37 m_sender(sender) ,
38 m_security(security) ,
39 m_store(store) ,
40 m_config(config) ,
41 m_sasl(GAuth::SaslServerFactory::newSaslServer(server_secrets,true,sasl_server_config,config.sasl_server_challenge_domain)) ,
42 m_peer_address(peer_address) ,
43 m_fsm(State::sStart,State::sEnd,State::s_Same,State::s_Any)
44{
45 // (dont send anything to the peer from this ctor -- the Sender object is not fuly constructed)
46
47 m_fsm( Event::eStat , State::sActive , State::sActive , &ServerProtocol::doStat ) ;
48 m_fsm( Event::eList , State::sActive , State::sActive , &ServerProtocol::doList ) ;
49 m_fsm( Event::eRetr , State::sActive , State::sData , &ServerProtocol::doRetr , State::sActive ) ;
50 m_fsm( Event::eTop , State::sActive , State::sData , &ServerProtocol::doTop , State::sActive ) ;
51 m_fsm( Event::eDele , State::sActive , State::sActive , &ServerProtocol::doDele ) ;
52 m_fsm( Event::eNoop , State::sActive , State::sActive , &ServerProtocol::doNoop ) ;
53 m_fsm( Event::eRset , State::sActive , State::sActive , &ServerProtocol::doRset ) ;
54 m_fsm( Event::eUidl , State::sActive , State::sActive , &ServerProtocol::doUidl ) ;
55 m_fsm( Event::eSent , State::sData , State::sActive , &ServerProtocol::doNothing ) ;
56 m_fsm( Event::eUser , State::sStart , State::sStart , &ServerProtocol::doUser ) ;
57 m_fsm( Event::ePass , State::sStart , State::sActive , &ServerProtocol::doPass , State::sStart ) ;
58 m_fsm( Event::eApop , State::sStart , State::sActive , &ServerProtocol::doApop , State::sStart ) ;
59 m_fsm( Event::eQuit , State::sStart , State::sEnd , &ServerProtocol::doQuitEarly ) ;
60 m_fsm( Event::eCapa , State::sStart , State::sStart , &ServerProtocol::doCapa ) ;
61 m_fsm( Event::eCapa , State::sActive , State::sActive , &ServerProtocol::doCapa ) ;
62 if( m_security.securityEnabled() )
63 m_fsm( Event::eStls , State::sStart , State::sStart , &ServerProtocol::doStls , State::sStart ) ;
64 m_fsm( Event::eAuth , State::sStart , State::sAuth , &ServerProtocol::doAuth , State::sStart ) ;
65 m_fsm( Event::eAuthData , State::sAuth , State::sAuth , &ServerProtocol::doAuthData , State::sStart ) ;
66 m_fsm( Event::eAuthComplete , State::sAuth , State::sActive , &ServerProtocol::doAuthComplete ) ;
67 m_fsm( Event::eCapa , State::sActive , State::sActive , &ServerProtocol::doCapa ) ;
68 m_fsm( Event::eQuit , State::sActive , State::sEnd , &ServerProtocol::doQuit ) ;
69}
70
72{
73 sendInit() ;
74}
75
76void GPop::ServerProtocol::sendInit()
77{
78 std::string greeting = std::string("+OK ",4U).append(m_text.greeting()) ;
79 if( m_sasl->init( m_secure , "APOP" ) )
80 {
81 m_sasl_init_apop = true ;
82 std::string apop_challenge = m_sasl->initialChallenge() ;
83 if( !apop_challenge.empty() )
84 {
85 greeting.append( 1U , ' ' ) ;
86 greeting.append( apop_challenge ) ;
87 }
88 }
89 sendLine( std::move(greeting) ) ;
90}
91
92void GPop::ServerProtocol::sendOk()
93{
94 sendLine( "+OK\r\n"_sv , true ) ;
95}
96
97void GPop::ServerProtocol::sendError( const std::string & more )
98{
99 if( more.empty() )
100 sendError() ;
101 else
102 sendLine( std::string("-ERR ",5U).append(more) ) ;
103}
104
105void GPop::ServerProtocol::sendError()
106{
107 sendLine( "-ERR\r\n"_sv , true ) ;
108}
109
110void GPop::ServerProtocol::apply( const std::string & line )
111{
112 // decode the event
113 Event event = m_fsm.state() == State::sAuth ? Event::eAuthData : commandEvent(commandWord(line)) ;
114
115 // log the input
116 std::string log_text = G::Str::printable(line) ;
117 if( event == Event::ePass )
118 log_text = (commandPart(line,0U)+" [password not logged]") ;
119 if( event == Event::eAuthData || event == Event::eAuthComplete )
120 log_text = "[authentication response not logged]" ;
121 if( event == Event::eAuth && !commandPart(line,1U).empty() )
122 log_text = commandPart(line,0U) + " " + commandPart(line,1U) ;
123 G_LOG( "GPop::ServerProtocol: rx<<: \"" << log_text << "\"" ) ;
124
125 // apply the event to the state machine
126 State new_state = m_fsm.apply( *this , event , line ) ;
127 const bool protocol_error = new_state == State::s_Any ;
128 if( protocol_error )
129 {
130 G_DEBUG( "GPop::ServerProtocol::apply: protocol error: " << static_cast<int>(event) << " " << static_cast<int>(m_fsm.state()) ) ;
131 sendError() ;
132 }
133
134 // squirt data down the pipe if appropriate
135 if( new_state == State::sData )
136 sendContent() ;
137}
138
139void GPop::ServerProtocol::sendContent()
140{
141 // send until no more content or until blocked by flow-control
142 std::string line( 200 , '.' ) ;
143 std::size_t n = 0 ;
144 bool eot = false ;
145 while( sendContentLine(line,eot) && !eot )
146 n++ ;
147
148 G_LOG( "GPop::ServerProtocol: tx>>: [" << n << " line(s) of content]" ) ;
149 if( eot )
150 {
151 G_LOG( "GPop::ServerProtocol: tx>>: \".\"" ) ;
152 m_content.reset() ; // free up resources
153 m_fsm.apply( *this , Event::eSent , "" ) ; // State::sData -> State::sActive
154 }
155}
156
158{
159 // flow control is not an issue for protocol responses because we
160 // always send a complete protocol response in one go -- however,
161 // message content is sent in chunks so the resume() has to send
162 // the next bit
163 G_DEBUG( "GPop::ServerProtocol::resume: flow control released" ) ;
164 if( m_fsm.state() == State::sData )
165 sendContent() ;
166}
167
168bool GPop::ServerProtocol::sendContentLine( std::string & line , bool & eot )
169{
170 G_ASSERT( m_content != nullptr ) ;
171
172 bool limited = m_in_body && m_body_limit == 0L ;
173 if( m_body_limit > 0L && m_in_body )
174 m_body_limit-- ;
175
176 line.erase( 1U ) ; // leave "."
177 bool eof = !G::Str::readLine( *m_content , line ,
178 m_config.crlf_only ? G::Str::Eol::CrLf : G::Str::Eol::Cr_Lf_CrLf ,
179 /*pre_erase_result=*/false ) ;
180
181 eot = eof || limited ;
182 if( eot ) line.erase( 1U ) ;
183 line.append( "\r\n" , 2U ) ;
184 std::size_t offset = eot ? 0U : ( line.at(1U) == '.' ? 0U : 1U ) ;
185
186 if( !m_in_body && line.length() == (offset+2U) )
187 m_in_body = true ;
188
189 return m_sender.protocolSend( line , offset ) ;
190}
191
192int GPop::ServerProtocol::commandNumber( const std::string & line , int default_ , std::size_t index ) const
193{
194 int number = default_ ;
195 try
196 {
197 number = G::Str::toInt( commandParameter(line,index) ) ;
198 }
199 catch( G::Str::Overflow & ) // defaulted
200 {
201 }
202 catch( G::Str::InvalidFormat & ) // defaulted
203 {
204 }
205 return number ;
206}
207
208std::string GPop::ServerProtocol::commandWord( const std::string & line ) const
209{
210 return G::Str::upper(commandPart(line,0U)) ;
211}
212
213std::string GPop::ServerProtocol::commandPart( const std::string & line , std::size_t index )
214{
215 std::string_view line_sv( line ) ;
216 G::StringTokenView t( line_sv , G::Str::ws() ) ;
217 for( std::size_t i = 0 ; i < index ; ++t , i++ )
218 {;}
219 return t.valid() ? G::sv_to_string(t()) : std::string() ;
220}
221
222std::string GPop::ServerProtocol::commandParameter( const std::string & line_in , std::size_t index ) const
223{
224 return commandPart( line_in , index ) ;
225}
226
227GPop::ServerProtocol::Event GPop::ServerProtocol::commandEvent( std::string_view command )
228{
229 if( command == "QUIT"_sv ) return Event::eQuit ;
230 if( command == "STAT"_sv ) return Event::eStat ;
231 if( command == "LIST"_sv ) return Event::eList ;
232 if( command == "RETR"_sv ) return Event::eRetr ;
233 if( command == "DELE"_sv ) return Event::eDele ;
234 if( command == "NOOP"_sv ) return Event::eNoop ;
235 if( command == "RSET"_sv ) return Event::eRset ;
236 //
237 if( command == "TOP"_sv ) return Event::eTop ;
238 if( command == "UIDL"_sv ) return Event::eUidl ;
239 if( command == "USER"_sv ) return Event::eUser ;
240 if( command == "PASS"_sv ) return Event::ePass ;
241 if( command == "APOP"_sv ) return Event::eApop ;
242 if( command == "AUTH"_sv ) return Event::eAuth ;
243 if( command == "CAPA"_sv ) return Event::eCapa ;
244 if( command == "STLS"_sv ) return Event::eStls ;
245
246 return Event::eUnknown ;
247}
248
249void GPop::ServerProtocol::doQuitEarly( const std::string & , bool & )
250{
251 sendLine( std::string("+OK ",4).append(m_text.quit()) ) ;
252 throw ProtocolDone() ;
253}
254
255void GPop::ServerProtocol::doQuit( const std::string & , bool & )
256{
257 m_store_list.commit() ;
258 sendLine( std::string("+OK ",4U).append(m_text.quit()) ) ;
259 throw ProtocolDone() ;
260}
261
262void GPop::ServerProtocol::doStat( const std::string & , bool & )
263{
264 sendLine( std::string("+OK ",4U)
265 .append(std::to_string(m_store_list.messageCount()))
266 .append(1U,' ')
267 .append(std::to_string(m_store_list.totalByteCount())) ) ;
268}
269
270void GPop::ServerProtocol::doUidl( const std::string & line , bool & )
271{
272 sendList( line , true ) ;
273}
274
275void GPop::ServerProtocol::doList( const std::string & line , bool & )
276{
277 sendList( line , false ) ;
278}
279
280void GPop::ServerProtocol::sendList( const std::string & line , bool uidl )
281{
282 std::string id_string = commandParameter( line ) ;
283
284 // parse and check the id if supplied
285 int id = -1 ;
286 if( !id_string.empty() )
287 {
288 id = commandNumber( line , -1 ) ;
289 if( !m_store_list.valid(id) )
290 {
291 sendError( "invalid id" ) ;
292 return ;
293 }
294 }
295
296 // send back the list with sizes or uidls
297 std::ostringstream ss ;
298 ss << "+OK " ;
299 bool multi_line = id == -1 ;
300 if( multi_line )
301 {
302 ss << m_store_list.messageCount() << " message(s)" << "\r\n" ;
303 std::size_t i = 1 ;
304 for( auto item_p = m_store_list.cbegin() ; item_p != m_store_list.cend() ; ++item_p , ++i )
305 {
306 const auto & item = *item_p ;
307 if( !item.deleted )
308 {
309 ss << i << " " ;
310 if( uidl ) ss << item.uidl() ;
311 if( !uidl ) ss << item.size ;
312 ss << "\r\n" ;
313 }
314 }
315 ss << "." ;
316 sendLines( ss ) ;
317 }
318 else
319 {
320 auto item = m_store_list.get( id ) ;
321 ss << id << " " ;
322 if( uidl ) ss << item.uidl() ;
323 if( !uidl ) ss << item.size ;
324 sendLine( ss.str() ) ;
325 }
326}
327
328void GPop::ServerProtocol::doRetr( const std::string & line , bool & more )
329{
330 int id = commandNumber( line , -1 ) ;
331 if( id == -1 || !m_store_list.valid(id) )
332 {
333 more = false ; // stay in the same state
334 sendError() ;
335 }
336 else
337 {
338 m_content = m_store_list.content(id) ;
339 m_body_limit = -1L ;
340
341 std::ostringstream ss ;
342 ss << "+OK " << m_store_list.byteCount(id) << " octets" ;
343 sendLine( ss.str() ) ;
344 }
345}
346
347void GPop::ServerProtocol::doTop( const std::string & line , bool & more )
348{
349 int id = commandNumber( line , -1 , 1U ) ;
350 int n = commandNumber( line , -1 , 2U ) ;
351 G_DEBUG( "ServerProtocol::doTop: " << id << ", " << n ) ;
352 if( id == -1 || !m_store_list.valid(id) || n < 0 )
353 {
354 more = false ; // stay in the same state
355 sendError() ;
356 }
357 else
358 {
359 m_content = m_store_list.content( id ) ;
360 m_body_limit = n ;
361 m_in_body = false ;
362 sendOk() ;
363 }
364}
365
366void GPop::ServerProtocol::doDele( const std::string & line , bool & )
367{
368 int id = commandNumber( line , -1 ) ;
369 if( id == -1 || !m_store_list.valid(id) )
370 {
371 sendError() ;
372 }
373 else
374 {
375 m_store_list.remove( id ) ;
376 sendOk() ;
377 }
378}
379
380void GPop::ServerProtocol::doRset( const std::string & , bool & )
381{
382 m_store_list.rollback() ;
383 sendOk() ;
384}
385
386void GPop::ServerProtocol::doNoop( const std::string & , bool & )
387{
388 sendOk() ;
389}
390
391void GPop::ServerProtocol::doNothing( const std::string & , bool & )
392{
393}
394
395void GPop::ServerProtocol::doAuth( const std::string & line , bool & ok )
396{
397 std::string mechanism = G::Str::upper( commandParameter(line) ) ;
398
399 if( mechanism.empty() )
400 {
401 // non-standard, but some clients expect a list of mechanisms
402 ok = false ; // => stay in start state
403 std::string list = mechanisms() ;
404 G::Str::replaceAll( list , " "_sv , "\r\n"_sv ) ;
405 std::ostringstream ss ;
406 ss << "+OK\r\n" ;
407 if( !list.empty() )
408 ss << list << "\r\n" ;
409 ss << "." ;
410 sendLines( ss ) ;
411 }
412 else if( mechanisms().empty() )
413 {
414 ok = false ;
415 sendError( "must use STLS before authentication" ) ;
416 }
417 else
418 {
419 std::string initial_response = commandParameter(line,2) ;
420 if( initial_response == "=" )
421 initial_response.clear() ; // RFC-5034
422
423 m_sasl_init_apop = false ;
424 if( !m_sasl->init( m_secure , mechanism ) )
425 {
426 ok = false ;
427 sendError( "invalid mechanism" ) ;
428 }
429 else if( m_sasl->mustChallenge() && !initial_response.empty() )
430 {
431 ok = false ;
432 sendError( "invalid initial response" ) ;
433 }
434 else if( !initial_response.empty() )
435 {
436 m_fsm.apply( *this , Event::eAuthData , initial_response ) ;
437 }
438 else
439 {
440 std::string initial_challenge = m_sasl->initialChallenge() ;
441 sendLine( std::string("+ ",2U).append(G::Base64::encode(initial_challenge)) ) ;
442 }
443 }
444}
445
446void GPop::ServerProtocol::doAuthData( const std::string & line , bool & ok )
447{
448 if( line == "*" )
449 {
450 ok = false ;
451 sendError() ;
452 return ;
453 }
454
455 bool done = false ;
456 std::string challenge = m_sasl->apply( G::Base64::decode(line) , done ) ;
457 if( done && m_sasl->authenticated() )
458 {
459 m_fsm.apply( *this , Event::eAuthComplete , "" ) ;
460 }
461 else if( done )
462 {
463 ok = false ; // => start
464 sendError() ;
465 }
466 else
467 {
468 sendLine( std::string("+ ",2U).append(G::Base64::encode(challenge)) ) ;
469 }
470}
471
472void GPop::ServerProtocol::doAuthComplete( const std::string & , bool & )
473{
474 G_LOG_S( "GPop::ServerProtocol: pop authentication of " << m_sasl->id() << " connected from " << m_peer_address.displayString() ) ;
475 m_user = m_sasl->id() ;
476 m_store.prepare( m_user ) ;
477 readStore( m_user ) ;
478 sendOk() ;
479}
480
481void GPop::ServerProtocol::readStore( const std::string & user )
482{
483 m_store_user = std::make_unique<StoreUser>( m_store , user ) ;
484 m_store_list = StoreList( *m_store_user , m_store.allowDelete() ) ;
485}
486
487void GPop::ServerProtocol::doStls( const std::string & , bool & )
488{
489 G_ASSERT( m_security.securityEnabled() ) ;
490 sendOk() ; // "please start tls"
491 m_security.securityStart() ;
492}
493
495{
496 m_secure = true ;
497 sendOk() ; // "hello (again)"
498}
499
500bool GPop::ServerProtocol::mechanismsIncludePlain() const
501{
502 return mechanisms().find("PLAIN") != std::string::npos ;
503}
504
505std::string GPop::ServerProtocol::mechanisms() const
506{
507 G::StringArray m = m_sasl->mechanisms( m_secure ) ;
508 m.erase( std::remove( m.begin() , m.end() , "APOP" ) , m.end() ) ;
509 return G::Str::join( " " , m ) ;
510}
511
512void GPop::ServerProtocol::doCapa( const std::string & , bool & )
513{
514 std::ostringstream ss ;
515 ss << "+OK " << m_text.capa() << "\r\n" ;
516
517 // USER/PASS POP3 authentication uses the PLAIN SASL mechanism
518 // so only advertise it if it is available
519 if( mechanismsIncludePlain() )
520 ss << "USER\r\n" ;
521
522 ss << "CAPA\r\nTOP\r\nUIDL\r\n" ;
523
524 if( m_security.securityEnabled() )
525 ss << "STLS\r\n" ;
526
527 if( !mechanisms().empty() )
528 ss << "SASL " << mechanisms() << "\r\n" ;
529
530 ss << "." ;
531 sendLines( ss ) ;
532}
533
534void GPop::ServerProtocol::doUser( const std::string & line , bool & )
535{
536 if( mechanismsIncludePlain() )
537 {
538 m_user = commandParameter(line) ;
539 sendLine( std::string("+OK ",4U).append(m_text.user(commandParameter(line))) ) ;
540 }
541 else
542 {
543 sendError( "no SASL PLAIN mechanism to do USER/PASS authentication" ) ;
544 }
545}
546
547void GPop::ServerProtocol::doPass( const std::string & line , bool & ok )
548{
549 m_sasl_init_apop = false ;
550 if( !m_user.empty() && m_sasl->init(m_secure,"PLAIN") ) // (USER/PASS uses SASL PLAIN)
551 {
552 std::string rsp = m_user + std::string(1U,'\0').append(m_user).append(1U,'\0').append(commandParameter(line)) ;
553 bool done = false ;
554 GDEF_IGNORE_RETURN m_sasl->apply( rsp , done ) ;
555 if( done && m_sasl->authenticated() )
556 {
557 m_store.prepare( m_user ) ;
558 readStore( m_user ) ;
559 sendOk() ;
560 }
561 else
562 {
563 ok = false ;
564 sendError() ;
565 }
566 }
567 else
568 {
569 ok = false ;
570 sendError() ;
571 }
572}
573
574void GPop::ServerProtocol::doApop( const std::string & line , bool & ok )
575{
576 if( m_sasl_init_apop )
577 {
578 std::string rsp = commandParameter(line,1) + " " + commandParameter(line,2) ;
579 bool done = false ;
580 GDEF_IGNORE_RETURN m_sasl->apply( rsp , done ) ;
581 if( done && m_sasl->authenticated() )
582 {
583 m_user = m_sasl->id() ;
584 m_store.prepare( m_user ) ;
585 readStore( m_user ) ;
586 sendOk() ;
587 }
588 else
589 {
590 ok = false ;
591 sendError() ;
592 }
593 }
594 else
595 {
596 ok = false ;
597 sendError() ;
598 }
599}
600
601void GPop::ServerProtocol::sendLine( std::string_view line , bool has_crlf )
602{
603 if( has_crlf )
604 {
605 G_LOG( "GPop::ServerProtocol: tx>>: \"" << G::Str::printable(G::Str::trimmedView(line,"\r\n"_sv)) << "\"" ) ;
606 m_sender.protocolSend( line , 0U ) ;
607 }
608 else
609 {
610 G_LOG( "GPop::ServerProtocol: tx>>: \"" << G::Str::printable(line) << "\"" ) ;
611 m_sender.protocolSend( std::string(line.data(),line.size()).append("\r\n",2U) , 0U ) ;
612 }
613}
614
615void GPop::ServerProtocol::sendLine( std::string && line ) // NOLINT cppcoreguidelines-rvalue-reference-param-not-moved
616{
617 G_LOG( "GPop::ServerProtocol: tx>>: \"" << G::Str::printable(line) << "\"" ) ;
618 m_sender.protocolSend( line.append("\r\n",2U) , 0U ) ;
619}
620
621void GPop::ServerProtocol::sendLines( std::ostringstream & ss )
622{
623 ss << "\r\n" ;
624 const std::string s = ss.str() ;
625 if( G::LogOutput::Instance::atVerbose() )
626 {
627 std::size_t lines = std::count( s.begin() , s.end() , '\n' ) ;
628 const std::size_t npos = std::string::npos ;
629 std::size_t p0 = 0U ;
630 std::size_t p1 = s.find( '\n' ) ;
631 for( std::size_t i = 0U ; i < lines ; i++ , p0 = p1+1U , p1 = s.find('\n',p0+1U) )
632 {
633 G_ASSERT( p0 != npos && p0 < s.size() ) ;
634 std::size_t n = p1 == npos ? (s.size()-p0) : (p1-p0) ;
635 if( n && p1 && p1 != npos && s.at(p1-1U) == '\r' ) --n ;
636 if( lines <= 7U || i < 4U || i > (lines-3U) )
637 G_LOG( "GPop::ServerProtocol: tx>>: \"" << G::Str::printable(s.substr(p0,n)) << "\"" ) ;
638 else if( i == 4U )
639 G_LOG( "GPop::ServerProtocol: tx>>: [" << (lines-6U) << " lines]" ) ;
640 if( p1 == npos || (p1+1U) == s.size() )
641 break ;
642 }
643 }
644 m_sender.protocolSend( s , 0U ) ;
645}
646
647// ===
648
650{
651}
652
653std::string GPop::ServerProtocolText::greeting() const
654{
655 return "POP3 server ready" ;
656}
657
658std::string GPop::ServerProtocolText::quit() const
659{
660 return "signing off" ;
661}
662
663std::string GPop::ServerProtocolText::capa() const
664{
665 return "capability list follows" ;
666}
667
668std::string GPop::ServerProtocolText::user( const std::string & id ) const
669{
670 return std::string("user: ",6U).append(id) ;
671}
672
An interface used by GAuth::SaslServer to obtain authentication secrets.
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:63
ServerProtocolText(const GNet::Address &peer)
Constructor.
An interface used by ServerProtocol to enable TLS.
An interface used by ServerProtocol to send protocol replies.
An interface used by ServerProtocol to provide response text strings.
void resume()
Called when the Sender can send again.
void secure()
Called when the server connection becomes secure.
void init()
Starts the protocol.
void apply(const std::string &line)
Called on receipt of a string from the client.
ServerProtocol(Sender &sender, Security &security, Store &store, const GAuth::SaslServerSecrets &server_secrets, const std::string &sasl_server_config, const Text &text, const GNet::Address &peer_address, const Config &config)
Constructor.
A message store.
Definition: gpopstore.h:47
static std::string encode(std::string_view, std::string_view line_break={})
Encodes the given string, optionally inserting line-breaks to limit the line length.
Definition: gbase64.cpp:79
static std::string decode(std::string_view, bool throw_on_invalid=false, bool strict=true)
Decodes the given string.
Definition: gbase64.cpp:84
static std::string_view trimmedView(std::string_view s, std::string_view ws) noexcept
Returns a trim()med view of the input view.
Definition: gstr.cpp:351
static int toInt(std::string_view s)
Converts string 's' to an int.
Definition: gstr.cpp:538
static std::string join(std::string_view sep, const StringArray &strings)
Concatenates an array of strings with separators.
Definition: gstr.cpp:1221
static std::istream & readLine(std::istream &stream, std::string &result, std::string_view eol={}, bool pre_erase_result=true, std::size_t limit=0U)
Reads a line from the stream using the given line terminator, which may be multi-character.
Definition: gstr.cpp:958
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 ...
Definition: gstr.cpp:913
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...
Definition: gstr.cpp:836
static unsigned int replaceAll(std::string &s, std::string_view from, std::string_view to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
Definition: gstr.cpp:247
static std::string_view ws() noexcept
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1265
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
Definition: gstringtoken.h:54
SASL authentication classes.
Definition: gcram.cpp:38
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstringarray.h:30
STL namespace.
A structure containing configuration parameters for ServerProtocol.