Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

client.cpp

00001 /*
00002   Copyright (c) 2004-2008 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 #elif defined( _WIN32_WCE )
00016 # include "../config.h.win"
00017 #else
00018 # include "config.h"
00019 #endif
00020 
00021 #include "client.h"
00022 #include "capabilities.h"
00023 #include "rostermanager.h"
00024 #include "disco.h"
00025 #include "error.h"
00026 #include "logsink.h"
00027 #include "nonsaslauth.h"
00028 #include "prep.h"
00029 #include "stanzaextensionfactory.h"
00030 #include "stanzaextension.h"
00031 #include "tag.h"
00032 #include "tlsbase.h"
00033 #include "util.h"
00034 
00035 #if !defined( _WIN32 ) && !defined( _WIN32_WCE )
00036 # include <unistd.h>
00037 #endif
00038 
00039 #ifndef _WIN32_WCE
00040 # include <iostream>
00041 # include <sstream>
00042 #else
00043 # include <stdio.h>
00044 #endif
00045 
00046 namespace gloox
00047 {
00048 
00049   // ---- Client::ResourceBind ----
00050   Client::ResourceBind::ResourceBind( const std::string& resource, bool bind )
00051     : StanzaExtension( ExtResourceBind ), m_jid( JID() ), m_bind( bind )
00052   {
00053     prep::resourceprep( resource, m_resource );
00054     m_valid = true;
00055   }
00056 
00057   Client::ResourceBind::ResourceBind( const Tag* tag )
00058     : StanzaExtension( ExtResourceBind ), m_resource( EmptyString ), m_bind( true )
00059   {
00060     if( !tag )
00061       return;
00062 
00063     if( tag->name() == "unbind" )
00064       m_bind = false;
00065     else if( tag->name() == "bind" )
00066       m_bind = true;
00067     else
00068       return;
00069 
00070     if( tag->hasChild( "jid" ) )
00071       m_jid.setJID( tag->findChild( "jid" )->cdata() );
00072     else if( tag->hasChild( "resource" ) )
00073       m_resource = tag->findChild( "resource" )->cdata();
00074 
00075     m_valid = true;
00076   }
00077 
00078   Client::ResourceBind::~ResourceBind()
00079   {
00080   }
00081 
00082   const std::string& Client::ResourceBind::filterString() const
00083   {
00084     static const std::string filter = "/iq/bind[@xmlns='" + XMLNS_STREAM_BIND + "']"
00085         "|/iq/unbind[@xmlns='" + XMLNS_STREAM_BIND + "']";
00086     return filter;
00087   }
00088 
00089   Tag* Client::ResourceBind::tag() const
00090   {
00091     if( !m_valid )
00092       return 0;
00093 
00094     Tag* t = new Tag( m_bind ? "bind" : "unbind" );
00095     t->setXmlns( XMLNS_STREAM_BIND );
00096 
00097     if( m_bind && m_resource.empty() && m_jid )
00098       new Tag( t, "jid", m_jid.full() );
00099     else
00100       new Tag( t, "resource", m_resource );
00101 
00102     return t;
00103   }
00104   // ---- ~Client::ResourceBind ----
00105 
00106   // ---- Client::SessionCreation ----
00107   Tag* Client::SessionCreation::tag() const
00108   {
00109     Tag* t = new Tag( "session" );
00110     t->setXmlns( XMLNS_STREAM_SESSION );
00111     return t;
00112   }
00113   // ---- Client::SessionCreation ----
00114 
00115   // ---- Client ----
00116   Client::Client( const std::string& server )
00117     : ClientBase( XMLNS_CLIENT, server ),
00118       m_rosterManager( 0 ), m_auth( 0 ),
00119       m_presence( Presence::Available, JID() ), m_resourceBound( false ),
00120       m_forceNonSasl( false ), m_manageRoster( true ), m_doAuth( false ),
00121       m_streamFeatures( 0 ), m_priority( 0 )
00122   {
00123     m_jid.setServer( server );
00124     init();
00125   }
00126 
00127   Client::Client( const JID& jid, const std::string& password, int port )
00128     : ClientBase( XMLNS_CLIENT, password, EmptyString, port ),
00129       m_rosterManager( 0 ), m_auth( 0 ),
00130       m_presence( Presence::Available, JID() ), m_resourceBound( false ),
00131       m_forceNonSasl( false ), m_manageRoster( true ), m_doAuth( true ),
00132       m_streamFeatures( 0 ), m_priority( 0 )
00133   {
00134     m_jid = jid;
00135     m_server = m_jid.serverRaw();
00136     init();
00137   }
00138 
00139   Client::~Client()
00140   {
00141     delete m_rosterManager;
00142     delete m_auth;
00143   }
00144 
00145   void Client::init()
00146   {
00147     m_rosterManager = new RosterManager( this );
00148     m_disco->setIdentity( "client", "bot" );
00149     registerStanzaExtension( new ResourceBind( 0 ) );
00150     registerStanzaExtension( new Capabilities( m_disco ) );
00151     m_presence.addExtension( new Capabilities( m_disco ) );
00152   }
00153 
00154   void Client::setUsername( const std::string &username )
00155   {
00156     m_jid.setUsername( username );
00157     m_doAuth = true;
00158   }
00159 
00160   bool Client::handleNormalNode( Tag* tag )
00161   {
00162     if( tag->name() == "features" && tag->xmlns() == XMLNS_STREAM )
00163     {
00164       m_streamFeatures = getStreamFeatures( tag );
00165 
00166       if( m_tls == TLSRequired && !m_encryptionActive
00167           && ( !m_encryption || !( m_streamFeatures & StreamFeatureStartTls ) ) )
00168       {
00169         logInstance().err( LogAreaClassClient, "Client is configured to require"
00170                                 " TLS but either the server didn't offer TLS or"
00171                                 " TLS support is not compiled in." );
00172         disconnect( ConnTlsNotAvailable );
00173       }
00174       else if( m_tls > TLSDisabled && m_encryption && !m_encryptionActive
00175           && ( m_streamFeatures & StreamFeatureStartTls ) )
00176       {
00177         notifyStreamEvent( StreamEventEncryption );
00178         startTls();
00179       }
00180       else if( m_sasl )
00181       {
00182         if( m_authed )
00183         {
00184           if( m_streamFeatures & StreamFeatureBind )
00185           {
00186             notifyStreamEvent( StreamEventResourceBinding );
00187             bindResource( resource() );
00188           }
00189         }
00190         else if( m_doAuth && !username().empty() && !password().empty() )
00191         {
00192           if( m_streamFeatures & SaslMechDigestMd5 && m_availableSaslMechs & SaslMechDigestMd5
00193               && !m_forceNonSasl )
00194           {
00195             notifyStreamEvent( StreamEventAuthentication );
00196             startSASL( SaslMechDigestMd5 );
00197           }
00198           else if( m_streamFeatures & SaslMechPlain && m_availableSaslMechs & SaslMechPlain
00199                    && !m_forceNonSasl )
00200           {
00201             notifyStreamEvent( StreamEventAuthentication );
00202             startSASL( SaslMechPlain );
00203           }
00204           else if( m_streamFeatures & StreamFeatureIqAuth || m_forceNonSasl )
00205           {
00206             notifyStreamEvent( StreamEventAuthentication );
00207             nonSaslLogin();
00208           }
00209           else
00210           {
00211             logInstance().err( LogAreaClassClient, "the server doesn't support"
00212                                            " any auth mechanisms we know about" );
00213             disconnect( ConnNoSupportedAuth );
00214           }
00215         }
00216         else if( m_doAuth && !m_clientCerts.empty() && !m_clientKey.empty()
00217                  && m_streamFeatures & SaslMechExternal && m_availableSaslMechs & SaslMechExternal )
00218         {
00219           notifyStreamEvent( StreamEventAuthentication );
00220           startSASL( SaslMechExternal );
00221         }
00222 #ifdef _WIN32
00223         else if( m_doAuth && m_streamFeatures & SaslMechGssapi && m_availableSaslMechs & SaslMechGssapi )
00224         {
00225           notifyStreamEvent( StreamEventAuthentication );
00226           startSASL( SaslMechGssapi );
00227         }
00228 #endif
00229         else if( m_doAuth && m_streamFeatures & SaslMechAnonymous
00230                  && m_availableSaslMechs & SaslMechAnonymous )
00231         {
00232           notifyStreamEvent( StreamEventAuthentication );
00233           startSASL( SaslMechAnonymous );
00234         }
00235         else
00236         {
00237           notifyStreamEvent( StreamEventFinished );
00238           connected();
00239         }
00240       }
00241       else if( m_compress && m_compression && !m_compressionActive
00242           && ( m_streamFeatures & StreamFeatureCompressZlib ) )
00243       {
00244         notifyStreamEvent( StreamEventCompression );
00245         negotiateCompression( StreamFeatureCompressZlib );
00246       }
00247 //       else if( ( m_streamFeatures & StreamFeatureCompressDclz )
00248 //               && m_connection->initCompression( StreamFeatureCompressDclz ) )
00249 //       {
00250 //         negotiateCompression( StreamFeatureCompressDclz );
00251 //       }
00252       else if( m_streamFeatures & StreamFeatureIqAuth )
00253       {
00254         notifyStreamEvent( StreamEventAuthentication );
00255         nonSaslLogin();
00256       }
00257       else
00258       {
00259         logInstance().err( LogAreaClassClient, "fallback: the server doesn't "
00260                                    "support any auth mechanisms we know about" );
00261         disconnect( ConnNoSupportedAuth );
00262       }
00263     }
00264     else
00265     {
00266       const std::string& name  = tag->name(),
00267                          xmlns = tag->findAttribute( XMLNS );
00268       if( name == "proceed" && xmlns == XMLNS_STREAM_TLS )
00269       {
00270         logInstance().dbg( LogAreaClassClient, "starting TLS handshake..." );
00271 
00272         if( m_encryption )
00273         {
00274           m_encryptionActive = true;
00275           m_encryption->handshake();
00276         }
00277       }
00278       else if( name == "failure" )
00279       {
00280         if( xmlns == XMLNS_STREAM_TLS )
00281         {
00282           logInstance().err( LogAreaClassClient, "TLS handshake failed (server-side)!" );
00283           disconnect( ConnTlsFailed );
00284         }
00285         else if( xmlns == XMLNS_COMPRESSION )
00286         {
00287           logInstance().err( LogAreaClassClient, "stream compression init failed!" );
00288           disconnect( ConnCompressionFailed );
00289         }
00290         else if( xmlns == XMLNS_STREAM_SASL )
00291         {
00292           logInstance().err( LogAreaClassClient, "SASL authentication failed!" );
00293           processSASLError( tag );
00294           disconnect( ConnAuthenticationFailed );
00295         }
00296       }
00297       else if( name == "compressed" && xmlns == XMLNS_COMPRESSION )
00298       {
00299         logInstance().dbg( LogAreaClassClient, "stream compression inited" );
00300         m_compressionActive = true;
00301         header();
00302       }
00303       else if( name == "challenge" && xmlns == XMLNS_STREAM_SASL )
00304       {
00305         logInstance().dbg( LogAreaClassClient, "processing SASL challenge" );
00306         processSASLChallenge( tag->cdata() );
00307       }
00308       else if( name == "success" && xmlns == XMLNS_STREAM_SASL )
00309       {
00310         logInstance().dbg( LogAreaClassClient, "SASL authentication successful" );
00311         setAuthed( true );
00312         header();
00313       }
00314       else
00315         return false;
00316     }
00317 
00318     return true;
00319   }
00320 
00321   int Client::getStreamFeatures( Tag* tag )
00322   {
00323     if( tag->name() != "features" || tag->xmlns() != XMLNS_STREAM )
00324       return 0;
00325 
00326     int features = 0;
00327 
00328     if( tag->hasChild( "starttls", XMLNS, XMLNS_STREAM_TLS ) )
00329       features |= StreamFeatureStartTls;
00330 
00331     if( tag->hasChild( "mechanisms", XMLNS, XMLNS_STREAM_SASL ) )
00332       features |= getSaslMechs( tag->findChild( "mechanisms" ) );
00333 
00334     if( tag->hasChild( "bind", XMLNS, XMLNS_STREAM_BIND ) )
00335       features |= StreamFeatureBind;
00336 
00337     if( tag->hasChild( "unbind", XMLNS, XMLNS_STREAM_BIND ) )
00338       features |= StreamFeatureUnbind;
00339 
00340     if( tag->hasChild( "session", XMLNS, XMLNS_STREAM_SESSION ) )
00341       features |= StreamFeatureSession;
00342 
00343     if( tag->hasChild( "auth", XMLNS, XMLNS_STREAM_IQAUTH ) )
00344       features |= StreamFeatureIqAuth;
00345 
00346     if( tag->hasChild( "register", XMLNS, XMLNS_STREAM_IQREGISTER ) )
00347       features |= StreamFeatureIqRegister;
00348 
00349     if( tag->hasChild( "compression", XMLNS, XMLNS_STREAM_COMPRESS ) )
00350       features |= getCompressionMethods( tag->findChild( "compression" ) );
00351 
00352     if( features == 0 )
00353       features = StreamFeatureIqAuth;
00354 
00355     return features;
00356   }
00357 
00358   int Client::getSaslMechs( Tag* tag )
00359   {
00360     int mechs = SaslMechNone;
00361 
00362     const std::string mech = "mechanism";
00363 
00364     if( tag->hasChildWithCData( mech, "DIGEST-MD5" ) )
00365       mechs |= SaslMechDigestMd5;
00366 
00367     if( tag->hasChildWithCData( mech, "PLAIN" ) )
00368       mechs |= SaslMechPlain;
00369 
00370     if( tag->hasChildWithCData( mech, "ANONYMOUS" ) )
00371       mechs |= SaslMechAnonymous;
00372 
00373     if( tag->hasChildWithCData( mech, "EXTERNAL" ) )
00374       mechs |= SaslMechExternal;
00375 
00376     if( tag->hasChildWithCData( mech, "GSSAPI" ) )
00377       mechs |= SaslMechGssapi;
00378 
00379     return mechs;
00380   }
00381 
00382   int Client::getCompressionMethods( Tag* tag )
00383   {
00384     int meths = 0;
00385 
00386     if( tag->hasChildWithCData( "method", "zlib" ) )
00387       meths |= StreamFeatureCompressZlib;
00388 
00389     if( tag->hasChildWithCData( "method", "lzw" ) )
00390       meths |= StreamFeatureCompressDclz;
00391 
00392     return meths;
00393   }
00394 
00395   void Client::handleIqIDForward( const IQ& iq, int context )
00396   {
00397     switch( context )
00398     {
00399       case CtxResourceUnbind:
00400         // we don't store known resources anyway
00401         break;
00402       case CtxResourceBind:
00403         processResourceBind( iq );
00404         break;
00405       case CtxSessionEstablishment:
00406         processCreateSession( iq );
00407         break;
00408       default:
00409         break;
00410     }
00411   }
00412 
00413   bool Client::bindOperation( const std::string& resource, bool bind )
00414   {
00415     if( !( m_streamFeatures & StreamFeatureUnbind ) && m_resourceBound )
00416       return false;
00417 
00418     IQ iq( IQ::Set, JID(), getID() );
00419     iq.addExtension( new ResourceBind( resource, bind ) );
00420 
00421     send( iq, this, bind ? CtxResourceBind : CtxResourceUnbind );
00422     return true;
00423   }
00424 
00425   bool Client::selectResource( const std::string& resource )
00426   {
00427     if( !( m_streamFeatures & StreamFeatureUnbind ) )
00428       return false;
00429 
00430     m_selectedResource = resource;
00431 
00432     return true;
00433   }
00434 
00435   void Client::processResourceBind( const IQ& iq )
00436   {
00437     switch( iq.subtype() )
00438     {
00439       case IQ::Result:
00440       {
00441         const ResourceBind* rb = iq.findExtension<ResourceBind>( ExtResourceBind );
00442         if( !rb || !rb->jid() )
00443         {
00444           notifyOnResourceBindError( 0 );
00445           break;
00446         }
00447 
00448         m_jid = rb->jid();
00449         m_resourceBound = true;
00450         notifyOnResourceBind( m_jid.resource() );
00451 
00452         if( m_streamFeatures & StreamFeatureSession )
00453           createSession();
00454         else
00455           connected();
00456         break;
00457       }
00458       case IQ::Error:
00459       {
00460         notifyOnResourceBindError( iq.error() );
00461         break;
00462       }
00463       default:
00464         break;
00465     }
00466   }
00467 
00468   void Client::createSession()
00469   {
00470     notifyStreamEvent( StreamEventSessionCreation );
00471     IQ iq( IQ::Set, JID(), getID() );
00472     iq.addExtension( new SessionCreation() );
00473     send( iq, this, CtxSessionEstablishment );
00474   }
00475 
00476   void Client::processCreateSession( const IQ& iq )
00477   {
00478     switch( iq.subtype() )
00479     {
00480       case IQ::Result:
00481         connected();
00482         break;
00483       case IQ::Error:
00484         notifyOnSessionCreateError( iq.error() );
00485         break;
00486       default:
00487         break;
00488     }
00489   }
00490 
00491   void Client::negotiateCompression( StreamFeature method )
00492   {
00493     Tag* t = new Tag( "compress", XMLNS, XMLNS_COMPRESSION );
00494 
00495     if( method == StreamFeatureCompressZlib )
00496       new Tag( t, "method", "zlib" );
00497 
00498     if( method == StreamFeatureCompressDclz )
00499       new Tag( t, "method", "lzw" );
00500 
00501     send( t );
00502   }
00503 
00504   void Client::setPresence( Presence::PresenceType pres, int priority,
00505                             const std::string& status )
00506   {
00507     m_presence.setPresence( pres );
00508     m_presence.setPriority( priority );
00509     m_presence.addStatus( status );
00510     sendPresence( m_presence );
00511   }
00512 
00513   void Client::setPresence( const JID& to, Presence::PresenceType pres, int priority,
00514                             const std::string& status )
00515   {
00516     Presence p( pres, to, status, priority );
00517     sendPresence( p );
00518   }
00519 
00520   void Client::sendPresence( const Presence& pres )
00521   {
00522     if( state() >= StateConnected )
00523       send( pres );
00524   }
00525 
00526   void Client::disableRoster()
00527   {
00528     m_manageRoster = false;
00529     delete m_rosterManager;
00530     m_rosterManager = 0;
00531   }
00532 
00533   void Client::nonSaslLogin()
00534   {
00535     if( !m_auth )
00536       m_auth = new NonSaslAuth( this );
00537     m_auth->doAuth( m_sid );
00538   }
00539 
00540   void Client::connected()
00541   {
00542     if( m_authed )
00543     {
00544       if( m_manageRoster )
00545       {
00546         notifyStreamEvent( StreamEventRoster );
00547         m_rosterManager->fill();
00548       }
00549       else
00550         rosterFilled();
00551     }
00552     else
00553     {
00554       notifyStreamEvent( StreamEventFinished );
00555       notifyOnConnect();
00556     }
00557   }
00558 
00559   void Client::rosterFilled()
00560   {
00561     sendPresence( m_presence );
00562     notifyStreamEvent( StreamEventFinished );
00563     notifyOnConnect();
00564   }
00565 
00566   void Client::disconnect()
00567   {
00568     disconnect( ConnUserDisconnected );
00569   }
00570 
00571   void Client::disconnect( ConnectionError reason )
00572   {
00573     m_resourceBound = false;
00574     m_authed = false;
00575     m_streamFeatures = 0;
00576     ClientBase::disconnect( reason );
00577   }
00578 
00579   void Client::cleanup()
00580   {
00581     m_authed = false;
00582     m_resourceBound = false;
00583     m_streamFeatures = 0;
00584   }
00585 
00586 }

Generated on Tue Apr 22 15:10:10 2008 for gloox by  doxygen 1.4.1