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