gloox  0.9.9.12
clientbase.cpp
1 /*
2  Copyright (c) 2005-2008 by Jakob Schroeter <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 #ifdef _WIN32
16 # include "../config.h.win"
17 #elif defined( _WIN32_WCE )
18 # include "../config.h.win"
19 #else
20 # include "config.h"
21 #endif
22 
23 #include "clientbase.h"
24 #include "connectionbase.h"
25 #include "tlsbase.h"
26 #include "compressionbase.h"
27 #include "connectiontcpclient.h"
28 #include "disco.h"
29 #include "messagesessionhandler.h"
30 #include "parser.h"
31 #include "tag.h"
32 #include "stanza.h"
33 #include "connectionlistener.h"
34 #include "iqhandler.h"
35 #include "messagehandler.h"
36 #include "presencehandler.h"
37 #include "rosterlistener.h"
38 #include "subscriptionhandler.h"
39 #include "loghandler.h"
40 #include "taghandler.h"
41 #include "mucinvitationhandler.h"
42 #include "jid.h"
43 #include "base64.h"
44 #include "md5.h"
45 #include "tlsdefault.h"
46 #include "compressionzlib.h"
47 
48 #include <cstdlib>
49 #include <string>
50 #include <map>
51 #include <list>
52 #include <algorithm>
53 
54 #ifndef _WIN32_WCE
55 # include <sstream>
56 # include <iomanip>
57 #endif
58 
59 namespace gloox
60 {
61 
62  ClientBase::ClientBase( const std::string& ns, const std::string& server, int port )
63  : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
64  m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
65  m_compress( true ), m_authed( false ), m_sasl( true ), m_tls( TLSOptional ), m_port( port ),
66  m_availableSaslMechs( SaslMechAll ),
67  m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
68  m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
69  m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
70  m_parser( 0 ), m_authError( AuthErrorUndefined ), m_streamError( StreamErrorUndefined ),
71  m_streamErrorAppCondition( 0 ), m_selectedSaslMech( SaslMechNone ),
72  m_idCount( 0 ), m_autoMessageSession( false )
73  {
74  init();
75  }
76 
77  ClientBase::ClientBase( const std::string& ns, const std::string& password,
78  const std::string& server, int port )
79  : m_connection( 0 ), m_encryption( 0 ), m_compression( 0 ), m_disco( 0 ), m_namespace( ns ),
80  m_password( password ),
81  m_xmllang( "en" ), m_server( server ), m_compressionActive( false ), m_encryptionActive( false ),
82  m_compress( true ), m_authed( false ), m_block( false ), m_sasl( true ), m_tls( TLSOptional ),
83  m_port( port ), m_availableSaslMechs( SaslMechAll ),
84  m_statisticsHandler( 0 ), m_mucInvitationHandler( 0 ),
85  m_messageSessionHandlerChat( 0 ), m_messageSessionHandlerGroupchat( 0 ),
86  m_messageSessionHandlerHeadline( 0 ), m_messageSessionHandlerNormal( 0 ),
87  m_parser( 0 ), m_authError( AuthErrorUndefined ), m_streamError( StreamErrorUndefined ),
88  m_streamErrorAppCondition( 0 ), m_selectedSaslMech( SaslMechNone ),
89  m_idCount( 0 ), m_autoMessageSession( false )
90  {
91  init();
92  }
93 
94  void ClientBase::init()
95  {
96  if( !m_disco )
97  {
98  m_disco = new Disco( this );
99  m_disco->setVersion( "based on gloox", GLOOX_VERSION );
100  }
101 
102  m_streamError = StreamErrorUndefined;
103 
104  m_block = false;
105 
106  m_stats.totalBytesSent = 0;
107  m_stats.totalBytesReceived = 0;
108  m_stats.compressedBytesSent = 0;
109  m_stats.compressedBytesReceived = 0;
110  m_stats.uncompressedBytesSent = 0;
111  m_stats.uncompressedBytesReceived = 0;
112  m_stats.totalStanzasSent = 0;
113  m_stats.totalStanzasReceived = 0;
114  m_stats.iqStanzasSent = 0;
115  m_stats.iqStanzasReceived = 0;
116  m_stats.messageStanzasSent = 0;
117  m_stats.messageStanzasReceived = 0;
118  m_stats.s10nStanzasSent = 0;
119  m_stats.s10nStanzasReceived = 0;
120  m_stats.presenceStanzasSent = 0;
121  m_stats.presenceStanzasReceived = 0;
122  m_stats.encryption = false;
123  m_stats.compression = false;
124 
125  cleanup();
126  }
127 
129  {
130  delete m_connection;
131  delete m_encryption;
132  delete m_compression;
133  delete m_parser;
134  delete m_disco;
135 
136  MessageSessionList::const_iterator it = m_messageSessions.begin();
137  for( ; it != m_messageSessions.end(); ++it )
138  delete (*it);
139 
140  PresenceJidHandlerList::const_iterator it1 = m_presenceJidHandlers.begin();
141  for( ; it1 != m_presenceJidHandlers.end(); ++it1 )
142  delete (*it1).jid;
143  }
144 
146  {
147  if( !m_connection || m_connection->state() == StateDisconnected )
148  return ConnNotConnected;
149 
150  return m_connection->recv( timeout );
151  }
152 
153  bool ClientBase::connect( bool block )
154  {
155  if( m_server.empty() )
156  return false;
157 
158  if( !m_parser )
159  m_parser = new Parser( this );
160 
161  if( !m_connection )
162  m_connection = new ConnectionTCPClient( this, m_logInstance, m_server, m_port );
163 
164  if( m_connection->state() >= StateConnecting )
165  return true;
166 
167  if( !m_encryption )
168  m_encryption = getDefaultEncryption();
169 
170  if( m_encryption )
171  {
172  m_encryption->setCACerts( m_cacerts );
173  m_encryption->setClientCert( m_clientKey, m_clientCerts );
174  }
175 
176  if( !m_compression )
177  m_compression = getDefaultCompression();
178 
179  m_logInstance.log( LogLevelDebug, LogAreaClassClientbase, "This is gloox " + GLOOX_VERSION
180  + ", connecting..." );
181  m_block = block;
182  ConnectionError ret = m_connection->connect();
183  return ret == ConnNoError;
184  }
185 
187  {
188  if( !tag )
189  {
190  logInstance().log( LogLevelDebug, LogAreaClassClientbase, "stream closed" );
191  disconnect( ConnStreamClosed );
192  return;
193  }
194 
195  Stanza *stanza = new Stanza( tag );
196 
198  ++m_stats.totalStanzasReceived;
199 
200  if( tag->name() == "stream:stream" )
201  {
202  const std::string& version = stanza->findAttribute( "version" );
203  if( !checkStreamVersion( version ) )
204  {
205  logInstance().log( LogLevelDebug, LogAreaClassClientbase, "This server is not XMPP-compliant"
206  " (it does not send a 'version' attribute). Please fix it or try another one.\n" );
207  disconnect( ConnStreamVersionError );
208  return;
209  }
210 
211  m_sid = stanza->findAttribute( "id" );
212  handleStartNode();
213  }
214  else if( tag->name() == "stream:error" )
215  {
216  handleStreamError( stanza );
217  disconnect( ConnStreamError );
218  }
219  else
220  {
221  if( !handleNormalNode( stanza ) )
222  {
223  switch( stanza->type() )
224  {
225  case StanzaIq:
226  notifyIqHandlers( stanza );
227  ++m_stats.iqStanzasReceived;
228  break;
229  case StanzaPresence:
230  notifyPresenceHandlers( stanza );
231  ++m_stats.presenceStanzasReceived;
232  break;
233  case StanzaS10n:
234  notifySubscriptionHandlers( stanza );
235  ++m_stats.s10nStanzasReceived;
236  break;
237  case StanzaMessage:
238  notifyMessageHandlers( stanza );
239  ++m_stats.messageStanzasReceived;
240  break;
241  default:
242  notifyTagHandlers( tag );
243  break;
244  }
245  }
246  }
247 
248  if( m_statisticsHandler )
249  m_statisticsHandler->handleStatistics( getStatistics() );
250 
251  delete stanza;
252  }
253 
254  void ClientBase::handleCompressedData( const std::string& data )
255  {
256  if( m_encryption && m_encryptionActive )
257  m_encryption->encrypt( data );
258  else if( m_connection )
259  m_connection->send( data );
260  else
261  m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Compression finished, but chain broken" );
262  }
263 
264  void ClientBase::handleDecompressedData( const std::string& data )
265  {
266  if( m_parser )
267  parse( data );
268  else
269  m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Decompression finished, but chain broken" );
270  }
271 
272  void ClientBase::handleEncryptedData( const TLSBase* /*base*/, const std::string& data )
273  {
274  if( m_connection )
275  m_connection->send( data );
276  else
277  m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Encryption finished, but chain broken" );
278  }
279 
280  void ClientBase::handleDecryptedData( const TLSBase* /*base*/, const std::string& data )
281  {
282  if( m_compression && m_compressionActive )
283  m_compression->decompress( data );
284  else if( m_parser )
285  parse( data );
286  else
287  m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Decryption finished, but chain broken" );
288  }
289 
290  void ClientBase::handleHandshakeResult( const TLSBase* /*base*/, bool success, CertInfo &certinfo )
291  {
292  if( success )
293  {
294  if( !notifyOnTLSConnect( certinfo ) )
295  {
296  logInstance().log( LogLevelError, LogAreaClassClientbase, "Server's certificate rejected!" );
297  disconnect( ConnTlsFailed );
298  }
299  else
300  {
301  logInstance().log( LogLevelDebug, LogAreaClassClientbase, "connection encryption active" );
302  header();
303  }
304  }
305  else
306  {
307  logInstance().log( LogLevelError, LogAreaClassClientbase, "TLS handshake failed!" );
308  disconnect( ConnTlsFailed );
309  }
310  }
311 
312  void ClientBase::handleReceivedData( const ConnectionBase* /*connection*/, const std::string& data )
313  {
314  if( m_encryption && m_encryptionActive )
315  m_encryption->decrypt( data );
316  else if( m_compression && m_compressionActive )
317  m_compression->decompress( data );
318  else if( m_parser )
319  parse( data );
320  else
321  m_logInstance.log( LogLevelError, LogAreaClassClientbase, "Received data, but chain broken" );
322  }
323 
324  void ClientBase::handleConnect( const ConnectionBase* /*connection*/ )
325  {
326  header();
327  if( m_block && m_connection )
328  {
329  m_connection->receive();
330  }
331  }
332 
333  void ClientBase::handleDisconnect( const ConnectionBase* /*connection*/, ConnectionError reason )
334  {
335  if( m_connection )
336  m_connection->cleanup();
337  notifyOnDisconnect( reason );
338  }
339 
340  void ClientBase::disconnect( ConnectionError reason )
341  {
342  if( m_connection && m_connection->state() >= StateConnecting )
343  {
344  if( reason != ConnTlsFailed )
345  send( "</stream:stream>" );
346 
347  m_connection->disconnect();
348  m_connection->cleanup();
349  m_parser->reset();
350 
351  if( m_encryption )
352  m_encryption->cleanup();
353 
354  m_encryptionActive = false;
355  m_compressionActive = false;
356 
357  notifyOnDisconnect( reason );
358  }
359  }
360 
361  void ClientBase::parse( const std::string& data )
362  {
363  if( m_parser && !m_parser->feed( data ) )
364  {
365  m_logInstance.log( LogLevelError, LogAreaClassClientbase, "parse error: " + data );
366  Tag* e = new Tag( "stream:error" );
367  new Tag( e, "restricted-xml", "xmlns", XMLNS_XMPP_STREAM );
368  send( e );
369  disconnect( ConnParseError );
370  }
371  }
372 
373  void ClientBase::header()
374  {
375  std::string head = "<?xml version='1.0' ?>";
376  head += "<stream:stream to='" + m_jid.server() + "' xmlns='" + m_namespace + "' ";
377  head += "xmlns:stream='http://etherx.jabber.org/streams' xml:lang='" + m_xmllang + "' ";
378  head += "version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR + "'>";
379  send( head );
380  }
381 
382  bool ClientBase::hasTls()
383  {
384 #if defined( HAVE_GNUTLS ) || defined( HAVE_OPENSSL ) || defined( HAVE_WINTLS )
385  return true;
386 #else
387  return false;
388 #endif
389  }
390 
391  void ClientBase::startTls()
392  {
393  Tag *start = new Tag( "starttls" );
394  start->addAttribute( "xmlns", XMLNS_STREAM_TLS );
395  send( start );
396  }
397 
398  void ClientBase::setServer( const std::string &server )
399  {
400  m_server = server;
401  if( m_connection )
402  m_connection->setServer( server );
403  }
404 
405  void ClientBase::setClientCert( const std::string& clientKey, const std::string& clientCerts )
406  {
407  m_clientKey = clientKey;
408  m_clientCerts = clientCerts;
409  }
410 
411  void ClientBase::startSASL( SaslMechanism type )
412  {
413  m_selectedSaslMech = type;
414 
415  Tag *a = new Tag( "auth" );
416  a->addAttribute( "xmlns", XMLNS_STREAM_SASL );
417 
418  switch( type )
419  {
420  case SaslMechDigestMd5:
421  a->addAttribute( "mechanism", "DIGEST-MD5" );
422  break;
423  case SaslMechPlain:
424  {
425  a->addAttribute( "mechanism", "PLAIN" );
426 
427  std::string tmp;
428  if( m_authzid )
429  tmp += m_authzid.bare();
430 
431  tmp += '\0';
432  tmp += m_jid.username();
433  tmp += '\0';
434  tmp += m_password;
435  a->setCData( Base64::encode64( tmp ) );
436  break;
437  }
438  case SaslMechAnonymous:
439  a->addAttribute( "mechanism", "ANONYMOUS" );
440  a->setCData( getID() );
441  break;
442  case SaslMechExternal:
443  a->addAttribute( "mechanism", "EXTERNAL" );
444  if( m_authzid )
445  a->setCData( Base64::encode64( m_authzid.bare() ) );
446  else
447  a->setCData( Base64::encode64( m_jid.bare() ) );
448  break;
449  case SaslMechGssapi:
450  {
451 #ifdef _WIN32
452  a->addAttribute( "mechanism", "GSSAPI" );
453 // The client calls GSS_Init_sec_context, passing in 0 for
454 // input_context_handle (initially) and a targ_name equal to output_name
455 // from GSS_Import_Name called with input_name_type of
456 // GSS_C_NT_HOSTBASED_SERVICE and input_name_string of
457 // "service@hostname" where "service" is the service name specified in
458 // the protocol's profile, and "hostname" is the fully qualified host
459 // name of the server. The client then responds with the resulting
460 // output_token.
461  std::string token;
462  a->setCData( Base64::encode64( token ) );
463 // etc... see gssapi-sasl-draft.txt
464 #else
466  "GSSAPI is not supported on this platform. You should never see this." );
467 #endif
468  break;
469  }
470  default:
471  break;
472  }
473 
474  send( a );
475  }
476 
477  void ClientBase::processSASLChallenge( const std::string& challenge )
478  {
479  Tag *t = new Tag( "response" );
480  t->addAttribute( "xmlns", XMLNS_STREAM_SASL );
481 
482  const std::string& decoded = Base64::decode64( challenge );
483 
484  switch( m_selectedSaslMech )
485  {
486  case SaslMechDigestMd5:
487  {
488  if( decoded.substr( 0, 7 ) == "rspauth" )
489  {
490  break;
491  }
492  std::string realm;
493  size_t r_pos = decoded.find( "realm=" );
494  if( r_pos != std::string::npos )
495  {
496  size_t r_end = decoded.find( "\"", r_pos + 7 );
497  realm = decoded.substr( r_pos + 7, r_end - (r_pos + 7 ) );
498  }
499  else
500  realm = m_jid.server();
501 
502  size_t n_pos = decoded.find( "nonce=" );
503  if( n_pos == std::string::npos )
504  {
505  return;
506  }
507 
508  size_t n_end = decoded.find( "\"", n_pos + 7 );
509  while( decoded.substr( n_end-1, 1 ) == "\\" )
510  n_end = decoded.find( "\"", n_end + 1 );
511  std::string nonce = decoded.substr( n_pos + 7, n_end - ( n_pos + 7 ) );
512 
513  std::string cnonce;
514 #ifdef _WIN32_WCE
515  char cn[4*8+1];
516  for( int i = 0; i < 4; ++i )
517  sprintf( cn + i*8, "%08x", rand() );
518  cnonce.assign( cn, 4*8 );
519 #else
520  std::ostringstream cn;
521  for( int i = 0; i < 4; ++i )
522  cn << std::hex << std::setw( 8 ) << std::setfill( '0' ) << rand();
523  cnonce = cn.str();
524 #endif
525 
526  MD5 md5;
527  md5.feed( m_jid.username() );
528  md5.feed( ":" );
529  md5.feed( realm );
530  md5.feed( ":" );
531  md5.feed( m_password );
532  md5.finalize();
533  const std::string& a1_h = md5.binary();
534  md5.reset();
535  md5.feed( a1_h );
536  md5.feed( ":" );
537  md5.feed( nonce );
538  md5.feed( ":" );
539  md5.feed( cnonce );
540  md5.finalize();
541  const std::string& a1 = md5.hex();
542  md5.reset();
543  md5.feed( "AUTHENTICATE:xmpp/" );
544  md5.feed( m_jid.server() );
545  md5.finalize();
546  const std::string& a2 = md5.hex();
547  md5.reset();
548  md5.feed( a1 );
549  md5.feed( ":" );
550  md5.feed( nonce );
551  md5.feed( ":00000001:" );
552  md5.feed( cnonce );
553  md5.feed( ":auth:" );
554  md5.feed( a2 );
555  md5.finalize();
556  const std::string& response_value = md5.hex();
557 
558  std::string response = "username=\"" + m_jid.username() + "\",realm=\"" + realm;
559  response += "\",nonce=\""+ nonce + "\",cnonce=\"" + cnonce;
560  response += "\",nc=00000001,qop=auth,digest-uri=\"xmpp/" + m_jid.server() + "\",response=";
561  response += response_value;
562  response += ",charset=utf-8";
563 
564  if( m_authzid )
565  response += ",authzid=" + m_authzid.bare();
566 
567  t->setCData( Base64::encode64( response ) );
568 
569  break;
570  }
571  case SaslMechGssapi:
572 #ifdef _WIN32
573  // see gssapi-sasl-draft.txt
574 #else
575  m_logInstance.log( LogLevelError, LogAreaClassClientbase,
576  "Huh, received GSSAPI challenge?! This should have never happened!" );
577 #endif
578  break;
579  default:
580  // should never happen.
581  break;
582  }
583 
584  send( t );
585  }
586 
587  void ClientBase::processSASLError( Stanza *stanza )
588  {
589  if( stanza->hasChild( "aborted" ) )
590  m_authError = SaslAborted;
591  else if( stanza->hasChild( "incorrect-encoding" ) )
592  m_authError = SaslIncorrectEncoding;
593  else if( stanza->hasChild( "invalid-authzid" ) )
594  m_authError = SaslInvalidAuthzid;
595  else if( stanza->hasChild( "invalid-mechanism" ) )
596  m_authError = SaslInvalidMechanism;
597  else if( stanza->hasChild( "mechanism-too-weak" ) )
598  m_authError = SaslMechanismTooWeak;
599  else if( stanza->hasChild( "not-authorized" ) )
600  m_authError = SaslNotAuthorized;
601  else if( stanza->hasChild( "temporary-auth-failure" ) )
602  m_authError = SaslTemporaryAuthFailure;
603  }
604 
605  void ClientBase::send( Tag *tag )
606  {
607  if( !tag )
608  return;
609 
610  send( tag->xml() );
611 
612  switch( tag->type() )
613  {
614  case StanzaIq:
615  ++m_stats.iqStanzasSent;
616  break;
617  case StanzaMessage:
618  ++m_stats.messageStanzasSent;
619  break;
620  case StanzaS10n:
621  ++m_stats.s10nStanzasSent;
622  break;
623  case StanzaPresence:
624  ++m_stats.presenceStanzasSent;
625  break;
626  default:
627  break;
628  }
629  ++m_stats.totalStanzasSent;
630 
631  delete tag;
632 
633  if( m_statisticsHandler )
634  m_statisticsHandler->handleStatistics( getStatistics() );
635  }
636 
637  void ClientBase::send( const std::string& xml )
638  {
639  if( m_connection && m_connection->state() == StateConnected )
640  {
641  if( m_compression && m_compressionActive )
642  m_compression->compress( xml );
643  else if( m_encryption && m_encryptionActive )
644  m_encryption->encrypt( xml );
645  else
646  m_connection->send( xml );
647 
649  }
650  }
651 
653  {
654 // if( m_connection )
655 // m_connection->getStatistics( m_stats.totalBytesReceived, m_stats.totalBytesSent,
656 // m_stats.compressedBytesReceived, m_stats.compressedBytesSent,
657 // m_stats.uncompressedBytesReceived, m_stats.uncompressedBytesSent,
658 // m_stats.compression );
659  return m_stats;
660  }
661 
663  {
664  return m_connection ? m_connection->state() : StateDisconnected;
665  }
666 
668  {
669  send( " " );
670  }
671 
672  void ClientBase::xmppPing( const JID& to )
673  {
674  const std::string& id = getID();
675 
676  Tag *iq = new Tag( "iq" );
677  iq->addAttribute( "to", to.full() );
678  iq->addAttribute( "id", id );
679  iq->addAttribute( "type", "get" );
680  Tag *p = new Tag( iq, "ping" );
681  p->addAttribute( "xmlns", XMLNS_XMPP_PING );
682 
683  send( iq );
684  }
685 
686  const std::string ClientBase::getID()
687  {
688 #ifdef _WIN32_WCE
689  char r[8+1];
690  sprintf( r, "%08x", rand() );
691  std::string ret( r, 8 );
692  return std::string( "uid" ) + ret;
693 #else
694  std::ostringstream oss;
695  oss << ++m_idCount;
696  return std::string( "uid" ) + oss.str();
697 #endif
698  }
699 
700  bool ClientBase::checkStreamVersion( const std::string& version )
701  {
702  if( version.empty() )
703  return false;
704 
705  int major = 0;
706  int minor = 0;
707  int myMajor = atoi( XMPP_STREAM_VERSION_MAJOR.c_str() );
708 
709  size_t dot = version.find( "." );
710  if( !version.empty() && dot && dot != std::string::npos )
711  {
712  major = atoi( version.substr( 0, dot ).c_str() );
713  minor = atoi( version.substr( dot ).c_str() );
714  }
715 
716  return myMajor >= major;
717  }
718 
720  {
721  return m_logInstance;
722  }
723 
725  {
726  if( m_connection )
727  {
728  delete m_connection;
729  }
730  m_connection = cb;
731  }
732 
734  {
735  if( m_encryption )
736  {
737  delete m_encryption;
738  }
739  m_encryption = tb;
740  }
741 
743  {
744  if( m_compression )
745  {
746  delete m_compression;
747  }
748  m_compression = cb;
749  }
750 
751  void ClientBase::handleStreamError( Stanza *stanza )
752  {
754  const Tag::TagList& c = stanza->children();
755  Tag::TagList::const_iterator it = c.begin();
756  for( ; it != c.end(); ++it )
757  {
758  if( (*it)->name() == "bad-format" )
759  err = StreamErrorBadFormat;
760  else if( (*it)->name() == "bad-namespace-prefix" )
762  else if( (*it)->name() == "conflict" )
763  err = StreamErrorConflict;
764  else if( (*it)->name() == "connection-timeout" )
766  else if( (*it)->name() == "host-gone" )
767  err = StreamErrorHostGone;
768  else if( (*it)->name() == "host-unknown" )
770  else if( (*it)->name() == "improper-addressing" )
772  else if( (*it)->name() == "internal-server-error" )
774  else if( (*it)->name() == "invalid-from" )
776  else if( (*it)->name() == "invalid-id" )
777  err = StreamErrorInvalidId;
778  else if( (*it)->name() == "invalid-namespace" )
780  else if( (*it)->name() == "invalid-xml" )
781  err = StreamErrorInvalidXml;
782  else if( (*it)->name() == "not-authorized" )
784  else if( (*it)->name() == "policy-violation" )
786  else if( (*it)->name() == "remote-connection-failed" )
788  else if( (*it)->name() == "resource-constraint" )
790  else if( (*it)->name() == "restricted-xml" )
792  else if( (*it)->name() == "see-other-host" )
793  {
795  m_streamErrorCData = stanza->findChild( "see-other-host" )->cdata();
796  }
797  else if( (*it)->name() == "system-shutdown" )
799  else if( (*it)->name() == "undefined-condition" )
801  else if( (*it)->name() == "unsupported-encoding" )
803  else if( (*it)->name() == "unsupported-stanza-type" )
805  else if( (*it)->name() == "unsupported-version" )
807  else if( (*it)->name() == "xml-not-well-formed" )
809  else if( (*it)->name() == "text" )
810  {
811  const std::string& lang = (*it)->findAttribute( "xml:lang" );
812  if( !lang.empty() )
813  m_streamErrorText[lang] = (*it)->cdata();
814  else
815  m_streamErrorText["default"] = (*it)->cdata();
816  }
817  else
818  m_streamErrorAppCondition = (*it);
819 
820  if( err != StreamErrorUndefined && (*it)->hasAttribute( "xmlns", XMLNS_XMPP_STREAM ) )
821  m_streamError = err;
822  }
823  }
824 
825  const std::string ClientBase::streamErrorText( const std::string& lang ) const
826  {
827  StringMap::const_iterator it = m_streamErrorText.find( lang );
828  return ( it != m_streamErrorText.end() ) ? (*it).second : std::string();
829  }
830 
832  {
833  if( types & StanzaMessageChat || types == 0 )
834  m_messageSessionHandlerChat = msh;
835 
836  if( types & StanzaMessageNormal || types == 0 )
837  m_messageSessionHandlerNormal = msh;
838 
839  if( types & StanzaMessageGroupchat || types == 0 )
840  m_messageSessionHandlerGroupchat = msh;
841 
842  if( types & StanzaMessageHeadline || types == 0 )
843  m_messageSessionHandlerHeadline = msh;
844  }
845 
847  {
848  if( ph )
849  m_presenceHandlers.push_back( ph );
850  }
851 
853  {
854  if( ph )
855  m_presenceHandlers.remove( ph );
856  }
857 
859  {
860  if( ph && jid )
861  {
862  JidPresHandlerStruct jph;
863  jph.jid = new JID( jid.bare() );
864  jph.ph = ph;
865  m_presenceJidHandlers.push_back( jph );
866  }
867  }
868 
870  {
871  PresenceJidHandlerList::iterator t;
872  PresenceJidHandlerList::iterator it = m_presenceJidHandlers.begin();
873  while( it != m_presenceJidHandlers.end() )
874  {
875  t = it;
876  ++it;
877  if( ( !ph || (*t).ph == ph ) && (*t).jid->bare() == jid.bare() )
878  {
879  delete (*t).jid;
880  m_presenceJidHandlers.erase( t );
881  }
882  }
883  }
884 
885  void ClientBase::trackID( IqHandler *ih, const std::string& id, int context )
886  {
887  if( ih && !id.empty() )
888  {
889  TrackStruct track;
890  track.ih = ih;
891  track.context = context;
892  m_iqIDHandlers[id] = track;
893  }
894  }
895 
897  {
898  IqTrackMap::iterator t;
899  IqTrackMap::iterator it = m_iqIDHandlers.begin();
900  while( it != m_iqIDHandlers.end() )
901  {
902  t = it;
903  ++it;
904  if( ih == (*t).second.ih )
905  m_iqIDHandlers.erase( t );
906  }
907  }
908 
909  void ClientBase::registerIqHandler( IqHandler *ih, const std::string& xmlns )
910  {
911  if( ih && !xmlns.empty() )
912  m_iqNSHandlers[xmlns] = ih;
913  }
914 
915  void ClientBase::removeIqHandler( const std::string& xmlns )
916  {
917  if( !xmlns.empty() )
918  m_iqNSHandlers.erase( xmlns );
919  }
920 
922  {
923  if( session )
924  m_messageSessions.push_back( session );
925  }
926 
928  {
929  if( !session )
930  return;
931 
932  MessageSessionList::iterator it = std::find( m_messageSessions.begin(), m_messageSessions.end(),
933  session );
934  if( it != m_messageSessions.end() )
935  {
936  delete (*it);
937  m_messageSessions.erase( it );
938  }
939  }
940 
942  {
943  if( mh )
944  m_messageHandlers.push_back( mh );
945  }
946 
948  {
949  if( mh )
950  m_messageHandlers.remove( mh );
951  }
952 
954  {
955  if( sh )
956  m_subscriptionHandlers.push_back( sh );
957  }
958 
960  {
961  if( sh )
962  m_subscriptionHandlers.remove( sh );
963  }
964 
965  void ClientBase::registerTagHandler( TagHandler *th, const std::string& tag, const std::string& xmlns )
966  {
967  if( th && !tag.empty() )
968  {
969  TagHandlerStruct ths;
970  ths.tag = tag;
971  ths.xmlns = xmlns;
972  ths.th = th;
973  m_tagHandlers.push_back( ths );
974  }
975  }
976 
977  void ClientBase::removeTagHandler( TagHandler *th, const std::string& tag, const std::string& xmlns )
978  {
979  if( th )
980  {
981  TagHandlerList::iterator it = m_tagHandlers.begin();
982  for( ; it != m_tagHandlers.end(); ++it )
983  {
984  if( (*it).th == th && (*it).tag == tag && (*it).xmlns == xmlns )
985  m_tagHandlers.erase( it );
986  }
987  }
988  }
989 
991  {
992  if( sh )
993  m_statisticsHandler = sh;
994  }
995 
997  {
998  m_statisticsHandler = 0;
999  }
1000 
1002  {
1003  if( mih )
1004  {
1005  m_mucInvitationHandler = mih;
1006  m_disco->addFeature( XMLNS_MUC );
1007  }
1008  }
1009 
1011  {
1012  m_mucInvitationHandler = 0;
1013  m_disco->removeFeature( XMLNS_MUC );
1014  }
1015 
1017  {
1018  if( cl )
1019  m_connectionListeners.push_back( cl );
1020  }
1021 
1023  {
1024  if( cl )
1025  m_connectionListeners.remove( cl );
1026  }
1027 
1028  void ClientBase::notifyOnConnect()
1029  {
1030  ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1031  for( ; it != m_connectionListeners.end(); ++it )
1032  {
1033  (*it)->onConnect();
1034  }
1035  }
1036 
1037  void ClientBase::notifyOnDisconnect( ConnectionError e )
1038  {
1039  ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1040  for( ; it != m_connectionListeners.end(); ++it )
1041  {
1042  (*it)->onDisconnect( e );
1043  }
1044  init();
1045  }
1046 
1047  bool ClientBase::notifyOnTLSConnect( const CertInfo& info )
1048  {
1049  ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1050  for( ; it != m_connectionListeners.end() && (*it)->onTLSConnect( info ); ++it )
1051  ;
1052  return m_stats.encryption = ( it == m_connectionListeners.end() );
1053  }
1054 
1055  void ClientBase::notifyOnResourceBindError( ResourceBindError error )
1056  {
1057  ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1058  for( ; it != m_connectionListeners.end(); ++it )
1059  {
1060  (*it)->onResourceBindError( error );
1061  }
1062  }
1063 
1064  void ClientBase::notifyOnSessionCreateError( SessionCreateError error )
1065  {
1066  ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1067  for( ; it != m_connectionListeners.end(); ++it )
1068  {
1069  (*it)->onSessionCreateError( error );
1070  }
1071  }
1072 
1073  void ClientBase::notifyStreamEvent( StreamEvent event )
1074  {
1075  ConnectionListenerList::const_iterator it = m_connectionListeners.begin();
1076  for( ; it != m_connectionListeners.end(); ++it )
1077  {
1078  (*it)->onStreamEvent( event );
1079  }
1080  }
1081 
1082  void ClientBase::notifyPresenceHandlers( Stanza *stanza )
1083  {
1084  bool match = false;
1085  PresenceJidHandlerList::const_iterator t;
1086  PresenceJidHandlerList::const_iterator itj = m_presenceJidHandlers.begin();
1087  while( itj != m_presenceJidHandlers.end() )
1088  {
1089  t = itj++;
1090  if( (*t).jid->bare() == stanza->from().bare() && (*t).ph )
1091  {
1092  (*t).ph->handlePresence( stanza );
1093  match = true;
1094  }
1095  }
1096  if( match )
1097  return;
1098 
1099  PresenceHandlerList::const_iterator it = m_presenceHandlers.begin();
1100  for( ; it != m_presenceHandlers.end(); ++it )
1101  {
1102  (*it)->handlePresence( stanza );
1103  }
1104  }
1105 
1106  void ClientBase::notifySubscriptionHandlers( Stanza *stanza )
1107  {
1108  SubscriptionHandlerList::const_iterator it = m_subscriptionHandlers.begin();
1109  for( ; it != m_subscriptionHandlers.end(); ++it )
1110  {
1111  (*it)->handleSubscription( stanza );
1112  }
1113  }
1114 
1115  void ClientBase::notifyIqHandlers( Stanza *stanza )
1116  {
1117  bool res = false;
1118 
1119  IqHandlerMap::const_iterator it = m_iqNSHandlers.begin();
1120  for( ; it != m_iqNSHandlers.end(); ++it )
1121  {
1122  if( stanza->hasChildWithAttrib( "xmlns", (*it).first ) )
1123  {
1124  if( (*it).second->handleIq( stanza ) )
1125  res = true;
1126  }
1127  }
1128 
1129  IqTrackMap::iterator it_id = m_iqIDHandlers.find( stanza->id() );
1130  if( it_id != m_iqIDHandlers.end() )
1131  {
1132  if( (*it_id).second.ih->handleIqID( stanza, (*it_id).second.context ) )
1133  res = true;
1134  m_iqIDHandlers.erase( it_id );
1135  }
1136 
1137  if( !res && ( stanza->type() == StanzaIq ) &&
1138  ( ( stanza->subtype() == StanzaIqGet ) || ( stanza->subtype() == StanzaIqSet ) ) )
1139  {
1140  Tag *iq = new Tag( "iq" );
1141  iq->addAttribute( "type", "error" );
1142  iq->addAttribute( "id", stanza->id() );
1143  iq->addAttribute( "to", stanza->from().full() );
1144  Tag *e = new Tag( iq, "error", "type", "cancel", false );
1145  new Tag( e, "service-unavailable", "xmlns", XMLNS_XMPP_STANZAS );
1146  send( iq );
1147  }
1148  }
1149 
1150  void ClientBase::notifyMessageHandlers( Stanza *stanza )
1151  {
1152  if( m_mucInvitationHandler )
1153  {
1154  Tag *x = stanza->findChild( "x", "xmlns", XMLNS_MUC_USER );
1155  if( x && x->hasChild( "invite" ) )
1156  {
1157  Tag *i = x->findChild( "invite" );
1158  JID invitee( i->findAttribute( "from" ) );
1159 
1160  Tag * t = i->findChild( "reason" );
1161  std::string reason ( t ? t->cdata() : "" );
1162 
1163  t = x->findChild( "password" );
1164  std::string password ( t ? t->cdata() : "" );
1165 
1166  m_mucInvitationHandler->handleMUCInvitation( stanza->from(), invitee,
1167  reason, stanza->body(), password,
1168  i->hasChild( "continue" ) );
1169  return;
1170  }
1171  }
1172 
1173  MessageSessionList::const_iterator it1 = m_messageSessions.begin();
1174  for( ; it1 != m_messageSessions.end(); ++it1 )
1175  {
1176  if( (*it1)->target().full() == stanza->from().full() &&
1177  ( stanza->thread().empty() || (*it1)->threadID() == stanza->thread() ) &&
1178  ( (*it1)->types() & stanza->subtype() || (*it1)->types() == StanzaSubUndefined ) )
1179  {
1180  (*it1)->handleMessage( stanza );
1181  return;
1182  }
1183  }
1184 
1185  it1 = m_messageSessions.begin();
1186  for( ; it1 != m_messageSessions.end(); ++it1 )
1187  {
1188  if( (*it1)->target().bare() == stanza->from().bare() &&
1189  ( stanza->thread().empty() || (*it1)->threadID() == stanza->thread() ) &&
1190  ( (*it1)->types() & stanza->subtype() || (*it1)->types() == StanzaSubUndefined ) )
1191  {
1192  (*it1)->handleMessage( stanza );
1193  return;
1194  }
1195  }
1196 
1197  MessageSessionHandler *msHandler = 0;
1198 
1199  switch( stanza->subtype() )
1200  {
1201  case StanzaMessageChat:
1202  msHandler = m_messageSessionHandlerChat;
1203  break;
1204  case StanzaMessageNormal:
1205  msHandler = m_messageSessionHandlerNormal;
1206  break;
1208  msHandler = m_messageSessionHandlerGroupchat;
1209  break;
1210  case StanzaMessageHeadline:
1211  msHandler = m_messageSessionHandlerHeadline;
1212  break;
1213  default:
1214  break;
1215  }
1216 
1217  if( msHandler )
1218  {
1219  MessageSession *session = new MessageSession( this, stanza->from(), true, stanza->subtype() );
1220  msHandler->handleMessageSession( session );
1221  session->handleMessage( stanza );
1222  }
1223  else
1224  {
1225  MessageHandlerList::const_iterator it = m_messageHandlers.begin();
1226  for( ; it != m_messageHandlers.end(); ++it )
1227  {
1228  (*it)->handleMessage( stanza );
1229  }
1230  }
1231  }
1232 
1233  void ClientBase::notifyTagHandlers( Tag *tag )
1234  {
1235  TagHandlerList::const_iterator it = m_tagHandlers.begin();
1236  for( ; it != m_tagHandlers.end(); ++it )
1237  {
1238  if( (*it).tag == tag->name() && tag->hasAttribute( "xmlns", (*it).xmlns ) )
1239  (*it).th->handleTag( tag );
1240  }
1241  }
1242 
1243  CompressionBase* ClientBase::getDefaultCompression()
1244  {
1245  if( !m_compress )
1246  return 0;
1247 
1248 #ifdef HAVE_ZLIB
1249  return new CompressionZlib( this );
1250 #else
1251  return 0;
1252 #endif
1253  }
1254 
1255  TLSBase* ClientBase::getDefaultEncryption()
1256  {
1257  if( m_tls == TLSDisabled || !hasTls() )
1258  return 0;
1259 
1260  return new TLSDefault( this, m_server );
1261  }
1262 
1263 }