35 m_security(security) ,
37 m_store_lock(m_store) ,
40 m_peer_address(peer_address) ,
41 m_fsm(sStart,sEnd,s_Same,s_Any) ,
48 m_fsm.
addTransition( eStat , sActive , sActive , &GPop::ServerProtocol::doStat ) ;
49 m_fsm.
addTransition( eList , sActive , sActive , &GPop::ServerProtocol::doList ) ;
50 m_fsm.
addTransition( eRetr , sActive , sData , &GPop::ServerProtocol::doRetr , sActive ) ;
51 m_fsm.
addTransition( eTop , sActive , sData , &GPop::ServerProtocol::doTop , sActive ) ;
52 m_fsm.
addTransition( eDele , sActive , sActive , &GPop::ServerProtocol::doDele ) ;
53 m_fsm.
addTransition( eNoop , sActive , sActive , &GPop::ServerProtocol::doNoop ) ;
54 m_fsm.
addTransition( eRset , sActive , sActive , &GPop::ServerProtocol::doRset ) ;
55 m_fsm.
addTransition( eUidl , sActive , sActive , &GPop::ServerProtocol::doUidl ) ;
56 m_fsm.
addTransition( eSent , sData , sActive , &GPop::ServerProtocol::doNothing ) ;
57 m_fsm.
addTransition( eUser , sStart , sStart , &GPop::ServerProtocol::doUser ) ;
58 m_fsm.
addTransition( ePass , sStart , sActive , &GPop::ServerProtocol::doPass , sStart ) ;
59 m_fsm.
addTransition( eApop , sStart , sActive , &GPop::ServerProtocol::doApop , sStart ) ;
60 m_fsm.
addTransition( eQuit , sStart , sEnd , &GPop::ServerProtocol::doQuitEarly ) ;
61 m_fsm.
addTransition( eCapa , sStart , sStart , &GPop::ServerProtocol::doCapa ) ;
62 m_fsm.
addTransition( eCapa , sActive , sActive , &GPop::ServerProtocol::doCapa ) ;
64 m_fsm.
addTransition( eStls , sStart , sStart , &GPop::ServerProtocol::doStls , sStart ) ;
65 m_fsm.
addTransition( eAuth , sStart , sAuth , &GPop::ServerProtocol::doAuth , sStart ) ;
66 m_fsm.
addTransition( eAuthData , sAuth , sActive , &GPop::ServerProtocol::doAuthData , sStart ) ;
67 m_fsm.
addTransition( eCapa , sActive , sActive , &GPop::ServerProtocol::doCapa ) ;
68 m_fsm.
addTransition( eQuit , sActive , sEnd , &GPop::ServerProtocol::doQuit ) ;
80 void GPop::ServerProtocol::sendInit()
82 std::string greeting = std::string() +
"+OK " + m_text.greeting() ;
83 std::string apop_challenge = m_auth.challenge() ;
84 if( ! apop_challenge.empty() )
86 greeting.append(
" " ) ;
87 greeting.append( apop_challenge ) ;
92 void GPop::ServerProtocol::sendOk()
97 void GPop::ServerProtocol::sendError(
const std::string & more )
102 send( std::string() +
"-ERR " + more ) ;
105 void GPop::ServerProtocol::sendError()
113 Event
event = m_fsm.state() == sAuth ? eAuthData : commandEvent(commandWord(line)) ;
118 log_text = (commandPart(line,0U)+
" [password not logged]") ;
119 if( event == eAuthData )
120 log_text = (
"[password not logged]") ;
121 if( event == eAuth && !commandPart(line,1U).empty() )
122 log_text = (commandPart(line,0U)+
" "+commandPart(line,1U) +
" [password not logged]") ;
123 G_LOG(
"GPop::ServerProtocol: rx<<: \"" << log_text <<
"\"" ) ;
126 State new_state = m_fsm.apply( *
this , event , line ) ;
127 const bool protocol_error = new_state == s_Any ;
132 if( new_state == sData )
136 void GPop::ServerProtocol::sendContent()
139 std::string line( 200 ,
'.' ) ;
141 bool end_of_content = false ;
142 while( sendContentLine(line,end_of_content) )
145 G_LOG(
"GPop::ServerProtocol: tx>>: [" << n <<
" line(s) of content]" ) ;
149 G_LOG(
"GPop::ServerProtocol: tx>>: ." ) ;
151 m_fsm.apply( *
this , eSent ,
"" ) ;
157 G_DEBUG(
"GPop::ServerProtocol::resume: flow control released" ) ;
158 if( m_fsm.state() == sData )
162 bool GPop::ServerProtocol::sendContentLine( std::string & line ,
bool & stop )
164 G_ASSERT( m_content.get() != NULL ) ;
167 bool limited = m_in_body && m_body_limit == 0L ;
168 if( m_body_limit > 0L && m_in_body )
176 bool eof = m_content->fail() || m_content->bad() ;
181 line.append( crlf() ) ;
185 line.append( crlf() ) ;
186 offset = line.at(1U) ==
'.' ? 0U : 1U ;
190 if( !m_in_body && line.length() == (offset+2U) )
194 bool line_fully_sent = m_sender.protocolSend( line , offset ) ;
197 stop = ( limited || eof ) && line_fully_sent ;
198 const bool pause = limited || eof || ! line_fully_sent ;
202 int GPop::ServerProtocol::commandNumber(
const std::string & line ,
int default_ ,
size_t index )
const
204 int number = default_ ;
209 catch( G::Str::Overflow & )
212 catch( G::Str::InvalidFormat & )
218 std::string GPop::ServerProtocol::commandWord(
const std::string & line )
const
223 std::string GPop::ServerProtocol::commandPart(
const std::string & line ,
size_t index )
const
227 if( index >= part.size() )
return std::string() ;
228 G::Strings::iterator p = part.begin() ;
229 for( ; index > 0 ; ++p , index-- ) ;
233 std::string GPop::ServerProtocol::commandParameter(
const std::string & line_in ,
size_t index )
const
235 return commandPart( line_in , index ) ;
238 GPop::ServerProtocol::Event GPop::ServerProtocol::commandEvent(
const std::string & command )
const
240 if( command ==
"QUIT" )
return eQuit ;
241 if( command ==
"STAT" )
return eStat ;
242 if( command ==
"LIST" )
return eList ;
243 if( command ==
"RETR" )
return eRetr ;
244 if( command ==
"DELE" )
return eDele ;
245 if( command ==
"NOOP" )
return eNoop ;
246 if( command ==
"RSET" )
return eRset ;
248 if( command ==
"TOP" )
return eTop ;
249 if( command ==
"UIDL" )
return eUidl ;
250 if( command ==
"USER" )
return eUser ;
251 if( command ==
"PASS" )
return ePass ;
252 if( command ==
"APOP" )
return eApop ;
253 if( command ==
"AUTH" )
return eAuth ;
254 if( command ==
"CAPA" )
return eCapa ;
255 if( command ==
"STLS" )
return eStls ;
260 void GPop::ServerProtocol::doQuitEarly(
const std::string & ,
bool & )
262 send( std::string() +
"+OK " + m_text.quit() ) ;
263 throw ProtocolDone() ;
266 void GPop::ServerProtocol::doQuit(
const std::string & ,
bool & )
268 m_store_lock.commit() ;
269 send( std::string() +
"+OK " + m_text.quit() ) ;
270 throw ProtocolDone() ;
273 void GPop::ServerProtocol::doStat(
const std::string & ,
bool & )
275 std::ostringstream ss ;
276 ss <<
"+OK " << m_store_lock.messageCount() <<
" " << m_store_lock.totalByteCount() ;
280 void GPop::ServerProtocol::doUidl(
const std::string & line ,
bool & )
282 sendList( line ,
true ) ;
285 void GPop::ServerProtocol::doList(
const std::string & line ,
bool & )
287 sendList( line ,
false ) ;
290 void GPop::ServerProtocol::sendList(
const std::string & line ,
bool uidl )
292 std::string id_string = commandParameter( line ) ;
296 if( ! id_string.empty() )
298 id = commandNumber( line , -1 ) ;
299 if( !m_store_lock.valid(
id) )
301 sendError(
"invalid id" ) ;
307 bool multi_line =
id == -1 ;
309 std::ostringstream ss ;
311 if( multi_line ) ss << list.size() <<
" message(s)" << crlf() ;
312 for( GPop::StoreLock::List::iterator p = list.begin() ; p != list.end() ; ++p )
314 ss << (*p).id <<
" " ;
315 if( uidl ) ss << (*p).uidl ;
316 if( !uidl ) ss << (*p).size ;
317 if( multi_line ) ss << crlf() ;
319 if( multi_line ) ss <<
"." ;
323 void GPop::ServerProtocol::doRetr(
const std::string & line ,
bool & more )
325 int id = commandNumber( line , -1 ) ;
326 if(
id == -1 || ! m_store_lock.valid(
id) )
333 std::auto_ptr<std::istream> content( m_store_lock.get(
id) ) ;
334 m_content <<= content.release() ;
337 std::ostringstream ss ;
338 ss <<
"+OK " << m_store_lock.byteCount(
id) <<
" octets" ;
343 void GPop::ServerProtocol::doTop(
const std::string & line ,
bool & more )
345 int id = commandNumber( line , -1 , 1U ) ;
346 int n = commandNumber( line , -1 , 2U ) ;
347 G_DEBUG(
"ServerProtocol::doTop: " <<
id <<
", " << n ) ;
348 if(
id == -1 || ! m_store_lock.valid(
id) || n < 0 )
355 std::auto_ptr<std::istream> content( m_store_lock.get(
id) ) ;
356 m_content <<= content.release() ;
363 void GPop::ServerProtocol::doDele(
const std::string & line ,
bool & )
365 int id = commandNumber( line , -1 ) ;
366 if(
id == -1 || ! m_store_lock.valid(
id) )
372 m_store_lock.remove(
id ) ;
377 void GPop::ServerProtocol::doRset(
const std::string & ,
bool & )
379 m_store_lock.rollback() ;
383 void GPop::ServerProtocol::doNoop(
const std::string & ,
bool & )
388 void GPop::ServerProtocol::doNothing(
const std::string & ,
bool & )
392 void GPop::ServerProtocol::doAuth(
const std::string & line ,
bool & ok )
394 std::string mechanism =
G::Str::upper( commandParameter(line) ) ;
396 if( mechanism.empty() )
400 std::string list = m_auth.mechanisms() ;
406 else if( m_auth.sensitive() && ! m_secure )
411 sendError(
"must use STLS before authentication" ) ;
415 std::string initial_response = commandParameter(line,2) ;
416 if( initial_response ==
"=" )
417 initial_response = std::string() ;
422 bool supported = mechanism !=
"LOGIN" && m_auth.init( mechanism ) ;
426 sendError(
"invalid mechanism" ) ;
428 else if( m_auth.mustChallenge() && !initial_response.empty() )
431 sendError(
"invalid initial response" ) ;
433 else if( !initial_response.empty() )
435 m_fsm.apply( *
this , eAuthData , initial_response ) ;
439 std::string initial_challenge = m_auth.challenge() ;
445 void GPop::ServerProtocol::doAuthData(
const std::string & line ,
bool & ok )
452 m_user = m_auth.id() ;
461 void GPop::ServerProtocol::lockStore()
463 m_store_lock.lock( m_user ) ;
464 G_LOG_S(
"GPop::ServerProtocol: pop authentication of " << m_user
465 <<
" connected from " << m_peer_address.displayString() ) ;
468 void GPop::ServerProtocol::doStls(
const std::string & ,
bool & )
470 G_ASSERT( m_security.securityEnabled() ) ;
472 m_security.securityStart() ;
481 bool GPop::ServerProtocol::mechanismsIncludePlain()
const
483 return m_auth.valid() && m_auth.mechanisms().find(
"PLAIN") != std::string::npos ;
486 std::string GPop::ServerProtocol::mechanismsWithoutLogin()
const
491 result = m_auth.mechanisms() ;
498 void GPop::ServerProtocol::doCapa(
const std::string & ,
bool & )
500 send( std::string() +
"+OK " + m_text.capa() ) ;
504 if( mechanismsIncludePlain() )
511 if( m_security.securityEnabled() )
517 std::string mechanisms = std::string(1U,
' ') + mechanismsWithoutLogin() ;
518 if( mechanisms.length() > 1U )
519 send( std::string() +
"SASL" + mechanisms ) ;
524 void GPop::ServerProtocol::doUser(
const std::string & line ,
bool & )
526 if( mechanismsIncludePlain() )
528 m_user = commandParameter(line) ;
529 send( std::string() +
"+OK " + m_text.user(commandParameter(line)) ) ;
533 sendError(
"no SASL PLAIN mechanism to do USER/PASS authentication" ) ;
537 void GPop::ServerProtocol::doPass(
const std::string & line ,
bool & ok )
540 std::string rsp = m_user + std::string(1U,
'\0') + m_user + std::string(1U,
'\0') + commandParameter(line) ;
541 ok = !m_user.empty() && m_auth.valid() && m_auth.init(
"PLAIN") &&
542 m_auth.authenticated( rsp , std::string() ) ;
554 void GPop::ServerProtocol::doApop(
const std::string & line ,
bool & ok )
556 m_user = commandParameter(line,1) ;
557 std::string rsp = m_user +
" " + commandParameter(line,2) ;
558 ok = m_auth.valid() && m_auth.init(
"APOP") && m_auth.authenticated(rsp,std::string()) ;
566 m_user = std::string() ;
571 void GPop::ServerProtocol::send( std::string line )
574 line.append( crlf() ) ;
575 m_sender.protocolSend( line , 0U ) ;
578 const std::string & GPop::ServerProtocol::crlf()
580 static const std::string s(
"\015\012" ) ;
592 return "POP3 server ready" ;
597 return "signing off" ;
602 return "capability list follows" ;
607 return std::string() +
"user: " + id ;
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
virtual bool securityEnabled() const =0
A simple interface to a store of secrets as used in authentication.
virtual std::string quit() const
Final override from GPop::ServerProtocol::Text.
static int toInt(const std::string &s)
Converts string 's' to an int.
std::list< std::string > Strings
A std::list of std::strings.
virtual std::string greeting() const
Final override from GPop::ServerProtocol::Text.
The Address class encapsulates an IP transport address.
virtual std::string capa() const
Final override from GPop::ServerProtocol::Text.
static void splitIntoTokens(const std::string &in, Strings &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
void init()
Starts the protocol.
virtual ~ServerProtocol()
Destructor.
An interface used by ServerProtocol to send protocol replies.
An interface used by ServerProtocol to enable TLS.
ServerProtocolText(GNet::Address peer)
Constructor.
static bool replace(std::string &s, const std::string &from, const std::string &to, size_type *pos_p=NULL)
Replaces 'from' with 'to', starting at offset '*pos_p'.
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurences of sub-string 'from' with 'to'...
An interface used by ServerProtocol to provide response text strings.
virtual std::string user(const std::string &id) const
Final override from GPop::ServerProtocol::Text.
static std::string upper(const std::string &s)
Returns a copy of 's' in which all lowercase characters have been replaced by uppercase characters...
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
ServerProtocol(Sender &sender, Security &security, Store &store, const Secrets &secrets, const Text &text, GNet::Address peer_address, Config config)
Constructor.
A structure containing configuration parameters for ServerProtocol. NOT USED.
void addTransition(Event event, State from, State to, Action action)
Adds a transition.
void secure()
Called when the server connection becomes secure.
static std::string decode(const std::string &)
Decodes the given string.
void apply(const std::string &line)
Called on receipt of a string from the client.
static std::string encode(const std::string &s, const std::string &line_break)
Encodes the given string.
void resume()
Called when the Sender can send again.