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 
73 namespace gloox
74 {
75 
76   // ---- ClientBase::Ping ----
Ping()77   ClientBase::Ping::Ping()
78     : StanzaExtension( ExtPing )
79   {
80   }
81 
~Ping()82   ClientBase::Ping::~Ping()
83   {
84   }
85 
filterString() const86   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 ----
ClientBase(const std::string & ns,const std::string & server,int port)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 
ClientBase(const std::string & ns,const std::string & password,const std::string & server,int port)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 
init()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 );
142       m_disco->addFeature( XMLNS_XMPP_PING );
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 
~ClientBase()155   ClientBase::~ClientBase()
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 
165     util::clearList( m_presenceExtensions );
166     util::clearMap( m_smQueue );
167 
168     setConnectionImpl( 0 );
169     setEncryptionImpl( 0 );
170     setCompressionImpl( 0 );
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 
recv(int timeout)183   ConnectionError ClientBase::recv( int timeout )
184   {
185     if( !m_connection || m_connection->state() == StateDisconnected )
186       return ConnNotConnected;
187 
188     return m_connection->recv( timeout );
189   }
190 
connect(bool block)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 
199     if( m_connection->state() >= StateConnecting )
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;
211     ConnectionError ret = m_connection->connect();
212     if( ret != ConnNoError )
213       return false;
214 
215     if( m_block )
216       m_connection->receive();
217 
218     return true;
219   }
220 
handleTag(Tag * tag)221   void ClientBase::handleTag( Tag* tag )
222   {
223     if( !tag )
224     {
225       logInstance().dbg( LogAreaClassClientbase, "stream closed" );
226       disconnect( ConnStreamClosed );
227       return;
228     }
229 
230     logInstance().dbg( LogAreaXmlIncoming, tag->xml() );
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" );
240         disconnect( ConnStreamVersionError );
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 );
250       disconnect( ConnStreamError );
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;
266             if( m_smContext >= CtxSMEnabled )
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;
277             if( m_smContext >= CtxSMEnabled )
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             }
302             if( m_smContext >= CtxSMEnabled )
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 
handleCompressedData(const std::string & data)319   void ClientBase::handleCompressedData( const std::string& data )
320   {
321     if( m_encryption && m_encryptionActive )
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 
handleDecompressedData(const std::string & data)329   void ClientBase::handleDecompressedData( const std::string& data )
330   {
331     parse( data );
332   }
333 
handleEncryptedData(const TLSBase *,const std::string & data)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 
handleDecryptedData(const TLSBase *,const std::string & data)342   void ClientBase::handleDecryptedData( const TLSBase* /*base*/, const std::string& data )
343   {
344     if( m_compression && m_compressionActive )
345       m_compression->decompress( data );
346     else
347       parse( data );
348   }
349 
handleHandshakeResult(const TLSBase *,bool success,CertInfo & certinfo)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!" );
357         disconnect( ConnTlsFailed );
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!" );
368       disconnect( ConnTlsFailed );
369     }
370   }
371 
handleReceivedData(const ConnectionBase *,const std::string & data)372   void ClientBase::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data )
373   {
374     if( m_encryption && m_encryptionActive )
375       m_encryption->decrypt( data );
376     else if( m_compression && m_compressionActive )
377       m_compression->decompress( data );
378     else
379       parse( data );
380   }
381 
handleConnect(const ConnectionBase *)382   void ClientBase::handleConnect( const ConnectionBase* /*connection*/ )
383   {
384     header();
385   }
386 
handleDisconnect(const ConnectionBase *,ConnectionError reason)387   void ClientBase::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason )
388   {
389     if( m_connection )
390       m_connection->cleanup();
391 
392     if( m_encryption )
393       m_encryption->cleanup();
394 
395     if( m_compression )
396       m_compression->cleanup();
397 
398     m_encryptionActive = false;
399     m_compressionActive = false;
400 
401     notifyOnDisconnect( reason );
402   }
403 
disconnect(ConnectionError reason)404   void ClientBase::disconnect( ConnectionError reason )
405   {
406     if( !m_connection || m_connection->state() < StateConnecting )
407       return;
408 
409     if( reason != ConnTlsFailed )
410       send( "</stream:stream>" );
411 
412     m_connection->disconnect();
413     m_connection->cleanup();
414 
415     if( m_encryption )
416       m_encryption->cleanup();
417 
418     if( m_compression )
419       m_compression->cleanup();
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 
parse(const std::string & data)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 );
445       disconnect( ConnParseError );
446     }
447   }
448 
header()449   void ClientBase::header()
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 
hasTls()458   bool ClientBase::hasTls()
459   {
460 #if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS )
461     return true;
462 #else
463     return false;
464 #endif
465   }
466 
startTls()467   void ClientBase::startTls()
468   {
469     send( new Tag( "starttls", XMLNS, XMLNS_STREAM_TLS ) );
470   }
471 
setServer(const std::string & server)472   void ClientBase::setServer( const std::string &server )
473   {
474     m_server = server;
475     if( m_connection )
476       m_connection->setServer( server );
477   }
478 
setClientCert(const std::string & clientKey,const std::string & clientCerts)479   void ClientBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
480   {
481     m_clientKey = clientKey;
482     m_clientCerts = clientCerts;
483   }
484 
startSASL(SaslMechanism type)485   void ClientBase::startSASL( SaslMechanism type )
486   {
487     m_selectedSaslMech = type;
488 
489     Tag* a = new Tag( "auth", XMLNS, XMLNS_STREAM_SASL );
490 
491     switch( type )
492     {
493       case SaslMechScramSha1Plus:
494       case SaslMechScramSha1:
495       {
496         if( type == SaslMechScramSha1 )
497         {
498           if( ( m_availableSaslMechs & SaslMechScramSha1Plus ) != SaslMechScramSha1Plus )
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       }
527       case SaslMechDigestMd5:
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       }
548       case SaslMechAnonymous:
549         a->addAttribute( "mechanism", "ANONYMOUS" );
550         break;
551       case SaslMechExternal:
552         a->addAttribute( "mechanism", "EXTERNAL" );
553         a->setCData( Base64::encode64( m_authzid ? m_authzid.bare() : m_jid.bare() ) );
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
571         logInstance().err( LogAreaClassClientbase,
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
645         logInstance().err( LogAreaClassClientbase,
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 
hmac(const std::string & key,const std::string & str)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 
hi(const std::string & str,const std::string & salt,int iter)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 
processSASLChallenge(const std::string & challenge)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     {
715       case SaslMechScramSha1Plus:
716       case SaslMechScramSha1:
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       }
762       case SaslMechDigestMd5:
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         {
878           logInstance().err( LogAreaClassClientbase,
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 
processSASLError(Tag * tag)899   void ClientBase::processSASLError( Tag* tag )
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 
processSASLSuccess(const std::string & payload)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 
send(IQ & iq,IqHandler * ih,int context,bool del)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 
send(const IQ & iq)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 
send(const Message & msg)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 
send(const Subscription & sub)983   void ClientBase::send( const Subscription& sub )
984   {
985     ++m_stats.s10nStanzasSent;
986     Tag* tag = sub.tag();
987     addFrom( tag );
988     addNamespace( tag );
989     send( tag, true, false );
990   }
991 
send(const Presence & pres)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 
send(Tag * tag)1004   void ClientBase::send( Tag* tag )
1005   {
1006     if( !tag )
1007       return;
1008 
1009     send( tag, false, true );
1010   }
1011 
send(Tag * tag,bool queue,bool del)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 
send(const std::string & xml)1034   void ClientBase::send( const std::string& xml )
1035   {
1036     if( m_connection && m_connection->state() == StateConnected )
1037     {
1038       if( m_compression && m_compressionActive )
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 
1045       logInstance().dbg( LogAreaXmlOutgoing, xml );
1046     }
1047   }
1048 
checkQueue(int handled,bool resend)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 
sendQueue()1075   const TagList ClientBase::sendQueue()
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 
addFrom(Tag * tag)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 
addNamespace(Tag * tag)1094   void ClientBase::addNamespace( Tag* tag )
1095   {
1096     if( !tag || !tag->xmlns().empty() )
1097       return;
1098 
1099     tag->setXmlns( m_namespace );
1100   }
1101 
registerStanzaExtension(StanzaExtension * ext)1102   void ClientBase::registerStanzaExtension( StanzaExtension* ext )
1103   {
1104     if( !m_seFactory )
1105       m_seFactory = new StanzaExtensionFactory();
1106 
1107     m_seFactory->registerExtension( ext );
1108   }
1109 
removeStanzaExtension(int ext)1110   bool ClientBase::removeStanzaExtension( int ext )
1111   {
1112     if( !m_seFactory )
1113       return false;
1114 
1115     return m_seFactory->removeExtension( ext );
1116   }
1117 
getStatistics()1118   StatisticsStruct ClientBase::getStatistics()
1119   {
1120     if( m_connection )
1121       m_connection->getStatistics( m_stats.totalBytesReceived, m_stats.totalBytesSent );
1122 
1123     return m_stats;
1124   }
1125 
state() const1126   ConnectionState ClientBase::state() const
1127   {
1128     return m_connection ? m_connection->state() : StateDisconnected;
1129   }
1130 
whitespacePing()1131   void ClientBase::whitespacePing()
1132   {
1133     send( " " );
1134   }
1135 
xmppPing(const JID & to,EventHandler * eh)1136   void ClientBase::xmppPing( const JID& to, EventHandler* eh )
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 
handleIq(const IQ & iq)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 
handleIqID(const IQ & iq,int context)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 
getID()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 
checkStreamVersion(const std::string & version)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 
setConnectionImpl(ConnectionBase * connection)1199   void ClientBase::setConnectionImpl( ConnectionBase* connection )
1200   {
1201     ConnectionBase* old = m_connection;
1202     m_connection = connection;
1203     m_customConnection = true;
1204     if( old )
1205       delete old;
1206   }
1207 
setEncryptionImpl(TLSBase * encryption)1208   void ClientBase::setEncryptionImpl( TLSBase* encryption )
1209   {
1210     TLSBase* old = m_encryption;
1211     m_encryption = encryption;
1212     if( old )
1213       delete old;
1214   }
1215 
setCompressionImpl(CompressionBase * compression)1216   void ClientBase::setCompressionImpl( CompressionBase* compression )
1217   {
1218     CompressionBase* old = m_compression;
1219     m_compression = compression;
1220     if( old )
1221       delete old;
1222   }
1223 
handleStreamError(Tag * tag)1224   void ClientBase::handleStreamError( Tag* tag )
1225   {
1226     StreamError err = StreamErrorUndefined;
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" )
1233         err = StreamErrorBadFormat;
1234       else if( name == "bad-namespace-prefix" )
1235         err = StreamErrorBadNamespacePrefix;
1236       else if( name == "conflict" )
1237         err = StreamErrorConflict;
1238       else if( name == "connection-timeout" )
1239         err = StreamErrorConnectionTimeout;
1240       else if( name == "host-gone" )
1241         err = StreamErrorHostGone;
1242       else if( name == "host-unknown" )
1243         err = StreamErrorHostUnknown;
1244       else if( name == "improper-addressing" )
1245         err = StreamErrorImproperAddressing;
1246       else if( name == "internal-server-error" )
1247         err = StreamErrorInternalServerError;
1248       else if( name == "invalid-from" )
1249         err = StreamErrorInvalidFrom;
1250       else if( name == "invalid-id" )
1251         err = StreamErrorInvalidId;
1252       else if( name == "invalid-namespace" )
1253         err = StreamErrorInvalidNamespace;
1254       else if( name == "invalid-xml" )
1255         err = StreamErrorInvalidXml;
1256       else if( name == "not-authorized" )
1257         err = StreamErrorNotAuthorized;
1258       else if( name == "policy-violation" )
1259         err = StreamErrorPolicyViolation;
1260       else if( name == "remote-connection-failed" )
1261         err = StreamErrorRemoteConnectionFailed;
1262       else if( name == "resource-constraint" )
1263         err = StreamErrorResourceConstraint;
1264       else if( name == "restricted-xml" )
1265         err = StreamErrorRestrictedXml;
1266       else if( name == "see-other-host" )
1267       {
1268         err = StreamErrorSeeOtherHost;
1269         m_streamErrorCData = tag->findChild( "see-other-host" )->cdata();
1270       }
1271       else if( name == "system-shutdown" )
1272         err = StreamErrorSystemShutdown;
1273       else if( name == "undefined-condition" )
1274         err = StreamErrorUndefinedCondition;
1275       else if( name == "unsupported-encoding" )
1276         err = StreamErrorUnsupportedEncoding;
1277       else if( name == "unsupported-stanza-type" )
1278         err = StreamErrorUnsupportedStanzaType;
1279       else if( name == "unsupported-version" )
1280         err = StreamErrorUnsupportedVersion;
1281       else if( name == "not-well-formed" )
1282         err = StreamErrorXmlNotWellFormed;
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 
streamErrorText(const std::string & lang) const1299   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 
registerMessageSessionHandler(MessageSessionHandler * msh,int types)1305   void ClientBase::registerMessageSessionHandler( MessageSessionHandler* msh, int types )
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 
registerPresenceHandler(PresenceHandler * ph)1320   void ClientBase::registerPresenceHandler( PresenceHandler* ph )
1321   {
1322     if( ph )
1323       m_presenceHandlers.push_back( ph );
1324   }
1325 
removePresenceHandler(PresenceHandler * ph)1326   void ClientBase::removePresenceHandler( PresenceHandler* ph )
1327   {
1328     if( ph )
1329       m_presenceHandlers.remove( ph );
1330   }
1331 
registerPresenceHandler(const JID & jid,PresenceHandler * ph)1332   void ClientBase::registerPresenceHandler( const JID& jid, PresenceHandler* ph )
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 
removePresenceHandler(const JID & jid,PresenceHandler * ph)1343   void ClientBase::removePresenceHandler( const JID& jid, PresenceHandler* ph )
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 
removeIDHandler(IqHandler * ih)1359   void ClientBase::removeIDHandler( IqHandler* ih )
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 
registerIqHandler(IqHandler * ih,int exttype)1374   void ClientBase::registerIqHandler( IqHandler* ih, int exttype )
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 
removeIqHandler(IqHandler * ih,int exttype)1391   void ClientBase::removeIqHandler( IqHandler* ih, int exttype )
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 
registerMessageSession(MessageSession * session)1409   void ClientBase::registerMessageSession( MessageSession* session )
1410   {
1411     if( session )
1412       m_messageSessions.push_back( session );
1413   }
1414 
disposeMessageSession(MessageSession * session)1415   void ClientBase::disposeMessageSession( MessageSession* session )
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 
registerMessageHandler(MessageHandler * mh)1430   void ClientBase::registerMessageHandler( MessageHandler* mh )
1431   {
1432     if( mh )
1433       m_messageHandlers.push_back( mh );
1434   }
1435 
removeMessageHandler(MessageHandler * mh)1436   void ClientBase::removeMessageHandler( MessageHandler* mh )
1437   {
1438     if( mh )
1439       m_messageHandlers.remove( mh );
1440   }
1441 
registerSubscriptionHandler(SubscriptionHandler * sh)1442   void ClientBase::registerSubscriptionHandler( SubscriptionHandler* sh )
1443   {
1444     if( sh )
1445       m_subscriptionHandlers.push_back( sh );
1446   }
1447 
removeSubscriptionHandler(SubscriptionHandler * sh)1448   void ClientBase::removeSubscriptionHandler( SubscriptionHandler* sh )
1449   {
1450     if( sh )
1451       m_subscriptionHandlers.remove( sh );
1452   }
1453 
registerTagHandler(TagHandler * th,const std::string & tag,const std::string & xmlns)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 
removeTagHandler(TagHandler * th,const std::string & tag,const std::string & xmlns)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 
registerStatisticsHandler(StatisticsHandler * sh)1489   void ClientBase::registerStatisticsHandler( StatisticsHandler* sh )
1490   {
1491     if( sh )
1492       m_statisticsHandler = sh;
1493   }
1494 
removeStatisticsHandler()1495   void ClientBase::removeStatisticsHandler()
1496   {
1497     m_statisticsHandler = 0;
1498   }
1499 
registerMUCInvitationHandler(MUCInvitationHandler * mih)1500   void ClientBase::registerMUCInvitationHandler( MUCInvitationHandler* mih )
1501   {
1502     if( mih )
1503     {
1504       m_mucInvitationHandler = mih;
1505       m_disco->addFeature( XMLNS_MUC );
1506     }
1507   }
1508 
removeMUCInvitationHandler()1509   void ClientBase::removeMUCInvitationHandler()
1510   {
1511     m_mucInvitationHandler = 0;
1512     m_disco->removeFeature( XMLNS_MUC );
1513   }
1514 
registerConnectionListener(ConnectionListener * cl)1515   void ClientBase::registerConnectionListener( ConnectionListener* cl )
1516   {
1517     if( cl )
1518       m_connectionListeners.push_back( cl );
1519   }
1520 
removeConnectionListener(ConnectionListener * cl)1521   void ClientBase::removeConnectionListener( ConnectionListener* cl )
1522   {
1523     if( cl )
1524       m_connectionListeners.remove( cl );
1525   }
1526 
notifyOnConnect()1527   void ClientBase::notifyOnConnect()
1528   {
1529     util::ForEach( m_connectionListeners, &ConnectionListener::onConnect );
1530   }
1531 
notifyOnDisconnect(ConnectionError e)1532   void ClientBase::notifyOnDisconnect( ConnectionError e )
1533   {
1534     util::ForEach( m_connectionListeners, &ConnectionListener::onDisconnect, e );
1535     init();
1536   }
1537 
notifyOnTLSConnect(const CertInfo & info)1538   bool ClientBase::notifyOnTLSConnect( const CertInfo& info )
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 
notifyOnResourceBindError(const Error * error)1546   void ClientBase::notifyOnResourceBindError( const Error* error )
1547   {
1548     util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBindError, error );
1549   }
1550 
notifyOnResourceBind(const std::string & resource)1551   void ClientBase::notifyOnResourceBind( const std::string& resource )
1552   {
1553     util::ForEach( m_connectionListeners, &ConnectionListener::onResourceBind, resource );
1554   }
1555 
notifyOnSessionCreateError(const Error * error)1556   void ClientBase::notifyOnSessionCreateError( const Error* error )
1557   {
1558     util::ForEach( m_connectionListeners, &ConnectionListener::onSessionCreateError, error );
1559   }
1560 
notifyStreamEvent(StreamEvent event)1561   void ClientBase::notifyStreamEvent( StreamEvent event )
1562   {
1563     util::ForEach( m_connectionListeners, &ConnectionListener::onStreamEvent, event );
1564   }
1565 
notifyPresenceHandlers(Presence & pres)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 
notifySubscriptionHandlers(Subscription & s10n)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 
notifyIqHandlers(IQ & iq)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 
notifyMessageHandlers(Message & msg)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 
notifyTagHandlers(Tag * tag)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 
addPresenceExtension(StanzaExtension * se)1767   void ClientBase::addPresenceExtension( StanzaExtension* se )
1768   {
1769     if( !se )
1770       return;
1771 
1772     removePresenceExtension( se->extensionType() );
1773     m_presenceExtensions.push_back( se );
1774   }
1775 
removePresenceExtension(int type)1776   bool ClientBase::removePresenceExtension( int type )
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 
getRandom()1792   std::string ClientBase::getRandom()
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 
getDefaultCompression()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 
getDefaultEncryption()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 }
1831