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 #ifndef CLIENT_H__
15 #define CLIENT_H__
16 
17 #include "clientbase.h"
18 #include "presence.h"
19 
20 #include <string>
21 
22 namespace gloox
23 {
24 
25   class Capabilities;
26   class RosterManager;
27   class NonSaslAuth;
28   class IQ;
29 
30   /**
31    * @brief This class implements a basic Jabber/XMPP Client.
32    *
33    * It supports @ref sasl_auth as well as TLS (Encryption), which can be
34    * switched on/off separately. They are used automatically if the server supports them.
35    *
36    * To use, create a new Client instance and feed it connection credentials, either in the Constructor or
37    * afterwards using the setters. You should then register packet handlers implementing the corresponding
38    * Interfaces (ConnectionListener, PresenceHandler, MessageHandler, IqHandler, SubscriptionHandler),
39    * and call @ref connect() to establish the connection to the server.
40    *
41    * @note While the MessageHandler interface is still available (and will be in future versions)
42    * it is now recommended to use the new @link gloox::MessageSession MessageSession @endlink for any
43    * serious messaging.
44    *
45    * Simple usage example:
46    * @code
47    * using namespace gloox;
48    *
49    * void TestProg::doIt()
50    * {
51    *   Client* j = new Client( "user@server/resource", "password" );
52    *   j->registerPresenceHandler( this );
53    *   j->disco()->setVersion( "TestProg", "1.0" );
54    *   j->disco()->setIdentity( "client", "bot" );
55    *   j->connect();
56    * }
57    *
58    * virtual void TestProg::presenceHandler( Presence* presence )
59    * {
60    *   // handle incoming presence packets here
61    * }
62    * @endcode
63    *
64    * However, you can skip the presence handling stuff if you make use of the RosterManager.
65    *
66    * By default, the library handles a few (incoming) IQ namespaces on the application's behalf. These
67    * include:
68    * @li jabber:iq:roster: by default the server-side roster is fetched and handled. Use
69    * @ref rosterManager() and @ref RosterManager to interact with the Roster.
70    * @li @xep{0092} (Software Version): If no version is specified, a name of "based on gloox" with
71    * gloox's current version is announced.
72    * @li @xep{0030} (Service Discovery): All supported/available services are announced. No items are
73    * returned.
74    * @note As of gloox 0.9, by default a priority of 0 is sent along with the initial presence.
75    * @note As of gloox 0.9, initial presence is automatically sent. Presence: available, Priority: 0.
76    * To disable sending of initial Presence use setPresence() with a value of Unavailable
77    * prior to connecting.
78    *
79    * @section sasl_auth SASL Authentication
80    *
81    * Besides the simple, IQ-based authentication (@xep{0078}), gloox supports several SASL (Simple
82    * Authentication and Security Layer, RFC 2222) authentication mechanisms.
83    * @li DIGEST-MD5: This mechanism is preferred over all other mechanisms if username and password are
84    * provided to the Client instance. It is secure even without TLS encryption.
85    * @li PLAIN: This mechanism is used if DIGEST-MD5 is not available. It is @b not secure without
86    * encryption.
87    * @li ANONYMOUS This mechanism is used if neither username nor password are set. The server generates
88    * random, temporary username and resource and may restrict available services.
89    * @li EXTERNAL This mechanism is currently only available if client certificate and private key
90    * are provided. The server tries to figure out who the client is by external means -- for instance,
91    * using the provided certificate or even the IP address. (The restriction to certificate/key
92    * availability is likely to be lifted in the future.)
93    *
94    * Of course, all these mechanisms are not tried unless the server offers them.
95    *
96    * @section stream_management Stream Management
97    *
98    * To enable Stream Management (@xep{0198}), call @ref setStreamManagement() with the first parameter set to @b true
99    * at any time. This will tell the server to enable Stream Management, if the feature is available. Once switched on,
100    * Stream Management can not be disabled for a given active stream. However, setting the first
101    * parameter to @b false, it can be disabled inside gloox so that Stream Management will not be used
102    * for subsequent connections.
103    *
104    * To enable the stream resumption feature, pass @b true as the second parameter to @ref setStreamManagement().
105    * Upon re-connect after an unexpected (i.e. neither user-triggered nor server-triggered) disconnect, gloox will try
106    * to resume the stream and re-send any non-acknowledged stanzas automatically.
107    * For stream resumption to work you have to re-connect using the very same Client instance.
108    *
109    * After an unexpected disconnect you may check the send queue using @link ClientBase::sendQueue() sendQueue() @endlink.
110    * Stanzas in the queue have been sent but not yet acknowledged by the server. Depending on the circumstances of the
111    * disconnect, this does not mean that those stanzas have not been received by the recipient.
112    *
113    *
114    * @author Jakob Schröter <js@camaya.net>
115    */
116   class GLOOX_API Client : public ClientBase
117   {
118     public:
119 
120       friend class NonSaslAuth;
121       friend class Parser;
122 
123       /**
124        * Constructs a new Client which can be used for account registration only.
125        * SASL and TLS are on by default. The port will be determined by looking up SRV records.
126        * Alternatively, you can set the port explicitly by calling @ref setPort().
127        * @param server The server to connect to.
128        */
129       Client( const std::string& server );
130 
131       /**
132        * Constructs a new Client.
133        * SASL and TLS are on by default. This should be the default constructor for most use cases.
134        * The server address will be taken from the JID. The actual host will be resolved using SRV
135        * records. The domain part of the JID is used as a fallback in case no SRV record is found, or
136        * you can set the server address separately by calling @ref setServer().
137        * @param jid A full Jabber ID used for connecting to the server.
138        * @param password The password used for authentication.
139        * @param port The port to connect to. The default of -1 means to look up the port via DNS SRV.
140        */
141       Client( const JID& jid, const std::string& password, int port = -1 );
142 
143       /**
144        * Virtual destructor.
145        */
146       virtual ~Client();
147 
148       /**
149        * Use this function to bind an additional resource or to @b re-try to bind a
150        * resource in case previous binding failed and you were notified by means of
151        * ConnectionListener::onResourceBindError(). Use hasResourceBind() to find out if the
152        * server supports binding of multiple resources. bindResource() is a NOOP if it doesn't.
153        * @note ConnectionListener::onResourceBound() and ConnectionListener::onResourceBindError()
154        * will be called in case of success and failure, respectively.
155        * @param resource The resource identifier to bind. May be empty. In that case
156        * the server will assign a unique resource identifier.
157        * @return Returns @b true if binding of multiple resources is supported, @b false
158        * otherwise. A return value of @b true does not indicate that the resource was
159        * successfully bound.
160        * @note It is not necessary to call this function to bind the initial, main, resource.
161        * @since 1.0
162        */
bindResource(const std::string & resource)163       bool bindResource( const std::string& resource )
164         { return bindOperation( resource, true ); }
165 
166       /**
167        * Use this function to select a resource identifier that has been bound
168        * previously by means of bindResource(). It is not necessary to call this function
169        * if only one resource is bound. Use hasResourceBind() to find out if the
170        * server supports binding of multiple resources.
171        * @param resource A resource string that has been bound previously.
172        * @note If the resource string has not been bound previously, future sending of
173        * stanzas will fail.
174        */
175       bool selectResource( const std::string& resource );
176 
177       /**
178        * This function can be used to find out whether the server supports binding of multiple
179        * resources.
180        * @return @b True if binding of multiple resources is supported by the server,
181        * @b false otherwise.
182        */
hasResourceBind()183       bool hasResourceBind() const { return ((m_streamFeatures & StreamFeatureUnbind) == StreamFeatureUnbind); }
184 
185       /**
186        * Use this function to unbind a resource identifier that has been bound
187        * previously by means of bindResource(). Use hasResourceBind() to find out if the
188        * server supports binding of multiple resources. unbindResource() is a NOOP if it doesn't.
189        * @param resource A resource string that has been bound previously.
190        * @note Servers are encouraged to terminate the connection should the only bound
191        * resource be unbound.
192        */
unbindResource(const std::string & resource)193       bool unbindResource( const std::string& resource )
194         { return bindOperation( resource, false ); }
195 
196       /**
197        * Returns the current prepped main resource.
198        * @return The resource used to connect.
199        */
resource()200       const std::string& resource() const { return m_jid.resource(); }
201 
202       /**
203        * This function enables Stream Management (@xep{0198}) if the server supports it.
204        * Optionally, stream resumption can be disabled.
205        * @note You can use this function at any time. However, gloox will make sure Stream Management
206        * requests are sent only when allowed by the specification.
207        * @param enable Enable or disable Stream Management. Note: once enabled on a connection, Stream
208        * Management can not be disabled for that connection.
209        * @param resume Tells the server whether to enable stream resumption. Defaults to @b true.
210        * @note This function is part of @xep{0198}.
211        * @since 1.0.4
212        */
213       void setStreamManagement( bool enable = true, bool resume = true );
214 
215       /**
216        * Use this function to send an unrequested 'ack' to the server to let it know the number of handled stanzas.
217        * You may use this function at any time. However, gloox will also reply to incoming 'ack requests' automatically.
218        * These automatic 'acks' are not announced anywhere in gloox.
219        * This function is a no-op if called in situations where sending an ack is not
220        * allowed by the protocol.
221        * @note This function is part of @xep{0198}.
222        * @since 1.0.4
223        */
224       void ackStreamManagement();
225 
226       /**
227        * Use this function to request the number of handled stanzas from the server.
228        * You may use this function at any time. gloox does not send any such requests
229        * automatically.
230        * @note This function is part of @xep{0198}.
231        * @since 1.0.4
232        */
233       void reqStreamManagement();
234 
235       /**
236        * Returns the current priority.
237        * @return The priority of the current resource.
238        */
priority()239       int priority() const { return m_presence.priority(); }
240 
241       /**
242        * Sets the username to use to connect to the XMPP server.
243        * @param username The username to authenticate with.
244        */
245       void setUsername( const std::string &username );
246 
247       /**
248        * Sets the main resource to use to connect to the XMPP server.
249        * @param resource The resource to use to log into the server.
250        */
setResource(const std::string & resource)251       void setResource( const std::string &resource ) { m_jid.setResource( resource ); }
252 
253       /**
254        * Sends directed presence to the given JID. This is a NOOP if there's no active connection.
255        * To broadcast presence use setPresence( Presence::PresenceType, int, const std::string& ).
256        * @param to The JID to send directed Presence to.
257        * @param pres The presence to send.
258        * @param priority The priority to include. Legal values: -128 <= priority <= 127
259        * @param status The optional status message to include.
260        * @note This function does not include any presence extensions (as added by
261        * means of addPresenceExtension()) to the stanza.
262        */
263       void setPresence( const JID& to, Presence::PresenceType pres, int priority,
264                         const std::string& status = EmptyString );
265 
266       /**
267        * Use this function to set the entity's presence, that is, to broadcast presence to all
268        * subscribed entities. To send directed presence, use
269        * setPresence( const JID&, Presence::PresenceType, int, const std::string& ).
270        * If used prior to establishing a connection, the set values will be sent with
271        * the initial presence stanza.
272        * If used while a connection already is established, a presence stanza will be
273        * sent out immediately.
274        * @param pres The Presence value to set.
275        * @param priority An optional priority value. Legal values: -128 <= priority <= 127
276        * @param status An optional message describing the presence state.
277        * @since 0.9
278        */
279       void setPresence( Presence::PresenceType pres, int priority,
280                         const std::string& status = EmptyString );
281 
282       /**
283        * Use this function to broadcast the entity's presence to all
284        * subscribed entities. This is a NOOP if there's no active connection.
285        * To send directed presence, use
286        * setPresence( const JID&, Presence::PresenceType, int, const std::string& ).
287        * If used while a connection already is established a repective presence stanza will be
288        * sent out immediately. Use presence() to modify the Presence object.
289        * @note When login is finished, initial presence will be sent automatically.
290        * So you do not need to call this function after login.
291        * @since 1.0
292        */
setPresence()293       void setPresence() { sendPresence( m_presence ); }
294 
295       /**
296        * Use this function to indicate a client state of 'active' to the Server.
297        * See @xep{0352} for more information.
298        * This function is a no-op if the server does not support Client State Indication (i.e. nothing is sent).
299        * Use hasClientStateIndication() to find out.
300        * @since 1.0.19
301        */
setActive()302       void setActive() { if( hasClientStateIndication() )
303                            send( new Tag( "active", XMLNS, XMLNS_CLIENT_STATE_INDICATION ) ); }
304 
305       /**
306        * Use this function to indicate a client state of 'inactive' to the Server.
307        * See @xep{0352} for more information.
308        * This function is a no-op if the server does not support Client State Indication (i.e. nothing is sent).
309        * Use hasClientStateIndication() to find out.
310        * @since 1.0.19
311        */
setInactive()312       void setInactive() { if( hasClientStateIndication() )
313                              send( new Tag( "inactive", XMLNS, XMLNS_CLIENT_STATE_INDICATION ) ); }
314 
315       /**
316        * Use this function to find out whether the server supports Client State Indication (@xep{0352}).
317        * @return @b True if the server supports @xep{0352}, @b false if not.
318        * XEP Version: 0.2
319        * @since 1.0.19
320        */
hasClientStateIndication()321       bool hasClientStateIndication() const {
322         return ((m_streamFeatures & StreamFeatureClientStateIndication) == StreamFeatureClientStateIndication); }
323 
324       /**
325        * Returns the current presence.
326        * @return The current presence.
327        */
presence()328       Presence& presence() { return m_presence; }
329 
330       /**
331        * This is a temporary hack to enforce Non-SASL login. You should not need to use it.
332        * @param force Whether to force non-SASL auth. Default @b true.
333        * @deprecated Please update the server to properly support SASL instead.
334        */
335       GLOOX_DEPRECATED void setForceNonSasl( bool force = true ) { m_forceNonSasl = force; }
336 
337       /**
338        * Disables the automatic roster management.
339        * You have to keep track of incoming presence yourself if
340        * you want to have a roster.
341        */
342       void disableRoster();
343 
344       /**
345        * This function gives access to the @c RosterManager object.
346        * @return A pointer to the RosterManager.
347        */
rosterManager()348       RosterManager* rosterManager() { return m_rosterManager; }
349 
350       /**
351        * Disconnects from the server.
352        */
353       void disconnect();
354 
355       /**
356        * Initiates a login attempt (currently SASL External not supported).
357        * This is useful after registering a new account. Simply use setUsername() and setPassword(),
358        * and call login().
359        * @return @b True if a login attempt could be started, @b false otherwise. A return
360        * value of @b true does not indicate that login was successful.
361        */
362       bool login();
363 
364     protected:
365       /**
366        * Initiates non-SASL login.
367        */
368       void nonSaslLogin();
369 
370     private:
371 #ifdef CLIENT_TEST
372     public:
373 #endif
374       /**
375        * @brief This is an implementation of a resource binding StanzaExtension.
376        *
377        * @author Jakob Schröter <js@camaya.net>
378        * @since 1.0
379        */
380       class ResourceBind : public StanzaExtension
381       {
382 
383         public:
384           /**
385            * Constructs a new object with the given resource string.
386            * @param resource The resource to set.
387            * @param bind Indicates whether this is an bind or unbind request.
388            * Defaults to @b true (bind).
389            */
390           ResourceBind( const std::string& resource, bool bind = true );
391 
392           /**
393            * Constructs a new object from the given Tag.
394            * @param tag The Tag to parse.
395            */
396           ResourceBind( const Tag* tag );
397 
398           /**
399            * Destructor.
400            */
401           ~ResourceBind();
402 
403           /**
404            * Returns the requested resource.
405            * @return The requested resource.
406            */
resource()407           const std::string& resource() const { return m_resource; }
408 
409           /**
410            * Returns the assigned JID.
411            * @return The assigned JID.
412            */
jid()413           const JID& jid() const { return m_jid; }
414 
415           /**
416            * Use this function to find out whether the extension contains a
417            * bind or unbind request.
418            * @return @b True if the extension contains an unbind request, @b false otherwise.
419            */
unbind()420           bool unbind() const { return !m_bind; }
421 
422           // reimplemented from StanzaExtension
423           virtual const std::string& filterString() const;
424 
425           // reimplemented from StanzaExtension
newInstance(const Tag * tag)426           virtual StanzaExtension* newInstance( const Tag* tag ) const
427           {
428             return new ResourceBind( tag );
429           }
430 
431           // reimplemented from StanzaExtension
432           virtual Tag* tag() const;
433 
434           // reimplemented from StanzaExtension
clone()435           virtual StanzaExtension* clone() const
436           {
437             return new ResourceBind( *this );
438           }
439 
440         private:
441           std::string m_resource;
442           JID m_jid;
443           bool m_bind;
444       };
445 
446       /**
447        * @brief This is an implementation of a session creating StanzaExtension.
448        *
449        * @author Jakob Schröter <js@camaya.net>
450        * @since 1.0
451        */
452       class SessionCreation : public StanzaExtension
453       {
454 
455         public:
456           /**
457            * Constructs a new object.
458            */
SessionCreation()459           SessionCreation() : StanzaExtension( ExtSessionCreation ) {}
460 
461           /**
462            * Destructor.
463            */
~SessionCreation()464           ~SessionCreation() {}
465 
466           // reimplemented from StanzaExtension
filterString()467           virtual const std::string& filterString() const { return EmptyString; }
468 
469           // reimplemented from StanzaExtension
newInstance(const Tag * tag)470           virtual StanzaExtension* newInstance( const Tag* tag ) const
471             { (void)tag; return 0; }
472 
473           // reimplemented from StanzaExtension
474           virtual Tag* tag() const;
475 
476           // reimplemented from StanzaExtension
clone()477           virtual StanzaExtension* clone() const
478             { return 0; }
479 
480       };
481 
handleStartNode(const Tag *)482       virtual void handleStartNode( const Tag* /*start*/ ) {}
483       virtual bool handleNormalNode( Tag* tag );
484       virtual void disconnect( ConnectionError reason );
485       virtual void handleIqIDForward( const IQ& iq, int context );
486 
487       int getStreamFeatures( Tag* tag );
488       int getSaslMechs( Tag* tag );
489       int getCompressionMethods( Tag* tag );
490       void processResourceBind( const IQ& iq );
491       void processCreateSession( const IQ& iq );
492       void sendPresence( Presence& pres );
493       void createSession();
494       void negotiateCompression( StreamFeature method );
495       void connected();
496       virtual void rosterFilled();
497       virtual void cleanup();
498       bool bindOperation( const std::string& resource, bool bind );
499       void sendStreamManagement();
500 
501       void init();
502 
503       enum TrackContext
504       {
505         CtxResourceBind = 1000,  // must be higher than the last element in ClientBase's TrackContext
506         CtxResourceUnbind,
507         CtxSessionEstablishment
508       };
509 
510       RosterManager* m_rosterManager;
511       NonSaslAuth* m_auth;
512 
513       Presence m_presence;
514 
515       bool m_forceNonSasl;
516       bool m_manageRoster;
517 
518       std::string m_smId;
519       std::string m_smLocation;
520       bool m_smResume;
521       bool m_smWanted;
522       int m_smMax;
523 
524       int m_streamFeatures;
525 
526   };
527 
528 }
529 
530 #endif // CLIENT_H__
531