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