gloox 1.0.24
clientbase.cpp
1/*
2 Copyright (c) 2005-2019 by Jakob Schröter <js@camaya.net>
3 This file is part of the gloox library. http://camaya.net/gloox
4
5 This software is distributed under a license. The full license
6 agreement can be found in the file LICENSE in this distribution.
7 This software may not be copied, modified, sold or distributed
8 other than expressed in the named license agreement.
9
10 This software is distributed without any warranty.
11*/
12
13
14
15#include "config.h"
16
17#include "base64.h"
18#include "clientbase.h"
19#include "compressionbase.h"
20#include "compressionzlib.h"
21#include "connectionbase.h"
22#include "connectionlistener.h"
23#include "connectiontcpclient.h"
24#include "disco.h"
25#include "error.h"
26#include "eventhandler.h"
27#include "event.h"
28#include "iq.h"
29#include "iqhandler.h"
30#include "jid.h"
31#include "loghandler.h"
32#include "md5.h"
33#include "message.h"
34#include "messagehandler.h"
35#include "messagesessionhandler.h"
36#include "mucinvitationhandler.h"
37#include "mucroom.h"
38#include "mutexguard.h"
39#include "presence.h"
40#include "presencehandler.h"
41#include "rosterlistener.h"
42#include "stanzaextensionfactory.h"
43#include "sha.h"
44#include "subscription.h"
45#include "subscriptionhandler.h"
46#include "tag.h"
47#include "taghandler.h"
48#include "tlsbase.h"
49#include "tlsdefault.h"
50#include "prep.h"
51#include "util.h"
52
53#include <cstdlib>
54#include <string>
55#include <map>
56#include <list>
57#include <algorithm>
58#include <cmath>
59#include <ctime>
60#include <cstdio>
61
62#include <string.h> // for memset()
63
64#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
65#include <tchar.h>
66# ifdef __MINGW32__
67# ifndef SecureZeroMemory
68# define SecureZeroMemory(p,s) RtlFillMemory((p),(s),0)
69# endif
70# endif
71#endif
72
73namespace gloox
74{
75
76 // ---- ClientBase::Ping ----
77 ClientBase::Ping::Ping()
78 : StanzaExtension( ExtPing )
79 {
80 }
81
82 ClientBase::Ping::~Ping()
83 {
84 }
85
86 const std::string& ClientBase::Ping::filterString() const
87 {
88 static const std::string filter = "/iq/ping[@xmlns='" + XMLNS_XMPP_PING + "']";
89 return filter;
90 }
91 // ---- ~ClientBase::Ping ----
92
93 // ---- ClientBase ----
94 ClientBase::ClientBase( const std::string& ns, const std::string& server, int port )
95 : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
96 m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
97 m_compress( true ), m_authed( false ), m_resourceBound( false ), m_block( false ), m_sasl( true ),
98 m_tls( TLSOptional ), m_port( port ),
99 m_availableSaslMechs( SaslMechAll ), m_smContext( CtxSMInvalid ), m_smHandled( 0 ),
100 m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
101 m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
102 m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
103 m_parser( this ), m_seFactory( 0 ), m_authError( AuthErrorUndefined ),
104 m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ),
105 m_selectedSaslMech( SaslMechNone ), m_customConnection( false ),
106 m_smSent( 0 )
107 {
108 init();
109 }
110
111 ClientBase::ClientBase( const std::string& ns, const std::string& password,
112 const std::string& server, int port )
113 : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
114 m_password( password ),
115 m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
116 m_compress( true ), m_authed( false ), m_resourceBound( false ), m_block( false ), m_sasl( true ),
117 m_tls( TLSOptional ), m_port( port ),
118 m_availableSaslMechs( SaslMechAll ), m_smContext( CtxSMInvalid ), m_smHandled( 0 ),
119 m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
120 m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
121 m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
122 m_parser( this ), m_seFactory( 0 ), m_authError( AuthErrorUndefined ),
123 m_streamError( StreamErrorUndefined ), m_streamErrorAppCondition( 0 ),
124 m_selectedSaslMech( SaslMechNone ), m_customConnection( false ),
125 m_smSent( 0 )
126 {
127 init();
128 }
129
130 void ClientBase::init()
131 {
132 srand( static_cast<unsigned int>( time( 0 ) ) );
133 SHA sha;
134 sha.feed( util::long2string( time( 0 ) ) );
135 sha.feed( util::int2string( rand() ) );
136 m_uniqueBaseId = sha.hex();
137
138 if( !m_disco )
139 {
140 m_disco = new Disco( this );
141 m_disco->setVersion( "based on gloox", GLOOX_VERSION );
143 }
144
145 registerStanzaExtension( new Error() );
146 registerStanzaExtension( new Ping() );
147 registerIqHandler( this, ExtPing );
148
149 m_streamError = StreamErrorUndefined;
150 m_block = false;
151 memset( &m_stats, 0, sizeof( m_stats ) );
152 cleanup();
153 }
154
156 {
157 m_iqHandlerMapMutex.lock();
158 m_iqIDHandlers.clear();
159 m_iqHandlerMapMutex.unlock();
160
161 m_iqExtHandlerMapMutex.lock();
162 m_iqExtHandlers.clear();
163 m_iqExtHandlerMapMutex.unlock();
164
166 util::clearMap( m_smQueue );
167
171 delete m_seFactory;
172 m_seFactory = 0; // to avoid usage when Disco gets deleted below
173 delete m_disco;
174 m_disco = 0;
175
176 util::clearList( m_messageSessions );
177
178 PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin();
179 for( ; it1 != m_presenceJidHandlers.end(); ++it1 )
180 delete (*it1).jid;
181 }
182
184 {
186 return ConnNotConnected;
187
188 return m_connection->recv( timeout );
189 }
190
191 bool ClientBase::connect( bool block )
192 {
193 if( m_server.empty() )
194 return false;
195
196 if( !m_connection )
197 m_connection = new ConnectionTCPClient( this, m_logInstance, m_server, m_port );
198
200 return true;
201
202 if( !m_encryption )
203 m_encryption = getDefaultEncryption();
204
205 if( !m_compression )
206 m_compression = getDefaultCompression();
207
208 m_logInstance.dbg( LogAreaClassClientbase, "This is gloox " + GLOOX_VERSION + ", connecting to "
209 + m_server + ( ( m_customConnection )?( " using a custom connection" ):( m_port > 0 ? ( ":" + util::int2string( m_port ) ) : EmptyString ) ) + "..." );
210 m_block = block;
212 if( ret != ConnNoError )
213 return false;
214
215 if( m_block )
217
218 return true;
219 }
220
222 {
223 if( !tag )
224 {
225 logInstance().dbg( LogAreaClassClientbase, "stream closed" );
227 return;
228 }
229
231 ++m_stats.totalStanzasReceived;
232
233 if( tag->name() == "stream" && tag->xmlns() == XMLNS_STREAM )
234 {
235 const std::string& version = tag->findAttribute( "version" );
236 if( !checkStreamVersion( version ) )
237 {
238 logInstance().dbg( LogAreaClassClientbase, "This server is not XMPP-compliant"
239 " (it does not send a 'version' attribute). Please fix it or try another one.\n" );
241 return;
242 }
243
244 m_sid = tag->findAttribute( "id" );
245 handleStartNode( tag );
246 }
247 else if( tag->name() == "error" && tag->xmlns() == XMLNS_STREAM )
248 {
249 handleStreamError( tag );
251 }
252 else
253 {
254 if( !handleNormalNode( tag ) )
255 {
256 if( tag->xmlns().empty() || tag->xmlns() == XMLNS_CLIENT )
257 {
258 if( tag->name() == "iq" )
259 {
260 IQ iq( tag );
261 m_seFactory->addExtensions( iq, tag );
262 if( iq.hasEmbeddedStanza() )
263 m_seFactory->addExtensions( *iq.embeddedStanza(), iq.embeddedTag() );
264 notifyIqHandlers( iq );
265 ++m_stats.iqStanzasReceived;
267 ++m_smHandled;
268 }
269 else if( tag->name() == "message" )
270 {
271 Message msg( tag );
272 m_seFactory->addExtensions( msg, tag );
273 if( msg.hasEmbeddedStanza() )
274 m_seFactory->addExtensions( *msg.embeddedStanza(), msg.embeddedTag() );
275 notifyMessageHandlers( msg );
276 ++m_stats.messageStanzasReceived;
278 ++m_smHandled;
279 }
280 else if( tag->name() == "presence" )
281 {
282 const std::string& type = tag->findAttribute( TYPE );
283 if( type == "subscribe" || type == "unsubscribe"
284 || type == "subscribed" || type == "unsubscribed" )
285 {
286 Subscription sub( tag );
287 m_seFactory->addExtensions( sub, tag );
288 if( sub.hasEmbeddedStanza() )
289 m_seFactory->addExtensions( *sub.embeddedStanza(), sub.embeddedTag() );
290 notifySubscriptionHandlers( sub );
291 ++m_stats.s10nStanzasReceived;
292 }
293 else
294 {
295 Presence pres( tag );
296 m_seFactory->addExtensions( pres, tag );
297 if( pres.hasEmbeddedStanza() )
298 m_seFactory->addExtensions( *pres.embeddedStanza(), pres.embeddedTag() );
299 notifyPresenceHandlers( pres );
300 ++m_stats.presenceStanzasReceived;
301 }
303 ++m_smHandled;
304 }
305 else
306 m_logInstance.err( LogAreaClassClientbase, "Invalid stanza received: " + tag->name() );
307 }
308 else
309 {
310 notifyTagHandlers( tag );
311 }
312 }
313 }
314
315 if( m_statisticsHandler )
316 m_statisticsHandler->handleStatistics( getStatistics() );
317 }
318
319 void ClientBase::handleCompressedData( const std::string& data )
320 {
322 m_encryption->encrypt( data );
323 else if( m_connection )
324 m_connection->send( data );
325 else
326 m_logInstance.err( LogAreaClassClientbase, "Compression finished, but chain broken" );
327 }
328
329 void ClientBase::handleDecompressedData( const std::string& data )
330 {
331 parse( data );
332 }
333
334 void ClientBase::handleEncryptedData( const TLSBase* /*base*/, const std::string& data )
335 {
336 if( m_connection )
337 m_connection->send( data );
338 else
339 m_logInstance.err( LogAreaClassClientbase, "Encryption finished, but chain broken" );
340 }
341
342 void ClientBase::handleDecryptedData( const TLSBase* /*base*/, const std::string& data )
343 {
345 m_compression->decompress( data );
346 else
347 parse( data );
348 }
349
350 void ClientBase::handleHandshakeResult( const TLSBase* /*base*/, bool success, CertInfo &certinfo )
351 {
352 if( success )
353 {
354 if( !notifyOnTLSConnect( certinfo ) )
355 {
356 logInstance().err( LogAreaClassClientbase, "Server's certificate rejected!" );
358 }
359 else
360 {
361 logInstance().dbg( LogAreaClassClientbase, "connection encryption active" );
362 header();
363 }
364 }
365 else
366 {
367 logInstance().err( LogAreaClassClientbase, "TLS handshake failed!" );
369 }
370 }
371
372 void ClientBase::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data )
373 {
375 m_encryption->decrypt( data );
377 m_compression->decompress( data );
378 else
379 parse( data );
380 }
381
382 void ClientBase::handleConnect( const ConnectionBase* /*connection*/ )
383 {
384 header();
385 }
386
387 void ClientBase::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason )
388 {
389 if( m_connection )
391
392 if( m_encryption )
394
395 if( m_compression )
397
398 m_encryptionActive = false;
399 m_compressionActive = false;
400
401 notifyOnDisconnect( reason );
402 }
403
405 {
407 return;
408
409 if( reason != ConnTlsFailed )
410 send( "</stream:stream>" );
411
414
415 if( m_encryption )
417
418 if( m_compression )
420
421 m_encryptionActive = false;
422 m_compressionActive = false;
423 m_smSent = 0;
424
425 notifyOnDisconnect( reason );
426
427#ifdef CLIENTBASE_TEST
428 m_nextId.reset();
429#endif
430 }
431
432 void ClientBase::parse( const std::string& data )
433 {
434 std::string copy = data;
435 int i = 0;
436 if( ( i = m_parser.feed( copy ) ) >= 0 )
437 {
438 std::string error = "parse error (at pos ";
439 error += util::int2string( i );
440 error += "): ";
441 m_logInstance.err( LogAreaClassClientbase, error + copy );
442 Tag* e = new Tag( "stream:error" );
443 new Tag( e, "restricted-xml", "xmlns", XMLNS_XMPP_STREAM );
444 send( e );
446 }
447 }
448
450 {
451 std::string head = "<?xml version='1.0' ?>";
452 head += "<stream:stream to='" + m_jid.server() + "' xmlns='" + m_namespace + "' ";
453 head += "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='" + m_xmllang + "' ";
454 head += "version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR + "'>";
455 send( head );
456 }
457
459 {
460#if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS )
461 return true;
462#else
463 return false;
464#endif
465 }
466
468 {
469 send( new Tag( "starttls", XMLNS, XMLNS_STREAM_TLS ) );
470 }
471
472 void ClientBase::setServer( const std::string &server )
473 {
475 if( m_connection )
477 }
478
479 void ClientBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
480 {
481 m_clientKey = clientKey;
482 m_clientCerts = clientCerts;
483 }
484
486 {
487 m_selectedSaslMech = type;
488
489 Tag* a = new Tag( "auth", XMLNS, XMLNS_STREAM_SASL );
490
491 switch( type )
492 {
495 {
496 if( type == SaslMechScramSha1 )
497 {
499 m_gs2Header = "y,";
500 else
501 m_gs2Header = "n,";
502 a->addAttribute( "mechanism", "SCRAM-SHA-1" );
503 }
504 else // SaslMechScramSha1Plus
505 {
506 m_gs2Header = "p=tls-unique,";
507 a->addAttribute( "mechanism", "SCRAM-SHA-1-PLUS" );
508 }
509
510 std::string t;
511 if( m_authzid && prep::saslprep( m_authzid.bare(), t ) )
512 m_gs2Header += "a=" + t;
513
514 m_gs2Header += ",";
515
516 m_clientFirstMessageBare = "n=";
517 if( !m_authcid.empty() && prep::saslprep( m_authcid, t ) )
518 m_clientFirstMessageBare += t;
519 else if( prep::saslprep( m_jid.username(), t ) )
520 m_clientFirstMessageBare += t;
521
522 m_clientFirstMessageBare += ",r=" + getRandom();
523
524 a->setCData( Base64::encode64( m_gs2Header + m_clientFirstMessageBare ) );
525 break;
526 }
528 a->addAttribute( "mechanism", "DIGEST-MD5" );
529 break;
530 case SaslMechPlain:
531 {
532 a->addAttribute( "mechanism", "PLAIN" );
533
534 std::string tmp;
535 if( m_authzid )
536 tmp += m_authzid.bare();
537
538 tmp += '\0';
539 if( !m_authcid.empty() )
540 tmp += m_authcid;
541 else
542 tmp += m_jid.username();
543 tmp += '\0';
544 tmp += m_password;
545 a->setCData( Base64::encode64( tmp ) );
546 break;
547 }
549 a->addAttribute( "mechanism", "ANONYMOUS" );
550 break;
551 case SaslMechExternal:
552 a->addAttribute( "mechanism", "EXTERNAL" );
554 break;
555 case SaslMechGssapi:
556 {
557#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
558 a->addAttribute( "mechanism", "GSSAPI" );
559// The client calls GSS_Init_sec_context, passing in 0 for
560// input_context_handle (initially) and a targ_name equal to output_name
561// from GSS_Import_Name called with input_name_type of
562// GSS_C_NT_HOSTBASED_SERVICE and input_name_string of
563// "service@hostname" where "service" is the service name specified in
564// the protocol's profile, and "hostname" is the fully qualified host
565// name of the server. The client then responds with the resulting
566// output_token.
567 std::string token;
568 a->setCData( Base64::encode64( token ) );
569// etc... see gssapi-sasl-draft.txt
570#else
572 "SASL GSSAPI is not supported on this platform. You should never see this." );
573#endif
574 break;
575 }
576 case SaslMechNTLM:
577 {
578#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
579 a->addAttribute( "mechanism", "NTLM" );
580 SEC_WINNT_AUTH_IDENTITY_W identity, *ident = 0;
581 memset( &identity, 0, sizeof( identity ) );
582
583 WCHAR *usernameW = 0, *domainW = 0, *passwordW = 0;
584 int cchUsernameW = 0, cchDomainW = 0, cchPasswordW = 0;
585
586 if( m_jid.username().length() > 0 )
587 {
588 // NOTE: The return values of MultiByteToWideChar will include room
589 // for the NUL character since we use -1 for the input length.
590
591 cchUsernameW = ::MultiByteToWideChar( CP_UTF8, 0, m_jid.username().c_str(), -1, 0, 0 );
592 if( cchUsernameW > 0 )
593 {
594 usernameW = new WCHAR[cchUsernameW];
595 ::MultiByteToWideChar( CP_UTF8, 0, m_jid.username().c_str(), -1, usernameW, cchUsernameW );
596 // Guarantee its NUL terminated.
597 usernameW[cchUsernameW-1] = L'\0';
598 }
599 cchDomainW = ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, 0, 0 );
600 if( cchDomainW > 0 )
601 {
602 domainW = new WCHAR[cchDomainW];
603 ::MultiByteToWideChar( CP_UTF8, 0, m_ntlmDomain.c_str(), -1, domainW, cchDomainW );
604 // Guarantee its NUL terminated.
605 domainW[cchDomainW-1] = L'\0';
606 }
607 cchPasswordW = ::MultiByteToWideChar( CP_UTF8, 0, m_password.c_str(), -1, 0, 0 );
608 if( cchPasswordW > 0 )
609 {
610 passwordW = new WCHAR[cchPasswordW];
611 ::MultiByteToWideChar( CP_UTF8, 0, m_password.c_str(), -1, passwordW, cchPasswordW );
612 // Guarantee its NUL terminated.
613 passwordW[cchPasswordW-1] = L'\0';
614 }
615 identity.User = (unsigned short*)usernameW;
616 identity.UserLength = (unsigned long)cchUsernameW-1;
617 identity.Domain = (unsigned short*)domainW;
618 identity.DomainLength = (unsigned long)cchDomainW-1;
619 identity.Password = (unsigned short*)passwordW;
620 identity.PasswordLength = (unsigned long)cchPasswordW-1;
621 identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
622 ident = &identity;
623 }
624
625 AcquireCredentialsHandleW( 0, L"NTLM", SECPKG_CRED_OUTBOUND, 0, ident, 0, 0, &m_credHandle, 0 );
626
627 if( usernameW != 0 )
628 {
629 delete[] usernameW;
630 usernameW = 0;
631 }
632 if( domainW != 0 )
633 {
634 delete[] domainW;
635 domainW = 0;
636 }
637 if( passwordW != 0 )
638 {
639 ::SecureZeroMemory( passwordW, cchPasswordW* sizeof( WCHAR ) );
640 delete[] passwordW;
641 passwordW = 0;
642 }
643
644#else
646 "SASL NTLM is not supported on this platform. You should never see this." );
647#endif
648 break;
649 }
650 default:
651 break;
652 }
653
654 send( a );
655 }
656
657 std::string ClientBase::hmac( const std::string& key, const std::string& str )
658 {
659 SHA sha;
660 std::string key_ = key;
661 if( key_.length() > 64 )
662 {
663 sha.feed( key_ );
664 key_ = sha.binary();
665 sha.reset();
666 }
667 unsigned char ipad[65];
668 unsigned char opad[65];
669 memset( ipad, '\0', sizeof( ipad ) );
670 memset( opad, '\0', sizeof( opad ) );
671 memcpy( ipad, key_.c_str(), key_.length() );
672 memcpy( opad, key_.c_str(), key_.length() );
673 for( int i = 0; i < 64; i++ )
674 {
675 ipad[i] ^= 0x36;
676 opad[i] ^= 0x5c;
677 }
678 sha.feed( ipad, 64 );
679 sha.feed( str );
680 key_ = sha.binary();
681 sha.reset();
682 sha.feed( opad, 64 );
683 sha.feed( key_ );
684
685 return sha.binary(); // hex() for testing
686 }
687
688 std::string ClientBase::hi( const std::string& str, const std::string& salt, int iter )
689 {
690 int xored[20];
691 memset( xored, '\0', sizeof( xored ) );
692 std::string tmp = salt;
693 tmp.append( "\0\0\0\1", 4 );
694 for( int i = 0; i < iter; ++i )
695 {
696 tmp = hmac( str, tmp );
697 for( int j = 0; j < 20; ++j )
698 xored[j] ^= tmp.c_str()[j];
699 }
700 std::string n;
701 for( int i=0; i < 20 ;++i )
702 n.push_back( static_cast<char>( xored[i] ) );
703
704 return n;
705 }
706
707 void ClientBase::processSASLChallenge( const std::string& challenge )
708 {
709 Tag* t = new Tag( "response", XMLNS, XMLNS_STREAM_SASL );
710
711 const std::string& decoded = Base64::decode64( challenge );
712
713 switch( m_selectedSaslMech )
714 {
717 {
718 std::string snonce, salt, tmp;
719 int iter = 0;
720 std::string::size_type posn = decoded.find( "r=" );
721 std::string::size_type poss = decoded.find( "s=" );
722 std::string::size_type posi = decoded.find( "i=" );
723 if( posn == std::string::npos || poss == std::string::npos || posi == std::string::npos )
724 break;
725
726 snonce = decoded.substr( posn + 2, poss - posn - 3 );
727 salt = Base64::decode64( decoded.substr( poss + 2, posi - poss - 3 ) );
728 tmp = decoded.substr( posi + 2, decoded.length() - posi - 2 );
729 iter = atoi( tmp.c_str() );
730
731 if( !prep::saslprep( m_password, tmp ) )
732 break;
733
734 std::string saltedPwd = hi( tmp, salt, iter );
735 std::string ck = hmac( saltedPwd, "Client Key" );
736 SHA sha;
737 sha.feed( ck );
738 std::string storedKey = sha.binary();
739
740 if( m_selectedSaslMech == SaslMechScramSha1Plus )
741 tmp = "c=" + Base64::encode64( m_gs2Header + m_encryption->channelBinding() );
742 else
743 tmp = "c=biws";
744 tmp += ",r=" + snonce;
745
746 std::string authMessage = m_clientFirstMessageBare + "," + decoded + "," + tmp; // client-final-message-without-proof
747 std::string clientSignature = hmac( storedKey, authMessage );
748 unsigned char clientProof[20]; // ck XOR clientSignature
749 memcpy( clientProof, ck.c_str(), 20 );
750 for( int i = 0; i < 20; ++i )
751 clientProof[i] ^= clientSignature.c_str()[i];
752 std::string serverKey = hmac( saltedPwd, "Server Key" );
753 m_serverSignature = hmac( serverKey, authMessage );
754
755 tmp += ",p=";
756 tmp.append( Base64::encode64( std::string( reinterpret_cast<const char*>( clientProof ), 20 ) ) );
757
758 t->setCData( Base64::encode64( tmp ) );
759
760 break;
761 }
763 {
764 if( !decoded.compare( 0, 7, "rspauth" ) )
765 break;
766
767 std::string realm;
768 std::string::size_type end = 0;
769 std::string::size_type pos = decoded.find( "realm=" );
770 if( pos != std::string::npos )
771 {
772 end = decoded.find( '"', pos + 7 );
773 realm = decoded.substr( pos + 7, end - ( pos + 7 ) );
774 }
775 else
776 realm = m_jid.server();
777
778 pos = decoded.find( "nonce=" );
779 if( pos == std::string::npos )
780 return;
781
782 end = decoded.find( '"', pos + 7 );
783 while( decoded[end-1] == '\\' )
784 end = decoded.find( '"', end + 1 );
785 std::string nonce = decoded.substr( pos + 7, end - ( pos + 7 ) );
786
787 std::string cnonce = getRandom();
788
789 MD5 md5;
790 md5.feed( m_jid.username() );
791 md5.feed( ":" );
792 md5.feed( realm );
793 md5.feed( ":" );
794 md5.feed( m_password );
795 md5.finalize();
796 const std::string& a1_h = md5.binary();
797 md5.reset();
798 md5.feed( a1_h );
799 md5.feed( ":" );
800 md5.feed( nonce );
801 md5.feed( ":" );
802 md5.feed( cnonce );
803 md5.finalize();
804 const std::string& a1 = md5.hex();
805 md5.reset();
806 md5.feed( "AUTHENTICATE:xmpp/" );
807 md5.feed( m_jid.server() );
808 md5.finalize();
809 const std::string& a2 = md5.hex();
810 md5.reset();
811 md5.feed( a1 );
812 md5.feed( ":" );
813 md5.feed( nonce );
814 md5.feed( ":00000001:" );
815 md5.feed( cnonce );
816 md5.feed( ":auth:" );
817 md5.feed( a2 );
818 md5.finalize();
819
820 std::string response = "username=\"";
821 response += m_jid.username();
822 response += "\",realm=\"";
823 response += realm;
824 response += "\",nonce=\"";
825 response += nonce;
826 response += "\",cnonce=\"";
827 response += cnonce;
828 response += "\",nc=00000001,qop=auth,digest-uri=\"xmpp/";
829 response += m_jid.server();
830 response += "\",response=";
831 response += md5.hex();
832 response += ",charset=utf-8";
833
834 if( m_authzid )
835 response += ",authzid=" + m_authzid.bare();
836
837 t->setCData( Base64::encode64( response ) );
838
839 break;
840 }
841 case SaslMechGssapi:
842#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
843 // see gssapi-sasl-draft.txt
844#else
845 m_logInstance.err( LogAreaClassClientbase,
846 "Huh, received GSSAPI challenge?! This should have never happened!" );
847#endif
848 break;
849 case SaslMechNTLM:
850 {
851#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
852 bool type1 = ( decoded.length() < 7 ) ? true : false;
853
854 SecBuffer bufferIn = { type1 ? 0 : (unsigned long)decoded.length(),
855 SECBUFFER_TOKEN,
856 (void*)decoded.c_str() };
857 SecBufferDesc secIn = { 0, 1, &bufferIn };
858
859 char buffer[4096];
860
861 SecBuffer bufferOut = { sizeof( buffer ), SECBUFFER_TOKEN, buffer };
862 SecBufferDesc secOut = { 0, 1, &bufferOut };
863
864 TimeStamp timestamp;
865 unsigned long contextAttr;
866
867 SECURITY_STATUS status = InitializeSecurityContext( &m_credHandle, type1 ? 0 : &m_ctxtHandle,
868 0, ISC_REQ_MUTUAL_AUTH, 0, 0, &secIn, 0,
869 &m_ctxtHandle, &secOut, &contextAttr,
870 &timestamp );
871 std::string response;
872 if( SUCCEEDED( status ) )
873 {
874 response = std::string( (const char *)bufferOut.pvBuffer, bufferOut.cbBuffer );
875 }
876 else
877 {
879 "InitializeSecurityContext() failed, return value "
880 + util::int2string( status ) );
881 }
882
883 t->setCData( Base64::encode64( response ) );
884#else
885 m_logInstance.err( LogAreaClassClientbase,
886 "Huh, received NTLM challenge?! This should have never happened!" );
887#endif
888 break;
889 }
890
891 default:
892 // should never happen.
893 break;
894 }
895
896 send( t );
897 }
898
900 {
901 if( tag->hasChild( "aborted" ) )
902 m_authError = SaslAborted;
903 else if( tag->hasChild( "incorrect-encoding" ) )
904 m_authError = SaslIncorrectEncoding;
905 else if( tag->hasChild( "invalid-authzid" ) )
906 m_authError = SaslInvalidAuthzid;
907 else if( tag->hasChild( "invalid-mechanism" ) )
908 m_authError = SaslInvalidMechanism;
909 else if( tag->hasChild( "malformed-request" ) )
910 m_authError = SaslMalformedRequest;
911 else if( tag->hasChild( "mechanism-too-weak" ) )
912 m_authError = SaslMechanismTooWeak;
913 else if( tag->hasChild( "not-authorized" ) )
914 m_authError = SaslNotAuthorized;
915 else if( tag->hasChild( "temporary-auth-failure" ) )
916 m_authError = SaslTemporaryAuthFailure;
917
918#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
919 if( m_selectedSaslMech == SaslMechNTLM )
920 {
921 FreeCredentialsHandle( &m_credHandle );
922 DeleteSecurityContext( &m_ctxtHandle );
923 }
924#endif
925 }
926
927 bool ClientBase::processSASLSuccess( const std::string& payload )
928 {
929#if defined( _WIN32 ) && !defined( __SYMBIAN32__ )
930 if( m_selectedSaslMech == SaslMechNTLM )
931 {
932 FreeCredentialsHandle( &m_credHandle );
933 DeleteSecurityContext( &m_ctxtHandle );
934 }
935#endif
936 if( m_selectedSaslMech == SaslMechScramSha1 || m_selectedSaslMech == SaslMechScramSha1Plus )
937 {
938 const std::string decoded = Base64::decode64( payload );
939 if( decoded.length() < 3 || Base64::decode64( decoded.substr( 2 ) ) != m_serverSignature )
940 return false;
941 }
942
943 return true;
944 }
945
946 void ClientBase::send( IQ& iq, IqHandler* ih, int context, bool del )
947 {
948 if( ih && ( iq.subtype() == IQ::Set || iq.subtype() == IQ::Get ) )
949 {
950 if( iq.id().empty() )
951 iq.setID( getID() );
952
953 TrackStruct track;
954 track.ih = ih;
955 track.context = context;
956 track.del = del;
957 m_iqHandlerMapMutex.lock();
958 m_iqIDHandlers[iq.id()] = track;
959 m_iqHandlerMapMutex.unlock();
960 }
961
962 send( iq );
963 }
964
965 void ClientBase::send( const IQ& iq )
966 {
967 ++m_stats.iqStanzasSent;
968 Tag* tag = iq.tag();
969 addFrom( tag );
970 addNamespace( tag );
971 send( tag, true, false );
972 }
973
974 void ClientBase::send( const Message& msg )
975 {
976 ++m_stats.messageStanzasSent;
977 Tag* tag = msg.tag();
978 addFrom( tag );
979 addNamespace( tag );
980 send( tag, true, false );
981 }
982
984 {
985 ++m_stats.s10nStanzasSent;
986 Tag* tag = sub.tag();
987 addFrom( tag );
988 addNamespace( tag );
989 send( tag, true, false );
990 }
991
992 void ClientBase::send( const Presence& pres )
993 {
994 ++m_stats.presenceStanzasSent;
995 Tag* tag = pres.tag();
996 StanzaExtensionList::const_iterator it = m_presenceExtensions.begin();
997 for( ; it != m_presenceExtensions.end(); ++it )
998 tag->addChild( (*it)->tag() );
999 addFrom( tag );
1000 addNamespace( tag );
1001 send( tag, true, false );
1002 }
1003
1005 {
1006 if( !tag )
1007 return;
1008
1009 send( tag, false, true );
1010 }
1011
1012 void ClientBase::send( Tag* tag, bool queue, bool del )
1013 {
1014 if( !tag )
1015 return;
1016
1017 send( tag->xml() );
1018
1019 ++m_stats.totalStanzasSent;
1020
1021 if( m_statisticsHandler )
1022 m_statisticsHandler->handleStatistics( getStatistics() );
1023
1024 if( queue && m_smContext >= CtxSMEnabled )
1025 {
1026 m_queueMutex.lock();
1027 m_smQueue.insert( std::make_pair( ++m_smSent, tag ) );
1028 m_queueMutex.unlock();
1029 }
1030 else if( del || m_smContext < CtxSMEnabled )
1031 delete tag;
1032 }
1033
1034 void ClientBase::send( const std::string& xml )
1035 {
1037 {
1039 m_compression->compress( xml );
1040 else if( m_encryption && m_encryptionActive )
1041 m_encryption->encrypt( xml );
1042 else
1043 m_connection->send( xml );
1044
1046 }
1047 }
1048
1049 void ClientBase::checkQueue( int handled, bool resend )
1050 {
1051 if( m_smContext < CtxSMEnabled || handled < 0 )
1052 return;
1053
1054 util::MutexGuard mg( m_queueMutex );
1055 SMQueueMap::iterator it = m_smQueue.begin();
1056 while( it != m_smQueue.end() )
1057 {
1058 if( (*it).first <= handled )
1059 {
1060 delete (*it).second;
1061 m_smQueue.erase( it++ );
1062 }
1063 else if( resend && (*it).first > handled )
1064 {
1065 send( (*it).second, false, false );
1066 ++it;
1067 }
1068 else
1069 {
1070 ++it;
1071 }
1072 }
1073 }
1074
1076 {
1077 TagList l;
1078 util::MutexGuard mg( m_queueMutex );
1079 SMQueueMap::iterator it = m_smQueue.begin();
1080 for( ; it != m_smQueue.end(); ++it )
1081 l.push_back( (*it).second->clone() );
1082
1083 return l;
1084 }
1085
1086 void ClientBase::addFrom( Tag* tag )
1087 {
1088 if( !m_authed /* for IQ Auth */ || !m_resourceBound /* for resource binding */ || !tag || tag->hasAttribute( "from" ) )
1089 return;
1090
1091 tag->addAttribute( "from", m_jid.full() );
1092 }
1093
1094 void ClientBase::addNamespace( Tag* tag )
1095 {
1096 if( !tag || !tag->xmlns().empty() )
1097 return;
1098
1099 tag->setXmlns( m_namespace );
1100 }
1101
1103 {
1104 if( !m_seFactory )
1105 m_seFactory = new StanzaExtensionFactory();
1106
1107 m_seFactory->registerExtension( ext );
1108 }
1109
1111 {
1112 if( !m_seFactory )
1113 return false;
1114
1115 return m_seFactory->removeExtension( ext );
1116 }
1117
1119 {
1120 if( m_connection )
1122
1123 return m_stats;
1124 }
1125
1127 {
1129 }
1130
1132 {
1133 send( " " );
1134 }
1135
1137 {
1138 const std::string& id = getID();
1139 IQ iq( IQ::Get, to, id );
1140 iq.addExtension( new Ping() );
1141 m_dispatcher.registerEventHandler( eh, id );
1142 send( iq, this, XMPPPing );
1143 }
1144
1145 bool ClientBase::handleIq( const IQ& iq )
1146 {
1147 const Ping* p = iq.findExtension<Ping>( ExtPing );
1148 if( !p || iq.subtype() != IQ::Get )
1149 return false;
1150
1151 m_dispatcher.dispatch( Event( Event::PingPing, iq ) );
1152 IQ re( IQ::Result, iq.from(), iq.id() );
1153 send( re );
1154
1155 return true;
1156 }
1157
1158 void ClientBase::handleIqID( const IQ& iq, int context )
1159 {
1160 if( context == XMPPPing )
1161 m_dispatcher.dispatch( Event( ( iq.subtype() == IQ::Result ) ? Event::PingPong
1162 : Event::PingError, iq ),
1163 iq.id(), true );
1164 else
1165 handleIqIDForward( iq, context );
1166 }
1167
1168 const std::string ClientBase::getID()
1169 {
1170#ifdef CLIENTBASE_TEST // to create predictable UIDs in test mode
1171 return "uid" + util::int2string( m_nextId.increment() );
1172#else
1173 char r[48+1];
1174 sprintf( r, "%s%08x", m_uniqueBaseId.c_str(), m_nextId.increment() );
1175 std::string ret( r, 48 );
1176 return ret;
1177#endif
1178 }
1179
1180 bool ClientBase::checkStreamVersion( const std::string& version )
1181 {
1182 if( version.empty() )
1183 return false;
1184
1185 int major = 0;
1186// int minor = 0;
1187 int myMajor = atoi( XMPP_STREAM_VERSION_MAJOR.c_str() );
1188
1189 size_t dot = version.find( '.' );
1190 if( !version.empty() && dot && dot != std::string::npos )
1191 {
1192 major = atoi( version.substr( 0, dot ).c_str() );
1193// minor = atoi( version.substr( dot ).c_str() );
1194 }
1195
1196 return myMajor >= major;
1197 }
1198
1200 {
1202 m_connection = connection;
1203 m_customConnection = true;
1204 if( old )
1205 delete old;
1206 }
1207
1209 {
1210 TLSBase* old = m_encryption;
1211 m_encryption = encryption;
1212 if( old )
1213 delete old;
1214 }
1215
1217 {
1220 if( old )
1221 delete old;
1222 }
1223
1224 void ClientBase::handleStreamError( Tag* tag )
1225 {
1227 const TagList& c = tag->children();
1228 TagList::const_iterator it = c.begin();
1229 for( ; it != c.end(); ++it )
1230 {
1231 const std::string& name = (*it)->name();
1232 if( name == "bad-format" )
1234 else if( name == "bad-namespace-prefix" )
1236 else if( name == "conflict" )
1237 err = StreamErrorConflict;
1238 else if( name == "connection-timeout" )
1240 else if( name == "host-gone" )
1241 err = StreamErrorHostGone;
1242 else if( name == "host-unknown" )
1244 else if( name == "improper-addressing" )
1246 else if( name == "internal-server-error" )
1248 else if( name == "invalid-from" )
1250 else if( name == "invalid-id" )
1252 else if( name == "invalid-namespace" )
1254 else if( name == "invalid-xml" )
1256 else if( name == "not-authorized" )
1258 else if( name == "policy-violation" )
1260 else if( name == "remote-connection-failed" )
1262 else if( name == "resource-constraint" )
1264 else if( name == "restricted-xml" )
1266 else if( name == "see-other-host" )
1267 {
1269 m_streamErrorCData = tag->findChild( "see-other-host" )->cdata();
1270 }
1271 else if( name == "system-shutdown" )
1273 else if( name == "undefined-condition" )
1275 else if( name == "unsupported-encoding" )
1277 else if( name == "unsupported-stanza-type" )
1279 else if( name == "unsupported-version" )
1281 else if( name == "not-well-formed" )
1283 else if( name == "text" )
1284 {
1285 const std::string& lang = (*it)->findAttribute( "xml:lang" );
1286 if( !lang.empty() )
1287 m_streamErrorText[lang] = (*it)->cdata();
1288 else
1289 m_streamErrorText["default"] = (*it)->cdata();
1290 }
1291 else
1292 m_streamErrorAppCondition = (*it);
1293
1294 if( err != StreamErrorUndefined && (*it)->hasAttribute( XMLNS, XMLNS_XMPP_STREAM ) )
1295 m_streamError = err;
1296 }
1297 }
1298
1299 const std::string& ClientBase::streamErrorText( const std::string& lang ) const
1300 {
1301 StringMap::const_iterator it = m_streamErrorText.find( lang );
1302 return ( it != m_streamErrorText.end() ) ? (*it).second : EmptyString;
1303 }
1304
1306 {
1307 if( types & Message::Chat || types == 0 )
1308 m_messageSessionHandlerChat = msh;
1309
1310 if( types & Message::Normal || types == 0 )
1311 m_messageSessionHandlerNormal = msh;
1312
1313 if( types & Message::Groupchat || types == 0 )
1314 m_messageSessionHandlerGroupchat = msh;
1315
1316 if( types & Message::Headline || types == 0 )
1317 m_messageSessionHandlerHeadline = msh;
1318 }
1319
1321 {
1322 if( ph )
1323 m_presenceHandlers.push_back( ph );
1324 }
1325
1327 {
1328 if( ph )
1329 m_presenceHandlers.remove( ph );
1330 }
1331
1333 {
1334 if( ph && jid )
1335 {
1336 JidPresHandlerStruct jph;
1337 jph.jid = new JID( jid.bare() );
1338 jph.ph = ph;
1339 m_presenceJidHandlers.push_back( jph );
1340 }
1341 }
1342
1344 {
1345 PresenceJidHandlerList::iterator t;
1346 PresenceJidHandlerList::iterator it = m_presenceJidHandlers.begin();
1347 while( it != m_presenceJidHandlers.end() )
1348 {
1349 t = it;
1350 ++it;
1351 if( ( !ph || (*t).ph == ph ) && (*t).jid->bare() == jid.bare() )
1352 {
1353 delete (*t).jid;
1354 m_presenceJidHandlers.erase( t );
1355 }
1356 }
1357 }
1358
1360 {
1361 IqTrackMap::iterator t;
1362 m_iqHandlerMapMutex.lock();
1363 IqTrackMap::iterator it = m_iqIDHandlers.begin();
1364 while( it != m_iqIDHandlers.end() )
1365 {
1366 t = it;
1367 ++it;
1368 if( ih == (*t).second.ih )
1369 m_iqIDHandlers.erase( t );
1370 }
1371 m_iqHandlerMapMutex.unlock();
1372 }
1373
1375 {
1376 if( !ih )
1377 return;
1378
1379 util::MutexGuard m( m_iqExtHandlerMapMutex );
1380 typedef IqHandlerMap::const_iterator IQci;
1381 std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( exttype );
1382 for( IQci it = g.first; it != g.second; ++it )
1383 {
1384 if( (*it).second == ih )
1385 return;
1386 }
1387
1388 m_iqExtHandlers.insert( std::make_pair( exttype, ih ) );
1389 }
1390
1392 {
1393 if( !ih )
1394 return;
1395
1396 util::MutexGuard m( m_iqExtHandlerMapMutex );
1397 typedef IqHandlerMap::iterator IQi;
1398 std::pair<IQi, IQi> g = m_iqExtHandlers.equal_range( exttype );
1399 IQi it2;
1400 IQi it = g.first;
1401 while( it != g.second )
1402 {
1403 it2 = it++;
1404 if( (*it2).second == ih )
1405 m_iqExtHandlers.erase( it2 );
1406 }
1407 }
1408
1410 {
1411 if( session )
1412 m_messageSessions.push_back( session );
1413 }
1414
1416 {
1417 if( !session )
1418 return;
1419
1420 MessageSessionList::iterator it = std::find( m_messageSessions.begin(),
1421 m_messageSessions.end(),
1422 session );
1423 if( it != m_messageSessions.end() )
1424 {
1425 delete (*it);
1426 m_messageSessions.erase( it );
1427 }
1428 }
1429
1431 {
1432 if( mh )
1433 m_messageHandlers.push_back( mh );
1434 }
1435
1437 {
1438 if( mh )
1439 m_messageHandlers.remove( mh );
1440 }
1441
1443 {
1444 if( sh )
1445 m_subscriptionHandlers.push_back( sh );
1446 }
1447
1449 {
1450 if( sh )
1451 m_subscriptionHandlers.remove( sh );
1452 }
1453
1454 void ClientBase::registerTagHandler( TagHandler* th, const std::string& tag, const std::string& xmlns )
1455 {
1456 if( th && !tag.empty() )
1457 {
1458 TagHandlerStruct ths;
1459 ths.tag = tag;
1460 ths.xmlns = xmlns;
1461 ths.th = th;
1462 m_tagHandlers.push_back( ths );
1463 }
1464 }
1465
1466 void ClientBase::removeTagHandler( TagHandler* th, const std::string& tag, const std::string& xmlns )
1467 {
1468 if( th )
1469 {
1470 for( TagHandlerList::iterator it = m_tagHandlers.begin(); it != m_tagHandlers.end(); )
1471 {
1472 if( (*it).th == th && (*it).tag == tag && (*it).xmlns == xmlns )
1473 {
1474 // Normally we'd just assign it to the return value of the .erase() call,
1475 // which is either the next element, or .end(). However,
1476 // it's only since C++11 that this works; C++03 version returns void.
1477 // So instead, we do a post-increment. this increments the iterator to point
1478 // to the next element, then passes a copy of the old iterator (that is to the item to be deleted)
1479 m_tagHandlers.erase( it++ );
1480 }
1481 else
1482 {
1483 ++it;
1484 }
1485 }
1486 }
1487 }
1488
1490 {
1491 if( sh )
1492 m_statisticsHandler = sh;
1493 }
1494
1496 {
1497 m_statisticsHandler = 0;
1498 }
1499
1501 {
1502 if( mih )
1503 {
1504 m_mucInvitationHandler = mih;
1506 }
1507 }
1508
1510 {
1511 m_mucInvitationHandler = 0;
1513 }
1514
1516 {
1517 if( cl )
1518 m_connectionListeners.push_back( cl );
1519 }
1520
1522 {
1523 if( cl )
1524 m_connectionListeners.remove( cl );
1525 }
1526
1528 {
1529 util::ForEach( m_connectionListeners, &ConnectionListener::onConnect );
1530 }
1531
1532 void ClientBase::notifyOnDisconnect( ConnectionError e )
1533 {
1534 util::ForEach( m_connectionListeners, &ConnectionListener::onDisconnect, e );
1535 init();
1536 }
1537
1539 {
1540 ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1541 for( ; it != m_connectionListeners.end() && (*it)->onTLSConnect( info ); ++it )
1542 ;
1543 return m_stats.encryption = ( it == m_connectionListeners.end() );
1544 }
1545
1547 {
1548 util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBindError, error );
1549 }
1550
1551 void ClientBase::notifyOnResourceBind( const std::string& resource )
1552 {
1553 util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBind, resource );
1554 }
1555
1557 {
1558 util::ForEach( m_connectionListeners, &ConnectionListener::onSessionCreateError, error );
1559 }
1560
1562 {
1563 util::ForEach( m_connectionListeners, &ConnectionListener::onStreamEvent, event );
1564 }
1565
1566 void ClientBase::notifyPresenceHandlers( Presence& pres )
1567 {
1568 bool match = false;
1569 PresenceJidHandlerList::const_iterator t;
1570 PresenceJidHandlerList::const_iterator itj = m_presenceJidHandlers.begin();
1571 while( itj != m_presenceJidHandlers.end() )
1572 {
1573 t = itj++;
1574 if( (*t).jid->bare() == pres.from().bare() && (*t).ph )
1575 {
1576 (*t).ph->handlePresence( pres );
1577 match = true;
1578 }
1579 }
1580 if( match )
1581 return;
1582
1583 // FIXME remove this for() for 1.1:
1584 PresenceHandlerList::const_iterator it = m_presenceHandlers.begin();
1585 for( ; it != m_presenceHandlers.end(); ++it )
1586 {
1587 (*it)->handlePresence( pres );
1588 }
1589 // FIXME and reinstantiate this:
1590// util::ForEach( m_presenceHandlers, &PresenceHandler::handlePresence, pres );
1591 }
1592
1593 void ClientBase::notifySubscriptionHandlers( Subscription& s10n )
1594 {
1595 // FIXME remove this for() for 1.1:
1596 SubscriptionHandlerList::const_iterator it = m_subscriptionHandlers.begin();
1597 for( ; it != m_subscriptionHandlers.end(); ++it )
1598 {
1599 (*it)->handleSubscription( s10n );
1600 }
1601 // FIXME and reinstantiate this:
1602// util::ForEach( m_subscriptionHandlers, &SubscriptionHandler::handleSubscription, s10n );
1603 }
1604
1605 void ClientBase::notifyIqHandlers( IQ& iq )
1606 {
1607 m_iqHandlerMapMutex.lock();
1608 IqTrackMap::iterator it_id = m_iqIDHandlers.find( iq.id() );
1609 bool haveIdHandler = ( it_id != m_iqIDHandlers.end() );
1610 m_iqHandlerMapMutex.unlock();
1611 if( haveIdHandler && ( iq.subtype() == IQ::Result || iq.subtype() == IQ::Error ) )
1612 {
1613 (*it_id).second.ih->handleIqID( iq, (*it_id).second.context );
1614 if( (*it_id).second.del )
1615 delete (*it_id).second.ih;
1616 m_iqHandlerMapMutex.lock();
1617 m_iqIDHandlers.erase( it_id );
1618 m_iqHandlerMapMutex.unlock();
1619 return;
1620 }
1621
1622 if( iq.extensions().empty() )
1623 {
1624 if ( iq.subtype() == IQ::Get || iq.subtype() == IQ::Set )
1625 {
1626 IQ re( IQ::Error, iq.from(), iq.id() );
1627 re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorFeatureNotImplemented ) );
1628 send( re );
1629 }
1630 return;
1631 }
1632
1633 bool handled = false;
1634
1635 // FIXME remove for 1.1
1636// typedef IqHandlerMapXmlns::const_iterator IQciXmlns
1637// Tag *tag = iq.tag()->xmlns();
1638// std::pair<IQciXmlns, IQciXmlns> g = m_iqNSHandlers.equal_range( tag->xmlns() );
1639// for( IQciXmlns it = g.first; it != g.second; ++it )
1640// {
1641// if( (*it).second->handleIq( iq ) )
1642// res = true;
1643// }
1644// delete tag;
1645
1646 m_iqExtHandlerMapMutex.lock();
1647 typedef IqHandlerMap::const_iterator IQci;
1648 const StanzaExtensionList& sel = iq.extensions();
1649 StanzaExtensionList::const_iterator itse = sel.begin();
1650 for( ; !handled && itse != sel.end(); ++itse )
1651 {
1652 std::pair<IQci, IQci> g = m_iqExtHandlers.equal_range( (*itse)->extensionType() );
1653 for( IQci it = g.first; !handled && it != g.second; ++it )
1654 {
1655 if( (*it).second->handleIq( iq ) )
1656 handled = true;
1657 }
1658 }
1659 m_iqExtHandlerMapMutex.unlock();
1660
1661 if( !handled && ( iq.subtype() == IQ::Get || iq.subtype() == IQ::Set ) )
1662 {
1663 IQ re( IQ::Error, iq.from(), iq.id() );
1664 re.addExtension( new Error( StanzaErrorTypeCancel, StanzaErrorServiceUnavailable ) );
1665 send( re );
1666 }
1667 }
1668
1669 void ClientBase::notifyMessageHandlers( Message& msg )
1670 {
1671 if( m_mucInvitationHandler )
1672 {
1673 const MUCRoom::MUCUser* mu = msg.findExtension<MUCRoom::MUCUser>( ExtMUCUser );
1674 if( mu && mu->operation() == MUCRoom::OpInviteFrom )
1675 {
1676
1677 m_mucInvitationHandler->handleMUCInvitation( msg.from(),
1678 mu->jid() ? JID( *(mu->jid()) ) : JID(),
1679 mu->reason() ? *(mu->reason()) : EmptyString,
1680 msg.body(),
1681 mu->password() ? *(mu->password()) : EmptyString,
1682 mu->continued(),
1683 mu->thread() ? *(mu->thread()) : EmptyString );
1684 return;
1685 }
1686 }
1687
1688 MessageSessionList::const_iterator it1 = m_messageSessions.begin();
1689 for( ; it1 != m_messageSessions.end(); ++it1 )
1690 {
1691 if( (*it1)->target().full() == msg.from().full() &&
1692 ( msg.thread().empty()
1693 || (*it1)->threadID() == msg.thread()
1694 || !(*it1)->honorThreadID() ) &&
1695// FIXME don't use '== 0' here
1696 ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1697 {
1698 (*it1)->handleMessage( msg );
1699 return;
1700 }
1701 }
1702
1703 it1 = m_messageSessions.begin();
1704 for( ; it1 != m_messageSessions.end(); ++it1 )
1705 {
1706 if( (*it1)->target().bare() == msg.from().bare() &&
1707 ( msg.thread().empty()
1708 || (*it1)->threadID() == msg.thread()
1709 || !(*it1)->honorThreadID() ) &&
1710// FIXME don't use '== 0' here
1711 ( (*it1)->types() & msg.subtype() || (*it1)->types() == 0 ) )
1712 {
1713 (*it1)->handleMessage( msg );
1714 return;
1715 }
1716 }
1717
1718 MessageSessionHandler* msHandler = 0;
1719
1720 switch( msg.subtype() )
1721 {
1722 case Message::Chat:
1723 msHandler = m_messageSessionHandlerChat;
1724 break;
1725 case Message::Normal:
1726 msHandler = m_messageSessionHandlerNormal;
1727 break;
1728 case Message::Groupchat:
1729 msHandler = m_messageSessionHandlerGroupchat;
1730 break;
1731 case Message::Headline:
1732 msHandler = m_messageSessionHandlerHeadline;
1733 break;
1734 default:
1735 break;
1736 }
1737
1738 if( msHandler )
1739 {
1740 MessageSession* session = new MessageSession( this, msg.from(), true, msg.subtype() );
1741 msHandler->handleMessageSession( session );
1742 session->handleMessage( msg );
1743 }
1744 else
1745 {
1746 // FIXME remove this for() for 1.1:
1747 MessageHandlerList::const_iterator it = m_messageHandlers.begin();
1748 for( ; it != m_messageHandlers.end(); ++it )
1749 {
1750 (*it)->handleMessage( msg );
1751 }
1752 // FIXME and reinstantiate this:
1753// util::ForEach( m_messageHandlers, &MessageHandler::handleMessage, msg ); // FIXME remove for 1.1
1754 }
1755 }
1756
1757 void ClientBase::notifyTagHandlers( Tag* tag )
1758 {
1759 TagHandlerList::const_iterator it = m_tagHandlers.begin();
1760 for( ; it != m_tagHandlers.end(); ++it )
1761 {
1762 if( (*it).tag == tag->name() && tag->hasAttribute( XMLNS, (*it).xmlns ) )
1763 (*it).th->handleTag( tag );
1764 }
1765 }
1766
1768 {
1769 if( !se )
1770 return;
1771
1773 m_presenceExtensions.push_back( se );
1774 }
1775
1777 {
1778 StanzaExtensionList::iterator it = m_presenceExtensions.begin();
1779 for( ; it != m_presenceExtensions.end(); ++it )
1780 {
1781 if( (*it)->extensionType() == type )
1782 {
1783 delete (*it);
1784 m_presenceExtensions.erase( it );
1785 return true;
1786 }
1787 }
1788
1789 return false;
1790 }
1791
1793 {
1794 char cn[4*8+1];
1795 for( int i = 0; i < 4; ++i )
1796 sprintf( cn + i*8, "%08x", rand() );
1797 return std::string( cn, 4*8 );;
1798 }
1799
1800 CompressionBase* ClientBase::getDefaultCompression()
1801 {
1802 if( !m_compress )
1803 return 0;
1804
1805#ifdef HAVE_ZLIB
1806 CompressionBase* cmp = new CompressionZlib( this );
1807 if( cmp->init() )
1808 return cmp;
1809
1810 delete cmp;
1811#endif
1812 return 0;
1813 }
1814
1815 TLSBase* ClientBase::getDefaultEncryption()
1816 {
1817 if( m_tls == TLSDisabled || !hasTls() )
1818 return 0;
1819
1820 TLSDefault* tls = new TLSDefault( this, m_server );
1821 if( tls->init( m_clientKey, m_clientCerts, m_cacerts ) )
1822 return tls;
1823 else
1824 {
1825 delete tls;
1826 return 0;
1827 }
1828 }
1829
1830}
const std::string getID()
virtual void handleEncryptedData(const TLSBase *base, const std::string &data)
Definition: clientbase.cpp:334
SMContext m_smContext
Definition: clientbase.h:920
void addPresenceExtension(StanzaExtension *se)
void processSASLError(Tag *tag)
Definition: clientbase.cpp:899
bool compression() const
Definition: clientbase.h:214
std::string m_sid
Definition: clientbase.h:890
std::string m_clientCerts
Definition: clientbase.h:883
void registerMessageSessionHandler(MessageSessionHandler *msh, int types=0)
std::string m_authcid
Definition: clientbase.h:870
void registerStatisticsHandler(StatisticsHandler *sh)
LogSink & logInstance()
Definition: clientbase.h:599
void setCompressionImpl(CompressionBase *cb)
const std::string & server() const
Definition: clientbase.h:196
virtual void handleDisconnect(const ConnectionBase *connection, ConnectionError reason)
Definition: clientbase.cpp:387
void registerPresenceHandler(PresenceHandler *ph)
virtual ~ClientBase()
Definition: clientbase.cpp:155
TLSPolicy tls() const
Definition: clientbase.h:208
void xmppPing(const JID &to, EventHandler *eh)
virtual void handleCompressedData(const std::string &data)
Definition: clientbase.cpp:319
bool removePresenceExtension(int type)
virtual ConnectionError recv(int timeout=-1)
Definition: clientbase.cpp:183
void checkQueue(int handled, bool resend)
void removeSubscriptionHandler(SubscriptionHandler *sh)
void registerConnectionListener(ConnectionListener *cl)
void processSASLChallenge(const std::string &challenge)
Definition: clientbase.cpp:707
virtual void handleTag(Tag *tag)
Definition: clientbase.cpp:221
TLSBase * m_encryption
Definition: clientbase.h:872
ConnectionState state() const
void registerMessageHandler(MessageHandler *mh)
const std::string & streamErrorText(const std::string &lang="default") const
StanzaExtensionList m_presenceExtensions
Definition: clientbase.h:877
virtual void handleConnect(const ConnectionBase *connection)
Definition: clientbase.cpp:382
virtual void handleReceivedData(const ConnectionBase *connection, const std::string &data)
Definition: clientbase.cpp:372
virtual const std::string & password() const
Definition: clientbase.h:227
void removeIqHandler(IqHandler *ih, int exttype)
StatisticsStruct getStatistics()
bool processSASLSuccess(const std::string &payload)
Definition: clientbase.cpp:927
std::string m_xmllang
Definition: clientbase.h:887
virtual void handleDecompressedData(const std::string &data)
Definition: clientbase.cpp:329
bool m_compressionActive
Definition: clientbase.h:891
std::string m_clientKey
Definition: clientbase.h:884
void setClientCert(const std::string &clientKey, const std::string &clientCerts)
Definition: clientbase.cpp:479
virtual void handleDecryptedData(const TLSBase *base, const std::string &data)
Definition: clientbase.cpp:342
void registerSubscriptionHandler(SubscriptionHandler *sh)
void removeStatisticsHandler()
void notifyOnSessionCreateError(const Error *error)
bool notifyOnTLSConnect(const CertInfo &info)
void removeIDHandler(IqHandler *ih)
ClientBase(const std::string &ns, const std::string &server, int port=-1)
Definition: clientbase.cpp:94
CompressionBase * m_compression
Definition: clientbase.h:873
virtual void disconnect(ConnectionError reason)
Definition: clientbase.cpp:404
virtual bool checkStreamVersion(const std::string &version)
std::string m_password
Definition: clientbase.h:886
void setEncryptionImpl(TLSBase *tb)
bool removeStanzaExtension(int ext)
bool connect(bool block=true)
Definition: clientbase.cpp:191
std::string m_server
Definition: clientbase.h:888
std::string getRandom()
void removeMUCInvitationHandler()
const TagList sendQueue()
void send(Tag *tag)
void setConnectionImpl(ConnectionBase *cb)
void disposeMessageSession(MessageSession *session)
const JID & jid()
Definition: clientbase.h:147
void removeTagHandler(TagHandler *th, const std::string &tag, const std::string &xmlns)
void registerTagHandler(TagHandler *th, const std::string &tag, const std::string &xmlns)
std::string m_namespace
Definition: clientbase.h:885
void removeConnectionListener(ConnectionListener *cl)
TLSPolicy m_tls
Definition: clientbase.h:901
void notifyOnResourceBindError(const Error *error)
void registerIqHandler(IqHandler *ih, int exttype)
virtual void handleHandshakeResult(const TLSBase *base, bool success, CertInfo &certinfo)
Definition: clientbase.cpp:350
void removeMessageHandler(MessageHandler *mh)
void registerMessageSession(MessageSession *session)
void setServer(const std::string &server)
Definition: clientbase.cpp:472
void startSASL(SaslMechanism type)
Definition: clientbase.cpp:485
void notifyStreamEvent(StreamEvent event)
void registerStanzaExtension(StanzaExtension *ext)
ConnectionBase * m_connection
Definition: clientbase.h:871
void registerMUCInvitationHandler(MUCInvitationHandler *mih)
void removePresenceHandler(PresenceHandler *ph)
void notifyOnResourceBind(const std::string &resource)
This is an abstract base class for stream compression implementations.
virtual void compress(const std::string &data)=0
virtual void cleanup()=0
virtual bool init()=0
virtual void decompress(const std::string &data)=0
An abstract base class for a connection.
virtual void cleanup()
ConnectionState state() const
virtual ConnectionError recv(int timeout=-1)=0
virtual bool send(const std::string &data)=0
virtual ConnectionError connect()=0
virtual ConnectionError receive()=0
virtual void getStatistics(long int &totalIn, long int &totalOut)=0
virtual void disconnect()=0
void setServer(const std::string &server, int port=-1)
Derived classes can be registered as ConnectionListeners with the Client.
virtual void onConnect()=0
virtual void onDisconnect(ConnectionError e)=0
virtual void onResourceBind(const std::string &resource)
virtual void onResourceBindError(const Error *error)
virtual void onStreamEvent(StreamEvent event)
virtual void onSessionCreateError(const Error *error)
This is an implementation of a simple TCP connection.
This class implements XEP-0030 (Service Discovery) and XEP-0092 (Software Version).
Definition: disco.h:46
void removeFeature(const std::string &feature)
Definition: disco.h:430
void addFeature(const std::string &feature)
Definition: disco.h:422
void setVersion(const std::string &name, const std::string &version, const std::string &os=EmptyString)
Definition: disco.cpp:467
A stanza error abstraction implemented as a StanzaExtension.
Definition: error.h:35
void registerEventHandler(EventHandler *eh, const std::string &context)
void dispatch(const Event &event, const std::string &context, bool remove)
An base class for event handlers.
Definition: eventhandler.h:29
A base class for events.
Definition: event.h:29
@ PingError
Definition: event.h:39
@ PingPong
Definition: event.h:38
@ PingPing
Definition: event.h:37
An abstraction of an IQ stanza.
Definition: iq.h:34
IqType subtype() const
Definition: iq.h:74
@ Set
Definition: iq.h:46
@ Error
Definition: iq.h:49
@ Result
Definition: iq.h:48
@ Get
Definition: iq.h:45
virtual Tag * tag() const
Definition: iq.cpp:48
A virtual interface which can be reimplemented to receive IQ stanzas.
Definition: iqhandler.h:32
An abstraction of a JID.
Definition: jid.h:31
const std::string & username() const
Definition: jid.h:98
const std::string & server() const
Definition: jid.h:104
const std::string & full() const
Definition: jid.h:61
const std::string & bare() const
Definition: jid.h:67
void dbg(LogArea area, const std::string &message) const
Definition: logsink.h:66
void err(LogArea area, const std::string &message) const
Definition: logsink.h:84
An MD5 implementation.
Definition: md5.h:82
void feed(const unsigned char *data, int bytes)
Definition: md5.cpp:378
void finalize()
Definition: md5.cpp:416
const std::string binary()
Definition: md5.cpp:449
const std::string hex()
Definition: md5.cpp:436
void reset()
Definition: md5.cpp:461
A handler that can be used to receive invitations to MUC rooms.
virtual void handleMUCInvitation(const JID &room, const JID &from, const std::string &reason, const std::string &body, const std::string &password, bool cont, const std::string &thread)=0
A virtual interface which can be reimplemented to receive incoming message stanzas.
A virtual interface which can be reimplemented to receive incoming message sessions.
An abstraction of a message session between any two entities.
An abstraction of a message stanza.
Definition: message.h:34
virtual Tag * tag() const
Definition: message.cpp:69
int feed(std::string &data)
Definition: parser.cpp:161
A virtual interface which can be reimplemented to receive presence stanzas.
An abstraction of a presence stanza.
Definition: presence.h:33
virtual Tag * tag() const
Definition: presence.cpp:108
An implementation of SHA1.
Definition: sha.h:30
void feed(const unsigned char *data, unsigned length)
Definition: sha.cpp:89
const std::string binary()
Definition: sha.cpp:68
const std::string hex()
Definition: sha.cpp:53
void reset()
Definition: sha.cpp:48
A Factory that creates StanzaExtensions from Tags.
void registerExtension(StanzaExtension *ext)
void addExtensions(Stanza &stanza, Tag *tag)
This class abstracts a stanza extension, which is usually an XML child element in a specific namespac...
void addExtension(const StanzaExtension *se)
Definition: stanza.cpp:52
Stanza * embeddedStanza() const
Definition: stanza.cpp:69
const std::string & id() const
Definition: stanza.h:63
Tag * embeddedTag() const
Definition: stanza.cpp:76
bool hasEmbeddedStanza() const
Definition: stanza.h:135
const JID & from() const
Definition: stanza.h:51
const StanzaExtension * findExtension(int type) const
Definition: stanza.cpp:57
A virtual interface which can be reimplemented to receive connection statistics.
virtual void handleStatistics(const StatisticsStruct stats)=0
A virtual interface which can be reimplemented to receive incoming subscription stanzas.
An abstraction of a subscription stanza.
Definition: subscription.h:32
virtual Tag * tag() const
An abstract base class for TLS implementations.
Definition: tlsbase.h:32
virtual bool encrypt(const std::string &data)=0
virtual int decrypt(const std::string &data)=0
virtual const std::string channelBinding() const
Definition: tlsbase.h:117
virtual void cleanup()=0
A virtual interface which can be reimplemented to receive non-XMPP Core stanzas.
Definition: taghandler.h:33
This is an abstraction of an XML element.
Definition: tag.h:47
Tag * findChild(const std::string &name) const
Definition: tag.cpp:624
const std::string & name() const
Definition: tag.h:394
bool setCData(const std::string &cdata)
Definition: tag.cpp:447
const std::string xmlns() const
Definition: tag.cpp:543
bool addAttribute(Attribute *attr)
Definition: tag.cpp:354
bool hasChild(const std::string &name, const std::string &attr=EmptyString, const std::string &value=EmptyString) const
Definition: tag.cpp:615
void addChild(Tag *child)
Definition: tag.cpp:424
bool hasAttribute(const std::string &name, const std::string &value=EmptyString) const
Definition: tag.cpp:602
const std::string cdata() const
Definition: tag.cpp:497
const std::string & findAttribute(const std::string &name) const
Definition: tag.cpp:589
const std::string xml() const
Definition: tag.cpp:302
const TagList & children() const
Definition: tag.cpp:510
A simple implementation of a mutex guard.
Definition: mutexguard.h:32
const std::string decode64(const std::string &input)
Definition: base64.cpp:83
const std::string encode64(const std::string &input)
Definition: base64.cpp:38
bool saslprep(const std::string &input, std::string &out)
Definition: prep.cpp:95
void clearList(std::list< T * > &L)
Definition: util.h:152
void clearMap(std::map< Key, T * > &M)
Definition: util.h:169
void ForEach(T &t, F f)
Definition: util.h:96
The namespace for the gloox library.
Definition: adhoc.cpp:28
const std::string XMLNS_STREAM
Definition: gloox.cpp:84
std::list< Tag * > TagList
Definition: tag.h:31
const std::string GLOOX_VERSION
Definition: gloox.cpp:119
const std::string XMLNS_XMPP_PING
Definition: gloox.cpp:62
const std::string XMLNS_XMPP_STREAM
Definition: gloox.cpp:85
ConnectionError
Definition: gloox.h:684
@ ConnParseError
Definition: gloox.h:697
@ ConnNotConnected
Definition: gloox.h:715
@ ConnTlsFailed
Definition: gloox.h:705
@ ConnNoError
Definition: gloox.h:685
@ ConnStreamVersionError
Definition: gloox.h:688
@ ConnStreamClosed
Definition: gloox.h:689
@ ConnStreamError
Definition: gloox.h:686
std::list< const StanzaExtension * > StanzaExtensionList
Definition: gloox.h:1272
@ LogAreaXmlIncoming
Definition: gloox.h:1070
@ LogAreaXmlOutgoing
Definition: gloox.h:1071
@ LogAreaClassClientbase
Definition: gloox.h:1057
const std::string XMPP_STREAM_VERSION_MAJOR
Definition: gloox.cpp:117
@ SaslInvalidMechanism
Definition: gloox.h:1026
@ SaslMalformedRequest
Definition: gloox.h:1029
@ SaslIncorrectEncoding
Definition: gloox.h:1016
@ SaslMechanismTooWeak
Definition: gloox.h:1033
@ SaslTemporaryAuthFailure
Definition: gloox.h:1041
@ SaslAborted
Definition: gloox.h:1013
@ AuthErrorUndefined
Definition: gloox.h:1012
@ SaslNotAuthorized
Definition: gloox.h:1037
@ SaslInvalidAuthzid
Definition: gloox.h:1021
const std::string XMLNS_CLIENT
Definition: gloox.cpp:19
StreamEvent
Definition: gloox.h:653
const std::string XMLNS_STREAM_SASL
Definition: gloox.cpp:89
const std::string XMPP_STREAM_VERSION_MINOR
Definition: gloox.cpp:118
@ TLSOptional
Definition: gloox.h:724
@ TLSDisabled
Definition: gloox.h:723
const std::string EmptyString
Definition: gloox.cpp:124
const std::string XMLNS
Definition: gloox.cpp:122
const std::string TYPE
Definition: gloox.cpp:123
@ StanzaErrorServiceUnavailable
Definition: gloox.h:936
@ StanzaErrorFeatureNotImplemented
Definition: gloox.h:881
StreamError
Definition: gloox.h:775
@ StreamErrorBadNamespacePrefix
Definition: gloox.h:782
@ StreamErrorInvalidId
Definition: gloox.h:805
@ StreamErrorConflict
Definition: gloox.h:785
@ StreamErrorPolicyViolation
Definition: gloox.h:817
@ StreamErrorInvalidFrom
Definition: gloox.h:801
@ StreamErrorSeeOtherHost
Definition: gloox.h:827
@ StreamErrorRemoteConnectionFailed
Definition: gloox.h:820
@ StreamErrorRestrictedXml
Definition: gloox.h:824
@ StreamErrorUndefined
Definition: gloox.h:848
@ StreamErrorBadFormat
Definition: gloox.h:776
@ StreamErrorImproperAddressing
Definition: gloox.h:796
@ StreamErrorInvalidNamespace
Definition: gloox.h:807
@ StreamErrorResourceConstraint
Definition: gloox.h:822
@ StreamErrorInternalServerError
Definition: gloox.h:798
@ StreamErrorUnsupportedEncoding
Definition: gloox.h:837
@ StreamErrorUnsupportedStanzaType
Definition: gloox.h:840
@ StreamErrorSystemShutdown
Definition: gloox.h:832
@ StreamErrorUnsupportedVersion
Definition: gloox.h:842
@ StreamErrorConnectionTimeout
Definition: gloox.h:788
@ StreamErrorNotAuthorized
Definition: gloox.h:813
@ StreamErrorXmlNotWellFormed
Definition: gloox.h:846
@ StreamErrorUndefinedCondition
Definition: gloox.h:834
@ StreamErrorHostUnknown
Definition: gloox.h:793
@ StreamErrorHostGone
Definition: gloox.h:790
@ StreamErrorInvalidXml
Definition: gloox.h:811
@ StanzaErrorTypeCancel
Definition: gloox.h:859
SaslMechanism
Definition: gloox.h:757
@ SaslMechScramSha1Plus
Definition: gloox.h:760
@ SaslMechNTLM
Definition: gloox.h:767
@ SaslMechDigestMd5
Definition: gloox.h:761
@ SaslMechAll
Definition: gloox.h:768
@ SaslMechExternal
Definition: gloox.h:765
@ SaslMechGssapi
Definition: gloox.h:766
@ SaslMechScramSha1
Definition: gloox.h:759
@ SaslMechNone
Definition: gloox.h:758
@ SaslMechAnonymous
Definition: gloox.h:763
@ SaslMechPlain
Definition: gloox.h:762
ConnectionState
Definition: gloox.h:641
@ StateDisconnected
Definition: gloox.h:642
@ StateConnected
Definition: gloox.h:644
@ StateConnecting
Definition: gloox.h:643
const std::string XMLNS_STREAM_TLS
Definition: gloox.cpp:87
const std::string XMLNS_MUC
Definition: gloox.cpp:67