00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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
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
00094
00095
00096 Tag* Client::SessionCreation::tag() const
00097 {
00098 Tag* t = new Tag( "session" );
00099 t->setXmlns( XMLNS_STREAM_SESSION );
00100 return t;
00101 }
00102
00103
00104
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
00231
00232
00233
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
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 }