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