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
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
75{
76 sendInit() ;
77}
78
79void 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
95void GPop::ServerProtocol::sendOk()
96{
97 sendLine( "+OK" ) ;
98}
99
100void GPop::ServerProtocol::sendError( const std::string & more )
101{
102 if( more.empty() )
103 sendError() ;
104 else
105 sendLine( "-ERR " + more ) ;
106}
107
108void GPop::ServerProtocol::sendError()
109{
110 sendLine( "-ERR" ) ;
111}
112
113void 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
142void 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
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
171bool 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
211int 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
266void GPop::ServerProtocol::doQuitEarly( const std::string & , bool & )
267{
268 sendLine( "+OK " + m_text.quit() ) ;
269 throw ProtocolDone() ;
270}
271
272void GPop::ServerProtocol::doQuit( const std::string & , bool & )
273{
274 m_store_lock.commit() ;
275 sendLine( "+OK " + m_text.quit() ) ;
276 throw ProtocolDone() ;
277}
278
279void 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
286void GPop::ServerProtocol::doUidl( const std::string & line , bool & )
287{
288 sendList( line , true ) ;
289}
290
291void GPop::ServerProtocol::doList( const std::string & line , bool & )
292{
293 sendList( line , false ) ;
294}
295
296void 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
336void 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
355void 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
374void 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
388void GPop::ServerProtocol::doRset( const std::string & , bool & )
389{
390 m_store_lock.rollback() ;
391 sendOk() ;
392}
393
394void GPop::ServerProtocol::doNoop( const std::string & , bool & )
395{
396 sendOk() ;
397}
398
399void GPop::ServerProtocol::doNothing( const std::string & , bool & )
400{
401}
402
403void 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
456void 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
482void 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
490void GPop::ServerProtocol::lockStore()
491{
492 m_store_lock.lock( m_user ) ;
493}
494
495void 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
503{
504 m_secure = true ;
505 sendOk() ; // "hello (again)"
506}
507
508bool 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
519void 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
544void 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
557void 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
583void 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
609void 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
616void 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
646const std::string & GPop::ServerProtocol::crlf()
647{
648 static const std::string s( "\015\012" ) ;
649 return s ;
650}
651
652// ===
653
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
An interface used by GAuth::SaslServer to obtain authentication secrets.
The GNet::Address class encapsulates a TCP/UDP transport address.
Definition: gaddress.h:53
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:45
static std::string decode(const std::string &, bool throw_on_invalid=false, bool strict=true)
Decodes the given string.
Definition: gbase64.cpp:89
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
static LogOutput * instance() noexcept
Returns a pointer to the controlling LogOutput object.
Definition: glogoutput.cpp:128
static string_view ws()
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1255
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
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
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
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
static int toInt(const std::string &s)
Converts string 's' to an int.
Definition: gstr.cpp:507
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
static bool enabled() noexcept
Returns true if test features are enabled.
Definition: gtest.cpp:79
SASL authentication classes.
Definition: gcram.cpp:36
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:31
A structure containing configuration parameters for ServerProtocol, currently empty.