123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- /*
- * Copyright (C) 2008-2012 The QXmpp developers
- *
- * Authors:
- * Manjeet Dahiya
- * Jeremy Lainé
- *
- * Source:
- * http://code.google.com/p/qxmpp
- *
- * This file is a part of QXmpp library.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- */
- #include "QXmppConstants.h"
- #include "QXmppLogger.h"
- #include "QXmppStanza.h"
- #include "QXmppStream.h"
- #include "QXmppUtils.h"
- #include <QBuffer>
- #include <QDomDocument>
- #include <QHostAddress>
- #include <QRegExp>
- #include <QSslSocket>
- #include <QStringList>
- #include <QTime>
- #include <QXmlStreamWriter>
- static bool randomSeeded = false;
- static const QByteArray streamRootElementEnd = "</stream:stream>";
- class QXmppStreamPrivate
- {
- public:
- QXmppStreamPrivate();
- QByteArray dataBuffer;
- QSslSocket* socket;
- // stream state
- QByteArray streamStart;
- };
- QXmppStreamPrivate::QXmppStreamPrivate()
- : socket(0)
- {
- }
- /// Constructs a base XMPP stream.
- ///
- /// \param parent
- QXmppStream::QXmppStream(QObject *parent)
- : QXmppLoggable(parent),
- d(new QXmppStreamPrivate)
- {
- // Make sure the random number generator is seeded
- if (!randomSeeded)
- {
- qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(this));
- randomSeeded = true;
- }
- }
- /// Destroys a base XMPP stream.
- QXmppStream::~QXmppStream()
- {
- delete d;
- }
- /// Disconnects from the remote host.
- ///
- void QXmppStream::disconnectFromHost()
- {
- sendData(streamRootElementEnd);
- if (d->socket)
- {
- d->socket->flush();
- d->socket->disconnectFromHost();
- }
- }
- /// Handles a stream start event, which occurs when the underlying transport
- /// becomes ready (socket connected, encryption started).
- ///
- /// If you redefine handleStart(), make sure to call the base class's method.
- void QXmppStream::handleStart()
- {
- d->dataBuffer.clear();
- d->streamStart.clear();
- }
- /// Returns true if the stream is connected.
- ///
- bool QXmppStream::isConnected() const
- {
- return d->socket &&
- d->socket->state() == QAbstractSocket::ConnectedState;
- }
- /// Sends raw data to the peer.
- ///
- /// \param data
- bool QXmppStream::sendData(const QByteArray &data)
- {
- logSent(QString::fromUtf8(data));
- if (!d->socket || d->socket->state() != QAbstractSocket::ConnectedState)
- return false;
- return d->socket->write(data) == data.size();
- }
- /// Sends an XMPP packet to the peer.
- ///
- /// \param packet
- bool QXmppStream::sendPacket(const QXmppStanza &packet)
- {
- // prepare packet
- QByteArray data;
- QXmlStreamWriter xmlStream(&data);
- packet.toXml(&xmlStream);
- // send packet
- return sendData(data);
- }
- /// Returns the QSslSocket used for this stream.
- ///
- QSslSocket *QXmppStream::socket() const
- {
- return d->socket;
- }
- /// Sets the QSslSocket used for this stream.
- ///
- void QXmppStream::setSocket(QSslSocket *socket)
- {
- bool check;
- Q_UNUSED(check);
- d->socket = socket;
- if (!d->socket)
- return;
- // socket events
- check = connect(socket, SIGNAL(connected()),
- this, SLOT(_q_socketConnected()));
- Q_ASSERT(check);
- check = connect(socket, SIGNAL(encrypted()),
- this, SLOT(_q_socketEncrypted()));
- Q_ASSERT(check);
- check = connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
- this, SLOT(_q_socketError(QAbstractSocket::SocketError)));
- check = connect(socket, SIGNAL(readyRead()),
- this, SLOT(_q_socketReadyRead()));
- Q_ASSERT(check);
- }
- void QXmppStream::_q_socketConnected()
- {
- info(QString("Socket connected to %1 %2").arg(
- d->socket->peerAddress().toString(),
- QString::number(d->socket->peerPort())));
- handleStart();
- }
- void QXmppStream::_q_socketEncrypted()
- {
- debug("Socket encrypted");
- handleStart();
- }
- void QXmppStream::_q_socketError(QAbstractSocket::SocketError socketError)
- {
- Q_UNUSED(socketError);
- warning(QString("Socket error: " + socket()->errorString()));
- }
- void QXmppStream::_q_socketReadyRead()
- {
- d->dataBuffer.append(d->socket->readAll());
- // handle whitespace pings
- if (!d->dataBuffer.isEmpty() && d->dataBuffer.trimmed().isEmpty()) {
- d->dataBuffer.clear();
- handleStanza(QDomElement());
- }
- // FIXME : maybe these QRegExps could be static?
- QRegExp startStreamRegex("^(<\\?xml.*\\?>)?\\s*<stream:stream.*>");
- startStreamRegex.setMinimal(true);
- QRegExp endStreamRegex("</stream:stream>$");
- endStreamRegex.setMinimal(true);
- // check whether we need to add stream start / end elements
- QByteArray completeXml = d->dataBuffer;
- const QString strData = QString::fromUtf8(d->dataBuffer);
- bool streamStart = false;
- if (d->streamStart.isEmpty() && strData.contains(startStreamRegex))
- streamStart = true;
- else
- completeXml.prepend(d->streamStart);
- if (!strData.contains(endStreamRegex))
- completeXml.append(streamRootElementEnd);
- // check whether we have a valid XML document
- QDomDocument doc;
- if (!doc.setContent(completeXml, true))
- return;
- // remove data from buffer
- logReceived(strData);
- d->dataBuffer.clear();
- if (streamStart)
- d->streamStart = startStreamRegex.cap(0).toUtf8();
- // process stream start
- if (streamStart)
- handleStream(doc.documentElement());
- // process stanzas
- QDomElement nodeRecv = doc.documentElement().firstChildElement();
- while (!nodeRecv.isNull()) {
- handleStanza(nodeRecv);
- nodeRecv = nodeRecv.nextSiblingElement();
- }
- }
|