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