00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #ifdef WIN32
00014 #include "../config.h.win"
00015 #else
00016 #include "config.h"
00017 #endif
00018
00019 #include "client.h"
00020 #include "rostermanager.h"
00021 #include "disco.h"
00022 #include "nonsaslauth.h"
00023 #include "connection.h"
00024 #include "tag.h"
00025 #include "stanza.h"
00026
00027 #ifndef WIN32
00028 #include <unistd.h>
00029 #endif
00030
00031 #include <iostream>
00032 #include <sstream>
00033
00034 namespace gloox
00035 {
00036
00037 Client::Client( const std::string& server )
00038 : ClientBase( XMLNS_CLIENT, server ),
00039 m_rosterManager( 0 ), m_auth( 0 ), m_disco( 0 ),
00040 m_resourceBound( false ), m_autoPresence( false ), m_forceNonSasl( false ),
00041 m_manageRoster( true ), m_handleDisco( true ), m_doAuth( false ),
00042 m_streamFeatures( 0 ), m_priority( -1 )
00043 {
00044 m_jid.setServer( server );
00045 init();
00046 }
00047
00048 Client::Client( const JID& jid, const std::string& password, int port )
00049 : ClientBase( XMLNS_CLIENT, password, "", port ),
00050 m_rosterManager( 0 ), m_auth( 0 ), m_disco( 0 ),
00051 m_resourceBound( false ), m_autoPresence( false ), m_forceNonSasl( false ),
00052 m_manageRoster( true ), m_handleDisco( true ), m_doAuth( true ),
00053 m_streamFeatures( 0 ), m_priority( -1 )
00054 {
00055 m_jid = jid;
00056 m_server = m_jid.serverRaw();
00057 init();
00058 }
00059
00060 Client::Client( const std::string& username, const std::string& password,
00061 const std::string& server, const std::string& resource, int port )
00062 : ClientBase( XMLNS_CLIENT, password, server, port ),
00063 m_rosterManager( 0 ), m_auth( 0 ), m_disco( 0 ),
00064 m_resourceBound( false ), m_autoPresence( false ), m_forceNonSasl( false ),
00065 m_manageRoster( true ), m_handleDisco( true ), m_doAuth( true ),
00066 m_streamFeatures( 0 ), m_priority( -1 )
00067 {
00068 m_jid.setUsername( username );
00069 m_jid.setServer( server );
00070 m_jid.setResource( resource );
00071
00072 init();
00073 }
00074
00075 Client::~Client()
00076 {
00077 delete m_disco;
00078 delete m_rosterManager;
00079 delete m_auth;
00080 }
00081
00082 void Client::init()
00083 {
00084 m_disco = new Disco( this );
00085 m_rosterManager = new RosterManager( this, true );
00086 m_disco->setVersion( "based on gloox", GLOOX_VERSION );
00087 m_disco->setIdentity( "client", "bot" );
00088 }
00089
00090 bool Client::handleNormalNode( Stanza *stanza )
00091 {
00092 if( stanza->name() == "stream:features" )
00093 {
00094 m_streamFeatures = getStreamFeatures( stanza );
00095
00096 #ifdef HAVE_TLS
00097 if( tls() && hasTls() && !m_connection->isSecure() && ( m_streamFeatures & STREAM_FEATURE_STARTTLS ) )
00098 {
00099 startTls();
00100 return true;
00101 }
00102 #endif
00103
00104 #ifdef HAVE_ZLIB
00105 if( ( m_streamFeatures & STREAM_FEATURE_COMPRESS_ZLIB ) && m_connection->initCompression( true ) )
00106 {
00107 negotiateCompression( STREAM_FEATURE_COMPRESS_ZLIB );
00108 return true;
00109 }
00110 #endif
00111
00112 if( sasl() )
00113 {
00114 if( m_authed )
00115 {
00116 if( m_streamFeatures & STREAM_FEATURE_BIND )
00117 {
00118 bindResource();
00119 }
00120 }
00121 else if( m_doAuth && !username().empty() && !password().empty() )
00122 {
00123 if( m_streamFeatures & STREAM_FEATURE_SASL_DIGESTMD5 && !m_forceNonSasl )
00124 {
00125 startSASL( SASL_DIGEST_MD5 );
00126 }
00127 else if( m_streamFeatures & STREAM_FEATURE_SASL_PLAIN && !m_forceNonSasl )
00128 {
00129 startSASL( SASL_PLAIN );
00130 }
00131 else if( m_streamFeatures & STREAM_FEATURE_IQAUTH || m_forceNonSasl )
00132 {
00133 nonSaslLogin();
00134 }
00135 else
00136 {
00137 #ifdef DEBUG
00138 printf( "the server doesn't support any auth mechanisms we know about\n" );
00139 #endif
00140 disconnect( CONN_NO_SUPPORTED_AUTH );
00141 }
00142 }
00143 else if( m_doAuth && m_streamFeatures & STREAM_FEATURE_SASL_ANONYMOUS )
00144 {
00145 startSASL( SASL_ANONYMOUS );
00146 }
00147 else
00148 {
00149 connected();
00150 }
00151 }
00152 else if( m_streamFeatures & STREAM_FEATURE_IQAUTH )
00153 {
00154 nonSaslLogin();
00155 }
00156 else
00157 {
00158 #ifdef DEBUG
00159 printf( "the server doesn't support any auth mechanisms we know about\n" );
00160 #endif
00161 disconnect( CONN_NO_SUPPORTED_AUTH );
00162 }
00163 }
00164 #ifdef HAVE_TLS
00165 else if( ( stanza->name() == "proceed" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_TLS ) )
00166 {
00167 #ifdef DEBUG
00168 printf( "starting TLS handshake...\n" );
00169 #endif
00170 if( m_connection->tlsHandshake() )
00171 {
00172 if( !notifyOnTLSConnect( m_connection->fetchTLSInfo() ) )
00173 disconnect( CONN_TLS_FAILED );
00174 else
00175 {
00176 #ifdef DEBUG
00177 printf( "connection security is now %d\n", m_connection->isSecure() );
00178 #endif
00179 header();
00180 }
00181 }
00182 else
00183 disconnect( CONN_TLS_FAILED );
00184 }
00185 else if( ( stanza->name() == "failure" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_TLS ) )
00186 {
00187 #ifdef DEBUG
00188 printf( "tls handshake failed...\n" );
00189 #endif
00190 disconnect( CONN_TLS_FAILED );
00191 }
00192 #endif
00193 #ifdef HAVE_ZLIB
00194 else if( ( stanza->name() == "failure" ) && stanza->hasAttribute( "xmlns", XMLNS_COMPRESSION ) )
00195 {
00196 #ifdef DEBUG
00197 printf( "stream compression init failed...\n" );
00198 #endif
00199 disconnect( CONN_TLS_FAILED );
00200 }
00201 else if( ( stanza->name() == "compressed" ) && stanza->hasAttribute( "xmlns", XMLNS_COMPRESSION ) )
00202 {
00203 #ifdef DEBUG
00204 printf( "stream compression inited...\n" );
00205 #endif
00206 m_connection->setCompression( true );
00207 header();
00208 }
00209 #endif
00210 else if( ( stanza->name() == "challenge" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_SASL ) )
00211 {
00212 #ifdef DEBUG
00213 printf( "processing sasl challenge\n" );
00214 #endif
00215 processSASLChallenge( stanza->cdata() );
00216 }
00217 else if( ( stanza->name() == "failure" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_SASL ) )
00218 {
00219 #ifdef DEBUG
00220 printf( "sasl authentication failed...\n" );
00221 #endif
00222 processSASLError( stanza );
00223 disconnect( CONN_AUTHENTICATION_FAILED );
00224 }
00225 else if( ( stanza->name() == "success" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_SASL ) )
00226 {
00227 #ifdef DEBUG
00228 printf( "sasl auth successful...\n" );
00229 #endif
00230 setAuthed( true );
00231 header();
00232 }
00233 else
00234 {
00235 if( ( stanza->name() == "iq" ) && stanza->hasAttribute( "id", "bind" ) )
00236 {
00237 processResourceBind( stanza );
00238 }
00239 else if( ( stanza->name() == "iq" ) && stanza->hasAttribute( "id", "session" ) )
00240 processCreateSession( stanza );
00241 else
00242 return false;
00243 }
00244
00245 return true;
00246 }
00247
00248 int Client::getStreamFeatures( Stanza *stanza )
00249 {
00250 if( stanza->name() != "stream:features" )
00251 return 0;
00252
00253 int features = 0;
00254
00255 if( stanza->hasChild( "starttls", "xmlns", XMLNS_STREAM_TLS ) )
00256 features |= STREAM_FEATURE_STARTTLS;
00257
00258 if( stanza->hasChild( "mechanisms", "xmlns", XMLNS_STREAM_SASL ) )
00259 features |= getSaslMechs( stanza->findChild( "mechanisms" ) );
00260
00261 if( stanza->hasChild( "bind", "xmlns", XMLNS_STREAM_BIND ) )
00262 features |= STREAM_FEATURE_BIND;
00263
00264 if( stanza->hasChild( "session", "xmlns", XMLNS_STREAM_SESSION ) )
00265 features |= STREAM_FEATURE_SESSION;
00266
00267 if( stanza->hasChild( "auth", "xmlns", XMLNS_STREAM_IQAUTH ) )
00268 features |= STREAM_FEATURE_IQAUTH;
00269
00270 if( stanza->hasChild( "register", "xmlns", XMLNS_STREAM_IQREGISTER ) )
00271 features |= STREAM_FEATURE_IQREGISTER;
00272
00273 if( stanza->hasChild( "ack", "xmlns", XMLNS_STREAM_ACK ) )
00274 features |= STREAM_FEATURE_ACK;
00275
00276 if( stanza->hasChild( "compression", "xmlns", XMLNS_STREAM_COMPRESS ) )
00277 features |= getCompressionMethods( stanza->findChild( "compression" ) );
00278
00279 if( features == 0 )
00280 features = STREAM_FEATURE_IQAUTH;
00281
00282 return features;
00283 }
00284
00285 int Client::getSaslMechs( Tag *tag )
00286 {
00287 int mechs = 0;
00288
00289 if( tag->hasChildWithCData( "mechanism", "DIGEST-MD5" ) )
00290 mechs |= STREAM_FEATURE_SASL_DIGESTMD5;
00291
00292 if( tag->hasChildWithCData( "mechanism", "PLAIN" ) )
00293 mechs |= STREAM_FEATURE_SASL_PLAIN;
00294
00295 if( tag->hasChildWithCData( "mechanism", "ANONYMOUS" ) )
00296 mechs |= STREAM_FEATURE_SASL_ANONYMOUS;
00297
00298 return mechs;
00299 }
00300
00301 int Client::getCompressionMethods( Tag *tag )
00302 {
00303 int meths = 0;
00304
00305 if( tag->hasChildWithCData( "method", "zlib" ) )
00306 meths |= STREAM_FEATURE_COMPRESS_ZLIB;
00307
00308 return meths;
00309 }
00310
00311 void Client::bindResource()
00312 {
00313 if( !m_resourceBound )
00314 {
00315 Tag *iq = new Tag( "iq" );
00316 iq->addAttrib( "type", "set" );
00317 iq->addAttrib( "id", "bind" );
00318 Tag *b = new Tag( "bind" );
00319 b->addAttrib( "xmlns", XMLNS_STREAM_BIND );
00320 if( !resource().empty() )
00321 {
00322 Tag *r = new Tag( "resource", resource() );
00323 b->addChild( r );
00324 }
00325 iq->addChild( b );
00326
00327 send( iq );
00328 }
00329 }
00330
00331 void Client::processResourceBind( Stanza *stanza )
00332 {
00333 switch( stanza->subtype() )
00334 {
00335 case STANZA_IQ_RESULT:
00336 {
00337 Tag *bind = stanza->findChild( "bind" );
00338 Tag *jid = bind->findChild( "jid" );
00339 m_jid.setJID( jid->cdata() );
00340 m_resourceBound = true;
00341
00342 if( m_streamFeatures & STREAM_FEATURE_SESSION )
00343 createSession();
00344 else
00345 connected();
00346 break;
00347 }
00348 case STANZA_IQ_ERROR:
00349 {
00350 Tag *error = stanza->findChild( "error" );
00351 if( stanza->hasChild( "error", "type", "modify" )
00352 && error->hasChild( "bad-request", "xmlns", XMLNS_XMPP_STANZAS ) )
00353 {
00354 notifyOnResourceBindError( ConnectionListener::RB_BAD_REQUEST );
00355 }
00356 else if( stanza->hasChild( "error", "type", "cancel" ) )
00357 {
00358 if( error->hasChild( "not-allowed", "xmlns", XMLNS_XMPP_STANZAS ) )
00359 notifyOnResourceBindError( ConnectionListener::RB_NOT_ALLOWED );
00360 else if( error->hasChild( "conflict", "xmlns", XMLNS_XMPP_STANZAS ) )
00361 notifyOnResourceBindError( ConnectionListener::RB_CONFLICT );
00362 else
00363 notifyOnResourceBindError( ConnectionListener::RB_UNKNOWN_ERROR );
00364 }
00365 else
00366 notifyOnResourceBindError( ConnectionListener::RB_UNKNOWN_ERROR );
00367 break;
00368 }
00369 default:
00370 break;
00371 }
00372 }
00373
00374 void Client::createSession()
00375 {
00376 Tag *iq = new Tag( "iq" );
00377 iq->addAttrib( "type", "set" );
00378 iq->addAttrib( "id", "session" );
00379 Tag *s = new Tag( "session" );
00380 s->addAttrib( "xmlns", XMLNS_STREAM_SESSION );
00381 iq->addChild( s );
00382
00383 send( iq );
00384 }
00385
00386 void Client::processCreateSession( Stanza *stanza )
00387 {
00388 switch( stanza->subtype() )
00389 {
00390 case STANZA_IQ_RESULT:
00391 {
00392 connected();
00393 break;
00394 }
00395 case STANZA_IQ_ERROR:
00396 {
00397 Tag *error = stanza->findChild( "error" );
00398 if( stanza->hasChild( "error", "type", "wait" )
00399 && error->hasChild( "internal-server-error", "xmlns", XMLNS_XMPP_STANZAS ) )
00400 {
00401 notifyOnSessionCreateError( ConnectionListener::SC_INTERNAL_SERVER_ERROR );
00402 }
00403 else if( stanza->hasChild( "error", "type", "auth" )
00404 && error->hasChild( "forbidden", "xmlns", XMLNS_XMPP_STANZAS ) )
00405 {
00406 notifyOnSessionCreateError( ConnectionListener::SC_FORBIDDEN );
00407 }
00408 else if( stanza->hasChild( "error", "type", "cancel" )
00409 && error->hasChild( "conflict", "xmlns", XMLNS_XMPP_STANZAS ) )
00410 {
00411 notifyOnSessionCreateError( ConnectionListener::SC_CONFLICT );
00412 }
00413 else
00414 notifyOnSessionCreateError( ConnectionListener::SC_UNKNOWN_ERROR );
00415 break;
00416 }
00417 default:
00418 break;
00419 }
00420 }
00421
00422 void Client::negotiateCompression( StreamFeaturesEnum method )
00423 {
00424 Tag *t = new Tag( "compress" );
00425 t->addAttrib( "xmlns", XMLNS_COMPRESSION );
00426
00427 if( method == STREAM_FEATURE_COMPRESS_ZLIB )
00428 new Tag( t, "method", "zlib" );
00429
00430 send( t );
00431 }
00432
00433 void Client::disableDisco()
00434 {
00435 m_handleDisco = false;
00436 delete m_disco;
00437 m_disco = 0;
00438 }
00439
00440 void Client::disableRoster()
00441 {
00442 m_manageRoster = false;
00443 delete m_rosterManager;
00444 m_rosterManager = 0;
00445 }
00446
00447 void Client::nonSaslLogin()
00448 {
00449 m_auth = new NonSaslAuth( this, m_sid );
00450 m_auth->doAuth();
00451 }
00452
00453 void Client::sendInitialPresence()
00454 {
00455 Tag *p = new Tag( "presence" );
00456 std::ostringstream oss;
00457 oss << m_priority;
00458 Tag *prio= new Tag( "priority", oss.str() );
00459 p->addChild( prio );
00460 send( p );
00461 }
00462
00463 void Client::setInitialPriority( int priority )
00464 {
00465 if( priority < -128 )
00466 priority = -128;
00467 if( priority > 127 )
00468 priority = 127;
00469
00470 m_priority = priority;
00471 }
00472
00473 RosterManager* Client::rosterManager()
00474 {
00475 return m_rosterManager;
00476 }
00477
00478 Disco* Client::disco()
00479 {
00480 return m_disco;
00481 }
00482
00483 void Client::connected()
00484 {
00485 if( m_authed )
00486 {
00487 if( m_manageRoster )
00488 m_rosterManager->fill();
00489
00490 if( m_autoPresence )
00491 sendInitialPresence();
00492 }
00493
00494 notifyOnConnect();
00495 }
00496
00497 }