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 "logsink.h"
00023 #include "nonsaslauth.h"
00024 #include "connection.h"
00025 #include "tag.h"
00026 #include "stanza.h"
00027
00028 #ifndef WIN32
00029 #include <unistd.h>
00030 #endif
00031
00032 #include <iostream>
00033 #include <sstream>
00034
00035 namespace gloox
00036 {
00037
00038 Client::Client( const std::string& server )
00039 : ClientBase( XMLNS_CLIENT, server ),
00040 m_rosterManager( 0 ), m_auth( 0 ), m_disco( 0 ),
00041 m_resourceBound( false ), m_autoPresence( false ), m_forceNonSasl( false ),
00042 m_manageRoster( true ), m_handleDisco( true ), m_doAuth( false ),
00043 m_streamFeatures( 0 ), m_priority( -1 )
00044 {
00045 m_jid.setServer( server );
00046 init();
00047 }
00048
00049 Client::Client( const JID& jid, const std::string& password, int port )
00050 : ClientBase( XMLNS_CLIENT, password, "", port ),
00051 m_rosterManager( 0 ), m_auth( 0 ), m_disco( 0 ),
00052 m_resourceBound( false ), m_autoPresence( false ), m_forceNonSasl( false ),
00053 m_manageRoster( true ), m_handleDisco( true ), m_doAuth( true ),
00054 m_streamFeatures( 0 ), m_priority( -1 )
00055 {
00056 m_jid = jid;
00057 m_server = m_jid.serverRaw();
00058 init();
00059 }
00060
00061 Client::Client( const std::string& username, const std::string& password,
00062 const std::string& server, const std::string& resource, int port )
00063 : ClientBase( XMLNS_CLIENT, password, server, port ),
00064 m_rosterManager( 0 ), m_auth( 0 ), m_disco( 0 ),
00065 m_resourceBound( false ), m_autoPresence( false ), m_forceNonSasl( false ),
00066 m_manageRoster( true ), m_handleDisco( true ), m_doAuth( true ),
00067 m_streamFeatures( 0 ), m_priority( -1 )
00068 {
00069 m_jid.setUsername( username );
00070 m_jid.setServer( server );
00071 m_jid.setResource( resource );
00072
00073 init();
00074 }
00075
00076 Client::~Client()
00077 {
00078 delete m_disco;
00079 delete m_rosterManager;
00080 delete m_auth;
00081 }
00082
00083 void Client::init()
00084 {
00085 m_disco = new Disco( this );
00086 m_rosterManager = new RosterManager( this, true );
00087 m_disco->setVersion( "based on gloox", GLOOX_VERSION );
00088 m_disco->setIdentity( "client", "bot" );
00089 }
00090
00091 void Client::setUsername( const std::string &username )
00092 {
00093 m_jid.setUsername( username );
00094 m_doAuth = true;
00095 }
00096
00097 bool Client::handleNormalNode( Stanza *stanza )
00098 {
00099 if( stanza->name() == "stream:features" )
00100 {
00101 m_streamFeatures = getStreamFeatures( stanza );
00102
00103 #ifdef HAVE_TLS
00104 if( tls() && hasTls() && !m_connection->isSecure() && ( m_streamFeatures & StreamFeatureStartTls ) )
00105 {
00106 startTls();
00107 return true;
00108 }
00109 #endif
00110
00111 #ifdef HAVE_ZLIB
00112 if( ( m_streamFeatures & StreamFeatureCompressZlib )
00113 && m_connection->initCompression( StreamFeatureCompressZlib ) )
00114 {
00115 negotiateCompression( StreamFeatureCompressZlib );
00116 return true;
00117 }
00118
00119
00120
00121
00122
00123
00124 #endif
00125
00126 if( sasl() )
00127 {
00128 if( m_authed )
00129 {
00130 if( m_streamFeatures & StreamFeatureBind )
00131 {
00132 bindResource();
00133 }
00134 }
00135 else if( m_doAuth && !username().empty() && !password().empty() )
00136 {
00137 if( m_streamFeatures & StreamFeatureSaslDigestMd5 && !m_forceNonSasl )
00138 {
00139 startSASL( SaslDigestMd5 );
00140 }
00141 else if( m_streamFeatures & StreamFeatureSaslPlain && !m_forceNonSasl )
00142 {
00143 startSASL( SaslPlain );
00144 }
00145 else if( m_streamFeatures & StreamFeatureIqAuth || m_forceNonSasl )
00146 {
00147 nonSaslLogin();
00148 }
00149 else
00150 {
00151 logInstance().log( LogLevelError, LogAreaClassClient,
00152 "the server doesn't support any auth mechanisms we know about" );
00153 disconnect( ConnNoSupportedAuth );
00154 }
00155 }
00156 else if( m_doAuth && !m_clientCerts.empty() && !m_clientKey.empty()
00157 && m_streamFeatures & StreamFeatureSaslExternal )
00158 {
00159 startSASL( SaslExternal );
00160 }
00161 else if( m_doAuth && m_streamFeatures & StreamFeatureSaslAnonymous )
00162 {
00163 startSASL( SaslAnonymous );
00164 }
00165 else
00166 {
00167 connected();
00168 }
00169 }
00170 else if( m_streamFeatures & StreamFeatureIqAuth )
00171 {
00172 nonSaslLogin();
00173 }
00174 else
00175 {
00176 logInstance().log( LogLevelError, LogAreaClassClient,
00177 "the server doesn't support any auth mechanisms we know about" );
00178 disconnect( ConnNoSupportedAuth );
00179 }
00180 }
00181 #ifdef HAVE_TLS
00182 else if( ( stanza->name() == "proceed" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_TLS ) )
00183 {
00184 logInstance().log( LogLevelDebug, LogAreaClassClient, "starting TLS handshake..." );
00185
00186 if( m_connection->tlsHandshake() )
00187 {
00188 if( !notifyOnTLSConnect( m_connection->fetchTLSInfo() ) )
00189 disconnect( ConnTlsFailed );
00190 else
00191 {
00192 std::ostringstream oss;
00193 if( m_connection->isSecure() )
00194 {
00195 oss << "connection encryption active";
00196 logInstance().log( LogLevelDebug, LogAreaClassClient, oss.str() );
00197 }
00198 else
00199 {
00200 oss << "connection not encrypted!";
00201 logInstance().log( LogLevelWarning, LogAreaClassClient, oss.str() );
00202 }
00203
00204 header();
00205 }
00206 }
00207 else
00208 disconnect( ConnTlsFailed );
00209 }
00210 else if( ( stanza->name() == "failure" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_TLS ) )
00211 {
00212 logInstance().log( LogLevelError, LogAreaClassClient, "TLS handshake failed!" );
00213 disconnect( ConnTlsFailed );
00214 }
00215 #endif
00216 #ifdef HAVE_ZLIB
00217 else if( ( stanza->name() == "failure" ) && stanza->hasAttribute( "xmlns", XMLNS_COMPRESSION ) )
00218 {
00219 logInstance().log( LogLevelError, LogAreaClassClient, "stream compression init failed!" );
00220 disconnect( ConnTlsFailed );
00221 }
00222 else if( ( stanza->name() == "compressed" ) && stanza->hasAttribute( "xmlns", XMLNS_COMPRESSION ) )
00223 {
00224 logInstance().log( LogLevelDebug, LogAreaClassClient, "stream compression inited" );
00225 m_connection->enableCompression();
00226 header();
00227 }
00228 #endif
00229 else if( ( stanza->name() == "challenge" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_SASL ) )
00230 {
00231 logInstance().log( LogLevelDebug, LogAreaClassClient, "processing sasl challenge" );
00232 processSASLChallenge( stanza->cdata() );
00233 }
00234 else if( ( stanza->name() == "failure" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_SASL ) )
00235 {
00236 logInstance().log( LogLevelError, LogAreaClassClient, "sasl authentication failed!" );
00237 processSASLError( stanza );
00238 disconnect( ConnAuthenticationFailed );
00239 }
00240 else if( ( stanza->name() == "success" ) && stanza->hasAttribute( "xmlns", XMLNS_STREAM_SASL ) )
00241 {
00242 logInstance().log( LogLevelDebug, LogAreaClassClient, "sasl auth successful" );
00243 setAuthed( true );
00244 header();
00245 }
00246 else
00247 {
00248 if( ( stanza->name() == "iq" ) && stanza->hasAttribute( "id", "bind" ) )
00249 {
00250 processResourceBind( stanza );
00251 }
00252 else if( ( stanza->name() == "iq" ) && stanza->hasAttribute( "id", "session" ) )
00253 {
00254 processCreateSession( stanza );
00255 }
00256 else
00257 return false;
00258 }
00259
00260 return true;
00261 }
00262
00263 int Client::getStreamFeatures( Stanza *stanza )
00264 {
00265 if( stanza->name() != "stream:features" )
00266 return 0;
00267
00268 int features = 0;
00269
00270 if( stanza->hasChild( "starttls", "xmlns", XMLNS_STREAM_TLS ) )
00271 features |= StreamFeatureStartTls;
00272
00273 if( stanza->hasChild( "mechanisms", "xmlns", XMLNS_STREAM_SASL ) )
00274 features |= getSaslMechs( stanza->findChild( "mechanisms" ) );
00275
00276 if( stanza->hasChild( "bind", "xmlns", XMLNS_STREAM_BIND ) )
00277 features |= StreamFeatureBind;
00278
00279 if( stanza->hasChild( "session", "xmlns", XMLNS_STREAM_SESSION ) )
00280 features |= StreamFeatureSession;
00281
00282 if( stanza->hasChild( "auth", "xmlns", XMLNS_STREAM_IQAUTH ) )
00283 features |= StreamFeatureIqAuth;
00284
00285 if( stanza->hasChild( "register", "xmlns", XMLNS_STREAM_IQREGISTER ) )
00286 features |= StreamFeatureIqRegister;
00287
00288 if( stanza->hasChild( "ack", "xmlns", XMLNS_STREAM_ACK ) )
00289 features |= StreamFeatureAck;
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 = 0;
00303
00304 if( tag->hasChildWithCData( "mechanism", "DIGEST-MD5" ) )
00305 mechs |= StreamFeatureSaslDigestMd5;
00306
00307 if( tag->hasChildWithCData( "mechanism", "PLAIN" ) )
00308 mechs |= StreamFeatureSaslPlain;
00309
00310 if( tag->hasChildWithCData( "mechanism", "ANONYMOUS" ) )
00311 mechs |= StreamFeatureSaslAnonymous;
00312
00313 if( tag->hasChildWithCData( "mechanism", "EXTERNAL" ) )
00314 mechs |= StreamFeatureSaslExternal;
00315
00316 return mechs;
00317 }
00318
00319 int Client::getCompressionMethods( Tag *tag )
00320 {
00321 int meths = 0;
00322
00323 if( tag->hasChildWithCData( "method", "zlib" ) )
00324 meths |= StreamFeatureCompressZlib;
00325
00326 if( tag->hasChildWithCData( "method", "lzw" ) )
00327 meths |= StreamFeatureCompressDclz;
00328
00329 return meths;
00330 }
00331
00332 void Client::bindResource()
00333 {
00334 if( !m_resourceBound )
00335 {
00336 Tag *iq = new Tag( "iq" );
00337 iq->addAttribute( "type", "set" );
00338 iq->addAttribute( "id", "bind" );
00339 Tag *b = new Tag( iq, "bind" );
00340 b->addAttribute( "xmlns", XMLNS_STREAM_BIND );
00341 if( !resource().empty() )
00342 new Tag( b, "resource", resource() );
00343
00344 send( iq );
00345 }
00346 }
00347
00348 void Client::processResourceBind( Stanza *stanza )
00349 {
00350 switch( stanza->subtype() )
00351 {
00352 case StanzaIqResult:
00353 {
00354 Tag *bind = stanza->findChild( "bind" );
00355 Tag *jid = bind->findChild( "jid" );
00356 m_jid.setJID( jid->cdata() );
00357 m_resourceBound = true;
00358
00359 if( m_streamFeatures & StreamFeatureSession )
00360 createSession();
00361 else
00362 connected();
00363 break;
00364 }
00365 case StanzaIqError:
00366 {
00367 Tag *error = stanza->findChild( "error" );
00368 if( stanza->hasChild( "error", "type", "modify" )
00369 && error->hasChild( "bad-request", "xmlns", XMLNS_XMPP_STANZAS ) )
00370 {
00371 notifyOnResourceBindError( RbErrorBadRequest );
00372 }
00373 else if( stanza->hasChild( "error", "type", "cancel" ) )
00374 {
00375 if( error->hasChild( "not-allowed", "xmlns", XMLNS_XMPP_STANZAS ) )
00376 notifyOnResourceBindError( RbErrorNotAllowed );
00377 else if( error->hasChild( "conflict", "xmlns", XMLNS_XMPP_STANZAS ) )
00378 notifyOnResourceBindError( RbErrorConflict );
00379 else
00380 notifyOnResourceBindError( RbErrorUnknownError );
00381 }
00382 else
00383 notifyOnResourceBindError( RbErrorUnknownError );
00384 break;
00385 }
00386 default:
00387 break;
00388 }
00389 }
00390
00391 void Client::createSession()
00392 {
00393 Tag *iq = new Tag( "iq" );
00394 iq->addAttribute( "type", "set" );
00395 iq->addAttribute( "id", "session" );
00396 Tag *s = new Tag( iq, "session" );
00397 s->addAttribute( "xmlns", XMLNS_STREAM_SESSION );
00398
00399 send( iq );
00400 }
00401
00402 void Client::processCreateSession( Stanza *stanza )
00403 {
00404 switch( stanza->subtype() )
00405 {
00406 case StanzaIqResult:
00407 {
00408 connected();
00409 break;
00410 }
00411 case StanzaIqError:
00412 {
00413 Tag *error = stanza->findChild( "error" );
00414 if( stanza->hasChild( "error", "type", "wait" )
00415 && error->hasChild( "internal-server-error", "xmlns", XMLNS_XMPP_STANZAS ) )
00416 {
00417 notifyOnSessionCreateError( ScErrorInternalServerError );
00418 }
00419 else if( stanza->hasChild( "error", "type", "auth" )
00420 && error->hasChild( "forbidden", "xmlns", XMLNS_XMPP_STANZAS ) )
00421 {
00422 notifyOnSessionCreateError( ScErrorForbidden );
00423 }
00424 else if( stanza->hasChild( "error", "type", "cancel" )
00425 && error->hasChild( "conflict", "xmlns", XMLNS_XMPP_STANZAS ) )
00426 {
00427 notifyOnSessionCreateError( ScErrorConflict );
00428 }
00429 else
00430 notifyOnSessionCreateError( ScErrorUnknownError );
00431 break;
00432 }
00433 default:
00434 break;
00435 }
00436 }
00437
00438 void Client::negotiateCompression( StreamFeature method )
00439 {
00440 Tag *t = new Tag( "compress" );
00441 t->addAttribute( "xmlns", XMLNS_COMPRESSION );
00442
00443 if( method == StreamFeatureCompressZlib )
00444 new Tag( t, "method", "zlib" );
00445
00446 if( method == StreamFeatureCompressDclz )
00447 new Tag( t, "method", "lzw" );
00448
00449 send( t );
00450 }
00451
00452 void Client::disableDisco()
00453 {
00454 m_handleDisco = false;
00455 delete m_disco;
00456 m_disco = 0;
00457 }
00458
00459 void Client::disableRoster()
00460 {
00461 m_manageRoster = false;
00462 delete m_rosterManager;
00463 m_rosterManager = 0;
00464 }
00465
00466 void Client::nonSaslLogin()
00467 {
00468 if( !m_auth )
00469 m_auth = new NonSaslAuth( this );
00470 m_auth->doAuth( m_sid );
00471 }
00472
00473 void Client::sendInitialPresence()
00474 {
00475 Tag *p = new Tag( "presence" );
00476 std::ostringstream oss;
00477 oss << m_priority;
00478 new Tag( p, "priority", oss.str() );
00479
00480 send( p );
00481 }
00482
00483 void Client::setInitialPriority( int priority )
00484 {
00485 if( priority < -128 )
00486 priority = -128;
00487 if( priority > 127 )
00488 priority = 127;
00489
00490 m_priority = priority;
00491 }
00492
00493 RosterManager* Client::rosterManager()
00494 {
00495 return m_rosterManager;
00496 }
00497
00498 Disco* Client::disco()
00499 {
00500 return m_disco;
00501 }
00502
00503 void Client::connected()
00504 {
00505 if( m_authed )
00506 {
00507 if( m_manageRoster )
00508 m_rosterManager->fill();
00509 else
00510 rosterFilled();
00511 }
00512
00513 notifyOnConnect();
00514 }
00515
00516 void Client::rosterFilled()
00517 {
00518 if( m_autoPresence )
00519 sendInitialPresence();
00520 }
00521
00522 void Client::disconnect()
00523 {
00524 disconnect( ConnUserDisconnected );
00525 }
00526
00527 void Client::disconnect( ConnectionError reason )
00528 {
00529 m_resourceBound = false;
00530 m_authed = false;
00531 m_streamFeatures = 0;
00532 ClientBase::disconnect( reason );
00533 }
00534
00535 void Client::cleanup()
00536 {
00537 m_authed = false;
00538 m_resourceBound = false;
00539 m_streamFeatures = 0;
00540 }
00541
00542 }