1 /*
2   Copyright (c) 2004-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 #ifndef DISCO_H__
16 #define DISCO_H__
17 
18 #include "gloox.h"
19 
20 #include "iqhandler.h"
21 #include "jid.h"
22 
23 #include <string>
24 #include <list>
25 #include <map>
26 
27 namespace gloox
28 {
29 
30   class ClientBase;
31   class DataForm;
32   class DiscoHandler;
33   class DiscoNodeHandler;
34   class IQ;
35 
36   /**
37    * @brief This class implements @xep{0030} (Service Discovery) and @xep{0092} (Software Version).
38    *
39    * ClientBase will automatically instantiate a Disco object. It can be used to
40    * announce special features of your client, or its version, or...
41    *
42    * XEP version: 2.2
43    * @author Jakob Schröter <js@camaya.net>
44    */
45   class GLOOX_API Disco : public IqHandler
46   {
47     friend class ClientBase;
48 
49     public:
50 
51       class Identity; // declared below class Info
52 
53       /**
54        * A list of pointers to Identity objects. Used with Disco::Info.
55        */
56       typedef std::list<Identity*> IdentityList;
57 
58       /**
59        * @brief An abstraction of a Disco Info element (from Service Discovery, @xep{0030})
60        * as a StanzaExtension.
61        *
62        * @author Jakob Schröter <js@camaya.net>
63        * @since 1.0
64        */
65       class GLOOX_API Info : public StanzaExtension
66       {
67         friend class Disco;
68 
69         public:
70           /**
71            * Returns the queried node identifier, if any.
72            * @return The node identifier. May be empty.
73            */
node()74           const std::string& node() const { return m_node; }
75 
76           /**
77            * Returns the entity's supported features.
78            * @return A list of supported features/namespaces.
79            */
features()80           const StringList& features() const { return m_features; }
81 
82           /**
83            * Use this function to check if the entity the Info came from supports agiven feature.
84            * @param feature The feature to check for.
85            * @return @b True if the entity announces support for the feature, @b false otherwise.
86            */
87           bool hasFeature( const std::string& feature ) const;
88 
89           /**
90            * Returns the entity's identities.
91            * @return A list of pointers to Identity objects.
92            */
identities()93           const IdentityList& identities() const { return m_identities; }
94 
95           /**
96            * Returns an optionally included data form. This is used by e.g. MUC (@xep{0045}).
97            * @return An optional data form included in the disco#info. May be 0.
98            */
form()99           const DataForm* form() const { return m_form; }
100 
101           /**
102            * Adds an optional DataForm, e.g. for @xep{0232}. Only one form can be added
103            * at this point.
104            * @param form An optional DataForm to include in the Info reply.
105            * The form will be owned by and deleted on destruction of the Info object.
106            * @note If called more than once the previously set form will be deleted.
107            */
108           void setForm( DataForm* form );
109 
110           // reimplemented from StanzaExtension
111           virtual const std::string& filterString() const;
112 
113           // reimplemented from StanzaExtension
newInstance(const Tag * tag)114           virtual StanzaExtension* newInstance( const Tag* tag ) const
115           {
116             return new Info( tag );
117           }
118 
119           // reimplemented from StanzaExtension
clone()120           virtual StanzaExtension* clone() const
121           {
122             return new Info( *this );
123           }
124 
125           // reimplemented from StanzaExtension
126           virtual Tag* tag() const;
127 
128         private:
129 #ifdef DISCO_INFO_TEST
130         public:
131 #endif
132           /**
133            * Creates a empty Info object, suitable for making disco#info requests.
134            * @param node The node identifier to query (optional).
135            * @param defaultFeatures Indicates whether or not the default features should be
136            * included in the Info. Should be @b false for requests, @b true for replies.
137            * Defaults to @b false.
138            */
139           Info( const std::string& node = EmptyString, bool defaultFeatures = false );
140 
141            /**
142            * Creates an Info object from the given Tag.
143            * @param tag A &lt;query&gt; tag in the disco#info namespace, (possibly) containing
144            * a disco#info reply.
145            */
146           Info( const Tag* tag );
147 
148           /**
149            * Copy constructor.
150            * @param info An Info object to copy.
151            */
152           Info( const Info& info );
153 
154           /**
155            * Virtual destructor.
156            */
157           virtual ~Info();
158 
159           /**
160            * Sets the current info node.
161            * @param node The info node.
162            */
setNode(const std::string & node)163           void setNode( const std::string& node ) { m_node = node; }
164 
165           /**
166            * This function can be used to set the entity's features.
167            * @param features A list of supported features/namespaces.
168            */
setFeatures(const StringList & features)169           void setFeatures( const StringList& features )
170           {
171             StringList fl( features );
172             fl.sort(); // needed on win32
173             m_features.merge( fl );
174           }
175 
176           /**
177            * This function can be used to set the entity's identities.
178            * @param identities A list of pointers to the entity's identities.
179            * @note The Identity objects pointed to will be owned by the Info object. The
180            * list should neither be used again nor should the Identity objects be deleted.
181            */
setIdentities(const IdentityList & identities)182           void setIdentities( const IdentityList& identities ) { m_identities = identities; }
183 
184           std::string m_node;
185           StringList m_features;
186           IdentityList m_identities;
187           DataForm* m_form;
188       };
189 
190       /**
191        * @brief An abstraction of a Disco identity (Service Discovery, @xep{0030}).
192        *
193        * @author Jakob Schröter <js@camaya.net>
194        * @since 1.0
195        */
196       class GLOOX_API Identity
197       {
198         friend class Info;
199         friend class Disco;
200 
201         public:
202           /**
203            * Constructs a Disco Identity from a category, type and name.
204            * See http://www.xmpp.org/registrar/disco-categories.html for more info.
205            * @param category The identity's category.
206            * @param type The identity's type.
207            * @param name The identity's name.
208            */
209           Identity( const std::string& category,
210                     const std::string& type,
211                     const std::string& name );
212 
213           /**
214            * Copy Contructor.
215            * @param id An Identity to create a new Identity object from.
216            */
217           Identity( const Identity& id );
218 
219           /**
220            * Destructor.
221            */
222           ~Identity();
223 
224           /**
225            * Returns the identity's category.
226            * @return The identity's category.
227            */
category()228           const std::string& category() const { return m_category; }
229 
230           /**
231            * Returns the identity's type.
232            * @return The identity's type.
233            */
type()234           const std::string& type() const { return m_type; }
235 
236           /**
237            * Returns the identity's name.
238            * @return The identity's name.
239            */
name()240           const std::string& name() const { return m_name; }
241 
242           /**
243            * Creates and returns a Tag representation of this identity.
244            * @return A Tag, or 0.
245            */
246           Tag* tag() const;
247 
248         private:
249           /**
250            * Creates a Disco Identity from the given Tag.
251            * @param tag A Tag representation of a disco identity.
252            */
253           Identity( const Tag* tag );
254 
255           std::string m_category;   /**< The identity's category. */
256           std::string m_type;       /**< The identity's type. */
257           std::string m_name;       /**< The identity's name. */
258 
259       };
260 
261       class Item; // declared below class Items
262 
263       /**
264        * A list of pointers to Item objects. Used with Disco::Items.
265        */
266       typedef std::list<Item*> ItemList;
267 
268       /**
269        * @brief An abstraction of a Disco query element (from Service Discovery, @xep{0030})
270        * in the disco#items namespace, implemented as a StanzaExtension.
271        *
272        * @author Jakob Schröter <js@camaya.net>
273        * @since 1.0
274        */
275       class GLOOX_API Items : public StanzaExtension
276       {
277         friend class Disco;
278 
279         public:
280           // This needs to be public so one can proactively send a list of adhoc commands
281           // see @xep{0050}
282           /**
283            * Creates an empty Items object, suitable for making disco#items requests.
284            * @param node The node identifier to query (optional).
285            */
286           Items( const std::string& node = EmptyString );
287 
288           /**
289            * Virtual destructor.
290            */
291           virtual ~Items();
292 
293           /**
294            * This function can be used to set the entity's/node's items.
295            * @param items A list of pointers to the entity's/node's items.
296            * @note The Item objects pointed to will be owned by the Items object. The
297            * list should neither be used again nor should the Item objects be deleted.
298            */
299           void setItems( const ItemList& items );
300 
301           /**
302            * Returns the queried node identifier, if any.
303            * @return The node identifier. May be empty.
304            */
node()305           const std::string& node() const { return m_node; }
306 
307           /**
308            * Returns the entity's/node's items.
309            * @return A list of pointers to Item objects.
310            */
items()311           const ItemList& items() const { return m_items; }
312 
313           // reimplemented from StanzaExtension
314           virtual const std::string& filterString() const;
315 
316           // reimplemented from StanzaExtension
newInstance(const Tag * tag)317           virtual StanzaExtension* newInstance( const Tag* tag ) const
318           {
319             return new Items( tag );
320           }
321 
322           // reimplemented from StanzaExtension
323           virtual Tag* tag() const;
324 
325           // reimplemented from StanzaExtension
clone()326           virtual StanzaExtension* clone() const
327           {
328             return new Items( *this );
329           }
330 
331         private:
332 #ifdef DISCO_ITEMS_TEST
333         public:
334 #endif
335           /**
336            * Creates an Items object from the given Tag.
337            * @param tag A &lt;query&gt; tag in the disco#items namespace, (possibly) containing
338            * a disco#items reply.
339            */
340           Items( const Tag* tag );
341 
342           std::string m_node;
343           ItemList m_items;
344       };
345 
346       /**
347        * @brief An abstraction of a Disco item (Service Discovery, @xep{0030}).
348        *
349        * @author Jakob Schröter <js@camaya.net>
350        * @since 1.0
351        */
352       class GLOOX_API Item
353       {
354         friend class Items;
355 
356         public:
357           /**
358            * Constructs a Disco Item from a JID, node and name.
359            * @param jid The item's JID.
360            * @param node The item's type.
361            * @param name The item's name.
362            */
Item(const JID & jid,const std::string & node,const std::string & name)363           Item( const JID& jid,
364                 const std::string& node,
365                 const std::string& name )
366           : m_jid( jid ), m_node( node ), m_name( name ) {}
367 
368           /**
369            * Destructor.
370            */
~Item()371           ~Item() {}
372 
373           /**
374            * Returns the item's JID.
375            * @return The item's JID.
376            */
jid()377           const JID& jid() const { return m_jid; }
378 
379           /**
380            * Returns the item's node.
381            * @return The item's node.
382            */
node()383           const std::string& node() const { return m_node; }
384 
385           /**
386            * Returns the item's name.
387            * @return The item's name.
388            */
name()389           const std::string& name() const { return m_name; }
390 
391           /**
392            * Creates and returns a Tag representation of this item.
393            * @return A Tag, or 0.
394            */
395           Tag* tag() const;
396 
397         private:
398           /**
399            * Creates a Disco Item from the given Tag.
400            * @param tag A Tag representation of a Disco item.
401            */
402           Item( const Tag* tag );
403 
404           JID m_jid;                /**< The item's jid. */
405           std::string m_node;       /**< The item's type. */
406           std::string m_name;       /**< The item's name. */
407 
408       };
409 
410       /**
411        * Adds a feature to the list of supported Jabber features.
412        * The list will be posted as an answer to IQ queries in the
413        * "http://jabber.org/protocol/disco#info" namespace.
414        * These IQ packets will also be forwarded to the
415        * application's IqHandler, if it listens to the @c disco\#info namespace.
416        * By default, disco(very) queries are handled by the library.
417        * By default, all supported, not disabled features are announced.
418        * @param feature A feature (namespace) the host app supports.
419        * @note Use this function for non-queryable features. For nodes that shall
420        * answer to @c disco\#info queries, use registerNodeHandler().
421        */
addFeature(const std::string & feature)422       void addFeature( const std::string& feature )
423         { m_features.push_back( feature ); }
424 
425       /**
426        * Removes the given feature from the list of advertised client features.
427        * @param feature The feature to remove.
428        * @since 0.9
429        */
removeFeature(const std::string & feature)430       void removeFeature( const std::string& feature )
431         { m_features.remove( feature ); }
432 
433       /**
434        * Lets you retrieve the features this Disco instance supports.
435        * @param defaultFeatures Include default features. Defaults to @b false.
436        * @return A list of supported features/namespaces.
437        */
438       const StringList features( bool defaultFeatures = false ) const;
439 
440       /**
441        * Queries the given JID for general infomation according to
442        * @xep{0030} (Service Discovery).
443        * To receive the results inherit from DiscoHandler and register with the Disco object.
444        * @param to The destination-JID of the query.
445        * @param node An optional node to query. Not inserted if empty.
446        * @param dh The DiscoHandler to notify about results.
447        * @param context A context identifier.
448        * @param tid An optional id that is going to be used as the IQ request's id. Only
449        * necessary if you need to know the request's id.
450        */
451       void getDiscoInfo( const JID& to, const std::string& node, DiscoHandler* dh, int context,
452                          const std::string& tid = EmptyString )
453         { getDisco( to, node, dh, context, GetDiscoInfo, tid ); }
454 
455       /**
456        * Queries the given JID for its items according to
457        * @xep{0030} (Service Discovery).
458        * To receive the results inherit from DiscoHandler and register with the Disco object.
459        * @param to The destination-JID of the query.
460        * @param node An optional node to query. Not inserted if empty.
461        * @param dh The DiscoHandler to notify about results.
462        * @param context A context identifier.
463        * @param tid An optional id that is going to be used as the IQ request's id. Only
464        * necessary if you need to know the request's id.
465        */
466       void getDiscoItems( const JID& to, const std::string& node, DiscoHandler* dh, int context,
467                           const std::string& tid = EmptyString )
468         { getDisco( to, node, dh, context, GetDiscoItems, tid ); }
469 
470       /**
471        * Sets the version of the host application using this library.
472        * The library takes care of jabber:iq:version requests. These
473        * IQ packets will not be forwarded to the IqHandlers.
474        * @param name The name to be returned to inquiring clients.
475        * @param version The version to be returned to inquiring clients.
476        * @param os The operating system to announce. Default: don't include.
477        */
478       void setVersion( const std::string& name, const std::string& version,
479                        const std::string& os = EmptyString );
480 
481       /**
482        * Returns the application's advertised name.
483        * @return The application's advertised name.
484        */
name()485       const std::string& name() const { return m_versionName; }
486 
487       /**
488        * Returns the application's advertised version.
489        * @return The application's advertised version.
490        */
version()491       const std::string& version() const { return m_versionVersion; }
492 
493       /**
494        * Returns the application's advertised operating system.
495        * @return The application's advertised operating system.
496        */
os()497       const std::string& os() const { return m_versionOs; }
498 
499       /**
500        * Sets the identity of this entity.
501        * The library uses this information to answer disco#info requests
502        * with a correct identity.
503        * @xep{0030} requires an entity to have at least one identity. See @xep{0030}
504        * for more information on categories and types.
505        * @param category The entity category of this client. Default: client. May not be empty.
506        * @param type The type of this entity. Default: bot. May not be empty.
507        * @param name The name of the entity. Default: empty.
508        * @note An entity can have more than one identity. You cann add more identities
509        * using addIdentity(). A call to setIdentity() will clear the list of identities
510        * and, after that, add the new identity given by the arguments to setIdentity().
511        */
512       void setIdentity( const std::string& category, const std::string& type,
513                         const std::string& name = EmptyString );
514 
515       /**
516        * Adds another identity to the list of identities.
517        * @param category The entity category of this client. Default: client. May not be empty.
518        * @param type The type of this entity. Default: bot. May not be empty.
519        * @param name The name of the entity. Default: empty.
520        */
521       void addIdentity( const std::string& category, const std::string& type,
522                         const std::string& name = EmptyString )
523         { m_identities.push_back( new Identity( category, type, name ) ); }
524 
525       /**
526        * Returns the entity's identities.
527        * @return The entity's identities.
528        */
identities()529       const IdentityList& identities() const { return m_identities; }
530 
531       /**
532        * Adds an optional DataForm to Disco:Info replies, e.g. for @xep{0232}.
533        * Only one form can be added at this point.
534        * @param form An optional DataForm to include in the Info reply.
535        * The form will be owned by and deleted on destruction of the Disco object.
536        * @note If called more than once the previously set form will be deleted.
537        */
538       void setForm( DataForm* form );
539 
540       /**
541        * Returns the DataForm set by setForm(). Used by Capabilities.
542        * @return The DataForm, or 0.
543        */
form()544       const DataForm* form() const { return m_form; }
545 
546       /**
547        * Use this function to register an @ref DiscoHandler with the Disco
548        * object. This is only necessary if you want to receive Disco-set requests. Else
549        * a one-time registration happens when calling getDiscoInfo() and getDiscoItems(), respectively.
550        * @param dh The DiscoHandler-derived object to register.
551        */
registerDiscoHandler(DiscoHandler * dh)552       void registerDiscoHandler( DiscoHandler* dh )
553         { m_discoHandlers.push_back( dh ); }
554 
555       /**
556        * Unregisters the given DiscoHandler.
557        * @param dh The DiscoHandler to unregister.
558        */
559       void removeDiscoHandler( DiscoHandler* dh );
560 
561       /**
562        * Use this function to register a @ref DiscoNodeHandler with the Disco
563        * object. The DiscoNodeHandler will receive disco#items queries which are
564        * directed to the corresponding node registered for the handler.
565        * @param nh The NodeHandler-derived object to register.
566        * @param node The node name to associate with this handler. Use an empty string to
567        * register for the root node.
568        */
569       void registerNodeHandler( DiscoNodeHandler* nh, const std::string& node );
570 
571       /**
572        * Removes the node handler for the given node.
573        * @param nh The NodeHandler to unregister.
574        * @param node The node for which the handler shall be removed. Use an empty string to
575        * remove the root node's handler.
576        */
577       void removeNodeHandler( DiscoNodeHandler* nh, const std::string& node );
578 
579       /**
580        * Removes all registered nodes of the given node handler.
581        * @param nh The NodeHandler to unregister.
582        */
583       void removeNodeHandlers( DiscoNodeHandler* nh );
584 
585       // reimplemented from IqHandler.
586       virtual bool handleIq( const IQ& iq );
587 
588       // reimplemented from IqHandler.
589       virtual void handleIqID( const IQ& iq, int context );
590 
591     private:
592 #ifdef DISCO_TEST
593     public:
594 #endif
595       Disco( ClientBase* parent );
596       virtual ~Disco();
597 
598       enum IdType
599       {
600         GetDiscoInfo,
601         GetDiscoItems
602       };
603 
604       void getDisco( const JID& to, const std::string& node, DiscoHandler* dh,
605                      int context, IdType idType, const std::string& tid );
606 
607       struct DiscoHandlerContext
608       {
609         DiscoHandler* dh;
610         int context;
611       };
612 
613       ClientBase* m_parent;
614 
615       typedef std::list<DiscoHandler*> DiscoHandlerList;
616       typedef std::list<DiscoNodeHandler*> DiscoNodeHandlerList;
617       typedef std::map<std::string, DiscoNodeHandlerList> DiscoNodeHandlerMap;
618       typedef std::map<std::string, DiscoHandlerContext> DiscoHandlerMap;
619 
620       DiscoHandlerList m_discoHandlers;
621       DiscoNodeHandlerMap m_nodeHandlers;
622       DiscoHandlerMap m_track;
623       IdentityList m_identities;
624       StringList m_features;
625       StringMap m_queryIDs;
626       DataForm* m_form;
627 
628       std::string m_versionName;
629       std::string m_versionVersion;
630       std::string m_versionOs;
631 
632   };
633 
634 }
635 
636 #endif // DISCO_H__
637