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
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 void Client::bindResource()
00336 {
00337 if( !m_resourceBound )
00338 {
00339 Tag *iq = new Tag( "iq" );
00340 iq->addAttribute( "type", "set" );
00341 iq->addAttribute( "id", "bind" );
00342 Tag *b = new Tag( iq, "bind" );
00343 b->addAttribute( "xmlns", XMLNS_STREAM_BIND );
00344 if( !resource().empty() )
00345 new Tag( b, "resource", resource() );
00346
00347 send( iq );
00348 }
00349 }
00350
00351 void Client::processResourceBind( Stanza *stanza )
00352 {
00353 switch( stanza->subtype() )
00354 {
00355 case StanzaIqResult:
00356 {
00357 Tag *bind = stanza->findChild( "bind" );
00358 Tag *jid = bind->findChild( "jid" );
00359 m_jid.setJID( jid->cdata() );
00360 m_resourceBound = true;
00361
00362 if( m_streamFeatures & StreamFeatureSession )
00363 createSession();
00364 else
00365 connected();
00366 break;
00367 }
00368 case StanzaIqError:
00369 {
00370 Tag *error = stanza->findChild( "error" );
00371 if( stanza->hasChild( "error", "type", "modify" )
00372 && error->hasChild( "bad-request", "xmlns", XMLNS_XMPP_STANZAS ) )
00373 {
00374 notifyOnResourceBindError( RbErrorBadRequest );
00375 }
00376 else if( stanza->hasChild( "error", "type", "cancel" ) )
00377 {
00378 if( error->hasChild( "not-allowed", "xmlns", XMLNS_XMPP_STANZAS ) )
00379 notifyOnResourceBindError( RbErrorNotAllowed );
00380 else if( error->hasChild( "conflict", "xmlns", XMLNS_XMPP_STANZAS ) )
00381 notifyOnResourceBindError( RbErrorConflict );
00382 else
00383 notifyOnResourceBindError( RbErrorUnknownError );
00384 }
00385 else
00386 notifyOnResourceBindError( RbErrorUnknownError );
00387 break;
00388 }
00389 default:
00390 break;
00391 }
00392 }
00393
00394 void Client::createSession()
00395 {
00396 notifyStreamEvent( StreamEventSessionCreation );
00397 Tag *iq = new Tag( "iq" );
00398 iq->addAttribute( "type", "set" );
00399 iq->addAttribute( "id", "session" );
00400 Tag *s = new Tag( iq, "session" );
00401 s->addAttribute( "xmlns", XMLNS_STREAM_SESSION );
00402
00403 send( iq );
00404 }
00405
00406 void Client::processCreateSession( Stanza *stanza )
00407 {
00408 switch( stanza->subtype() )
00409 {
00410 case StanzaIqResult:
00411 {
00412 connected();
00413 break;
00414 }
00415 case StanzaIqError:
00416 {
00417 Tag *error = stanza->findChild( "error" );
00418 if( stanza->hasChild( "error", "type", "wait" )
00419 && error->hasChild( "internal-server-error", "xmlns", XMLNS_XMPP_STANZAS ) )
00420 {
00421 notifyOnSessionCreateError( ScErrorInternalServerError );
00422 }
00423 else if( stanza->hasChild( "error", "type", "auth" )
00424 && error->hasChild( "forbidden", "xmlns", XMLNS_XMPP_STANZAS ) )
00425 {
00426 notifyOnSessionCreateError( ScErrorForbidden );
00427 }
00428 else if( stanza->hasChild( "error", "type", "cancel" )
00429 && error->hasChild( "conflict", "xmlns", XMLNS_XMPP_STANZAS ) )
00430 {
00431 notifyOnSessionCreateError( ScErrorConflict );
00432 }
00433 else
00434 notifyOnSessionCreateError( ScErrorUnknownError );
00435 break;
00436 }
00437 default:
00438 break;
00439 }
00440 }
00441
00442 void Client::negotiateCompression( StreamFeature method )
00443 {
00444 Tag *t = new Tag( "compress" );
00445 t->addAttribute( "xmlns", XMLNS_COMPRESSION );
00446
00447 if( method == StreamFeatureCompressZlib )
00448 new Tag( t, "method", "zlib" );
00449
00450 if( method == StreamFeatureCompressDclz )
00451 new Tag( t, "method", "lzw" );
00452
00453 send( t );
00454 }
00455
00456 void Client::addPresenceExtension( StanzaExtension *se )
00457 {
00458 m_presenceExtensions.push_back( se );
00459 }
00460
00461 void Client::removePresenceExtensions()
00462 {
00463 StanzaExtensionList::iterator it = m_presenceExtensions.begin();
00464 for( ; it != m_presenceExtensions.end(); ++it )
00465 {
00466 delete (*it);
00467 }
00468 }
00469
00470 void Client::setPresence( Presence presence, int priority, const std::string& status )
00471 {
00472 m_presence = presence;
00473 m_status = status;
00474
00475 if( priority < -128 )
00476 m_priority = -128;
00477 if( priority > 127 )
00478 m_priority = 127;
00479 else
00480 m_priority = priority;
00481
00482 sendPresence();
00483 }
00484
00485 void Client::disableRoster()
00486 {
00487 m_manageRoster = false;
00488 delete m_rosterManager;
00489 m_rosterManager = 0;
00490 }
00491
00492 void Client::nonSaslLogin()
00493 {
00494 if( !m_auth )
00495 m_auth = new NonSaslAuth( this );
00496 m_auth->doAuth( m_sid );
00497 }
00498
00499 void Client::sendPresence()
00500 {
00501 if( m_presence != PresenceUnknown &&
00502 state() >= StateConnected )
00503 {
00504 JID jid;
00505 Stanza *p = Stanza::createPresenceStanza( jid, m_status, m_presence );
00506 #ifdef _WIN32_WCE
00507 char tmp[5];
00508 tmp[4] = '\0';
00509 sprintf( tmp, "%s", m_priority );
00510 new Tag( p, "priority", tmp );
00511 #else
00512 std::ostringstream oss;
00513 oss << m_priority;
00514 new Tag( p, "priority", oss.str() );
00515 #endif
00516 StanzaExtensionList::const_iterator it = m_presenceExtensions.begin();
00517 for( ; it != m_presenceExtensions.end(); ++it )
00518 {
00519 p->addChild( (*it)->tag() );
00520 }
00521
00522 send( p );
00523 }
00524 }
00525
00526 void Client::connected()
00527 {
00528 if( m_authed )
00529 {
00530 if( m_manageRoster )
00531 {
00532 notifyStreamEvent( StreamEventRoster );
00533 m_rosterManager->fill();
00534 }
00535 else
00536 rosterFilled();
00537 }
00538 else
00539 {
00540 notifyStreamEvent( StreamEventFinished );
00541 notifyOnConnect();
00542 }
00543 }
00544
00545 void Client::rosterFilled()
00546 {
00547 sendPresence();
00548 notifyStreamEvent( StreamEventFinished );
00549 notifyOnConnect();
00550 }
00551
00552 void Client::disconnect()
00553 {
00554 disconnect( ConnUserDisconnected );
00555 }
00556
00557 void Client::disconnect( ConnectionError reason )
00558 {
00559 m_resourceBound = false;
00560 m_authed = false;
00561 m_streamFeatures = 0;
00562 ClientBase::disconnect( reason );
00563 }
00564
00565 void Client::cleanup()
00566 {
00567 m_authed = false;
00568 m_resourceBound = false;
00569 m_streamFeatures = 0;
00570 }
00571
00572 }