client.cpp

00001 /*
00002   Copyright (c) 2004-2006 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
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 //       if( ( m_streamFeatures & StreamFeatureCompressDclz )
00119 //               && m_connection->initCompression( StreamFeatureCompressDclz ) )
00120 //       {
00121 //         negotiateCompression( StreamFeatureCompressDclz );
00122 //         return true;
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 }

Generated on Tue May 1 14:20:20 2007 for gloox by  doxygen 1.5.1