client.cpp

00001 /*
00002   Copyright (c) 2004-2005 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 "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 }

Generated on Mon Jan 16 16:19:54 2006 for gloox by  doxygen 1.4.6