gloox 1.0
|
00001 /* 00002 Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net> 00003 This file is part of the gloox library. http://camaya.net/gloox 00004 00005 This software is distributed under a license. The full license 00006 agreement can be found in the file LICENSE in this distribution. 00007 This software may not be copied, modified, sold or distributed 00008 other than expressed in the named license agreement. 00009 00010 This software is distributed without any warranty. 00011 */ 00012 00013 #include "config.h" 00014 00015 #include "client.h" 00016 #include "capabilities.h" 00017 #include "rostermanager.h" 00018 #include "disco.h" 00019 #include "error.h" 00020 #include "logsink.h" 00021 #include "nonsaslauth.h" 00022 #include "prep.h" 00023 #include "stanzaextensionfactory.h" 00024 #include "stanzaextension.h" 00025 #include "tag.h" 00026 #include "tlsbase.h" 00027 #include "util.h" 00028 00029 #if !defined( _WIN32 ) && !defined( _WIN32_WCE ) 00030 # include <unistd.h> 00031 #endif 00032 00033 #include <cstdio> 00034 00035 namespace gloox 00036 { 00037 00038 // ---- Client::ResourceBind ---- 00039 Client::ResourceBind::ResourceBind( const std::string& resource, bool bind ) 00040 : StanzaExtension( ExtResourceBind ), m_jid( JID() ), m_bind( bind ) 00041 { 00042 prep::resourceprep( resource, m_resource ); 00043 m_valid = true; 00044 } 00045 00046 Client::ResourceBind::ResourceBind( const Tag* tag ) 00047 : StanzaExtension( ExtResourceBind ), m_resource( EmptyString ), m_bind( true ) 00048 { 00049 if( !tag ) 00050 return; 00051 00052 if( tag->name() == "unbind" ) 00053 m_bind = false; 00054 else if( tag->name() == "bind" ) 00055 m_bind = true; 00056 else 00057 return; 00058 00059 if( tag->hasChild( "jid" ) ) 00060 m_jid.setJID( tag->findChild( "jid" )->cdata() ); 00061 else if( tag->hasChild( "resource" ) ) 00062 m_resource = tag->findChild( "resource" )->cdata(); 00063 00064 m_valid = true; 00065 } 00066 00067 Client::ResourceBind::~ResourceBind() 00068 { 00069 } 00070 00071 const std::string& Client::ResourceBind::filterString() const 00072 { 00073 static const std::string filter = "/iq/bind[@xmlns='" + XMLNS_STREAM_BIND + "']" 00074 "|/iq/unbind[@xmlns='" + XMLNS_STREAM_BIND + "']"; 00075 return filter; 00076 } 00077 00078 Tag* Client::ResourceBind::tag() const 00079 { 00080 if( !m_valid ) 00081 return 0; 00082 00083 Tag* t = new Tag( m_bind ? "bind" : "unbind" ); 00084 t->setXmlns( XMLNS_STREAM_BIND ); 00085 00086 if( m_bind && m_resource.empty() && m_jid ) 00087 new Tag( t, "jid", m_jid.full() ); 00088 else 00089 new Tag( t, "resource", m_resource ); 00090 00091 return t; 00092 } 00093 // ---- ~Client::ResourceBind ---- 00094 00095 // ---- Client::SessionCreation ---- 00096 Tag* Client::SessionCreation::tag() const 00097 { 00098 Tag* t = new Tag( "session" ); 00099 t->setXmlns( XMLNS_STREAM_SESSION ); 00100 return t; 00101 } 00102 // ---- Client::SessionCreation ---- 00103 00104 // ---- Client ---- 00105 Client::Client( const std::string& server ) 00106 : ClientBase( XMLNS_CLIENT, server ), 00107 m_rosterManager( 0 ), m_auth( 0 ), 00108 m_presence( Presence::Available, JID() ), m_resourceBound( false ), 00109 m_forceNonSasl( false ), m_manageRoster( true ), 00110 m_streamFeatures( 0 ) 00111 { 00112 m_jid.setServer( server ); 00113 init(); 00114 } 00115 00116 Client::Client( const JID& jid, const std::string& password, int port ) 00117 : ClientBase( XMLNS_CLIENT, password, EmptyString, port ), 00118 m_rosterManager( 0 ), m_auth( 0 ), 00119 m_presence( Presence::Available, JID() ), m_resourceBound( false ), 00120 m_forceNonSasl( false ), m_manageRoster( true ), 00121 m_streamFeatures( 0 ) 00122 { 00123 m_jid = jid; 00124 m_server = m_jid.serverRaw(); 00125 init(); 00126 } 00127 00128 Client::~Client() 00129 { 00130 delete m_rosterManager; 00131 delete m_auth; 00132 } 00133 00134 void Client::init() 00135 { 00136 m_rosterManager = new RosterManager( this ); 00137 m_disco->setIdentity( "client", "bot" ); 00138 registerStanzaExtension( new ResourceBind( 0 ) ); 00139 registerStanzaExtension( new Capabilities() ); 00140 m_presenceExtensions.push_back( new Capabilities( m_disco ) ); 00141 } 00142 00143 void Client::setUsername( const std::string &username ) 00144 { 00145 m_jid.setUsername( username ); 00146 } 00147 00148 bool Client::handleNormalNode( Tag* tag ) 00149 { 00150 if( tag->name() == "features" && tag->xmlns() == XMLNS_STREAM ) 00151 { 00152 m_streamFeatures = getStreamFeatures( tag ); 00153 00154 if( m_tls == TLSRequired && !m_encryptionActive 00155 && ( !m_encryption || !( m_streamFeatures & StreamFeatureStartTls ) ) ) 00156 { 00157 logInstance().err( LogAreaClassClient, "Client is configured to require" 00158 " TLS but either the server didn't offer TLS or" 00159 " TLS support is not compiled in." ); 00160 disconnect( ConnTlsNotAvailable ); 00161 } 00162 else if( m_tls > TLSDisabled && m_encryption && !m_encryptionActive 00163 && ( m_streamFeatures & StreamFeatureStartTls ) ) 00164 { 00165 notifyStreamEvent( StreamEventEncryption ); 00166 startTls(); 00167 } 00168 else if( m_compress && m_compression && !m_compressionActive 00169 && ( m_streamFeatures & StreamFeatureCompressZlib ) ) 00170 { 00171 notifyStreamEvent( StreamEventCompression ); 00172 logInstance().warn( LogAreaClassClient, "The server offers compression, but negotiating Compression at this stage is not recommended. See XEP-0170 for details. We'll continue anyway." ); 00173 negotiateCompression( StreamFeatureCompressZlib ); 00174 } 00175 else if( m_sasl ) 00176 { 00177 if( m_authed ) 00178 { 00179 if( m_streamFeatures & StreamFeatureBind ) 00180 { 00181 notifyStreamEvent( StreamEventResourceBinding ); 00182 bindResource( resource() ); 00183 } 00184 } 00185 else if( !username().empty() && !password().empty() ) 00186 { 00187 if( !login() ) 00188 { 00189 logInstance().err( LogAreaClassClient, "The server doesn't support" 00190 " any auth mechanisms we know about" ); 00191 disconnect( ConnNoSupportedAuth ); 00192 } 00193 } 00194 else if( !m_clientCerts.empty() && !m_clientKey.empty() 00195 && m_streamFeatures & SaslMechExternal && m_availableSaslMechs & SaslMechExternal ) 00196 { 00197 notifyStreamEvent( StreamEventAuthentication ); 00198 startSASL( SaslMechExternal ); 00199 } 00200 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ ) 00201 else if( m_streamFeatures & SaslMechGssapi && m_availableSaslMechs & SaslMechGssapi ) 00202 { 00203 notifyStreamEvent( StreamEventAuthentication ); 00204 startSASL( SaslMechGssapi ); 00205 } 00206 else if( m_streamFeatures & SaslMechNTLM && m_availableSaslMechs & SaslMechNTLM ) 00207 { 00208 notifyStreamEvent( StreamEventAuthentication ); 00209 startSASL( SaslMechNTLM ); 00210 } 00211 #endif 00212 else if( m_streamFeatures & SaslMechAnonymous 00213 && m_availableSaslMechs & SaslMechAnonymous ) 00214 { 00215 notifyStreamEvent( StreamEventAuthentication ); 00216 startSASL( SaslMechAnonymous ); 00217 } 00218 else 00219 { 00220 notifyStreamEvent( StreamEventFinished ); 00221 connected(); 00222 } 00223 } 00224 else if( m_compress && m_compression && !m_compressionActive 00225 && ( m_streamFeatures & StreamFeatureCompressZlib ) ) 00226 { 00227 notifyStreamEvent( StreamEventCompression ); 00228 negotiateCompression( StreamFeatureCompressZlib ); 00229 } 00230 // else if( ( m_streamFeatures & StreamFeatureCompressDclz ) 00231 // && m_connection->initCompression( StreamFeatureCompressDclz ) ) 00232 // { 00233 // negotiateCompression( StreamFeatureCompressDclz ); 00234 // } 00235 else if( m_streamFeatures & StreamFeatureIqAuth ) 00236 { 00237 notifyStreamEvent( StreamEventAuthentication ); 00238 nonSaslLogin(); 00239 } 00240 else 00241 { 00242 logInstance().err( LogAreaClassClient, "fallback: the server doesn't " 00243 "support any auth mechanisms we know about" ); 00244 disconnect( ConnNoSupportedAuth ); 00245 } 00246 } 00247 else 00248 { 00249 const std::string& name = tag->name(), 00250 xmlns = tag->findAttribute( XMLNS ); 00251 if( name == "proceed" && xmlns == XMLNS_STREAM_TLS ) 00252 { 00253 logInstance().dbg( LogAreaClassClient, "starting TLS handshake..." ); 00254 00255 if( m_encryption ) 00256 { 00257 m_encryptionActive = true; 00258 m_encryption->handshake(); 00259 } 00260 } 00261 else if( name == "failure" ) 00262 { 00263 if( xmlns == XMLNS_STREAM_TLS ) 00264 { 00265 logInstance().err( LogAreaClassClient, "TLS handshake failed (server-side)!" ); 00266 disconnect( ConnTlsFailed ); 00267 } 00268 else if( xmlns == XMLNS_COMPRESSION ) 00269 { 00270 logInstance().err( LogAreaClassClient, "Stream compression init failed!" ); 00271 disconnect( ConnCompressionFailed ); 00272 } 00273 else if( xmlns == XMLNS_STREAM_SASL ) 00274 { 00275 logInstance().err( LogAreaClassClient, "SASL authentication failed!" ); 00276 processSASLError( tag ); 00277 disconnect( ConnAuthenticationFailed ); 00278 } 00279 } 00280 else if( name == "compressed" && xmlns == XMLNS_COMPRESSION ) 00281 { 00282 logInstance().dbg( LogAreaClassClient, "Stream compression initialized" ); 00283 m_compressionActive = true; 00284 header(); 00285 } 00286 else if( name == "challenge" && xmlns == XMLNS_STREAM_SASL ) 00287 { 00288 logInstance().dbg( LogAreaClassClient, "Processing SASL challenge" ); 00289 processSASLChallenge( tag->cdata() ); 00290 } 00291 else if( name == "success" && xmlns == XMLNS_STREAM_SASL ) 00292 { 00293 logInstance().dbg( LogAreaClassClient, "SASL authentication successful" ); 00294 processSASLSuccess(); 00295 setAuthed( true ); 00296 header(); 00297 } 00298 else 00299 return false; 00300 } 00301 00302 return true; 00303 } 00304 00305 int Client::getStreamFeatures( Tag* tag ) 00306 { 00307 if( tag->name() != "features" || tag->xmlns() != XMLNS_STREAM ) 00308 return 0; 00309 00310 int features = 0; 00311 00312 if( tag->hasChild( "starttls", XMLNS, XMLNS_STREAM_TLS ) ) 00313 features |= StreamFeatureStartTls; 00314 00315 if( tag->hasChild( "mechanisms", XMLNS, XMLNS_STREAM_SASL ) ) 00316 features |= getSaslMechs( tag->findChild( "mechanisms" ) ); 00317 00318 if( tag->hasChild( "bind", XMLNS, XMLNS_STREAM_BIND ) ) 00319 features |= StreamFeatureBind; 00320 00321 if( tag->hasChild( "unbind", XMLNS, XMLNS_STREAM_BIND ) ) 00322 features |= StreamFeatureUnbind; 00323 00324 if( tag->hasChild( "session", XMLNS, XMLNS_STREAM_SESSION ) ) 00325 features |= StreamFeatureSession; 00326 00327 if( tag->hasChild( "auth", XMLNS, XMLNS_STREAM_IQAUTH ) ) 00328 features |= StreamFeatureIqAuth; 00329 00330 if( tag->hasChild( "register", XMLNS, XMLNS_STREAM_IQREGISTER ) ) 00331 features |= StreamFeatureIqRegister; 00332 00333 if( tag->hasChild( "compression", XMLNS, XMLNS_STREAM_COMPRESS ) ) 00334 features |= getCompressionMethods( tag->findChild( "compression" ) ); 00335 00336 if( features == 0 ) 00337 features = StreamFeatureIqAuth; 00338 00339 return features; 00340 } 00341 00342 int Client::getSaslMechs( Tag* tag ) 00343 { 00344 int mechs = SaslMechNone; 00345 00346 const std::string mech = "mechanism"; 00347 00348 if( tag->hasChildWithCData( mech, "DIGEST-MD5" ) ) 00349 mechs |= SaslMechDigestMd5; 00350 00351 if( tag->hasChildWithCData( mech, "PLAIN" ) ) 00352 mechs |= SaslMechPlain; 00353 00354 if( tag->hasChildWithCData( mech, "ANONYMOUS" ) ) 00355 mechs |= SaslMechAnonymous; 00356 00357 if( tag->hasChildWithCData( mech, "EXTERNAL" ) ) 00358 mechs |= SaslMechExternal; 00359 00360 if( tag->hasChildWithCData( mech, "GSSAPI" ) ) 00361 mechs |= SaslMechGssapi; 00362 00363 if( tag->hasChildWithCData( mech, "NTLM" ) ) 00364 mechs |= SaslMechNTLM; 00365 00366 return mechs; 00367 } 00368 00369 int Client::getCompressionMethods( Tag* tag ) 00370 { 00371 int meths = 0; 00372 00373 if( tag->hasChildWithCData( "method", "zlib" ) ) 00374 meths |= StreamFeatureCompressZlib; 00375 00376 if( tag->hasChildWithCData( "method", "lzw" ) ) 00377 meths |= StreamFeatureCompressDclz; 00378 00379 return meths; 00380 } 00381 00382 bool Client::login() 00383 { 00384 bool retval = true; 00385 00386 if( m_streamFeatures & SaslMechDigestMd5 && m_availableSaslMechs & SaslMechDigestMd5 00387 && !m_forceNonSasl ) 00388 { 00389 notifyStreamEvent( StreamEventAuthentication ); 00390 startSASL( SaslMechDigestMd5 ); 00391 } 00392 else if( m_streamFeatures & SaslMechPlain && m_availableSaslMechs & SaslMechPlain 00393 && !m_forceNonSasl ) 00394 { 00395 notifyStreamEvent( StreamEventAuthentication ); 00396 startSASL( SaslMechPlain ); 00397 } 00398 else if( m_streamFeatures & StreamFeatureIqAuth || m_forceNonSasl ) 00399 { 00400 notifyStreamEvent( StreamEventAuthentication ); 00401 nonSaslLogin(); 00402 } 00403 else 00404 retval = false; 00405 00406 return retval; 00407 } 00408 00409 void Client::handleIqIDForward( const IQ& iq, int context ) 00410 { 00411 switch( context ) 00412 { 00413 case CtxResourceUnbind: 00414 // we don't store known resources anyway 00415 break; 00416 case CtxResourceBind: 00417 processResourceBind( iq ); 00418 break; 00419 case CtxSessionEstablishment: 00420 processCreateSession( iq ); 00421 break; 00422 default: 00423 break; 00424 } 00425 } 00426 00427 bool Client::bindOperation( const std::string& resource, bool bind ) 00428 { 00429 if( !( m_streamFeatures & StreamFeatureUnbind ) && m_resourceBound ) 00430 return false; 00431 00432 IQ iq( IQ::Set, JID(), getID() ); 00433 iq.addExtension( new ResourceBind( resource, bind ) ); 00434 00435 send( iq, this, bind ? CtxResourceBind : CtxResourceUnbind ); 00436 return true; 00437 } 00438 00439 bool Client::selectResource( const std::string& resource ) 00440 { 00441 if( !( m_streamFeatures & StreamFeatureUnbind ) ) 00442 return false; 00443 00444 m_selectedResource = resource; 00445 00446 return true; 00447 } 00448 00449 void Client::processResourceBind( const IQ& iq ) 00450 { 00451 switch( iq.subtype() ) 00452 { 00453 case IQ::Result: 00454 { 00455 const ResourceBind* rb = iq.findExtension<ResourceBind>( ExtResourceBind ); 00456 if( !rb || !rb->jid() ) 00457 { 00458 notifyOnResourceBindError( 0 ); 00459 break; 00460 } 00461 00462 m_jid = rb->jid(); 00463 m_resourceBound = true; 00464 m_selectedResource = m_jid.resource(); 00465 notifyOnResourceBind( m_jid.resource() ); 00466 00467 if( m_streamFeatures & StreamFeatureSession ) 00468 createSession(); 00469 else 00470 connected(); 00471 break; 00472 } 00473 case IQ::Error: 00474 { 00475 notifyOnResourceBindError( iq.error() ); 00476 break; 00477 } 00478 default: 00479 break; 00480 } 00481 } 00482 00483 void Client::createSession() 00484 { 00485 notifyStreamEvent( StreamEventSessionCreation ); 00486 IQ iq( IQ::Set, JID(), getID() ); 00487 iq.addExtension( new SessionCreation() ); 00488 send( iq, this, CtxSessionEstablishment ); 00489 } 00490 00491 void Client::processCreateSession( const IQ& iq ) 00492 { 00493 switch( iq.subtype() ) 00494 { 00495 case IQ::Result: 00496 connected(); 00497 break; 00498 case IQ::Error: 00499 notifyOnSessionCreateError( iq.error() ); 00500 break; 00501 default: 00502 break; 00503 } 00504 } 00505 00506 void Client::negotiateCompression( StreamFeature method ) 00507 { 00508 Tag* t = new Tag( "compress", XMLNS, XMLNS_COMPRESSION ); 00509 00510 if( method == StreamFeatureCompressZlib ) 00511 new Tag( t, "method", "zlib" ); 00512 00513 if( method == StreamFeatureCompressDclz ) 00514 new Tag( t, "method", "lzw" ); 00515 00516 send( t ); 00517 } 00518 00519 void Client::setPresence( Presence::PresenceType pres, int priority, 00520 const std::string& status ) 00521 { 00522 m_presence.setPresence( pres ); 00523 m_presence.setPriority( priority ); 00524 m_presence.addStatus( status ); 00525 sendPresence( m_presence ); 00526 } 00527 00528 void Client::setPresence( const JID& to, Presence::PresenceType pres, int priority, 00529 const std::string& status ) 00530 { 00531 Presence p( pres, to, status, priority ); 00532 sendPresence( p ); 00533 } 00534 00535 void Client::sendPresence( Presence& pres ) 00536 { 00537 if( state() < StateConnected ) 00538 return; 00539 00540 send( pres ); 00541 } 00542 00543 void Client::disableRoster() 00544 { 00545 m_manageRoster = false; 00546 delete m_rosterManager; 00547 m_rosterManager = 0; 00548 } 00549 00550 void Client::nonSaslLogin() 00551 { 00552 if( !m_auth ) 00553 m_auth = new NonSaslAuth( this ); 00554 m_auth->doAuth( m_sid ); 00555 } 00556 00557 void Client::connected() 00558 { 00559 if( m_authed ) 00560 { 00561 if( m_manageRoster ) 00562 { 00563 notifyStreamEvent( StreamEventRoster ); 00564 m_rosterManager->fill(); 00565 } 00566 else 00567 rosterFilled(); 00568 } 00569 else 00570 { 00571 notifyStreamEvent( StreamEventFinished ); 00572 notifyOnConnect(); 00573 } 00574 } 00575 00576 void Client::rosterFilled() 00577 { 00578 sendPresence( m_presence ); 00579 notifyStreamEvent( StreamEventFinished ); 00580 notifyOnConnect(); 00581 } 00582 00583 void Client::disconnect() 00584 { 00585 disconnect( ConnUserDisconnected ); 00586 } 00587 00588 void Client::disconnect( ConnectionError reason ) 00589 { 00590 m_resourceBound = false; 00591 m_authed = false; 00592 m_streamFeatures = 0; 00593 ClientBase::disconnect( reason ); 00594 } 00595 00596 void Client::cleanup() 00597 { 00598 m_authed = false; 00599 m_resourceBound = false; 00600 m_streamFeatures = 0; 00601 } 00602 00603 }