gloox  1.0
disco.cpp
1 /*
2  Copyright (c) 2004-2009 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 #include "disco.h"
15 #include "discohandler.h"
16 #include "dataform.h"
17 #include "error.h"
18 #include "clientbase.h"
19 #include "disconodehandler.h"
20 #include "softwareversion.h"
21 #include "util.h"
22 
23 
24 namespace gloox
25 {
26 
27  // ---- Disco::Identity ----
28  Disco::Identity::Identity( const std::string& category,
29  const std::string& type,
30  const std::string& name )
31  : m_category( category ), m_type( type ), m_name( name )
32  {
33  }
34 
35  Disco::Identity::Identity( const Tag* tag )
36  {
37  if( !tag || tag->name() != "identity" )
38  return;
39 
40  m_category = tag->findAttribute( "category" );
41  m_type = tag->findAttribute( "type" );
42  m_name = tag->findAttribute( "name" );
43  }
44 
46  : m_category( id.m_category ), m_type( id.m_type ), m_name( id.m_name )
47  {
48  }
49 
51  {
52  }
53 
55  {
56  if( m_category.empty() || m_type.empty() )
57  return 0;
58 
59  Tag* i = new Tag( "identity" );
60  i->addAttribute( "category", m_category );
61  i->addAttribute( "type", m_type );
62 
63  if( !m_name.empty() )
64  i->addAttribute( "name", m_name );
65 
66  return i;
67  }
68  // ---- ~Disco::Identity ----
69 
70  // ---- Disco::Info ----
71  Disco::Info::Info( const std::string& node, bool defaultFeatures )
72  : StanzaExtension( ExtDiscoInfo ), m_node( node ), m_form( 0 )
73  {
74  if( defaultFeatures )
75  {
76  m_features.push_back( XMLNS_DISCO_INFO );
77  m_features.push_back( XMLNS_DISCO_ITEMS );
78  }
79  }
80 
81  Disco::Info::Info( const Tag* tag )
82  : StanzaExtension( ExtDiscoInfo ), m_form( 0 )
83  {
84  if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_INFO )
85  return;
86 
87  m_node = tag->findAttribute( "node" );
88 
89  const TagList& l = tag->children();
90  TagList::const_iterator it = l.begin();
91  for( ; it != l.end(); ++it )
92  {
93  const std::string& name = (*it)->name();
94  if( name == "identity" )
95  m_identities.push_back( new Identity( (*it) ) );
96  else if( name == "feature" && (*it)->hasAttribute( "var" ) )
97  m_features.push_back( (*it)->findAttribute( "var" ) );
98  else if( !m_form && name == "x" && (*it)->xmlns() == XMLNS_X_DATA )
99  m_form = new DataForm( (*it) );
100  }
101  }
102 
103  Disco::Info::Info( const Info& info )
104  : StanzaExtension( ExtDiscoInfo ), m_node( info.m_node ), m_features( info.m_features ),
105  m_identities( info.m_identities ), m_form( info.m_form ? new DataForm( *(info.m_form) ) : 0 )
106  {
107  }
108 
109  Disco::Info::~Info()
110  {
111  delete m_form;
112  util::clearList( m_identities );
113  }
114 
116  {
117  delete m_form;
118  m_form = form;
119  }
120 
121  bool Disco::Info::hasFeature( const std::string& feature ) const
122  {
123  StringList::const_iterator it = m_features.begin();
124  for( ; it != m_features.end() && (*it) != feature; ++it )
125  ;
126  return it != m_features.end();
127  }
128 
129  const std::string& Disco::Info::filterString() const
130  {
131  static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_INFO + "']";
132  return filter;
133  }
134 
136  {
137  Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_INFO );
138 
139  if( !m_node.empty() )
140  t->addAttribute( "node", m_node );
141 
142  IdentityList::const_iterator it_i = m_identities.begin();
143  for( ; it_i != m_identities.end(); ++it_i )
144  t->addChild( (*it_i)->tag() );
145 
146  StringList::const_iterator it_f = m_features.begin();
147  for( ; it_f != m_features.end(); ++it_f )
148  new Tag( t, "feature", "var", (*it_f) );
149 
150  if( m_form )
151  t->addChild( m_form->tag() );
152 
153  return t;
154  }
155  // ---- ~Disco::Info ----
156 
157  // ---- Disco::Item ----
158  Disco::Item::Item( const Tag* tag )
159  {
160  if( !tag || tag->name() != "item" )
161  return;
162 
163  m_jid = tag->findAttribute( "jid" );
164  m_node = tag->findAttribute( "node" );
165  m_name = tag->findAttribute( "name" );
166  }
167 
169  {
170  if( !m_jid )
171  return 0;
172 
173  Tag* i = new Tag( "item" );
174  i->addAttribute( "jid", m_jid.full() );
175 
176  if( !m_node.empty() )
177  i->addAttribute( "node", m_node );
178  if( !m_name.empty() )
179  i->addAttribute( "name", m_name );
180 
181  return i;
182  }
183  // ---- ~Disco::Item ----
184 
185  // ---- Disco::Items ----
186  Disco::Items::Items( const std::string& node )
187  : StanzaExtension( ExtDiscoItems ), m_node( node )
188  {
189  }
190 
191  Disco::Items::Items( const Tag* tag )
193  {
194  if( !tag || tag->name() != "query" || tag->xmlns() != XMLNS_DISCO_ITEMS )
195  return;
196 
197  m_node = tag->findAttribute( "node" );
198 
199  const TagList& l = tag->children();
200  TagList::const_iterator it = l.begin();
201  for( ; it != l.end(); ++it )
202  {
203  const std::string& name = (*it)->name();
204  if( name == "item" )
205  m_items.push_back( new Item( (*it) ) );
206  }
207  }
208 
210  {
211  util::clearList( m_items );
212  }
213 
214  void Disco::Items::setItems( const ItemList& items )
215  {
216  util::clearList( m_items );
217  m_items = items;
218  }
219 
220 
221  const std::string& Disco::Items::filterString() const
222  {
223  static const std::string filter = "/iq/query[@xmlns='" + XMLNS_DISCO_ITEMS + "']";
224  return filter;
225  }
226 
228  {
229  Tag* t = new Tag( "query", XMLNS, XMLNS_DISCO_ITEMS );
230 
231  if( !m_node.empty() )
232  t->addAttribute( "node", m_node );
233 
234  ItemList::const_iterator it_i = m_items.begin();
235  for( ; it_i != m_items.end(); ++it_i )
236  t->addChild( (*it_i)->tag() );
237 
238  return t;
239  }
240  // ---- ~Disco::Items ----
241 
242  // ---- Disco ----
243  Disco::Disco( ClientBase* parent )
244  : m_parent( parent ), m_form( 0 )
245  {
247 // addFeature( XMLNS_DISCO_INFO ); //handled by Disco::Info now
248 // addFeature( XMLNS_DISCO_ITEMS ); //handled by Disco::Info now
249  if( m_parent )
250  {
251  m_parent->registerIqHandler( this, ExtDiscoInfo );
252  m_parent->registerIqHandler( this, ExtDiscoItems );
253  m_parent->registerIqHandler( this, ExtVersion );
254  m_parent->registerStanzaExtension( new Disco::Info() );
255  m_parent->registerStanzaExtension( new Disco::Items() );
256  m_parent->registerStanzaExtension( new SoftwareVersion() );
257  }
258  }
259 
260  Disco::~Disco()
261  {
262  util::clearList( m_identities );
263  delete m_form;
264 
265  if( m_parent )
266  {
267  m_parent->removeIqHandler( this, ExtDiscoInfo );
268  m_parent->removeIqHandler( this, ExtDiscoItems );
269  m_parent->removeIqHandler( this, ExtVersion );
270  m_parent->removeStanzaExtension( ExtDiscoInfo );
272  m_parent->removeStanzaExtension( ExtVersion );
273  m_parent->removeIDHandler( this );
274  }
275  }
276 
278  {
279  delete m_form;
280  m_form = form;
281  }
282 
283  bool Disco::handleIq( const IQ& iq )
284  {
285  switch( iq.subtype() )
286  {
287  case IQ::Get:
288  {
289  IQ re( IQ::Result, iq.from(), iq.id() );
290  re.setFrom( iq.to() );
291 
293  if( sv )
294  {
295  re.addExtension( new SoftwareVersion( m_versionName, m_versionVersion, m_versionOs ) );
296  m_parent->send( re );
297  return true;
298  }
299 
300  const Info *info = iq.findExtension<Info>( ExtDiscoInfo );
301  if( info )
302  {
303  Info *i = new Info( EmptyString, true );
304  if( !info->node().empty() )
305  {
306  i->setNode( info->node() );
309  DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( info->node() );
310  if( it == m_nodeHandlers.end() )
311  {
312  delete i;
313  IQ re( IQ::Error, iq.from(), iq.id() );
315  m_parent->send( re );
316  return true;
317  }
318  else
319  {
320  DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
321  for( ; in != (*it).second.end(); ++in )
322  {
323  IdentityList il = (*in)->handleDiscoNodeIdentities( iq.from(), info->node() );
324  il.sort(); // needed on win32
325  identities.merge( il );
326  StringList fl = (*in)->handleDiscoNodeFeatures( iq.from(), info->node() );
327  fl.sort(); // needed on win32
328  features.merge( fl );
329  }
330  }
331  i->setIdentities( identities );
332  i->setFeatures( features );
333  }
334  else
335  {
336  IdentityList il;
337  IdentityList::const_iterator it = m_identities.begin();
338  for( ; it != m_identities.end(); ++it )
339  {
340  il.push_back( new Identity( *(*it) ) );
341  }
342  i->setIdentities( il );
343  i->setFeatures( m_features );
344  if( m_form )
345  i->setForm( new DataForm( *m_form ) );
346  }
347 
348  re.addExtension( i );
349  m_parent->send( re );
350  return true;
351  }
352 
353  const Items *items = iq.findExtension<Items>( ExtDiscoItems );
354  if( items )
355  {
356  Items *i = new Items( items->node() );
357  if( !items->node().empty() )
358  {
359  DiscoNodeHandlerMap::const_iterator it = m_nodeHandlers.find( items->node() );
360  if( it == m_nodeHandlers.end() )
361  {
362  delete i;
363  IQ re( IQ::Error, iq.from(), iq.id() );
365  m_parent->send( re );
366  return true;
367  }
368  else
369  {
370  ItemList itemlist;
371  DiscoNodeHandlerList::const_iterator in = (*it).second.begin();
372  for( ; in != (*it).second.end(); ++in )
373  {
374  ItemList il = (*in)->handleDiscoNodeItems( iq.from(), iq.to(), items->node() );
375  il.sort(); // needed on win32
376  itemlist.merge( il );
377  }
378  i->setItems( itemlist );
379  }
380  }
381 
382  re.addExtension( i );
383  m_parent->send( re );
384  return true;
385  }
386  break;
387  }
388 
389  case IQ::Set:
390  {
391  bool res = false;
392  DiscoHandlerList::const_iterator it = m_discoHandlers.begin();
393  for( ; it != m_discoHandlers.end(); ++it )
394  {
395  if( (*it)->handleDiscoSet( iq ) )
396  res = true;
397  }
398  return res;
399  break;
400  }
401 
402  default:
403  break;
404  }
405  return false;
406  }
407 
408  void Disco::handleIqID( const IQ& iq, int context )
409  {
410  DiscoHandlerMap::iterator it = m_track.find( iq.id() );
411  if( it != m_track.end() && (*it).second.dh )
412  {
413  switch( iq.subtype() )
414  {
415  case IQ::Result:
416  switch( context )
417  {
418  case GetDiscoInfo:
419  {
420  const Info* di = iq.findExtension<Info>( ExtDiscoInfo );
421  if( di )
422  (*it).second.dh->handleDiscoInfo( iq.from(), *di, (*it).second.context );
423  break;
424  }
425  case GetDiscoItems:
426  {
427  const Items* di = iq.findExtension<Items>( ExtDiscoItems );
428  if( di )
429  (*it).second.dh->handleDiscoItems( iq.from(), *di, (*it).second.context );
430  break;
431  }
432  }
433  break;
434 
435  case IQ::Error:
436  {
437  (*it).second.dh->handleDiscoError( iq.from(), iq.error(), (*it).second.context );
438  break;
439  }
440 
441  default:
442  break;
443  }
444 
445  m_track.erase( it );
446  }
447  }
448 
449  void Disco::getDisco( const JID& to, const std::string& node, DiscoHandler* dh, int context,
450  IdType idType, const std::string& tid )
451  {
452  const std::string& id = tid.empty() ? m_parent->getID() : tid;
453 
454  IQ iq( IQ::Get, to, id );
455  if( idType == GetDiscoInfo )
456  iq.addExtension( new Info( node ) );
457  else
458  iq.addExtension( new Items( node ) );
459 
460  DiscoHandlerContext ct;
461  ct.dh = dh;
462  ct.context = context;
463  m_track[id] = ct;
464  m_parent->send( iq, this, idType );
465  }
466 
467  void Disco::setVersion( const std::string& name, const std::string& version, const std::string& os )
468  {
469  m_versionName = name;
470  m_versionVersion = version;
471  m_versionOs = os;
472  }
473 
474  void Disco::setIdentity( const std::string& category, const std::string& type,
475  const std::string& name )
476  {
477  util::clearList( m_identities );
478  addIdentity( category, type, name );
479  }
480 
482  {
483  m_discoHandlers.remove( dh );
484  DiscoHandlerMap::iterator t;
485  DiscoHandlerMap::iterator it = m_track.begin();
486  while( it != m_track.end() )
487  {
488  t = it;
489  ++it;
490  if( dh == (*t).second.dh )
491  {
492  m_track.erase( t );
493  }
494  }
495  }
496 
497  void Disco::registerNodeHandler( DiscoNodeHandler* nh, const std::string& node )
498  {
499  m_nodeHandlers[node].push_back( nh );
500  }
501 
502  void Disco::removeNodeHandler( DiscoNodeHandler* nh, const std::string& node )
503  {
504  DiscoNodeHandlerMap::iterator it = m_nodeHandlers.find( node );
505  if( it != m_nodeHandlers.end() )
506  {
507  (*it).second.remove( nh );
508  if( (*it).second.empty() )
509  m_nodeHandlers.erase( it );
510  }
511  }
512 
514  {
515  DiscoNodeHandlerMap::iterator it = m_nodeHandlers.begin();
516  DiscoNodeHandlerMap::iterator it2;
517  while( it != m_nodeHandlers.end() )
518  {
519  it2 = it++;
520  removeNodeHandler( nh, (*it2).first );
521  }
522  }
523 
524  const StringList Disco::features( bool defaultFeatures ) const
525  {
526  StringList f = m_features;
527  if( defaultFeatures )
528  {
529  f.push_back( XMLNS_DISCO_INFO );
530  f.push_back( XMLNS_DISCO_ITEMS );
531  }
532  return f;
533  }
534 
535 }