18 #include "clientbase.h"
19 #include "compressionbase.h"
20 #include "compressionzlib.h"
21 #include "connectionbase.h"
22 #include "connectionlistener.h"
23 #include "connectiontcpclient.h"
26 #include "eventhandler.h"
29 #include "iqhandler.h"
31 #include "loghandler.h"
34 #include "messagehandler.h"
35 #include "messagesessionhandler.h"
36 #include "mucinvitationhandler.h"
38 #include "mutexguard.h"
40 #include "presencehandler.h"
41 #include "rosterlistener.h"
42 #include "stanzaextensionfactory.h"
43 #include "subscription.h"
44 #include "subscriptionhandler.h"
46 #include "taghandler.h"
48 #include "tlsdefault.h"
62 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
70 ClientBase::Ping::Ping()
75 ClientBase::Ping::~Ping()
79 const std::string& ClientBase::Ping::filterString()
const
81 static const std::string filter =
"/iq/ping[@xmlns='" +
XMLNS_XMPP_PING +
"']";
88 : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
89 m_xmllang(
"en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
90 m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls(
TLSOptional ), m_port( port ),
92 m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
93 m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
94 m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
97 m_selectedSaslMech(
SaslMechNone ), m_autoMessageSession( false ), m_customConnection( false ),
98 m_uniqueBaseId( (unsigned int)( ( (unsigned long long)time( 0 ) & 0xFFFF ) << 16 ) | ( ( (unsigned long long)&m_nextId ) & 0xFFFF ) )
104 const std::string& server,
int port )
105 : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
106 m_password( password ),
107 m_xmllang(
"en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
108 m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls(
TLSOptional ),
109 m_port( port ), m_availableSaslMechs(
SaslMechAll ),
110 m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
111 m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
112 m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
115 m_selectedSaslMech(
SaslMechNone ), m_autoMessageSession( false ), m_customConnection( false ),
116 m_uniqueBaseId( (unsigned int)( ( (unsigned long long)time( 0 ) & 0xFFFF ) << 16 ) | ( ( (unsigned long long)&m_nextId ) & 0xFFFF ) )
121 void ClientBase::init()
136 memset( &m_stats, 0,
sizeof( m_stats ) );
142 m_iqHandlerMapMutex.
lock();
143 m_iqIDHandlers.clear();
144 m_iqHandlerMapMutex.
unlock();
146 m_iqExtHandlerMapMutex.
lock();
147 m_iqExtHandlers.clear();
148 m_iqExtHandlerMapMutex.
unlock();
160 PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin();
161 for( ; it1 != m_presenceJidHandlers.end(); ++it1 )
191 +
m_server + ( ( m_customConnection )?(
" using a custom connection" ):(
":" + util::int2string(
m_port ) ) ) +
"..." );
217 const std::string& version = tag->
findAttribute(
"version" );
221 " (it does not send a 'version' attribute). Please fix it or try another one.\n" );
227 handleStartNode( tag );
231 handleStreamError( tag );
236 if( !handleNormalNode( tag ) )
240 if( tag->
name() ==
"iq" )
244 notifyIqHandlers( iq );
247 else if( tag->
name() ==
"message" )
251 notifyMessageHandlers( msg );
254 else if( tag->
name() ==
"presence" )
257 if( type ==
"subscribe" || type ==
"unsubscribe"
258 || type ==
"subscribed" || type ==
"unsubscribed" )
262 notifySubscriptionHandlers( sub );
269 notifyPresenceHandlers( pres );
278 notifyTagHandlers( tag );
283 if( m_statisticsHandler )
369 notifyOnDisconnect( reason );
378 send(
"</stream:stream>" );
392 notifyOnDisconnect( reason );
395 void ClientBase::parse(
const std::string& data )
397 std::string copy = data;
399 if( ( i = m_parser.
feed( copy ) ) >= 0 )
401 std::string error =
"parse error (at pos ";
402 error += util::int2string( i );
405 Tag* e =
new Tag(
"stream:error" );
414 std::string head =
"<?xml version='1.0' ?>";
416 head +=
"xmlns:stream='http://etherx.jabber.org/streams' xml:lang='" +
m_xmllang +
"' ";
423 #if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS )
450 m_selectedSaslMech = type;
486 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
501 "SASL GSSAPI is not supported on this platform. You should never see this." );
507 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
509 SEC_WINNT_AUTH_IDENTITY_W identity, *ident = 0;
510 memset( &identity, 0,
sizeof( identity ) );
512 WCHAR *usernameW = 0, *domainW = NULL, *passwordW = 0;
513 int cchUsernameW = 0, cchDomainW = 0, cchPasswordW = 0;
520 cchUsernameW = ::MultiByteToWideChar( CP_UTF8, 0,
m_jid.
username().c_str(), -1, 0, 0 );
521 if( cchUsernameW > 0 )
523 usernameW =
new WCHAR[cchUsernameW];
524 ::MultiByteToWideChar( CP_UTF8, 0,
m_jid.
username().c_str(), -1, usernameW, cchUsernameW );
526 usernameW[cchUsernameW-1] = L
'\0';
528 cchDomainW = ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, 0, 0 );
531 domainW =
new WCHAR[cchDomainW];
532 ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, domainW, cchDomainW );
534 domainW[cchDomainW-1] = L
'\0';
536 cchPasswordW = ::MultiByteToWideChar( CP_UTF8, 0,
m_password.c_str(), -1, 0, 0 );
537 if( cchPasswordW > 0 )
539 passwordW =
new WCHAR[cchPasswordW];
540 ::MultiByteToWideChar( CP_UTF8, 0,
m_password.c_str(), -1, passwordW, cchPasswordW );
542 passwordW[cchPasswordW-1] = L
'\0';
544 identity.User = (
unsigned short*)usernameW;
545 identity.UserLength = (
unsigned long)cchUsernameW-1;
546 identity.Domain = (
unsigned short*)domainW;
547 identity.DomainLength = (
unsigned long)cchDomainW-1;
548 identity.Password = (
unsigned short*)passwordW;
549 identity.PasswordLength = (
unsigned long)cchPasswordW-1;
550 identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
554 AcquireCredentialsHandleW( 0, L
"NTLM", SECPKG_CRED_OUTBOUND, 0, ident, 0, 0, &m_credHandle, 0 );
568 ::SecureZeroMemory( passwordW, cchPasswordW*
sizeof( WCHAR ) );
575 "SASL NTLM is not supported on this platform. You should never see this." );
592 switch( m_selectedSaslMech )
596 if( !decoded.compare( 0, 7,
"rspauth" ) )
600 std::string::size_type end = 0;
601 std::string::size_type pos = decoded.find(
"realm=" );
602 if( pos != std::string::npos )
604 end = decoded.find(
'"', pos + 7 );
605 realm = decoded.substr( pos + 7, end - ( pos + 7 ) );
610 pos = decoded.find(
"nonce=" );
611 if( pos == std::string::npos )
614 end = decoded.find(
'"', pos + 7 );
615 while( decoded[end-1] ==
'\\' )
616 end = decoded.find(
'"', end + 1 );
617 std::string nonce = decoded.substr( pos + 7, end - ( pos + 7 ) );
621 for(
int i = 0; i < 4; ++i )
622 sprintf( cn + i*8,
"%08x", rand() );
623 cnonce.assign( cn, 4*8 );
632 const std::string& a1_h = md5.
binary();
640 const std::string& a1 = md5.
hex();
642 md5.
feed(
"AUTHENTICATE:xmpp/" );
645 const std::string& a2 = md5.
hex();
650 md5.
feed(
":00000001:" );
652 md5.
feed(
":auth:" );
656 std::string response =
"username=\"";
658 response +=
"\",realm=\"";
660 response +=
"\",nonce=\"";
662 response +=
"\",cnonce=\"";
664 response +=
"\",nc=00000001,qop=auth,digest-uri=\"xmpp/";
666 response +=
"\",response=";
667 response += md5.
hex();
668 response +=
",charset=utf-8";
678 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
682 "Huh, received GSSAPI challenge?! This should have never happened!" );
687 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
688 bool type1 = ( decoded.length() < 7 ) ?
true :
false;
690 SecBuffer bufferIn = { type1 ? 0 : (
unsigned long)decoded.length(),
692 (
void*)decoded.c_str() };
693 SecBufferDesc secIn = { 0, 1, &bufferIn };
697 SecBuffer bufferOut = {
sizeof( buffer ), SECBUFFER_TOKEN, buffer };
698 SecBufferDesc secOut = { 0, 1, &bufferOut };
701 unsigned long contextAttr;
703 SECURITY_STATUS status = InitializeSecurityContext( &m_credHandle, type1 ? 0 : &m_ctxtHandle,
704 0, ISC_REQ_MUTUAL_AUTH, 0, 0, &secIn, 0,
705 &m_ctxtHandle, &secOut, &contextAttr,
707 std::string response;
708 if( SUCCEEDED( status ) )
710 response = std::string( (
const char *)bufferOut.pvBuffer, bufferOut.cbBuffer );
715 "InitializeSecurityContext() failed, return value "
716 + util::int2string( status ) );
722 "Huh, received NTLM challenge?! This should have never happened!" );
739 else if( tag->
hasChild(
"incorrect-encoding" ) )
741 else if( tag->
hasChild(
"invalid-authzid" ) )
743 else if( tag->
hasChild(
"invalid-mechanism" ) )
745 else if( tag->
hasChild(
"malformed-request" ) )
747 else if( tag->
hasChild(
"mechanism-too-weak" ) )
749 else if( tag->
hasChild(
"not-authorized" ) )
751 else if( tag->
hasChild(
"temporary-auth-failure" ) )
754 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
757 FreeCredentialsHandle( &m_credHandle );
758 DeleteSecurityContext( &m_ctxtHandle );
765 #if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
768 FreeCredentialsHandle( &m_credHandle );
769 DeleteSecurityContext( &m_ctxtHandle );
778 if( iq.
id().empty() )
783 track.context = context;
785 m_iqHandlerMapMutex.
lock();
786 m_iqIDHandlers[iq.
id()] = track;
787 m_iqHandlerMapMutex.
unlock();
841 if( m_statisticsHandler )
862 void ClientBase::addFrom(
Tag* tag )
870 void ClientBase::addNamespace( Tag* tag )
872 if( !tag || !tag->xmlns().empty() )
914 const std::string&
id =
getID();
918 send( iq,
this, XMPPPing );
921 bool ClientBase::handleIq(
const IQ& iq )
934 void ClientBase::handleIqID(
const IQ& iq,
int context )
936 if( context == XMPPPing )
941 handleIqIDForward( iq, context );
947 sprintf( r,
"uid:%08x:%08x", m_uniqueBaseId, m_nextId.
increment() );
948 std::string ret( r, 21 );
954 if( version.empty() )
961 size_t dot = version.find(
'.' );
962 if( !version.empty() && dot && dot != std::string::npos )
964 major = atoi( version.substr( 0, dot ).c_str() );
965 minor = atoi( version.substr( dot ).c_str() );
968 return myMajor >= major;
978 m_customConnection =
true;
999 void ClientBase::handleStreamError(
Tag* tag )
1003 TagList::const_iterator it = c.begin();
1004 for( ; it != c.end(); ++it )
1006 const std::string& name = (*it)->name();
1007 if( name ==
"bad-format" )
1009 else if( name ==
"bad-namespace-prefix" )
1011 else if( name ==
"conflict" )
1013 else if( name ==
"connection-timeout" )
1015 else if( name ==
"host-gone" )
1017 else if( name ==
"host-unknown" )
1019 else if( name ==
"improper-addressing" )
1021 else if( name ==
"internal-server-error" )
1023 else if( name ==
"invalid-from" )
1025 else if( name ==
"invalid-id" )
1027 else if( name ==
"invalid-namespace" )
1029 else if( name ==
"invalid-xml" )
1031 else if( name ==
"not-authorized" )
1033 else if( name ==
"policy-violation" )
1035 else if( name ==
"remote-connection-failed" )
1037 else if( name ==
"resource-constraint" )
1039 else if( name ==
"restricted-xml" )
1041 else if( name ==
"see-other-host" )
1044 m_streamErrorCData = tag->
findChild(
"see-other-host" )->
cdata();
1046 else if( name ==
"system-shutdown" )
1048 else if( name ==
"undefined-condition" )
1050 else if( name ==
"unsupported-encoding" )
1052 else if( name ==
"unsupported-stanza-type" )
1054 else if( name ==
"unsupported-version" )
1056 else if( name ==
"xml-not-well-formed" )
1058 else if( name ==
"text" )
1060 const std::string& lang = (*it)->findAttribute(
"xml:lang" );
1062 m_streamErrorText[lang] = (*it)->cdata();
1064 m_streamErrorText[
"default"] = (*it)->cdata();
1067 m_streamErrorAppCondition = (*it);
1070 m_streamError = err;
1076 StringMap::const_iterator it = m_streamErrorText.find( lang );
1077 return ( it != m_streamErrorText.end() ) ? (*it).second :
EmptyString;
1083 m_messageSessionHandlerChat = msh;
1086 m_messageSessionHandlerNormal = msh;
1089 m_messageSessionHandlerGroupchat = msh;
1092 m_messageSessionHandlerHeadline = msh;
1098 m_presenceHandlers.push_back( ph );
1104 m_presenceHandlers.remove( ph );
1111 JidPresHandlerStruct jph;
1112 jph.jid =
new JID( jid.
bare() );
1114 m_presenceJidHandlers.push_back( jph );
1120 PresenceJidHandlerList::iterator t;
1121 PresenceJidHandlerList::iterator it = m_presenceJidHandlers.begin();
1122 while( it != m_presenceJidHandlers.end() )
1126 if( ( !ph || (*t).ph == ph ) && (*t).jid->bare() == jid.
bare() )
1129 m_presenceJidHandlers.erase( t );
1136 IqTrackMap::iterator t;
1137 m_iqHandlerMapMutex.
lock();
1138 IqTrackMap::iterator it = m_iqIDHandlers.begin();
1139 while( it != m_iqIDHandlers.end() )
1143 if( ih == (*t).second.ih )
1144 m_iqIDHandlers.erase( t );
1146 m_iqHandlerMapMutex.
unlock();
1155 typedef IqHandlerMap::const_iterator IQci;
1156 std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( exttype );
1157 for( IQci it = g.first; it != g.second; ++it )
1159 if( (*it).second == ih )
1163 m_iqExtHandlers.insert( std::make_pair( exttype, ih ) );
1172 typedef IqHandlerMap::iterator IQi;
1173 std::pair<IQi, IQi> g = m_iqExtHandlers.equal_range( exttype );
1176 while( it != g.second )
1179 if( (*it2).second == ih )
1180 m_iqExtHandlers.erase( it2 );
1187 m_messageSessions.push_back( session );
1195 MessageSessionList::iterator it = std::find( m_messageSessions.begin(),
1196 m_messageSessions.end(),
1198 if( it != m_messageSessions.end() )
1201 m_messageSessions.erase( it );
1208 m_messageHandlers.push_back( mh );
1214 m_messageHandlers.remove( mh );
1220 m_subscriptionHandlers.push_back( sh );
1226 m_subscriptionHandlers.remove( sh );
1231 if( th && !tag.empty() )
1233 TagHandlerStruct ths;
1237 m_tagHandlers.push_back( ths );
1245 TagHandlerList::iterator it = m_tagHandlers.begin();
1246 for( ; it != m_tagHandlers.end(); ++it )
1248 if( (*it).th == th && (*it).tag == tag && (*it).xmlns == xmlns )
1249 m_tagHandlers.erase( it );
1257 m_statisticsHandler = sh;
1262 m_statisticsHandler = 0;
1269 m_mucInvitationHandler = mih;
1276 m_mucInvitationHandler = 0;
1283 m_connectionListeners.push_back( cl );
1289 m_connectionListeners.remove( cl );
1305 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1306 for( ; it != m_connectionListeners.end() && (*it)->onTLSConnect( info ); ++it )
1308 return m_stats.
encryption = ( it == m_connectionListeners.end() );
1331 void ClientBase::notifyPresenceHandlers(
Presence& pres )
1334 PresenceJidHandlerList::const_iterator t;
1335 PresenceJidHandlerList::const_iterator itj = m_presenceJidHandlers.begin();
1336 while( itj != m_presenceJidHandlers.end() )
1339 if( (*t).jid->bare() == pres.
from().
bare() && (*t).ph )
1341 (*t).ph->handlePresence( pres );
1349 PresenceHandlerList::const_iterator it = m_presenceHandlers.begin();
1350 for( ; it != m_presenceHandlers.end(); ++it )
1352 (*it)->handlePresence( pres );
1358 void ClientBase::notifySubscriptionHandlers( Subscription& s10n )
1361 SubscriptionHandlerList::const_iterator it = m_subscriptionHandlers.begin();
1362 for( ; it != m_subscriptionHandlers.end(); ++it )
1364 (*it)->handleSubscription( s10n );
1370 void ClientBase::notifyIqHandlers( IQ& iq )
1372 m_iqHandlerMapMutex.
lock();
1373 IqTrackMap::iterator it_id = m_iqIDHandlers.find( iq.id() );
1374 bool haveIdHandler = ( it_id != m_iqIDHandlers.end() );
1375 m_iqHandlerMapMutex.
unlock();
1378 (*it_id).second.ih->handleIqID( iq, (*it_id).second.context );
1379 if( (*it_id).second.del )
1380 delete (*it_id).second.ih;
1381 m_iqHandlerMapMutex.
lock();
1382 m_iqIDHandlers.erase( it_id );
1383 m_iqHandlerMapMutex.
unlock();
1387 if( iq.extensions().empty() )
1398 bool handled =
false;
1411 m_iqExtHandlerMapMutex.
lock();
1412 typedef IqHandlerMap::const_iterator IQci;
1414 StanzaExtensionList::const_iterator itse = sel.begin();
1415 for( ; !handled && itse != sel.end(); ++itse )
1417 std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( (*itse)->extensionType() );
1418 for( IQci it = g.first; !handled && it != g.second; ++it )
1420 if( (*it).second->handleIq( iq ) )
1424 m_iqExtHandlerMapMutex.
unlock();
1426 if( !handled && ( iq.subtype() ==
IQ::Get || iq.subtype() ==
IQ::Set ) )
1434 void ClientBase::notifyMessageHandlers( Message& msg )
1436 if( m_mucInvitationHandler )
1438 const MUCRoom::MUCUser* mu = msg.findExtension<MUCRoom::MUCUser>(
ExtMUCUser );
1443 mu->jid() ? JID( *(mu->jid()) ) : JID(),
1453 MessageSessionList::const_iterator it1 = m_messageSessions.begin();
1454 for( ; it1 != m_messageSessions.end(); ++it1 )
1456 if( (*it1)->target().full() == msg.from().full() &&
1457 ( msg.thread().empty()
1458 || (*it1)->threadID() == msg.thread()
1459 || (*it1)->honorThreadID() ) &&
1461 ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1463 (*it1)->handleMessage( msg );
1468 it1 = m_messageSessions.begin();
1469 for( ; it1 != m_messageSessions.end(); ++it1 )
1471 if( (*it1)->target().bare() == msg.from().bare() &&
1472 ( msg.thread().empty()
1473 || (*it1)->threadID() == msg.thread()
1474 || (*it1)->honorThreadID() ) &&
1476 ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1478 (*it1)->handleMessage( msg );
1483 MessageSessionHandler* msHandler = 0;
1485 switch( msg.subtype() )
1488 msHandler = m_messageSessionHandlerChat;
1491 msHandler = m_messageSessionHandlerNormal;
1494 msHandler = m_messageSessionHandlerGroupchat;
1497 msHandler = m_messageSessionHandlerHeadline;
1507 MessageSession* session =
new MessageSession(
this, msg.from(),
true, msg.subtype() );
1509 session->handleMessage( msg );
1514 MessageHandlerList::const_iterator it = m_messageHandlers.begin();
1515 for( ; it != m_messageHandlers.end(); ++it )
1517 (*it)->handleMessage( msg );
1524 void ClientBase::notifyTagHandlers( Tag* tag )
1526 TagHandlerList::const_iterator it = m_tagHandlers.begin();
1527 for( ; it != m_tagHandlers.end(); ++it )
1529 if( (*it).tag == tag->name() && tag->hasAttribute(
XMLNS, (*it).xmlns ) )
1530 (*it).th->handleTag( tag );
1548 if( (*it)->extensionType() == type )
1574 TLSBase* ClientBase::getDefaultEncryption()
1579 TLSDefault*
tls =
new TLSDefault(
this,
m_server );