aboutsummaryrefslogtreecommitdiff
path: root/src/base/QXmppStream.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2012-02-08 12:51:15 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2012-02-08 12:51:15 +0000
commitdeb9d6cb53057ca8b90d10d6a3bdc5dcfd1b3ee4 (patch)
treed956bad28e28aadc3c83dbf88b3eddb5e1d9a9f4 /src/base/QXmppStream.cpp
parente8a1ad0cc608f12874ba4bafbd8282fa537ec9fb (diff)
downloadqxmpp-deb9d6cb53057ca8b90d10d6a3bdc5dcfd1b3ee4.tar.gz
move files common to client/server into "base"
Diffstat (limited to 'src/base/QXmppStream.cpp')
-rw-r--r--src/base/QXmppStream.cpp259
1 files changed, 259 insertions, 0 deletions
diff --git a/src/base/QXmppStream.cpp b/src/base/QXmppStream.cpp
new file mode 100644
index 00000000..d9f7ff88
--- /dev/null
+++ b/src/base/QXmppStream.cpp
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2008-2011 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 "QXmppPacket.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).secsTo(QTime::currentTime()));
+ 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 QXmppPacket &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(disconnected()),
+ this, SLOT(_q_socketDisconnected()));
+ 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);
+
+ // relay signals
+ check = connect(socket, SIGNAL(disconnected()),
+ this, SIGNAL(disconnected()));
+ 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_socketDisconnected()
+{
+ info("Socket disconnected");
+}
+
+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());
+
+ // 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();
+ }
+}
+
+