QXmppOutgoingClient.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. /*
  2. * Copyright (C) 2008-2012 The QXmpp developers
  3. *
  4. * Authors:
  5. * Manjeet Dahiya
  6. * Jeremy Lainé
  7. *
  8. * Source:
  9. * http://code.google.com/p/qxmpp
  10. *
  11. * This file is a part of QXmpp library.
  12. *
  13. * This library is free software; you can redistribute it and/or
  14. * modify it under the terms of the GNU Lesser General Public
  15. * License as published by the Free Software Foundation; either
  16. * version 2.1 of the License, or (at your option) any later version.
  17. *
  18. * This library is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  21. * Lesser General Public License for more details.
  22. *
  23. */
  24. #include <QCryptographicHash>
  25. #include <QNetworkProxy>
  26. #include <QSslSocket>
  27. #include <QUrl>
  28. #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
  29. #include <QDnsLookup>
  30. #else
  31. #include "qdnslookup.h"
  32. #endif
  33. #include "QXmppConfiguration.h"
  34. #include "QXmppConstants.h"
  35. #include "QXmppIq.h"
  36. #include "QXmppLogger.h"
  37. #include "QXmppMessage.h"
  38. #include "QXmppPresence.h"
  39. #include "QXmppOutgoingClient.h"
  40. #include "QXmppStreamFeatures.h"
  41. #include "QXmppNonSASLAuth.h"
  42. #include "QXmppSasl_p.h"
  43. #include "QXmppUtils.h"
  44. // IQ types
  45. #include "QXmppBindIq.h"
  46. #include "QXmppPingIq.h"
  47. #include "QXmppSessionIq.h"
  48. #include <QBuffer>
  49. #include <QCoreApplication>
  50. #include <QDomDocument>
  51. #include <QStringList>
  52. #include <QRegExp>
  53. #include <QHostAddress>
  54. #include <QXmlStreamWriter>
  55. #include <QTimer>
  56. class QXmppOutgoingClientPrivate
  57. {
  58. public:
  59. QXmppOutgoingClientPrivate(QXmppOutgoingClient *q);
  60. void connectToHost(const QString &host, quint16 port);
  61. // This object provides the configuration
  62. // required for connecting to the XMPP server.
  63. QXmppConfiguration config;
  64. QXmppStanza::Error::Condition xmppStreamError;
  65. // DNS
  66. QDnsLookup dns;
  67. // Stream
  68. QString streamId;
  69. QString streamFrom;
  70. QString streamVersion;
  71. // Redirection
  72. QString redirectHost;
  73. quint16 redirectPort;
  74. // Session
  75. QString bindId;
  76. QString sessionId;
  77. bool sessionAvailable;
  78. bool sessionStarted;
  79. // Authentication
  80. bool isAuthenticated;
  81. QString nonSASLAuthId;
  82. QXmppSaslClient *saslClient;
  83. // Timers
  84. QTimer *pingTimer;
  85. QTimer *timeoutTimer;
  86. private:
  87. QXmppOutgoingClient *q;
  88. };
  89. QXmppOutgoingClientPrivate::QXmppOutgoingClientPrivate(QXmppOutgoingClient *qq)
  90. : redirectPort(0)
  91. , sessionAvailable(false)
  92. , isAuthenticated(false)
  93. , saslClient(0)
  94. , q(qq)
  95. {
  96. }
  97. void QXmppOutgoingClientPrivate::connectToHost(const QString &host, quint16 port)
  98. {
  99. q->info(QString("Connecting to %1:%2").arg(host, QString::number(port)));
  100. // override CA certificates if requested
  101. if (!config.caCertificates().isEmpty())
  102. q->socket()->setCaCertificates(config.caCertificates());
  103. // respect proxy
  104. q->socket()->setProxy(config.networkProxy());
  105. #if (QT_VERSION >= QT_VERSION_CHECK(4, 8, 0))
  106. // set the name the SSL certificate should match
  107. q->socket()->setPeerVerifyName(config.domain());
  108. #endif
  109. // connect to host
  110. q->socket()->connectToHost(host, port);
  111. }
  112. /// Constructs an outgoing client stream.
  113. ///
  114. /// \param parent
  115. QXmppOutgoingClient::QXmppOutgoingClient(QObject *parent)
  116. : QXmppStream(parent),
  117. d(new QXmppOutgoingClientPrivate(this))
  118. {
  119. bool check;
  120. Q_UNUSED(check);
  121. // initialise socket
  122. QSslSocket *socket = new QSslSocket(this);
  123. setSocket(socket);
  124. check = connect(socket, SIGNAL(disconnected()),
  125. this, SLOT(_q_socketDisconnected()));
  126. Q_ASSERT(check);
  127. check = connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
  128. this, SLOT(socketSslErrors(QList<QSslError>)));
  129. Q_ASSERT(check);
  130. check = connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
  131. this, SLOT(socketError(QAbstractSocket::SocketError)));
  132. Q_ASSERT(check);
  133. // DNS lookups
  134. check = connect(&d->dns, SIGNAL(finished()),
  135. this, SLOT(_q_dnsLookupFinished()));
  136. Q_ASSERT(check);
  137. // XEP-0199: XMPP Ping
  138. d->pingTimer = new QTimer(this);
  139. check = connect(d->pingTimer, SIGNAL(timeout()),
  140. this, SLOT(pingSend()));
  141. Q_ASSERT(check);
  142. d->timeoutTimer = new QTimer(this);
  143. d->timeoutTimer->setSingleShot(true);
  144. check = connect(d->timeoutTimer, SIGNAL(timeout()),
  145. this, SLOT(pingTimeout()));
  146. Q_ASSERT(check);
  147. check = connect(this, SIGNAL(connected()),
  148. this, SLOT(pingStart()));
  149. Q_ASSERT(check);
  150. check = connect(this, SIGNAL(disconnected()),
  151. this, SLOT(pingStop()));
  152. Q_ASSERT(check);
  153. }
  154. /// Destroys an outgoing client stream.
  155. QXmppOutgoingClient::~QXmppOutgoingClient()
  156. {
  157. delete d;
  158. }
  159. /// Returns a reference to the stream's configuration.
  160. QXmppConfiguration& QXmppOutgoingClient::configuration()
  161. {
  162. return d->config;
  163. }
  164. /// Attempts to connect to the XMPP server.
  165. void QXmppOutgoingClient::connectToHost()
  166. {
  167. // if an explicit host was provided, connect to it
  168. if (!d->config.host().isEmpty() && d->config.port()) {
  169. d->connectToHost(d->config.host(), d->config.port());
  170. return;
  171. }
  172. // otherwise, lookup server
  173. const QString domain = configuration().domain();
  174. debug(QString("Looking up server for domain %1").arg(domain));
  175. d->dns.setName("_xmpp-client._tcp." + domain);
  176. d->dns.setType(QDnsLookup::SRV);
  177. d->dns.lookup();
  178. }
  179. void QXmppOutgoingClient::_q_dnsLookupFinished()
  180. {
  181. if (d->dns.error() == QDnsLookup::NoError &&
  182. !d->dns.serviceRecords().isEmpty()) {
  183. // take the first returned record
  184. d->connectToHost(
  185. d->dns.serviceRecords().first().target(),
  186. d->dns.serviceRecords().first().port());
  187. } else {
  188. // as a fallback, use domain as the host name
  189. warning(QString("Lookup for domain %1 failed: %2")
  190. .arg(d->dns.name(), d->dns.errorString()));
  191. d->connectToHost(d->config.domain(), d->config.port());
  192. }
  193. }
  194. /// Returns true if authentication has succeeded.
  195. bool QXmppOutgoingClient::isAuthenticated() const
  196. {
  197. return d->isAuthenticated;
  198. }
  199. /// Returns true if the socket is connected and a session has been started.
  200. bool QXmppOutgoingClient::isConnected() const
  201. {
  202. return QXmppStream::isConnected() && d->sessionStarted;
  203. }
  204. void QXmppOutgoingClient::_q_socketDisconnected()
  205. {
  206. debug("Socket disconnected");
  207. d->isAuthenticated = false;
  208. if (!d->redirectHost.isEmpty() && d->redirectPort > 0) {
  209. d->connectToHost(d->redirectHost, d->redirectPort);
  210. d->redirectHost = QString();
  211. d->redirectPort = 0;
  212. } else {
  213. emit disconnected();
  214. }
  215. }
  216. void QXmppOutgoingClient::socketSslErrors(const QList<QSslError> & error)
  217. {
  218. warning("SSL errors");
  219. for(int i = 0; i< error.count(); ++i)
  220. warning(error.at(i).errorString());
  221. if (configuration().ignoreSslErrors())
  222. socket()->ignoreSslErrors();
  223. }
  224. void QXmppOutgoingClient::socketError(QAbstractSocket::SocketError socketError)
  225. {
  226. Q_UNUSED(socketError);
  227. emit error(QXmppClient::SocketError);
  228. }
  229. /// \cond
  230. void QXmppOutgoingClient::handleStart()
  231. {
  232. QXmppStream::handleStart();
  233. // reset stream information
  234. d->streamId.clear();
  235. d->streamFrom.clear();
  236. d->streamVersion.clear();
  237. // reset authentication step
  238. if (d->saslClient) {
  239. delete d->saslClient;
  240. d->saslClient = 0;
  241. }
  242. // reset session information
  243. d->bindId.clear();
  244. d->sessionId.clear();
  245. d->sessionAvailable = false;
  246. d->sessionStarted = false;
  247. // start stream
  248. QByteArray data = "<?xml version='1.0'?><stream:stream to='";
  249. data.append(configuration().domain().toUtf8());
  250. data.append("' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>");
  251. sendData(data);
  252. }
  253. void QXmppOutgoingClient::handleStream(const QDomElement &streamElement)
  254. {
  255. if(d->streamId.isEmpty())
  256. d->streamId = streamElement.attribute("id");
  257. if (d->streamFrom.isEmpty())
  258. d->streamFrom = streamElement.attribute("from");
  259. if(d->streamVersion.isEmpty())
  260. {
  261. d->streamVersion = streamElement.attribute("version");
  262. // no version specified, signals XMPP Version < 1.0.
  263. // switch to old auth mechanism if enabled
  264. if(d->streamVersion.isEmpty() && configuration().useNonSASLAuthentication()) {
  265. sendNonSASLAuthQuery();
  266. }
  267. }
  268. }
  269. void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv)
  270. {
  271. // if we receive any kind of data, stop the timeout timer
  272. d->timeoutTimer->stop();
  273. const QString ns = nodeRecv.namespaceURI();
  274. // give client opportunity to handle stanza
  275. bool handled = false;
  276. emit elementReceived(nodeRecv, handled);
  277. if (handled)
  278. return;
  279. if(QXmppStreamFeatures::isStreamFeatures(nodeRecv))
  280. {
  281. QXmppStreamFeatures features;
  282. features.parse(nodeRecv);
  283. if (!socket()->isEncrypted())
  284. {
  285. // determine TLS mode to use
  286. const QXmppConfiguration::StreamSecurityMode localSecurity = configuration().streamSecurityMode();
  287. const QXmppStreamFeatures::Mode remoteSecurity = features.tlsMode();
  288. if (!socket()->supportsSsl() &&
  289. (localSecurity == QXmppConfiguration::TLSRequired ||
  290. remoteSecurity == QXmppStreamFeatures::Required))
  291. {
  292. warning("Disconnecting as TLS is required, but SSL support is not available");
  293. disconnectFromHost();
  294. return;
  295. }
  296. if (localSecurity == QXmppConfiguration::TLSRequired &&
  297. remoteSecurity == QXmppStreamFeatures::Disabled)
  298. {
  299. warning("Disconnecting as TLS is required, but not supported by the server");
  300. disconnectFromHost();
  301. return;
  302. }
  303. if (socket()->supportsSsl() &&
  304. localSecurity != QXmppConfiguration::TLSDisabled &&
  305. remoteSecurity != QXmppStreamFeatures::Disabled)
  306. {
  307. // enable TLS as it is support by both parties
  308. sendData("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
  309. return;
  310. }
  311. }
  312. // handle authentication
  313. const bool nonSaslAvailable = features.nonSaslAuthMode() != QXmppStreamFeatures::Disabled;
  314. const bool saslAvailable = !features.authMechanisms().isEmpty();
  315. if (saslAvailable && configuration().useSASLAuthentication())
  316. {
  317. // supported and preferred SASL auth mechanisms
  318. QStringList supportedMechanisms = QXmppSaslClient::availableMechanisms();
  319. const QString preferredMechanism = configuration().saslAuthMechanism();
  320. // determine SASL Authentication mechanism to use
  321. QStringList commonMechanisms;
  322. QString usedMechanism;
  323. foreach (const QString &mechanism, features.authMechanisms()) {
  324. if (supportedMechanisms.contains(mechanism))
  325. commonMechanisms << mechanism;
  326. }
  327. if (commonMechanisms.isEmpty()) {
  328. warning("No supported SASL Authentication mechanism available");
  329. disconnectFromHost();
  330. return;
  331. } else if (!commonMechanisms.contains(preferredMechanism)) {
  332. info(QString("Desired SASL Auth mechanism '%1' is not available, selecting first available one").arg(preferredMechanism));
  333. usedMechanism = commonMechanisms.first();
  334. } else {
  335. usedMechanism = preferredMechanism;
  336. }
  337. d->saslClient = QXmppSaslClient::create(usedMechanism, this);
  338. if (!d->saslClient) {
  339. warning("SASL mechanism negotiation failed");
  340. disconnectFromHost();
  341. return;
  342. }
  343. info(QString("SASL mechanism '%1' selected").arg(d->saslClient->mechanism()));
  344. d->saslClient->setHost(d->config.domain());
  345. d->saslClient->setServiceType("xmpp");
  346. if (d->saslClient->mechanism() == "X-FACEBOOK-PLATFORM") {
  347. d->saslClient->setUsername(configuration().facebookAppId());
  348. d->saslClient->setPassword(configuration().facebookAccessToken());
  349. } else if (d->saslClient->mechanism() == "X-MESSENGER-OAUTH2") {
  350. d->saslClient->setPassword(configuration().windowsLiveAccessToken());
  351. } else if (d->saslClient->mechanism() == "X-OAUTH2") {
  352. d->saslClient->setUsername(configuration().user());
  353. d->saslClient->setPassword(configuration().googleAccessToken());
  354. } else {
  355. d->saslClient->setUsername(configuration().user());
  356. d->saslClient->setPassword(configuration().password());
  357. }
  358. // send SASL auth request
  359. QByteArray response;
  360. if (!d->saslClient->respond(QByteArray(), response)) {
  361. warning("SASL initial response failed");
  362. disconnectFromHost();
  363. return;
  364. }
  365. sendPacket(QXmppSaslAuth(d->saslClient->mechanism(), response));
  366. return;
  367. } else if(nonSaslAvailable && configuration().useNonSASLAuthentication()) {
  368. sendNonSASLAuthQuery();
  369. return;
  370. }
  371. // store whether session is available
  372. d->sessionAvailable = (features.sessionMode() != QXmppStreamFeatures::Disabled);
  373. // check whether bind is available
  374. if (features.bindMode() != QXmppStreamFeatures::Disabled)
  375. {
  376. QXmppBindIq bind;
  377. bind.setType(QXmppIq::Set);
  378. bind.setResource(configuration().resource());
  379. d->bindId = bind.id();
  380. sendPacket(bind);
  381. return;
  382. }
  383. // check whether session is available
  384. if (d->sessionAvailable)
  385. {
  386. // start session if it is available
  387. QXmppSessionIq session;
  388. session.setType(QXmppIq::Set);
  389. session.setTo(configuration().domain());
  390. d->sessionId = session.id();
  391. sendPacket(session);
  392. } else {
  393. // otherwise we are done
  394. d->sessionStarted = true;
  395. emit connected();
  396. }
  397. }
  398. else if(ns == ns_stream && nodeRecv.tagName() == "error")
  399. {
  400. // handle redirects
  401. QRegExp redirectRegex("([^:]+)(:[0-9]+)?");
  402. if (redirectRegex.exactMatch(nodeRecv.firstChildElement("see-other-host").text())) {
  403. d->redirectHost = redirectRegex.cap(0);
  404. if (!redirectRegex.cap(2).isEmpty())
  405. d->redirectPort = redirectRegex.cap(2).mid(1).toUShort();
  406. else
  407. d->redirectPort = 5222;
  408. disconnectFromHost();
  409. return;
  410. }
  411. if (!nodeRecv.firstChildElement("conflict").isNull())
  412. d->xmppStreamError = QXmppStanza::Error::Conflict;
  413. else
  414. d->xmppStreamError = QXmppStanza::Error::UndefinedCondition;
  415. emit error(QXmppClient::XmppStreamError);
  416. }
  417. else if(ns == ns_tls)
  418. {
  419. if(nodeRecv.tagName() == "proceed")
  420. {
  421. debug("Starting encryption");
  422. socket()->startClientEncryption();
  423. return;
  424. }
  425. }
  426. else if(ns == ns_sasl)
  427. {
  428. if (!d->saslClient) {
  429. warning("SASL stanza received, but no mechanism selected");
  430. return;
  431. }
  432. if(nodeRecv.tagName() == "success")
  433. {
  434. debug("Authenticated");
  435. d->isAuthenticated = true;
  436. handleStart();
  437. }
  438. else if(nodeRecv.tagName() == "challenge")
  439. {
  440. QXmppSaslChallenge challenge;
  441. challenge.parse(nodeRecv);
  442. QByteArray response;
  443. if (d->saslClient->respond(challenge.value(), response)) {
  444. sendPacket(QXmppSaslResponse(response));
  445. } else {
  446. warning("Could not respond to SASL challenge");
  447. disconnectFromHost();
  448. }
  449. }
  450. else if(nodeRecv.tagName() == "failure")
  451. {
  452. QXmppSaslFailure failure;
  453. failure.parse(nodeRecv);
  454. if (failure.condition() == "not-authorized")
  455. d->xmppStreamError = QXmppStanza::Error::NotAuthorized;
  456. else
  457. d->xmppStreamError = QXmppStanza::Error::UndefinedCondition;
  458. emit error(QXmppClient::XmppStreamError);
  459. warning("Authentication failure");
  460. disconnectFromHost();
  461. }
  462. }
  463. else if(ns == ns_client)
  464. {
  465. if(nodeRecv.tagName() == "iq")
  466. {
  467. QDomElement element = nodeRecv.firstChildElement();
  468. QString id = nodeRecv.attribute("id");
  469. QString type = nodeRecv.attribute("type");
  470. if(type.isEmpty())
  471. warning("QXmppStream: iq type can't be empty");
  472. if(id == d->sessionId)
  473. {
  474. QXmppSessionIq session;
  475. session.parse(nodeRecv);
  476. // xmpp connection made
  477. d->sessionStarted = true;
  478. emit connected();
  479. }
  480. else if(QXmppBindIq::isBindIq(nodeRecv) && id == d->bindId)
  481. {
  482. QXmppBindIq bind;
  483. bind.parse(nodeRecv);
  484. // bind result
  485. if (bind.type() == QXmppIq::Result)
  486. {
  487. if (!bind.jid().isEmpty())
  488. {
  489. QRegExp jidRegex("^([^@/]+)@([^@/]+)/(.+)$");
  490. if (jidRegex.exactMatch(bind.jid()))
  491. {
  492. configuration().setUser(jidRegex.cap(1));
  493. configuration().setDomain(jidRegex.cap(2));
  494. configuration().setResource(jidRegex.cap(3));
  495. } else {
  496. warning("Bind IQ received with invalid JID: " + bind.jid());
  497. }
  498. }
  499. if (d->sessionAvailable)
  500. {
  501. // start session if it is available
  502. QXmppSessionIq session;
  503. session.setType(QXmppIq::Set);
  504. session.setTo(configuration().domain());
  505. d->sessionId = session.id();
  506. sendPacket(session);
  507. } else {
  508. // otherwise we are done
  509. d->sessionStarted = true;
  510. emit connected();
  511. }
  512. }
  513. }
  514. // extensions
  515. // XEP-0078: Non-SASL Authentication
  516. else if(id == d->nonSASLAuthId && type == "result")
  517. {
  518. // successful Non-SASL Authentication
  519. debug("Authenticated (Non-SASL)");
  520. d->isAuthenticated = true;
  521. // xmpp connection made
  522. d->sessionStarted = true;
  523. emit connected();
  524. }
  525. else if(QXmppNonSASLAuthIq::isNonSASLAuthIq(nodeRecv))
  526. {
  527. if(type == "result")
  528. {
  529. bool digest = !nodeRecv.firstChildElement("query").
  530. firstChildElement("digest").isNull();
  531. bool plain = !nodeRecv.firstChildElement("query").
  532. firstChildElement("password").isNull();
  533. bool plainText = false;
  534. if(plain && digest)
  535. {
  536. if(configuration().nonSASLAuthMechanism() ==
  537. QXmppConfiguration::NonSASLDigest)
  538. plainText = false;
  539. else
  540. plainText = true;
  541. }
  542. else if(plain)
  543. plainText = true;
  544. else if(digest)
  545. plainText = false;
  546. else
  547. {
  548. warning("No supported Non-SASL Authentication mechanism available");
  549. disconnectFromHost();
  550. return;
  551. }
  552. sendNonSASLAuth(plainText);
  553. }
  554. }
  555. // XEP-0199: XMPP Ping
  556. else if(QXmppPingIq::isPingIq(nodeRecv))
  557. {
  558. QXmppPingIq req;
  559. req.parse(nodeRecv);
  560. QXmppIq iq(QXmppIq::Result);
  561. iq.setId(req.id());
  562. iq.setTo(req.from());
  563. sendPacket(iq);
  564. }
  565. else
  566. {
  567. QXmppIq iqPacket;
  568. iqPacket.parse(nodeRecv);
  569. // if we didn't understant the iq, reply with error
  570. // except for "result" and "error" iqs
  571. if (type != "result" && type != "error")
  572. {
  573. QXmppIq iq(QXmppIq::Error);
  574. iq.setId(iqPacket.id());
  575. iq.setTo(iqPacket.from());
  576. QXmppStanza::Error error(QXmppStanza::Error::Cancel,
  577. QXmppStanza::Error::FeatureNotImplemented);
  578. iq.setError(error);
  579. sendPacket(iq);
  580. } else {
  581. emit iqReceived(iqPacket);
  582. }
  583. }
  584. }
  585. else if(nodeRecv.tagName() == "presence")
  586. {
  587. QXmppPresence presence;
  588. presence.parse(nodeRecv);
  589. // emit presence
  590. emit presenceReceived(presence);
  591. }
  592. else if(nodeRecv.tagName() == "message")
  593. {
  594. QXmppMessage message;
  595. message.parse(nodeRecv);
  596. // emit message
  597. emit messageReceived(message);
  598. }
  599. }
  600. }
  601. /// \endcond
  602. void QXmppOutgoingClient::pingStart()
  603. {
  604. const int interval = configuration().keepAliveInterval();
  605. // start ping timer
  606. if (interval > 0)
  607. {
  608. d->pingTimer->setInterval(interval * 1000);
  609. d->pingTimer->start();
  610. }
  611. }
  612. void QXmppOutgoingClient::pingStop()
  613. {
  614. // stop all timers
  615. d->pingTimer->stop();
  616. d->timeoutTimer->stop();
  617. }
  618. void QXmppOutgoingClient::pingSend()
  619. {
  620. // send ping packet
  621. QXmppPingIq ping;
  622. ping.setTo(configuration().domain());
  623. sendPacket(ping);
  624. // start timeout timer
  625. const int timeout = configuration().keepAliveTimeout();
  626. if (timeout > 0)
  627. {
  628. d->timeoutTimer->setInterval(timeout * 1000);
  629. d->timeoutTimer->start();
  630. }
  631. }
  632. void QXmppOutgoingClient::pingTimeout()
  633. {
  634. warning("Ping timeout");
  635. disconnectFromHost();
  636. emit error(QXmppClient::KeepAliveError);
  637. }
  638. void QXmppOutgoingClient::sendNonSASLAuth(bool plainText)
  639. {
  640. QXmppNonSASLAuthIq authQuery;
  641. authQuery.setType(QXmppIq::Set);
  642. authQuery.setUsername(configuration().user());
  643. if (plainText)
  644. authQuery.setPassword(configuration().password());
  645. else
  646. authQuery.setDigest(d->streamId, configuration().password());
  647. authQuery.setResource(configuration().resource());
  648. d->nonSASLAuthId = authQuery.id();
  649. sendPacket(authQuery);
  650. }
  651. void QXmppOutgoingClient::sendNonSASLAuthQuery()
  652. {
  653. QXmppNonSASLAuthIq authQuery;
  654. authQuery.setType(QXmppIq::Get);
  655. authQuery.setTo(d->streamFrom);
  656. // FIXME : why are we setting the username, XEP-0078 states we should
  657. // not attempt to guess the required fields?
  658. authQuery.setUsername(configuration().user());
  659. sendPacket(authQuery);
  660. }
  661. /// Returns the type of the last XMPP stream error that occured.
  662. QXmppStanza::Error::Condition QXmppOutgoingClient::xmppStreamError()
  663. {
  664. return d->xmppStreamError;
  665. }