QXmppStream.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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 "QXmppConstants.h"
  25. #include "QXmppLogger.h"
  26. #include "QXmppStanza.h"
  27. #include "QXmppStream.h"
  28. #include "QXmppUtils.h"
  29. #include <QBuffer>
  30. #include <QDomDocument>
  31. #include <QHostAddress>
  32. #include <QRegExp>
  33. #include <QSslSocket>
  34. #include <QStringList>
  35. #include <QTime>
  36. #include <QXmlStreamWriter>
  37. static bool randomSeeded = false;
  38. static const QByteArray streamRootElementEnd = "</stream:stream>";
  39. class QXmppStreamPrivate
  40. {
  41. public:
  42. QXmppStreamPrivate();
  43. QByteArray dataBuffer;
  44. QSslSocket* socket;
  45. // stream state
  46. QByteArray streamStart;
  47. };
  48. QXmppStreamPrivate::QXmppStreamPrivate()
  49. : socket(0)
  50. {
  51. }
  52. /// Constructs a base XMPP stream.
  53. ///
  54. /// \param parent
  55. QXmppStream::QXmppStream(QObject *parent)
  56. : QXmppLoggable(parent),
  57. d(new QXmppStreamPrivate)
  58. {
  59. // Make sure the random number generator is seeded
  60. if (!randomSeeded)
  61. {
  62. qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(this));
  63. randomSeeded = true;
  64. }
  65. }
  66. /// Destroys a base XMPP stream.
  67. QXmppStream::~QXmppStream()
  68. {
  69. delete d;
  70. }
  71. /// Disconnects from the remote host.
  72. ///
  73. void QXmppStream::disconnectFromHost()
  74. {
  75. sendData(streamRootElementEnd);
  76. if (d->socket)
  77. {
  78. d->socket->flush();
  79. d->socket->disconnectFromHost();
  80. }
  81. }
  82. /// Handles a stream start event, which occurs when the underlying transport
  83. /// becomes ready (socket connected, encryption started).
  84. ///
  85. /// If you redefine handleStart(), make sure to call the base class's method.
  86. void QXmppStream::handleStart()
  87. {
  88. d->dataBuffer.clear();
  89. d->streamStart.clear();
  90. }
  91. /// Returns true if the stream is connected.
  92. ///
  93. bool QXmppStream::isConnected() const
  94. {
  95. return d->socket &&
  96. d->socket->state() == QAbstractSocket::ConnectedState;
  97. }
  98. /// Sends raw data to the peer.
  99. ///
  100. /// \param data
  101. bool QXmppStream::sendData(const QByteArray &data)
  102. {
  103. logSent(QString::fromUtf8(data));
  104. if (!d->socket || d->socket->state() != QAbstractSocket::ConnectedState)
  105. return false;
  106. return d->socket->write(data) == data.size();
  107. }
  108. /// Sends an XMPP packet to the peer.
  109. ///
  110. /// \param packet
  111. bool QXmppStream::sendPacket(const QXmppStanza &packet)
  112. {
  113. // prepare packet
  114. QByteArray data;
  115. QXmlStreamWriter xmlStream(&data);
  116. packet.toXml(&xmlStream);
  117. // send packet
  118. return sendData(data);
  119. }
  120. /// Returns the QSslSocket used for this stream.
  121. ///
  122. QSslSocket *QXmppStream::socket() const
  123. {
  124. return d->socket;
  125. }
  126. /// Sets the QSslSocket used for this stream.
  127. ///
  128. void QXmppStream::setSocket(QSslSocket *socket)
  129. {
  130. bool check;
  131. Q_UNUSED(check);
  132. d->socket = socket;
  133. if (!d->socket)
  134. return;
  135. // socket events
  136. check = connect(socket, SIGNAL(connected()),
  137. this, SLOT(_q_socketConnected()));
  138. Q_ASSERT(check);
  139. check = connect(socket, SIGNAL(encrypted()),
  140. this, SLOT(_q_socketEncrypted()));
  141. Q_ASSERT(check);
  142. check = connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
  143. this, SLOT(_q_socketError(QAbstractSocket::SocketError)));
  144. check = connect(socket, SIGNAL(readyRead()),
  145. this, SLOT(_q_socketReadyRead()));
  146. Q_ASSERT(check);
  147. }
  148. void QXmppStream::_q_socketConnected()
  149. {
  150. info(QString("Socket connected to %1 %2").arg(
  151. d->socket->peerAddress().toString(),
  152. QString::number(d->socket->peerPort())));
  153. handleStart();
  154. }
  155. void QXmppStream::_q_socketEncrypted()
  156. {
  157. debug("Socket encrypted");
  158. handleStart();
  159. }
  160. void QXmppStream::_q_socketError(QAbstractSocket::SocketError socketError)
  161. {
  162. Q_UNUSED(socketError);
  163. warning(QString("Socket error: " + socket()->errorString()));
  164. }
  165. void QXmppStream::_q_socketReadyRead()
  166. {
  167. d->dataBuffer.append(d->socket->readAll());
  168. // handle whitespace pings
  169. if (!d->dataBuffer.isEmpty() && d->dataBuffer.trimmed().isEmpty()) {
  170. d->dataBuffer.clear();
  171. handleStanza(QDomElement());
  172. }
  173. // FIXME : maybe these QRegExps could be static?
  174. QRegExp startStreamRegex("^(<\\?xml.*\\?>)?\\s*<stream:stream.*>");
  175. startStreamRegex.setMinimal(true);
  176. QRegExp endStreamRegex("</stream:stream>$");
  177. endStreamRegex.setMinimal(true);
  178. // check whether we need to add stream start / end elements
  179. QByteArray completeXml = d->dataBuffer;
  180. const QString strData = QString::fromUtf8(d->dataBuffer);
  181. bool streamStart = false;
  182. if (d->streamStart.isEmpty() && strData.contains(startStreamRegex))
  183. streamStart = true;
  184. else
  185. completeXml.prepend(d->streamStart);
  186. if (!strData.contains(endStreamRegex))
  187. completeXml.append(streamRootElementEnd);
  188. // check whether we have a valid XML document
  189. QDomDocument doc;
  190. if (!doc.setContent(completeXml, true))
  191. return;
  192. // remove data from buffer
  193. logReceived(strData);
  194. d->dataBuffer.clear();
  195. if (streamStart)
  196. d->streamStart = startStreamRegex.cap(0).toUtf8();
  197. // process stream start
  198. if (streamStart)
  199. handleStream(doc.documentElement());
  200. // process stanzas
  201. QDomElement nodeRecv = doc.documentElement().firstChildElement();
  202. while (!nodeRecv.isNull()) {
  203. handleStanza(nodeRecv);
  204. nodeRecv = nodeRecv.nextSiblingElement();
  205. }
  206. }