QXmppClient.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. /*
  2. * Copyright (C) 2008-2012 The QXmpp developers
  3. *
  4. * Author:
  5. * Manjeet Dahiya
  6. *
  7. * Source:
  8. * http://code.google.com/p/qxmpp
  9. *
  10. * This file is a part of QXmpp library.
  11. *
  12. * This library is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU Lesser General Public
  14. * License as published by the Free Software Foundation; either
  15. * version 2.1 of the License, or (at your option) any later version.
  16. *
  17. * This library is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * Lesser General Public License for more details.
  21. *
  22. */
  23. #include <QSslSocket>
  24. #include <QTimer>
  25. #include "QXmppClient.h"
  26. #include "QXmppClientExtension.h"
  27. #include "QXmppConstants.h"
  28. #include "QXmppLogger.h"
  29. #include "QXmppOutgoingClient.h"
  30. #include "QXmppMessage.h"
  31. #include "QXmppUtils.h"
  32. #include "QXmppRosterManager.h"
  33. #include "QXmppVCardManager.h"
  34. #include "QXmppVersionManager.h"
  35. #include "QXmppEntityTimeManager.h"
  36. #include "QXmppDiscoveryManager.h"
  37. #include "QXmppDiscoveryIq.h"
  38. class QXmppClientPrivate
  39. {
  40. public:
  41. QXmppClientPrivate(QXmppClient *qq);
  42. QXmppPresence clientPresence; ///< Current presence of the client
  43. QList<QXmppClientExtension*> extensions;
  44. QXmppLogger *logger;
  45. QXmppOutgoingClient *stream; ///< Pointer to the XMPP stream
  46. // reconnection
  47. bool receivedConflict;
  48. int reconnectionTries;
  49. QTimer *reconnectionTimer;
  50. // managers
  51. QXmppRosterManager *rosterManager;
  52. QXmppVCardManager *vCardManager;
  53. QXmppVersionManager *versionManager;
  54. void addProperCapability(QXmppPresence& presence);
  55. int getNextReconnectTime() const;
  56. private:
  57. QXmppClient *q;
  58. };
  59. QXmppClientPrivate::QXmppClientPrivate(QXmppClient *qq)
  60. : clientPresence(QXmppPresence::Available)
  61. , logger(0)
  62. , stream(0)
  63. , receivedConflict(false)
  64. , reconnectionTries(0)
  65. , reconnectionTimer(0)
  66. , rosterManager(0)
  67. , vCardManager(0)
  68. , versionManager(0)
  69. , q(qq)
  70. {
  71. }
  72. void QXmppClientPrivate::addProperCapability(QXmppPresence& presence)
  73. {
  74. QXmppDiscoveryManager* ext = q->findExtension<QXmppDiscoveryManager>();
  75. if(ext) {
  76. presence.setCapabilityHash("sha-1");
  77. presence.setCapabilityNode(ext->clientCapabilitiesNode());
  78. presence.setCapabilityVer(ext->capabilities().verificationString());
  79. }
  80. }
  81. int QXmppClientPrivate::getNextReconnectTime() const
  82. {
  83. if (reconnectionTries < 5)
  84. return 10 * 1000;
  85. else if (reconnectionTries < 10)
  86. return 20 * 1000;
  87. else if (reconnectionTries < 15)
  88. return 40 * 1000;
  89. else
  90. return 60 * 1000;
  91. }
  92. /// \mainpage
  93. ///
  94. /// QXmpp is a cross-platform C++ XMPP client library based on the Qt
  95. /// framework. It tries to use Qt's programming conventions in order to ease
  96. /// the learning curve for new programmers.
  97. ///
  98. /// QXmpp based clients are built using QXmppClient instances which handle the
  99. /// establishment of the XMPP connection and provide a number of high-level
  100. /// "managers" to perform specific tasks. You can write your own managers to
  101. /// extend QXmpp by subclassing QXmppClientExtension.
  102. ///
  103. /// <B>Main Class:</B>
  104. /// - QXmppClient
  105. ///
  106. /// <B>Managers to perform specific tasks:</B>
  107. /// - QXmppRosterManager
  108. /// - QXmppVCardManager
  109. /// - QXmppTransferManager
  110. /// - QXmppMucManager
  111. /// - QXmppCallManager
  112. /// - QXmppArchiveManager
  113. /// - QXmppVersionManager
  114. /// - QXmppDiscoveryManager
  115. /// - QXmppEntityTimeManager
  116. ///
  117. /// <B>XMPP stanzas:</B> If you are interested in a more low-level API, you can refer to these
  118. /// classes.
  119. /// - QXmppIq
  120. /// - QXmppMessage
  121. /// - QXmppPresence
  122. ///
  123. /// <BR><BR>
  124. /// <B>Project Details:</B>
  125. ///
  126. /// Project Page: http://code.google.com/p/qxmpp/
  127. /// <BR>
  128. /// Report Issues: http://code.google.com/p/qxmpp/issues/
  129. /// <BR>
  130. /// New Releases: http://code.google.com/p/qxmpp/downloads/
  131. ///
  132. /// Creates a QXmppClient object.
  133. /// \param parent is passed to the QObject's constructor.
  134. /// The default value is 0.
  135. QXmppClient::QXmppClient(QObject *parent)
  136. : QXmppLoggable(parent),
  137. d(new QXmppClientPrivate(this))
  138. {
  139. bool check;
  140. Q_UNUSED(check);
  141. d->stream = new QXmppOutgoingClient(this);
  142. d->addProperCapability(d->clientPresence);
  143. check = connect(d->stream, SIGNAL(elementReceived(QDomElement,bool&)),
  144. this, SLOT(_q_elementReceived(QDomElement,bool&)));
  145. Q_ASSERT(check);
  146. check = connect(d->stream, SIGNAL(messageReceived(QXmppMessage)),
  147. this, SIGNAL(messageReceived(QXmppMessage)));
  148. Q_ASSERT(check);
  149. check = connect(d->stream, SIGNAL(presenceReceived(QXmppPresence)),
  150. this, SIGNAL(presenceReceived(QXmppPresence)));
  151. Q_ASSERT(check);
  152. check = connect(d->stream, SIGNAL(iqReceived(QXmppIq)),
  153. this, SIGNAL(iqReceived(QXmppIq)));
  154. Q_ASSERT(check);
  155. check = connect(d->stream->socket(), SIGNAL(stateChanged(QAbstractSocket::SocketState)),
  156. this, SLOT(_q_socketStateChanged(QAbstractSocket::SocketState)));
  157. Q_ASSERT(check);
  158. check = connect(d->stream, SIGNAL(connected()),
  159. this, SLOT(_q_streamConnected()));
  160. Q_ASSERT(check);
  161. check = connect(d->stream, SIGNAL(disconnected()),
  162. this, SLOT(_q_streamDisconnected()));
  163. Q_ASSERT(check);
  164. check = connect(d->stream, SIGNAL(error(QXmppClient::Error)),
  165. this, SLOT(_q_streamError(QXmppClient::Error)));
  166. Q_ASSERT(check);
  167. // reconnection
  168. d->reconnectionTimer = new QTimer(this);
  169. d->reconnectionTimer->setSingleShot(true);
  170. connect(d->reconnectionTimer, SIGNAL(timeout()),
  171. this, SLOT(_q_reconnect()));
  172. Q_ASSERT(check);
  173. // logging
  174. setLogger(QXmppLogger::getLogger());
  175. // create managers
  176. // TODO move manager references to d->extensions
  177. d->rosterManager = new QXmppRosterManager(this);
  178. addExtension(d->rosterManager);
  179. d->vCardManager = new QXmppVCardManager;
  180. addExtension(d->vCardManager);
  181. d->versionManager = new QXmppVersionManager;
  182. addExtension(d->versionManager);
  183. addExtension(new QXmppEntityTimeManager());
  184. addExtension(new QXmppDiscoveryManager());
  185. }
  186. /// Destructor, destroys the QXmppClient object.
  187. ///
  188. QXmppClient::~QXmppClient()
  189. {
  190. delete d;
  191. }
  192. /// Registers a new extension with the client.
  193. ///
  194. /// \param extension
  195. bool QXmppClient::addExtension(QXmppClientExtension* extension)
  196. {
  197. if (d->extensions.contains(extension))
  198. {
  199. qWarning("Cannot add extension, it has already been added");
  200. return false;
  201. }
  202. extension->setParent(this);
  203. extension->setClient(this);
  204. d->extensions << extension;
  205. return true;
  206. }
  207. /// Unregisters the given extension from the client. If the extension
  208. /// is found, it will be destroyed.
  209. ///
  210. /// \param extension
  211. bool QXmppClient::removeExtension(QXmppClientExtension* extension)
  212. {
  213. if (d->extensions.contains(extension))
  214. {
  215. d->extensions.removeAll(extension);
  216. delete extension;
  217. return true;
  218. } else {
  219. qWarning("Cannot remove extension, it was never added");
  220. return false;
  221. }
  222. }
  223. /// Returns a list containing all the client's extensions.
  224. ///
  225. QList<QXmppClientExtension*> QXmppClient::extensions()
  226. {
  227. return d->extensions;
  228. }
  229. /// Returns a modifiable reference to the current configuration of QXmppClient.
  230. /// \return Reference to the QXmppClient's configuration for the connection.
  231. QXmppConfiguration& QXmppClient::configuration()
  232. {
  233. return d->stream->configuration();
  234. }
  235. /// Attempts to connect to the XMPP server. Server details and other configurations
  236. /// are specified using the config parameter. Use signals connected(), error(QXmppClient::Error)
  237. /// and disconnected() to know the status of the connection.
  238. /// \param config Specifies the configuration object for connecting the XMPP server.
  239. /// This contains the host name, user, password etc. See QXmppConfiguration for details.
  240. /// \param initialPresence The initial presence which will be set for this user
  241. /// after establishing the session. The default value is QXmppPresence::Available
  242. void QXmppClient::connectToServer(const QXmppConfiguration& config,
  243. const QXmppPresence& initialPresence)
  244. {
  245. d->stream->configuration() = config;
  246. d->clientPresence = initialPresence;
  247. d->addProperCapability(d->clientPresence);
  248. d->stream->connectToHost();
  249. }
  250. /// Overloaded function to simply connect to an XMPP server with a JID and password.
  251. ///
  252. /// \param jid JID for the account.
  253. /// \param password Password for the account.
  254. void QXmppClient::connectToServer(const QString &jid, const QString &password)
  255. {
  256. QXmppConfiguration config;
  257. config.setJid(jid);
  258. config.setPassword(password);
  259. connectToServer(config);
  260. }
  261. /// After successfully connecting to the server use this function to send
  262. /// stanzas to the server. This function can solely be used to send various kind
  263. /// of stanzas to the server. QXmppStanza is a parent class of all the stanzas
  264. /// QXmppMessage, QXmppPresence, QXmppIq, QXmppBind, QXmppRosterIq, QXmppSession
  265. /// and QXmppVCard.
  266. ///
  267. /// \return Returns true if the packet was sent, false otherwise.
  268. ///
  269. /// Following code snippet illustrates how to send a message using this function:
  270. /// \code
  271. /// QXmppMessage message(from, to, message);
  272. /// client.sendPacket(message);
  273. /// \endcode
  274. ///
  275. /// \param packet A valid XMPP stanza. It can be an iq, a message or a presence stanza.
  276. ///
  277. bool QXmppClient::sendPacket(const QXmppStanza& packet)
  278. {
  279. return d->stream->sendPacket(packet);
  280. }
  281. /// Disconnects the client and the current presence of client changes to
  282. /// QXmppPresence::Unavailable and status text changes to "Logged out".
  283. ///
  284. /// \note Make sure that the clientPresence is changed to
  285. /// QXmppPresence::Available, if you are again calling connectToServer() after
  286. /// calling the disconnectFromServer() function.
  287. ///
  288. void QXmppClient::disconnectFromServer()
  289. {
  290. // cancel reconnection
  291. d->reconnectionTimer->stop();
  292. d->clientPresence.setType(QXmppPresence::Unavailable);
  293. d->clientPresence.setStatusText("Logged out");
  294. if (d->stream->isConnected())
  295. sendPacket(d->clientPresence);
  296. d->stream->disconnectFromHost();
  297. }
  298. /// Returns true if the client has authenticated with the XMPP server.
  299. bool QXmppClient::isAuthenticated() const
  300. {
  301. return d->stream->isAuthenticated();
  302. }
  303. /// Returns true if the client is connected to the XMPP server.
  304. ///
  305. bool QXmppClient::isConnected() const
  306. {
  307. return d->stream->isConnected();
  308. }
  309. /// Returns the reference to QXmppRosterManager object of the client.
  310. /// \return Reference to the roster object of the connected client. Use this to
  311. /// get the list of friends in the roster and their presence information.
  312. ///
  313. QXmppRosterManager& QXmppClient::rosterManager()
  314. {
  315. return *d->rosterManager;
  316. }
  317. /// Utility function to send message to all the resources associated with the
  318. /// specified bareJid. If there are no resources available, that is the contact
  319. /// is offline or not present in the roster, it will still send a message to
  320. /// the bareJid.
  321. ///
  322. /// \param bareJid bareJid of the receiving entity
  323. /// \param message Message string to be sent.
  324. void QXmppClient::sendMessage(const QString& bareJid, const QString& message)
  325. {
  326. QStringList resources = rosterManager().getResources(bareJid);
  327. if(!resources.isEmpty())
  328. {
  329. for(int i = 0; i < resources.size(); ++i)
  330. {
  331. sendPacket(QXmppMessage("", bareJid + "/" + resources.at(i), message));
  332. }
  333. }
  334. else
  335. {
  336. sendPacket(QXmppMessage("", bareJid, message));
  337. }
  338. }
  339. /// Returns the client's current state.
  340. QXmppClient::State QXmppClient::state() const
  341. {
  342. if (d->stream->isConnected())
  343. return QXmppClient::ConnectedState;
  344. else if (d->stream->socket()->state() != QAbstractSocket::UnconnectedState &&
  345. d->stream->socket()->state() != QAbstractSocket::ClosingState)
  346. return QXmppClient::ConnectingState;
  347. else
  348. return QXmppClient::DisconnectedState;
  349. }
  350. /// Returns the client's current presence.
  351. ///
  352. QXmppPresence QXmppClient::clientPresence() const
  353. {
  354. return d->clientPresence;
  355. }
  356. /// Changes the presence of the connected client.
  357. ///
  358. /// The connection to the server will be updated accordingly:
  359. ///
  360. /// \li If the presence type is QXmppPresence::Unavailable, the connection
  361. /// to the server will be closed.
  362. ///
  363. /// \li Otherwise, the connection to the server will be established
  364. /// as needed.
  365. ///
  366. /// \param presence QXmppPresence object
  367. ///
  368. void QXmppClient::setClientPresence(const QXmppPresence& presence)
  369. {
  370. d->clientPresence = presence;
  371. d->addProperCapability(d->clientPresence);
  372. if (presence.type() == QXmppPresence::Unavailable)
  373. {
  374. // cancel reconnection
  375. d->reconnectionTimer->stop();
  376. // NOTE: we can't call disconnect() because it alters
  377. // the client presence
  378. if (d->stream->isConnected())
  379. sendPacket(d->clientPresence);
  380. d->stream->disconnectFromHost();
  381. }
  382. else if (d->stream->isConnected())
  383. sendPacket(d->clientPresence);
  384. else
  385. connectToServer(d->stream->configuration(), presence);
  386. }
  387. /// Returns the socket error if error() is QXmppClient::SocketError.
  388. ///
  389. QAbstractSocket::SocketError QXmppClient::socketError()
  390. {
  391. return d->stream->socket()->error();
  392. }
  393. /// Returns the XMPP stream error if QXmppClient::Error is QXmppClient::XmppStreamError.
  394. ///
  395. QXmppStanza::Error::Condition QXmppClient::xmppStreamError()
  396. {
  397. return d->stream->xmppStreamError();
  398. }
  399. /// Returns the reference to QXmppVCardManager, implementation of XEP-0054.
  400. /// http://xmpp.org/extensions/xep-0054.html
  401. ///
  402. QXmppVCardManager& QXmppClient::vCardManager()
  403. {
  404. return *d->vCardManager;
  405. }
  406. /// Returns the reference to QXmppVersionManager, implementation of XEP-0092.
  407. /// http://xmpp.org/extensions/xep-0092.html
  408. ///
  409. QXmppVersionManager& QXmppClient::versionManager()
  410. {
  411. return *d->versionManager;
  412. }
  413. /// Give extensions a chance to handle incoming stanzas.
  414. ///
  415. /// \param element
  416. /// \param handled
  417. void QXmppClient::_q_elementReceived(const QDomElement &element, bool &handled)
  418. {
  419. foreach (QXmppClientExtension *extension, d->extensions)
  420. {
  421. if (extension->handleStanza(element))
  422. {
  423. handled = true;
  424. return;
  425. }
  426. }
  427. }
  428. void QXmppClient::_q_reconnect()
  429. {
  430. if (d->stream->configuration().autoReconnectionEnabled()) {
  431. debug("Reconnecting to server");
  432. d->stream->connectToHost();
  433. }
  434. }
  435. void QXmppClient::_q_socketStateChanged(QAbstractSocket::SocketState socketState)
  436. {
  437. Q_UNUSED(socketState);
  438. emit stateChanged(state());
  439. }
  440. /// At connection establishment, send initial presence.
  441. void QXmppClient::_q_streamConnected()
  442. {
  443. d->receivedConflict = false;
  444. d->reconnectionTries = 0;
  445. // notify managers
  446. emit connected();
  447. emit stateChanged(QXmppClient::ConnectedState);
  448. // send initial presence
  449. if (d->stream->isAuthenticated())
  450. sendPacket(d->clientPresence);
  451. }
  452. void QXmppClient::_q_streamDisconnected()
  453. {
  454. // notify managers
  455. emit disconnected();
  456. emit stateChanged(QXmppClient::DisconnectedState);
  457. }
  458. void QXmppClient::_q_streamError(QXmppClient::Error err)
  459. {
  460. if (d->stream->configuration().autoReconnectionEnabled()) {
  461. if (err == QXmppClient::XmppStreamError) {
  462. // if we receive a resource conflict, inhibit reconnection
  463. if (d->stream->xmppStreamError() == QXmppStanza::Error::Conflict)
  464. d->receivedConflict = true;
  465. } else if (err == QXmppClient::SocketError && !d->receivedConflict) {
  466. // schedule reconnect
  467. d->reconnectionTimer->start(d->getNextReconnectTime());
  468. } else if (err == QXmppClient::KeepAliveError) {
  469. // if we got a keepalive error, reconnect in one second
  470. d->reconnectionTimer->start(1000);
  471. }
  472. }
  473. // notify managers
  474. emit error(err);
  475. }
  476. /// Returns the QXmppLogger associated with the current QXmppClient.
  477. QXmppLogger *QXmppClient::logger() const
  478. {
  479. return d->logger;
  480. }
  481. /// Sets the QXmppLogger associated with the current QXmppClient.
  482. void QXmppClient::setLogger(QXmppLogger *logger)
  483. {
  484. if (logger != d->logger) {
  485. if (d->logger) {
  486. disconnect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
  487. d->logger, SLOT(log(QXmppLogger::MessageType,QString)));
  488. disconnect(this, SIGNAL(setGauge(QString,double)),
  489. d->logger, SLOT(setGauge(QString,double)));
  490. disconnect(this, SIGNAL(updateCounter(QString,qint64)),
  491. d->logger, SLOT(updateCounter(QString,qint64)));
  492. }
  493. d->logger = logger;
  494. if (d->logger) {
  495. connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
  496. d->logger, SLOT(log(QXmppLogger::MessageType,QString)));
  497. connect(this, SIGNAL(setGauge(QString,double)),
  498. d->logger, SLOT(setGauge(QString,double)));
  499. connect(this, SIGNAL(updateCounter(QString,qint64)),
  500. d->logger, SLOT(updateCounter(QString,qint64)));
  501. }
  502. emit loggerChanged(d->logger);
  503. }
  504. }