18 #include "clientbase.h"
19 #include "compressionbase.h"
20 #include "connectionbase.h"
21 #include "connectioncompression.h"
22 #include "connectionlistener.h"
23 #include "connectiontcpclient.h"
24 #include "connectiontls.h"
27 #include "eventhandler.h"
30 #include "iqhandler.h"
32 #include "loghandler.h"
35 #include "messagehandler.h"
36 #include "messagesessionhandler.h"
37 #include "mucinvitationhandler.h"
39 #include "mutexguard.h"
41 #include "presencehandler.h"
42 #include "rosterlistener.h"
43 #include "stanzaextensionfactory.h"
44 #include "subscription.h"
45 #include "subscriptionhandler.h"
47 #include "taghandler.h"
49 #include "tlsdefault.h"
64 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
72 ClientBase::Ping::Ping()
77 ClientBase::Ping::~Ping()
81 const std::string& ClientBase::Ping::filterString()
const
83 static const std::string filter =
"/iq/ping[@xmlns='" +
XMLNS_XMPP_PING +
"']";
90 : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
91 m_xmllang(
"en" ), m_server( server ),
92 m_compress( true ), m_authed( false ), m_sasl( true ), m_tls(
TLSOptional ), m_port( port ),
94 m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
95 m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
96 m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
99 m_transportConnection( 0 ), m_selectedSaslMech(
SaslMechNone ), m_autoMessageSession( false )
105 const std::string& server,
int port )
106 : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
107 m_password( password ),
108 m_xmllang(
"en" ), m_server( server ),
109 m_compress( true ), m_authed( false ), m_sasl( true ), m_tls(
TLSOptional ),
110 m_port( port ), m_availableSaslMechs(
SaslMechAll ),
111 m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
112 m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
113 m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
116 m_transportConnection( 0 ), m_selectedSaslMech(
SaslMechNone ), m_autoMessageSession( false )
121 void ClientBase::init()
138 memset( &m_stats, 0,
sizeof( m_stats ) );
144 m_iqHandlerMapMutex.
lock();
145 m_iqIDHandlers.clear();
146 m_iqHandlerMapMutex.
unlock();
149 m_iqExtHandlers.clear();
171 PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin();
172 for( ; it1 != m_presenceJidHandlers.end(); ++it1 )
224 const std::string& version = tag->
findAttribute(
"version" );
228 " (it does not send a 'version' attribute). Please fix it or try another one.\n" );
238 handleStreamError( tag );
243 if( !handleNormalNode( tag ) )
247 if( tag->
name() ==
"iq" )
251 notifyIqHandlers( iq );
254 else if( tag->
name() ==
"message" )
258 notifyMessageHandlers( msg );
261 else if( tag->
name() ==
"presence" )
264 if( type ==
"subscribe" || type ==
"unsubscribe"
265 || type ==
"subscribed" || type ==
"unsubscribed" )
269 notifySubscriptionHandlers( sub );
276 notifyPresenceHandlers( pres );
285 notifyTagHandlers( tag );
290 if( m_statisticsHandler )
346 notifyOnDisconnect( reason );
355 send(
"</stream:stream>" );
362 void ClientBase::parse(
const std::string& data )
364 std::string copy = data;
366 if( ( i = m_parser.
feed( copy ) ) >= 0 )
368 std::string error =
"parse error (at pos ";
369 error += util::int2string( i );
372 Tag* e =
new Tag(
"stream:error" );
381 std::string head =
"<?xml version='1.0' ?>";
383 head +=
"xmlns:stream='http://etherx.jabber.org/streams' xml:lang='" +
m_xmllang +
"' ";
390 #if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS )
397 bool ClientBase::hasCompression()
399 #if defined( HAVE_ZLIB ) || defined( HAVE_LZW )
426 m_selectedSaslMech = type;
462 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
477 "SASL GSSAPI is not supported on this platform. You should never see this." );
483 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
485 SEC_WINNT_AUTH_IDENTITY_W identity, *ident = 0;
486 memset( &identity, 0,
sizeof( identity ) );
488 WCHAR *usernameW = 0, *domainW = NULL, *passwordW = 0;
489 int cchUsernameW = 0, cchDomainW = 0, cchPasswordW = 0;
496 cchUsernameW = ::MultiByteToWideChar( CP_UTF8, 0,
m_jid.
username().c_str(), -1, 0, 0 );
497 if( cchUsernameW > 0 )
499 usernameW =
new WCHAR[cchUsernameW];
500 ::MultiByteToWideChar( CP_UTF8, 0,
m_jid.
username().c_str(), -1, usernameW, cchUsernameW );
502 usernameW[cchUsernameW-1] = L
'\0';
504 cchDomainW = ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, 0, 0 );
507 domainW =
new WCHAR[cchDomainW];
508 ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, domainW, cchDomainW );
510 domainW[cchDomainW-1] = L
'\0';
512 cchPasswordW = ::MultiByteToWideChar( CP_UTF8, 0,
m_password.c_str(), -1, 0, 0 );
513 if( cchPasswordW > 0 )
515 passwordW =
new WCHAR[cchPasswordW];
516 ::MultiByteToWideChar( CP_UTF8, 0,
m_password.c_str(), -1, passwordW, cchPasswordW );
518 passwordW[cchPasswordW-1] = L
'\0';
520 identity.User = (
unsigned short*)usernameW;
521 identity.UserLength = (
unsigned long)cchUsernameW-1;
522 identity.Domain = (
unsigned short*)domainW;
523 identity.DomainLength = (
unsigned long)cchDomainW-1;
524 identity.Password = (
unsigned short*)passwordW;
525 identity.PasswordLength = (
unsigned long)cchPasswordW-1;
526 identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
530 AcquireCredentialsHandleW( 0, L
"NTLM", SECPKG_CRED_OUTBOUND, 0, ident, 0, 0, &m_credHandle, 0 );
544 ::SecureZeroMemory( passwordW, cchPasswordW*
sizeof( WCHAR ) );
551 "SASL NTLM is not supported on this platform. You should never see this." );
568 switch( m_selectedSaslMech )
572 if( !decoded.compare( 0, 7,
"rspauth" ) )
576 std::string::size_type end = 0;
577 std::string::size_type pos = decoded.find(
"realm=" );
578 if( pos != std::string::npos )
580 end = decoded.find(
'"', pos + 7 );
581 realm = decoded.substr( pos + 7, end - ( pos + 7 ) );
586 pos = decoded.find(
"nonce=" );
587 if( pos == std::string::npos )
590 end = decoded.find(
'"', pos + 7 );
591 while( decoded[end-1] ==
'\\' )
592 end = decoded.find(
'"', end + 1 );
593 std::string nonce = decoded.substr( pos + 7, end - ( pos + 7 ) );
597 for(
int i = 0; i < 4; ++i )
598 sprintf( cn + i*8,
"%08x", rand() );
599 cnonce.assign( cn, 4*8 );
608 const std::string& a1_h = md5.
binary();
616 const std::string& a1 = md5.
hex();
618 md5.
feed(
"AUTHENTICATE:xmpp/" );
621 const std::string& a2 = md5.
hex();
626 md5.
feed(
":00000001:" );
628 md5.
feed(
":auth:" );
632 std::string response =
"username=\"";
634 response +=
"\",realm=\"";
636 response +=
"\",nonce=\"";
638 response +=
"\",cnonce=\"";
640 response +=
"\",nc=00000001,qop=auth,digest-uri=\"xmpp/";
642 response +=
"\",response=";
643 response += md5.
hex();
644 response +=
",charset=utf-8";
654 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
658 "Huh, received GSSAPI challenge?! This should have never happened!" );
663 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
664 bool type1 = ( decoded.length() < 7 ) ?
true :
false;
666 SecBuffer bufferIn = { type1 ? 0 : (
unsigned long)decoded.length(),
668 (
void*)decoded.c_str() };
669 SecBufferDesc secIn = { 0, 1, &bufferIn };
673 SecBuffer bufferOut = {
sizeof( buffer ), SECBUFFER_TOKEN, buffer };
674 SecBufferDesc secOut = { 0, 1, &bufferOut };
677 unsigned long contextAttr;
679 SECURITY_STATUS status = InitializeSecurityContext( &m_credHandle, type1 ? 0 : &m_ctxtHandle,
680 0, ISC_REQ_MUTUAL_AUTH, 0, 0, &secIn, 0,
681 &m_ctxtHandle, &secOut, &contextAttr,
683 std::string response;
684 if( SUCCEEDED( status ) )
686 response = std::string( (
const char *)bufferOut.pvBuffer, bufferOut.cbBuffer );
691 "InitializeSecurityContext() failed, return value "
692 + util::int2string( status ) );
698 "Huh, received NTLM challenge?! This should have never happened!" );
715 else if( tag->
hasChild(
"incorrect-encoding" ) )
717 else if( tag->
hasChild(
"invalid-authzid" ) )
719 else if( tag->
hasChild(
"invalid-mechanism" ) )
721 else if( tag->
hasChild(
"malformed-request" ) )
723 else if( tag->
hasChild(
"mechanism-too-weak" ) )
725 else if( tag->
hasChild(
"not-authorized" ) )
727 else if( tag->
hasChild(
"temporary-auth-failure" ) )
730 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
733 FreeCredentialsHandle( &m_credHandle );
734 DeleteSecurityContext( &m_ctxtHandle );
741 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
744 FreeCredentialsHandle( &m_credHandle );
745 DeleteSecurityContext( &m_ctxtHandle );
754 if( iq.
id().empty() )
759 track.context = context;
761 m_iqHandlerMapMutex.
lock();
762 m_iqIDHandlers[iq.
id()] = track;
763 m_iqHandlerMapMutex.
unlock();
817 if( m_statisticsHandler )
833 void ClientBase::addFrom( Tag* tag )
835 if( !
m_authed || !tag || tag->hasAttribute(
"from" ) )
838 tag->addAttribute(
"from",
m_jid.
full() );
841 void ClientBase::addNamespace( Tag* tag )
843 if( !tag || !tag->xmlns().empty() )
885 const std::string&
id =
getID();
889 send( iq,
this, XMPPPing );
892 bool ClientBase::handleIq(
const IQ& iq )
905 void ClientBase::handleIqID(
const IQ& iq,
int context )
907 if( context == XMPPPing )
912 handleIqIDForward( iq, context );
917 static unsigned int uniqueBaseID = (
unsigned int)time( 0 );
919 sprintf( r,
"uid:%08x:%08x", uniqueBaseID, rand() );
920 std::string ret( r, 21 );
926 if( version.empty() )
933 size_t dot = version.find(
'.' );
934 if( !version.empty() && dot && dot != std::string::npos )
936 major = atoi( version.substr( 0, dot ).c_str() );
937 minor = atoi( version.substr( dot ).c_str() );
940 return myMajor >= major;
952 void ClientBase::handleStreamError(
Tag* tag )
956 TagList::const_iterator it = c.begin();
957 for( ; it != c.end(); ++it )
959 const std::string& name = (*it)->name();
960 if( name ==
"bad-format" )
962 else if( name ==
"bad-namespace-prefix" )
964 else if( name ==
"conflict" )
966 else if( name ==
"connection-timeout" )
968 else if( name ==
"host-gone" )
970 else if( name ==
"host-unknown" )
972 else if( name ==
"improper-addressing" )
974 else if( name ==
"internal-server-error" )
976 else if( name ==
"invalid-from" )
978 else if( name ==
"invalid-id" )
980 else if( name ==
"invalid-namespace" )
982 else if( name ==
"invalid-xml" )
984 else if( name ==
"not-authorized" )
986 else if( name ==
"policy-violation" )
988 else if( name ==
"remote-connection-failed" )
990 else if( name ==
"resource-constraint" )
992 else if( name ==
"restricted-xml" )
994 else if( name ==
"see-other-host" )
999 else if( name ==
"system-shutdown" )
1001 else if( name ==
"undefined-condition" )
1003 else if( name ==
"unsupported-encoding" )
1005 else if( name ==
"unsupported-stanza-type" )
1007 else if( name ==
"unsupported-version" )
1009 else if( name ==
"xml-not-well-formed" )
1011 else if( name ==
"text" )
1013 const std::string& lang = (*it)->findAttribute(
"xml:lang" );
1015 m_streamErrorText[lang] = (*it)->cdata();
1017 m_streamErrorText[
"default"] = (*it)->cdata();
1020 m_streamErrorAppCondition = (*it);
1023 m_streamError = err;
1029 StringMap::const_iterator it = m_streamErrorText.find( lang );
1030 return ( it != m_streamErrorText.end() ) ? (*it).second :
EmptyString;
1036 m_messageSessionHandlerChat = msh;
1039 m_messageSessionHandlerNormal = msh;
1042 m_messageSessionHandlerGroupchat = msh;
1045 m_messageSessionHandlerHeadline = msh;
1051 m_presenceHandlers.push_back( ph );
1057 m_presenceHandlers.remove( ph );
1064 JidPresHandlerStruct jph;
1065 jph.jid =
new JID( jid.
bare() );
1067 m_presenceJidHandlers.push_back( jph );
1073 PresenceJidHandlerList::iterator t;
1074 PresenceJidHandlerList::iterator it = m_presenceJidHandlers.begin();
1075 while( it != m_presenceJidHandlers.end() )
1079 if( ( !ph || (*t).ph == ph ) && (*t).jid->bare() == jid.
bare() )
1082 m_presenceJidHandlers.erase( t );
1089 IqTrackMap::iterator t;
1090 m_iqHandlerMapMutex.
lock();
1091 IqTrackMap::iterator it = m_iqIDHandlers.begin();
1092 while( it != m_iqIDHandlers.end() )
1095 if( ih == (*t).second.ih )
1096 m_iqIDHandlers.erase( t );
1098 m_iqHandlerMapMutex.
unlock();
1107 typedef IqHandlerMap::const_iterator IQci;
1108 std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( exttype );
1109 for( IQci it = g.first; it != g.second; ++it )
1111 if( (*it).second == ih )
1115 m_iqExtHandlers.insert( std::make_pair( exttype, ih ) );
1124 typedef IqHandlerMap::iterator IQi;
1125 std::pair<IQi, IQi> g = m_iqExtHandlers.equal_range( exttype );
1128 while( it != g.second )
1131 if( (*it2).second == ih )
1132 m_iqExtHandlers.erase( it2 );
1139 m_messageSessions.push_back( session );
1147 MessageSessionList::iterator it = std::find( m_messageSessions.begin(),
1148 m_messageSessions.end(),
1150 if( it != m_messageSessions.end() )
1153 m_messageSessions.erase( it );
1160 m_messageHandlers.push_back( mh );
1166 m_messageHandlers.remove( mh );
1172 m_subscriptionHandlers.push_back( sh );
1178 m_subscriptionHandlers.remove( sh );
1183 if( th && !tag.empty() )
1185 TagHandlerStruct ths;
1189 m_tagHandlers.push_back( ths );
1197 TagHandlerList::iterator it = m_tagHandlers.begin();
1198 for( ; it != m_tagHandlers.end(); ++it )
1200 if( (*it).th == th && (*it).tag == tag && (*it).xmlns == xmlns )
1201 m_tagHandlers.erase( it );
1209 m_statisticsHandler = sh;
1214 m_statisticsHandler = 0;
1221 m_mucInvitationHandler = mih;
1228 m_mucInvitationHandler = 0;
1235 m_connectionListeners.push_back( cl );
1241 m_connectionListeners.remove( cl );
1257 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1258 for( ; it != m_connectionListeners.end() && (*it)->onTLSConnect( info ); ++it )
1260 return m_stats.
encryption = ( it == m_connectionListeners.end() );
1283 void ClientBase::notifyPresenceHandlers(
Presence& pres )
1286 PresenceJidHandlerList::const_iterator t;
1287 PresenceJidHandlerList::const_iterator itj = m_presenceJidHandlers.begin();
1288 while( itj != m_presenceJidHandlers.end() )
1291 if( (*t).jid->bare() == pres.
from().
bare() && (*t).ph )
1293 (*t).ph->handlePresence( pres );
1301 PresenceHandlerList::const_iterator it = m_presenceHandlers.begin();
1302 for( ; it != m_presenceHandlers.end(); ++it )
1304 (*it)->handlePresence( pres );
1310 void ClientBase::notifySubscriptionHandlers( Subscription& s10n )
1313 SubscriptionHandlerList::const_iterator it = m_subscriptionHandlers.begin();
1314 for( ; it != m_subscriptionHandlers.end(); ++it )
1316 (*it)->handleSubscription( s10n );
1322 void ClientBase::notifyIqHandlers( IQ& iq )
1324 m_iqHandlerMapMutex.
lock();
1325 IqTrackMap::iterator it_id = m_iqIDHandlers.find( iq.id() );
1326 bool haveIdHandler = ( it_id != m_iqIDHandlers.end() );
1327 m_iqHandlerMapMutex.
unlock();
1330 (*it_id).second.ih->handleIqID( iq, (*it_id).second.context );
1331 if( (*it_id).second.del )
1332 delete (*it_id).second.ih;
1333 m_iqHandlerMapMutex.
lock();
1334 m_iqIDHandlers.erase( it_id );
1335 m_iqHandlerMapMutex.
unlock();
1339 if( iq.extensions().empty() )
1350 bool handled =
false;
1364 typedef IqHandlerMap::const_iterator IQci;
1366 StanzaExtensionList::const_iterator itse = sel.begin();
1367 for( ; !handled && itse != sel.end(); ++itse )
1369 std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( (*itse)->extensionType() );
1370 for( IQci it = g.first; !handled && it != g.second; ++it )
1372 if( (*it).second->handleIq( iq ) )
1378 if( !handled && ( iq.subtype() ==
IQ::Get || iq.subtype() ==
IQ::Set ) )
1386 void ClientBase::notifyMessageHandlers( Message& msg )
1388 if( m_mucInvitationHandler )
1390 const MUCRoom::MUCUser* mu = msg.findExtension<MUCRoom::MUCUser>(
ExtMUCUser );
1395 mu->jid() ? JID( *(mu->jid()) ) : JID(),
1405 MessageSessionList::const_iterator it1 = m_messageSessions.begin();
1406 for( ; it1 != m_messageSessions.end(); ++it1 )
1408 if( (*it1)->target().full() == msg.from().full() &&
1409 ( msg.thread().empty()
1410 || (*it1)->threadID() == msg.thread()
1411 || (*it1)->honorThreadID() ) &&
1413 ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1415 (*it1)->handleMessage( msg );
1420 it1 = m_messageSessions.begin();
1421 for( ; it1 != m_messageSessions.end(); ++it1 )
1423 if( (*it1)->target().bare() == msg.from().bare() &&
1424 ( msg.thread().empty()
1425 || (*it1)->threadID() == msg.thread()
1426 || (*it1)->honorThreadID() ) &&
1428 ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1430 (*it1)->handleMessage( msg );
1435 MessageSessionHandler* msHandler = 0;
1437 switch( msg.subtype() )
1440 msHandler = m_messageSessionHandlerChat;
1443 msHandler = m_messageSessionHandlerNormal;
1446 msHandler = m_messageSessionHandlerGroupchat;
1449 msHandler = m_messageSessionHandlerHeadline;
1459 MessageSession* session =
new MessageSession(
this, msg.from(),
true, msg.subtype() );
1461 session->handleMessage( msg );
1466 MessageHandlerList::const_iterator it = m_messageHandlers.begin();
1467 for( ; it != m_messageHandlers.end(); ++it )
1469 (*it)->handleMessage( msg );
1476 void ClientBase::notifyTagHandlers( Tag* tag )
1478 TagHandlerList::const_iterator it = m_tagHandlers.begin();
1479 for( ; it != m_tagHandlers.end(); ++it )
1481 if( (*it).tag == tag->name() && tag->hasAttribute(
XMLNS, (*it).xmlns ) )
1482 (*it).th->handleTag( tag );
1500 if( (*it)->extensionType() == type )