E-MailRelay

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