gloox 1.0.27
disco.cpp
1/*
2 Copyright (c) 2004-2023 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#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
24namespace 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
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 );
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}
This is the common base class for a Jabber/XMPP Client and a Jabber Component.
Definition clientbase.h:79
const std::string getID()
void removeIqHandler(IqHandler *ih, int exttype)
void removeIDHandler(IqHandler *ih)
bool removeStanzaExtension(int ext)
void send(Tag *tag)
An abstraction of a XEP-0004 Data Form.
Definition dataform.h:57
virtual Tag * tag() const
Definition dataform.cpp:106
A virtual interface that enables objects to receive Service Discovery (XEP-0030) events.
Derived classes can be registered as NodeHandlers for certain nodes with the Disco object.
An abstraction of a Disco identity (Service Discovery, XEP-0030).
Definition disco.h:197
const std::string & name() const
Definition disco.h:240
Identity(const std::string &category, const std::string &type, const std::string &name)
Definition disco.cpp:28
Tag * tag() const
Definition disco.cpp:54
An abstraction of a Disco Info element (from Service Discovery, XEP-0030) as a StanzaExtension.
Definition disco.h:66
const std::string & node() const
Definition disco.h:74
void setForm(DataForm *form)
Definition disco.cpp:115
virtual const std::string & filterString() const
Definition disco.cpp:129
bool hasFeature(const std::string &feature) const
Definition disco.cpp:121
virtual Tag * tag() const
Definition disco.cpp:135
An abstraction of a Disco item (Service Discovery, XEP-0030).
Definition disco.h:353
Item(const JID &jid, const std::string &node, const std::string &name)
Definition disco.h:363
Tag * tag() const
Definition disco.cpp:168
An abstraction of a Disco query element (from Service Discovery, XEP-0030) in the disco::items namesp...
Definition disco.h:276
const std::string & node() const
Definition disco.h:305
virtual ~Items()
Definition disco.cpp:209
virtual const std::string & filterString() const
Definition disco.cpp:221
Items(const std::string &node=EmptyString)
Definition disco.cpp:186
virtual Tag * tag() const
Definition disco.cpp:227
void setItems(const ItemList &items)
Definition disco.cpp:214
void removeDiscoHandler(DiscoHandler *dh)
Definition disco.cpp:481
const std::string & name() const
Definition disco.h:485
const DataForm * form() const
Definition disco.h:544
void setForm(DataForm *form)
Definition disco.cpp:277
void removeNodeHandler(DiscoNodeHandler *nh, const std::string &node)
Definition disco.cpp:502
const std::string & version() const
Definition disco.h:491
virtual void handleIqID(const IQ &iq, int context)
Definition disco.cpp:408
virtual bool handleIq(const IQ &iq)
Definition disco.cpp:283
const IdentityList & identities() const
Definition disco.h:529
const std::string & os() const
Definition disco.h:497
void removeNodeHandlers(DiscoNodeHandler *nh)
Definition disco.cpp:513
void registerNodeHandler(DiscoNodeHandler *nh, const std::string &node)
Definition disco.cpp:497
void setIdentity(const std::string &category, const std::string &type, const std::string &name=EmptyString)
Definition disco.cpp:474
void addFeature(const std::string &feature)
Definition disco.h:422
void setVersion(const std::string &name, const std::string &version, const std::string &os=EmptyString)
Definition disco.cpp:467
const StringList features(bool defaultFeatures=false) const
Definition disco.cpp:524
std::list< Identity * > IdentityList
Definition disco.h:56
void addIdentity(const std::string &category, const std::string &type, const std::string &name=EmptyString)
Definition disco.h:521
std::list< Item * > ItemList
Definition disco.h:266
A stanza error abstraction implemented as a StanzaExtension.
Definition error.h:35
An abstraction of an IQ stanza.
Definition iq.h:34
IqType subtype() const
Definition iq.h:74
@ Set
Definition iq.h:46
@ Error
Definition iq.h:49
@ Result
Definition iq.h:48
@ Get
Definition iq.h:45
An abstraction of a JID.
Definition jid.h:31
This is an implementation of XEP-0092 as a StanzaExtension.
This class abstracts a stanza extension, which is usually an XML child element in a specific namespac...
void addExtension(const StanzaExtension *se)
Definition stanza.cpp:52
void setFrom(const JID &from)
Definition stanza.h:45
const std::string & id() const
Definition stanza.h:63
const Error * error() const
Definition stanza.cpp:47
const JID & from() const
Definition stanza.h:51
const JID & to() const
Definition stanza.h:57
const StanzaExtension * findExtension(int type) const
Definition stanza.cpp:57
This is an abstraction of an XML element.
Definition tag.h:47
const std::string & name() const
Definition tag.h:394
const std::string xmlns() const
Definition tag.cpp:543
bool addAttribute(Attribute *attr)
Definition tag.cpp:354
void addChild(Tag *child)
Definition tag.cpp:424
const std::string & findAttribute(const std::string &name) const
Definition tag.cpp:589
const TagList & children() const
Definition tag.cpp:510
void clearList(std::list< T * > &L)
Definition util.h:152
The namespace for the gloox library.
Definition adhoc.cpp:28
std::list< Tag * > TagList
Definition tag.h:31
const std::string XMLNS_VERSION
Definition gloox.cpp:39
std::list< std::string > StringList
Definition gloox.h:1251
const std::string XMLNS_DISCO_ITEMS
Definition gloox.cpp:24
const std::string XMLNS_X_DATA
Definition gloox.cpp:49
const std::string XMLNS_DISCO_INFO
Definition gloox.cpp:23
const std::string EmptyString
Definition gloox.cpp:124
const std::string XMLNS
Definition gloox.cpp:122
@ StanzaErrorItemNotFound
Definition gloox.h:893
@ StanzaErrorTypeCancel
Definition gloox.h:859