aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2010-08-23 05:40:15 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2010-08-23 05:40:15 +0000
commit80e0607c97aef44e88f8e5af281132668114534f (patch)
tree71594029b2bd5bf28dfe489dc1249d6c72b378be /src
parent85a3546164c0816a4b94d6fdb45ca64b1547f541 (diff)
downloadqxmpp-80e0607c97aef44e88f8e5af281132668114534f.tar.gz
* make QXmppStream an abstract class
* move client-specific code to QXmppOutgoingClient
Diffstat (limited to 'src')
-rw-r--r--src/QXmppArchiveManager.cpp4
-rw-r--r--src/QXmppArchiveManager.h6
-rw-r--r--src/QXmppCallManager.cpp4
-rw-r--r--src/QXmppCallManager.h6
-rw-r--r--src/QXmppClient.cpp12
-rw-r--r--src/QXmppMucManager.cpp4
-rw-r--r--src/QXmppMucManager.h6
-rw-r--r--src/QXmppOutgoingClient.cpp858
-rw-r--r--src/QXmppOutgoingClient.h151
-rw-r--r--src/QXmppRosterManager.cpp6
-rw-r--r--src/QXmppRosterManager.h6
-rw-r--r--src/QXmppStream.cpp1017
-rw-r--r--src/QXmppStream.h126
-rw-r--r--src/QXmppTransferManager.cpp4
-rw-r--r--src/QXmppTransferManager.h6
-rw-r--r--src/QXmppVCardManager.cpp4
-rw-r--r--src/QXmppVCardManager.h6
-rw-r--r--src/src.pro2
18 files changed, 1211 insertions, 1017 deletions
diff --git a/src/QXmppArchiveManager.cpp b/src/QXmppArchiveManager.cpp
index edbcfdd8..c46e2dc9 100644
--- a/src/QXmppArchiveManager.cpp
+++ b/src/QXmppArchiveManager.cpp
@@ -23,11 +23,11 @@
#include "QXmppArchiveIq.h"
#include "QXmppArchiveManager.h"
-#include "QXmppStream.h"
+#include "QXmppOutgoingClient.h"
#include <QDebug>
-QXmppArchiveManager::QXmppArchiveManager(QXmppStream *stream, QObject *parent)
+QXmppArchiveManager::QXmppArchiveManager(QXmppOutgoingClient *stream, QObject *parent)
: QObject(parent),
m_stream(stream)
{
diff --git a/src/QXmppArchiveManager.h b/src/QXmppArchiveManager.h
index 631bad4c..0fba89c8 100644
--- a/src/QXmppArchiveManager.h
+++ b/src/QXmppArchiveManager.h
@@ -31,7 +31,7 @@ class QXmppArchiveChat;
class QXmppArchiveChatIq;
class QXmppArchiveListIq;
class QXmppArchivePrefIq;
-class QXmppStream;
+class QXmppOutgoingClient;
/// \brief The QXmppArchiveManager class makes it possible to access message
/// archives as defined by XEP-0136: Message Archiving.
@@ -46,7 +46,7 @@ class QXmppArchiveManager : public QObject
Q_OBJECT
public:
- QXmppArchiveManager(QXmppStream* stream, QObject *parent = 0);
+ QXmppArchiveManager(QXmppOutgoingClient* stream, QObject *parent = 0);
void listCollections(const QString &jid, const QDateTime &start = QDateTime(), const QDateTime &end = QDateTime(), int max = 0);
void retrieveCollection(const QString &jid, const QDateTime &start, int max = 0);
@@ -66,7 +66,7 @@ private slots:
private:
// reference to xmpp stream (no ownership)
- QXmppStream* m_stream;
+ QXmppOutgoingClient* m_stream;
};
#endif
diff --git a/src/QXmppCallManager.cpp b/src/QXmppCallManager.cpp
index a95c90e2..379eed0b 100644
--- a/src/QXmppCallManager.cpp
+++ b/src/QXmppCallManager.cpp
@@ -27,7 +27,7 @@
#include "QXmppCodec.h"
#include "QXmppConstants.h"
#include "QXmppJingleIq.h"
-#include "QXmppStream.h"
+#include "QXmppOutgoingClient.h"
#include "QXmppStun.h"
#include "QXmppUtils.h"
@@ -408,7 +408,7 @@ qint64 QXmppCall::writeData(const char * data, qint64 maxSize)
return maxSize;
}
-QXmppCallManager::QXmppCallManager(QXmppStream *stream, QObject *parent)
+QXmppCallManager::QXmppCallManager(QXmppOutgoingClient *stream, QObject *parent)
: QObject(parent), m_stream(stream)
{
// setup logging
diff --git a/src/QXmppCallManager.h b/src/QXmppCallManager.h
index ba0d2212..7111ed1c 100644
--- a/src/QXmppCallManager.h
+++ b/src/QXmppCallManager.h
@@ -35,7 +35,7 @@ class QXmppIceConnection;
class QXmppJingleCandidate;
class QXmppJingleIq;
class QXmppJinglePayloadType;
-class QXmppStream;
+class QXmppOutgoingClient;
/// \brief The QXmppCall class represents a Voice-Over-IP call to a remote party.
///
@@ -181,7 +181,7 @@ class QXmppCallManager : public QObject
Q_OBJECT
public:
- QXmppCallManager(QXmppStream *stream, QObject *parent = 0);
+ QXmppCallManager(QXmppOutgoingClient *stream, QObject *parent = 0);
QXmppCall *call(const QString &jid);
signals:
@@ -212,7 +212,7 @@ private:
QList<QXmppCall*> m_calls;
// reference to xmpp stream (no ownership)
- QXmppStream* m_stream;
+ QXmppOutgoingClient* m_stream;
};
#endif
diff --git a/src/QXmppClient.cpp b/src/QXmppClient.cpp
index eda85a24..9732b959 100644
--- a/src/QXmppClient.cpp
+++ b/src/QXmppClient.cpp
@@ -24,7 +24,7 @@
#include "QXmppClient.h"
#include "QXmppLogger.h"
-#include "QXmppStream.h"
+#include "QXmppOutgoingClient.h"
#include "QXmppMessage.h"
#include "QXmppArchiveManager.h"
@@ -44,7 +44,7 @@ class QXmppClientPrivate
public:
QXmppClientPrivate();
- QXmppStream* stream; ///< Pointer to QXmppStream object a wrapper over
+ QXmppOutgoingClient* stream; ///< Pointer to QXmppOutgoingClient object a wrapper over
///< TCP socket and XMPP protocol
QXmppPresence clientPresence; ///< Stores the current presence of the connected client
@@ -111,9 +111,7 @@ QXmppClient::QXmppClient(QObject *parent)
: QObject(parent),
d(new QXmppClientPrivate)
{
- QSslSocket *socket = new QSslSocket;
- d->stream = new QXmppStream(socket, this);
- socket->setParent(d->stream);
+ d->stream = new QXmppOutgoingClient(this);
d->clientPresence.setExtensions(d->stream->presenceExtensions());
bool check = connect(d->stream, SIGNAL(elementReceived(const QDomElement&, bool&)),
@@ -140,11 +138,11 @@ QXmppClient::QXmppClient(QObject *parent)
SIGNAL(disconnected()));
Q_ASSERT(check);
- check = connect(d->stream, SIGNAL(xmppConnected()), this,
+ check = connect(d->stream, SIGNAL(connected()), this,
SLOT(xmppConnected()));
Q_ASSERT(check);
- check = connect(d->stream, SIGNAL(xmppConnected()), this,
+ check = connect(d->stream, SIGNAL(connected()), this,
SIGNAL(connected()));
Q_ASSERT(check);
diff --git a/src/QXmppMucManager.cpp b/src/QXmppMucManager.cpp
index 6be60c73..d9203d4b 100644
--- a/src/QXmppMucManager.cpp
+++ b/src/QXmppMucManager.cpp
@@ -25,10 +25,10 @@
#include "QXmppMessage.h"
#include "QXmppMucIq.h"
#include "QXmppMucManager.h"
-#include "QXmppStream.h"
+#include "QXmppOutgoingClient.h"
#include "QXmppUtils.h"
-QXmppMucManager::QXmppMucManager(QXmppStream* stream, QObject *parent)
+QXmppMucManager::QXmppMucManager(QXmppOutgoingClient* stream, QObject *parent)
: QObject(parent),
m_stream(stream)
{
diff --git a/src/QXmppMucManager.h b/src/QXmppMucManager.h
index 4e93757c..35295b18 100644
--- a/src/QXmppMucManager.h
+++ b/src/QXmppMucManager.h
@@ -33,7 +33,7 @@ class QXmppDataForm;
class QXmppMessage;
class QXmppMucAdminIq;
class QXmppMucOwnerIq;
-class QXmppStream;
+class QXmppOutgoingClient;
/// \brief The QXmppMucManager class makes it possible to interact with
/// multi-user chat rooms as defined by XEP-0045: Multi-User Chat.
@@ -45,7 +45,7 @@ class QXmppMucManager : public QObject
Q_OBJECT
public:
- QXmppMucManager(QXmppStream* stream, QObject *parent = 0);
+ QXmppMucManager(QXmppOutgoingClient* stream, QObject *parent = 0);
bool joinRoom(const QString &roomJid, const QString &nickName);
bool leaveRoom(const QString &roomJid);
@@ -74,7 +74,7 @@ private slots:
void presenceReceived(const QXmppPresence &presence);
private:
- QXmppStream *m_stream;
+ QXmppOutgoingClient *m_stream;
QMap<QString, QString> m_nickNames;
QMap<QString, QMap<QString, QXmppPresence> > m_participants;
};
diff --git a/src/QXmppOutgoingClient.cpp b/src/QXmppOutgoingClient.cpp
new file mode 100644
index 00000000..f9f939b2
--- /dev/null
+++ b/src/QXmppOutgoingClient.cpp
@@ -0,0 +1,858 @@
+/*
+ * Copyright (C) 2008-2010 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 <QSslSocket>
+
+#include "QXmppConfiguration.h"
+#include "QXmppConstants.h"
+#include "QXmppIq.h"
+#include "QXmppLogger.h"
+#include "QXmppMessage.h"
+#include "QXmppPacket.h"
+#include "QXmppPresence.h"
+#include "QXmppOutgoingClient.h"
+#include "QXmppStreamFeatures.h"
+#include "QXmppNonSASLAuth.h"
+#include "QXmppUtils.h"
+
+// IQ types
+#include "QXmppArchiveIq.h"
+#include "QXmppBind.h"
+#include "QXmppByteStreamIq.h"
+#include "QXmppDiscoveryIq.h"
+#include "QXmppIbbIq.h"
+#include "QXmppJingleIq.h"
+#include "QXmppMucIq.h"
+#include "QXmppPingIq.h"
+#include "QXmppRpcIq.h"
+#include "QXmppRosterIq.h"
+#include "QXmppSession.h"
+#include "QXmppStreamInitiationIq.h"
+#include "QXmppVCard.h"
+#include "QXmppVersionIq.h"
+
+#include <QBuffer>
+#include <QCoreApplication>
+#include <QDomDocument>
+#include <QStringList>
+#include <QRegExp>
+#include <QHostAddress>
+#include <QXmlStreamWriter>
+#include <QTimer>
+
+static bool randomSeeded = false;
+static const QString capabilitiesNode = "http://code.google.com/p/qxmpp";
+static const QByteArray streamRootElementEnd = "</stream:stream>";
+
+class QXmppOutgoingClientPrivate
+{
+public:
+ QXmppOutgoingClientPrivate();
+
+ // This object provides the configuration
+ // required for connecting to the XMPP server.
+ QXmppConfiguration config;
+ QAbstractSocket::SocketError socketError;
+ QXmppStanza::Error::Condition xmppStreamError;
+
+ // State data
+ int authStep;
+ QString bindId;
+ QString sessionId;
+ bool sessionAvailable;
+ QString streamId;
+ QString streamFrom;
+ QString streamVersion;
+ QString nonSASLAuthId;
+
+ // Timers
+ QTimer *pingTimer;
+ QTimer *timeoutTimer;
+};
+
+QXmppOutgoingClientPrivate::QXmppOutgoingClientPrivate()
+ : authStep(0),
+ sessionAvailable(false)
+{
+}
+
+QXmppOutgoingClient::QXmppOutgoingClient(QObject *parent)
+ : QXmppStream(parent),
+ d(new QXmppOutgoingClientPrivate)
+{
+ QSslSocket *socket = new QSslSocket(this);
+ setSocket(socket);
+
+ // initialise logger
+ setLogger(QXmppLogger::getLogger());
+
+ bool check = connect(socket, SIGNAL(sslErrors(const QList<QSslError>&)),
+ this, SLOT(socketSslErrors(const QList<QSslError>&)));
+ Q_ASSERT(check);
+
+ check = connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(socketError(QAbstractSocket::SocketError)));
+ Q_ASSERT(check);
+
+ // XEP-0199: XMPP Ping
+ d->pingTimer = new QTimer(this);
+ check = connect(d->pingTimer, SIGNAL(timeout()),
+ this, SLOT(pingSend()));
+ Q_ASSERT(check);
+
+ d->timeoutTimer = new QTimer(this);
+ d->timeoutTimer->setSingleShot(true);
+ check = connect(d->timeoutTimer, SIGNAL(timeout()),
+ this, SLOT(pingTimeout()));
+ Q_ASSERT(check);
+
+ check = connect(this, SIGNAL(connected()),
+ this, SLOT(pingStart()));
+ Q_ASSERT(check);
+
+ check = connect(this, SIGNAL(disconnected()),
+ this, SLOT(pingStop()));
+ Q_ASSERT(check);
+}
+
+QXmppOutgoingClient::~QXmppOutgoingClient()
+{
+ delete d;
+}
+
+QXmppConfiguration& QXmppOutgoingClient::configuration()
+{
+ return d->config;
+}
+
+void QXmppOutgoingClient::connectToHost()
+{
+ info(QString("Connecting to: %1:%2").arg(configuration().
+ host()).arg(configuration().port()));
+
+ socket()->setProxy(configuration().networkProxy());
+ socket()->connectToHost(configuration().host(),
+ configuration().port());
+}
+
+void QXmppOutgoingClient::socketSslErrors(const QList<QSslError> & error)
+{
+ warning("SSL errors");
+ for(int i = 0; i< error.count(); ++i)
+ warning(error.at(i).errorString());
+
+ if (configuration().ignoreSslErrors())
+ socket()->ignoreSslErrors();
+}
+
+void QXmppOutgoingClient::socketError(QAbstractSocket::SocketError ee)
+{
+ d->socketError = ee;
+ emit error(QXmppClient::SocketError);
+ warning(QString("Socket error: " + socket()->errorString()));
+}
+
+void QXmppOutgoingClient::handleStart()
+{
+ // reset authentication step
+ d->authStep = 0;
+
+ // start stream
+ QByteArray data = "<?xml version='1.0'?><stream:stream to='";
+ data.append(configuration().domain());
+ data.append("' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>");
+ sendData(data);
+}
+
+void QXmppOutgoingClient::handleStream(const QDomElement &streamElement)
+{
+ if(d->streamId.isEmpty())
+ d->streamId = streamElement.attribute("id");
+ if (d->streamFrom.isEmpty())
+ d->streamFrom = streamElement.attribute("from");
+ if(d->streamVersion.isEmpty())
+ {
+ d->streamVersion = streamElement.attribute("version");
+
+ // no version specified, signals XMPP Version < 1.0.
+ // switch to old auth mechanism
+ if(d->streamVersion.isEmpty())
+ sendNonSASLAuthQuery();
+ }
+}
+
+void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv)
+{
+ // if we receive any kind of data, stop the timeout timer
+ d->timeoutTimer->stop();
+
+ const QString ns = nodeRecv.namespaceURI();
+
+ // give client opportunity to handle stanza
+ bool handled = false;
+ emit elementReceived(nodeRecv, handled);
+ if (handled)
+ return;
+
+ if(QXmppStreamFeatures::isStreamFeatures(nodeRecv))
+ {
+ QXmppStreamFeatures features;
+ features.parse(nodeRecv);
+
+ if (!socket()->isEncrypted())
+ {
+ // determine TLS mode to use
+ const QXmppConfiguration::StreamSecurityMode localSecurity = configuration().streamSecurityMode();
+ const QXmppConfiguration::StreamSecurityMode remoteSecurity = features.securityMode();
+ if (!socket()->supportsSsl() &&
+ (localSecurity == QXmppConfiguration::TLSRequired ||
+ remoteSecurity == QXmppConfiguration::TLSRequired))
+ {
+ warning("Disconnecting as TLS is required, but SSL support is not available");
+ disconnectFromHost();
+ return;
+ }
+ if (localSecurity == QXmppConfiguration::TLSRequired &&
+ remoteSecurity == QXmppConfiguration::TLSDisabled)
+ {
+ warning("Disconnecting as TLS is required, but not supported by the server");
+ disconnectFromHost();
+ return;
+ }
+
+ if (socket()->supportsSsl() &&
+ remoteSecurity != QXmppConfiguration::TLSDisabled &&
+ localSecurity != QXmppConfiguration::TLSDisabled)
+ {
+ // enable TLS as it is support by both parties
+ sendData("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
+ return;
+ }
+ }
+
+ // handle authentication
+ const bool nonSaslAvailable = features.isNonSaslAuthAvailable();
+ const bool saslAvailable = !features.authMechanisms().isEmpty();
+ const bool useSasl = configuration().useSASLAuthentication();
+ if((saslAvailable && nonSaslAvailable && !useSasl) ||
+ (!saslAvailable && nonSaslAvailable))
+ {
+ sendNonSASLAuthQuery();
+ }
+ else if(saslAvailable)
+ {
+ // determine SASL Authentication mechanism to use
+ QList<QXmppConfiguration::SASLAuthMechanism> mechanisms = features.authMechanisms();
+ QXmppConfiguration::SASLAuthMechanism mechanism = configuration().sASLAuthMechanism();
+ if (mechanisms.isEmpty())
+ {
+ warning("No supported SASL Authentication mechanism available");
+ disconnectFromHost();
+ return;
+ }
+ else if (!mechanisms.contains(mechanism))
+ {
+ info("Desired SASL Auth mechanism is not available, selecting first available one");
+ mechanism = mechanisms.first();
+ }
+
+ // send SASL Authentication request
+ switch(mechanism)
+ {
+ case QXmppConfiguration::SASLPlain:
+ {
+ QString userPass('\0' + configuration().user() +
+ '\0' + configuration().passwd());
+ QByteArray data = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>";
+ data += userPass.toUtf8().toBase64();
+ data += "</auth>";
+ sendData(data);
+ }
+ break;
+ case QXmppConfiguration::SASLDigestMD5:
+ sendData("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>");
+ break;
+ case QXmppConfiguration::SASLAnonymous:
+ sendData("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>");
+ break;
+ }
+ }
+
+ // check whether bind is available
+ if (features.isBindAvailable())
+ {
+ QXmppBind bind(QXmppIq::Set);
+ bind.setResource(configuration().resource());
+ d->bindId = bind.id();
+ sendPacket(bind);
+ }
+
+ // check whether session is available
+ if (features.isSessionAvailable())
+ d->sessionAvailable = true;
+ }
+ else if(ns == ns_stream && nodeRecv.tagName() == "error")
+ {
+ if (!nodeRecv.firstChildElement("conflict").isNull())
+ d->xmppStreamError = QXmppStanza::Error::Conflict;
+ else
+ d->xmppStreamError = QXmppStanza::Error::UndefinedCondition;
+ emit error(QXmppClient::XmppStreamError);
+ }
+ else if(ns == ns_tls)
+ {
+ if(nodeRecv.tagName() == "proceed")
+ {
+ debug("Starting encryption");
+ socket()->startClientEncryption();
+ return;
+ }
+ }
+ else if(ns == ns_sasl)
+ {
+ if(nodeRecv.tagName() == "success")
+ {
+ debug("Authenticated");
+ handleStart();
+ }
+ else if(nodeRecv.tagName() == "challenge")
+ {
+ // TODO: Track which mechanism was used for when other SASL protocols which use challenges are supported
+ d->authStep++;
+ switch (d->authStep)
+ {
+ case 1 :
+ sendAuthDigestMD5ResponseStep1(nodeRecv.text());
+ break;
+ case 2 :
+ sendAuthDigestMD5ResponseStep2();
+ break;
+ default :
+ warning("Too many authentication steps");
+ disconnectFromHost();
+ break;
+ }
+ }
+ else if(nodeRecv.tagName() == "failure")
+ {
+ if (!nodeRecv.firstChildElement("not-authorized").isNull())
+ d->xmppStreamError = QXmppStanza::Error::NotAuthorized;
+ else
+ d->xmppStreamError = QXmppStanza::Error::UndefinedCondition;
+ emit error(QXmppClient::XmppStreamError);
+
+ warning("Authentication failure");
+ disconnectFromHost();
+ }
+ }
+ else if(ns == ns_client)
+ {
+
+ if(nodeRecv.tagName() == "iq")
+ {
+ QDomElement element = nodeRecv.firstChildElement();
+ QString id = nodeRecv.attribute("id");
+ QString type = nodeRecv.attribute("type");
+ if(type.isEmpty())
+ warning("QXmppStream: iq type can't be empty");
+
+ if(id == d->sessionId)
+ {
+ QXmppSession session;
+ session.parse(nodeRecv);
+
+ // get back add configuration whether to send
+ // roster and intial presence in beginning
+ // process SessionIq
+
+ // xmpp connection made
+ emit connected();
+ }
+ else if(QXmppBind::isBind(nodeRecv) && id == d->bindId)
+ {
+ QXmppBind bind;
+ bind.parse(nodeRecv);
+
+ // bind result
+ if (bind.type() == QXmppIq::Result)
+ {
+ if (!bind.jid().isEmpty())
+ {
+ QRegExp jidRegex("^([^@/]+)@([^@/]+)/(.+)$");
+ if (jidRegex.exactMatch(bind.jid()))
+ {
+ configuration().setUser(jidRegex.cap(1));
+ configuration().setDomain(jidRegex.cap(2));
+ configuration().setResource(jidRegex.cap(3));
+ } else {
+ warning("Bind IQ received with invalid JID: " + bind.jid());
+ }
+ }
+
+ // start session if it is available
+ if (d->sessionAvailable)
+ {
+ QXmppSession session(QXmppIq::Set);
+ session.setTo(configuration().domain());
+ d->sessionId = session.id();
+ sendPacket(session);
+ }
+ }
+ }
+ else if(QXmppRosterIq::isRosterIq(nodeRecv))
+ {
+ QXmppRosterIq rosterIq;
+ rosterIq.parse(nodeRecv);
+ emit rosterIqReceived(rosterIq);
+ }
+ // extensions
+
+ // XEP-0009: Jabber-RPC
+ else if(QXmppRpcInvokeIq::isRpcInvokeIq(nodeRecv))
+ {
+ QXmppRpcInvokeIq rpcIqPacket;
+ rpcIqPacket.parse(nodeRecv);
+ emit rpcCallInvoke(rpcIqPacket);
+ }
+ else if(QXmppRpcResponseIq::isRpcResponseIq(nodeRecv))
+ {
+ QXmppRpcResponseIq rpcResponseIq;
+ rpcResponseIq.parse(nodeRecv);
+ emit rpcCallResponse(rpcResponseIq);
+ }
+ else if(QXmppRpcErrorIq::isRpcErrorIq(nodeRecv))
+ {
+ QXmppRpcErrorIq rpcErrorIq;
+ rpcErrorIq.parse(nodeRecv);
+ emit rpcCallError(rpcErrorIq);
+ }
+
+ // XEP-0030: Service Discovery
+ else if(QXmppDiscoveryIq::isDiscoveryIq(nodeRecv))
+ {
+ QXmppDiscoveryIq discoIq;
+ discoIq.parse(nodeRecv);
+
+ if (discoIq.type() == QXmppIq::Get &&
+ discoIq.queryType() == QXmppDiscoveryIq::InfoQuery &&
+ (discoIq.queryNode().isEmpty() || discoIq.queryNode().startsWith(capabilitiesNode)))
+ {
+ // respond to info query
+ QXmppDiscoveryIq qxmppFeatures = capabilities();
+ qxmppFeatures.setId(discoIq.id());
+ qxmppFeatures.setTo(discoIq.from());
+ qxmppFeatures.setQueryNode(discoIq.queryNode());
+ sendPacket(qxmppFeatures);
+ } else {
+ emit discoveryIqReceived(discoIq);
+ }
+ }
+ // XEP-0045: Multi-User Chat
+ else if (QXmppMucAdminIq::isMucAdminIq(nodeRecv))
+ {
+ QXmppMucAdminIq mucIq;
+ mucIq.parse(nodeRecv);
+ emit mucAdminIqReceived(mucIq);
+ }
+ else if (QXmppMucOwnerIq::isMucOwnerIq(nodeRecv))
+ {
+ QXmppMucOwnerIq mucIq;
+ mucIq.parse(nodeRecv);
+ emit mucOwnerIqReceived(mucIq);
+ }
+ // XEP-0047 In-Band Bytestreams
+ else if(QXmppIbbCloseIq::isIbbCloseIq(nodeRecv))
+ {
+ QXmppIbbCloseIq ibbCloseIq;
+ ibbCloseIq.parse(nodeRecv);
+ emit ibbCloseIqReceived(ibbCloseIq);
+ }
+ else if(QXmppIbbDataIq::isIbbDataIq(nodeRecv))
+ {
+ QXmppIbbDataIq ibbDataIq;
+ ibbDataIq.parse(nodeRecv);
+ emit ibbDataIqReceived(ibbDataIq);
+ }
+ else if(QXmppIbbOpenIq::isIbbOpenIq(nodeRecv))
+ {
+ QXmppIbbOpenIq ibbOpenIq;
+ ibbOpenIq.parse(nodeRecv);
+ emit ibbOpenIqReceived(ibbOpenIq);
+ }
+ // XEP-0054: vcard-temp
+ else if(nodeRecv.firstChildElement("vCard").
+ namespaceURI() == ns_vcard)
+ {
+ QXmppVCard vcardIq;
+ vcardIq.parse(nodeRecv);
+ emit vCardIqReceived(vcardIq);
+ }
+ // XEP-0065: SOCKS5 Bytestreams
+ else if(QXmppByteStreamIq::isByteStreamIq(nodeRecv))
+ {
+ QXmppByteStreamIq byteStreamIq;
+ byteStreamIq.parse(nodeRecv);
+ emit byteStreamIqReceived(byteStreamIq);
+ }
+ // XEP-0078: Non-SASL Authentication
+ else if(id == d->nonSASLAuthId && type == "result")
+ {
+ // successful Non-SASL Authentication
+ debug("Authenticated (Non-SASL)");
+
+ // xmpp connection made
+ emit connected();
+ }
+ else if(QXmppNonSASLAuthIq::isNonSASLAuthIq(nodeRecv))
+ {
+ if(type == "result")
+ {
+ bool digest = !nodeRecv.firstChildElement("query").
+ firstChildElement("digest").isNull();
+ bool plain = !nodeRecv.firstChildElement("query").
+ firstChildElement("password").isNull();
+ bool plainText = false;
+
+ if(plain && digest)
+ {
+ if(configuration().nonSASLAuthMechanism() ==
+ QXmppConfiguration::NonSASLDigest)
+ plainText = false;
+ else
+ plainText = true;
+ }
+ else if(plain)
+ plainText = true;
+ else if(digest)
+ plainText = false;
+ else
+ {
+ warning("No supported Non-SASL Authentication mechanism available");
+ disconnectFromHost();
+ return;
+ }
+ sendNonSASLAuth(plainText);
+ }
+ }
+ // XEP-0092: Software Version
+ else if(QXmppVersionIq::isVersionIq(nodeRecv))
+ {
+ QXmppVersionIq versionIq;
+ versionIq.parse(nodeRecv);
+
+ if (versionIq.type() == QXmppIq::Get)
+ {
+ // respond to query
+ QXmppVersionIq responseIq;
+ responseIq.setType(QXmppIq::Result);
+ responseIq.setId(versionIq.id());
+ responseIq.setTo(versionIq.from());
+ responseIq.setName(qApp->applicationName());
+ responseIq.setVersion(qApp->applicationVersion());
+ sendPacket(responseIq);
+ } else {
+ emit iqReceived(versionIq);
+ }
+
+ }
+ // XEP-0095: Stream Initiation
+ else if(QXmppStreamInitiationIq::isStreamInitiationIq(nodeRecv))
+ {
+ QXmppStreamInitiationIq siIq;
+ siIq.parse(nodeRecv);
+ emit streamInitiationIqReceived(siIq);
+ }
+ // XEP-0136: Message Archiving
+ else if(QXmppArchiveChatIq::isArchiveChatIq(nodeRecv))
+ {
+ QXmppArchiveChatIq archiveIq;
+ archiveIq.parse(nodeRecv);
+ emit archiveChatIqReceived(archiveIq);
+ }
+ else if(QXmppArchiveListIq::isArchiveListIq(nodeRecv))
+ {
+ QXmppArchiveListIq archiveIq;
+ archiveIq.parse(nodeRecv);
+ emit archiveListIqReceived(archiveIq);
+ }
+ else if(QXmppArchivePrefIq::isArchivePrefIq(nodeRecv))
+ {
+ QXmppArchivePrefIq archiveIq;
+ archiveIq.parse(nodeRecv);
+ emit archivePrefIqReceived(archiveIq);
+ }
+ // XEP-0166: Jingle
+ else if(QXmppJingleIq::isJingleIq(nodeRecv))
+ {
+ QXmppJingleIq jingleIq;
+ jingleIq.parse(nodeRecv);
+ emit jingleIqReceived(jingleIq);
+ }
+ // XEP-0199: XMPP Ping
+ else if(QXmppPingIq::isPingIq(nodeRecv))
+ {
+ QXmppPingIq req;
+ req.parse(nodeRecv);
+
+ QXmppIq iq(QXmppIq::Result);
+ iq.setId(req.id());
+ iq.setTo(req.from());
+ iq.setFrom(req.to());
+ sendPacket(iq);
+ }
+ else
+ {
+ QXmppIq iqPacket;
+ iqPacket.parse(nodeRecv);
+
+ // if we didn't understant the iq, reply with error
+ // except for "result" and "error" iqs
+ if (type != "result" && type != "error")
+ {
+ QXmppIq iq(QXmppIq::Error);
+ iq.setId(iqPacket.id());
+ iq.setTo(iqPacket.from());
+ iq.setFrom(iqPacket.to());
+ QXmppStanza::Error error(QXmppStanza::Error::Cancel,
+ QXmppStanza::Error::FeatureNotImplemented);
+ iq.setError(error);
+ sendPacket(iq);
+ } else {
+ emit iqReceived(iqPacket);
+ }
+ }
+ }
+ else if(nodeRecv.tagName() == "presence")
+ {
+ QXmppPresence presence;
+ presence.parse(nodeRecv);
+
+ // emit presence
+ emit presenceReceived(presence);
+ }
+ else if(nodeRecv.tagName() == "message")
+ {
+ QXmppMessage message;
+ message.parse(nodeRecv);
+
+ // emit message
+ emit messageReceived(message);
+ }
+ }
+}
+
+void QXmppOutgoingClient::pingStart()
+{
+ const int interval = configuration().keepAliveInterval();
+ // start ping timer
+ if (interval > 0)
+ {
+ d->pingTimer->setInterval(interval * 1000);
+ d->pingTimer->start();
+ }
+}
+
+void QXmppOutgoingClient::pingStop()
+{
+ // stop all timers
+ d->pingTimer->stop();
+ d->timeoutTimer->stop();
+}
+
+void QXmppOutgoingClient::pingSend()
+{
+ // send ping packet
+ QXmppPingIq ping;
+ ping.setFrom(configuration().jid());
+ ping.setTo(configuration().domain());
+ sendPacket(ping);
+
+ // start timeout timer
+ const int timeout = configuration().keepAliveTimeout();
+ if (timeout > 0)
+ {
+ d->timeoutTimer->setInterval(timeout * 1000);
+ d->timeoutTimer->start();
+ }
+}
+
+void QXmppOutgoingClient::pingTimeout()
+{
+ warning("Ping timeout");
+ disconnectFromHost();
+ emit error(QXmppClient::KeepAliveError);
+}
+
+// challenge is BASE64 encoded string
+void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep1(const QString& challenge)
+{
+ QByteArray ba = QByteArray::fromBase64(challenge.toUtf8());
+
+ QMap<QByteArray, QByteArray> map = parseDigestMd5(ba);
+
+ if (!map.contains("nonce"))
+ {
+ warning("sendAuthDigestMD5ResponseStep1: Invalid input");
+ disconnectFromHost();
+ return;
+ }
+
+ const QByteArray user = configuration().user().toUtf8();
+ const QByteArray passwd = configuration().passwd().toUtf8();
+ const QByteArray domain = configuration().domain().toUtf8();
+ const QByteArray realm = map.value("realm");
+
+ QByteArray cnonce(32, 'm');
+ for(int n = 0; n < cnonce.size(); ++n)
+ cnonce[n] = (char)(256.0*qrand()/(RAND_MAX+1.0));
+
+ // The random data can the '=' char is not valid as it is a delimiter,
+ // so to be safe, base64 the nonce
+ cnonce = cnonce.toBase64();
+
+ const QByteArray nc = "00000001";
+ const QByteArray digest_uri = "xmpp/" + domain;
+ const QByteArray authzid = map.value("authzid");
+ const QByteArray nonce = map.value("nonce");
+ const QByteArray a1 = user + ':' + realm + ':' + passwd;
+
+ // Build response
+ QMap<QByteArray, QByteArray> response;
+ response["username"] = user;
+ if(!realm.isEmpty())
+ response["realm"] = realm;
+ response["nonce"] = map["nonce"];
+ response["cnonce"] = cnonce;
+ response["nc"] = nc;
+ response["qop"] = "auth";
+ response["digest-uri"] = digest_uri;
+ response["response"] = calculateDigestMd5(a1, nonce, nc, cnonce, digest_uri, authzid).toHex();
+ if(!authzid.isEmpty())
+ response["authzid"] = authzid;
+ response["charset"] = "utf-8";
+
+ const QByteArray data = serializeDigestMd5(response);
+ QByteArray packet = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
+ + data.toBase64() + "</response>";
+ sendData(packet);
+}
+
+void QXmppOutgoingClient::sendAuthDigestMD5ResponseStep2()
+{
+ sendData("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
+}
+
+void QXmppOutgoingClient::sendNonSASLAuth(bool plainText)
+{
+ QXmppNonSASLAuthIq authQuery;
+ authQuery.setType(QXmppIq::Set);
+ authQuery.setUsername(configuration().user());
+ if (plainText)
+ authQuery.setPassword(configuration().passwd());
+ else
+ authQuery.setDigest(d->streamId, configuration().passwd());
+ authQuery.setResource(configuration().resource());
+ d->nonSASLAuthId = authQuery.id();
+ sendPacket(authQuery);
+}
+
+void QXmppOutgoingClient::sendNonSASLAuthQuery()
+{
+ QXmppNonSASLAuthIq authQuery;
+ authQuery.setType(QXmppIq::Get);
+ authQuery.setTo(d->streamFrom);
+ // FIXME : why are we setting the username, XEP-0078 states we should
+ // not attempt to guess the required fields?
+ authQuery.setUsername(configuration().user());
+ sendPacket(authQuery);
+}
+
+QAbstractSocket::SocketError QXmppOutgoingClient::socketError()
+{
+ return d->socketError;
+}
+
+QXmppStanza::Error::Condition QXmppOutgoingClient::xmppStreamError()
+{
+ return d->xmppStreamError;
+}
+
+QXmppDiscoveryIq QXmppOutgoingClient::capabilities() const
+{
+ QXmppDiscoveryIq iq;
+ iq.setType(QXmppIq::Result);
+ iq.setQueryType(QXmppDiscoveryIq::InfoQuery);
+
+ // features
+ QStringList features;
+ features
+ << ns_rpc // XEP-0009: Jabber-RPC
+ << ns_disco_info // XEP-0030: Service Discovery
+ << ns_ibb // XEP-0047: In-Band Bytestreams
+ << ns_vcard // XEP-0054: vcard-temp
+ << ns_bytestreams // XEP-0065: SOCKS5 Bytestreams
+ << ns_chat_states // XEP-0085: Chat State Notifications
+ << ns_version // XEP-0092: Software Version
+ << ns_stream_initiation // XEP-0095: Stream Initiation
+ << ns_stream_initiation_file_transfer // XEP-0096: SI File Transfer
+ << ns_capabilities // XEP-0115 : Entity Capabilities
+ << ns_jingle // XEP-0166 : Jingle
+ << ns_jingle_rtp // XEP-0167 : Jingle RTP Sessions
+ << ns_jingle_rtp_audio
+ << ns_jingle_ice_udp // XEP-0176 : Jingle ICE-UDP Transport Method
+ << ns_ping; // XEP-0199: XMPP Ping
+ iq.setFeatures(features);
+
+ // identities
+ QList<QXmppDiscoveryIq::Identity> identities;
+ QXmppDiscoveryIq::Identity identity;
+
+ identity.setCategory("automation");
+ identity.setType("rpc");
+ identities.append(identity);
+
+ identity.setCategory("client");
+ identity.setType("pc");
+ identity.setName(QString("%1 %2").arg(qApp->applicationName(), qApp->applicationVersion()));
+ identities.append(identity);
+
+ iq.setIdentities(identities);
+ return iq;
+}
+
+QXmppElementList QXmppOutgoingClient::presenceExtensions() const
+{
+ QXmppElementList extensions;
+
+ QXmppElement caps;
+ caps.setTagName("c");
+ caps.setAttribute("xmlns", ns_capabilities);
+ caps.setAttribute("hash", "sha-1");
+ caps.setAttribute("node", capabilitiesNode);
+ caps.setAttribute("ver", capabilities().verificationString().toBase64());
+ extensions << caps;
+
+ return extensions;
+}
+
diff --git a/src/QXmppOutgoingClient.h b/src/QXmppOutgoingClient.h
new file mode 100644
index 00000000..1e217c28
--- /dev/null
+++ b/src/QXmppOutgoingClient.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2008-2010 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.
+ *
+ */
+
+
+#ifndef QXMPPOUTGOINGCLIENT_H
+#define QXMPPOUTGOINGCLIENT_H
+
+#include "QXmppClient.h"
+#include "QXmppStanza.h"
+#include "QXmppStream.h"
+
+class QDomElement;
+class QSslError;
+
+class QXmppClient;
+class QXmppConfiguration;
+class QXmppPacket;
+class QXmppPresence;
+class QXmppIq;
+class QXmppBind;
+class QXmppRosterIq;
+class QXmppVCard;
+class QXmppMessage;
+class QXmppRpcResponseIq;
+class QXmppRpcErrorIq;
+class QXmppArchiveChatIq;
+class QXmppArchiveListIq;
+class QXmppArchivePrefIq;
+class QXmppByteStreamIq;
+class QXmppDiscoveryIq;
+class QXmppIbbCloseIq;
+class QXmppIbbDataIq;
+class QXmppIbbOpenIq;
+class QXmppJingleIq;
+class QXmppMucAdminIq;
+class QXmppMucOwnerIq;
+class QXmppStreamInitiationIq;
+class QXmppVersionIq;
+
+class QXmppOutgoingClientPrivate;
+
+/// \brief The QXmppStream class represents an outgoing client-to-server
+/// stream.
+///
+
+class QXmppOutgoingClient : public QXmppStream
+{
+ Q_OBJECT
+
+public:
+ QXmppOutgoingClient(QObject *parent);
+ ~QXmppOutgoingClient();
+
+ void connectToHost();
+
+ QAbstractSocket::SocketError socketError();
+ QXmppStanza::Error::Condition xmppStreamError();
+
+ QXmppConfiguration& configuration();
+
+ QXmppElementList presenceExtensions() const;
+
+signals:
+ void error(QXmppClient::Error);
+
+ void presenceReceived(const QXmppPresence&);
+ void messageReceived(const QXmppMessage&);
+ void iqReceived(const QXmppIq&);
+ void rosterIqReceived(const QXmppRosterIq&);
+
+ // XEP-0009: Jabber-RPC
+ void rpcCallInvoke(const QXmppRpcInvokeIq &invoke);
+ void rpcCallResponse(const QXmppRpcResponseIq& result);
+ void rpcCallError(const QXmppRpcErrorIq &err);
+
+ // XEP-0030: Service Discovery
+ void discoveryIqReceived(const QXmppDiscoveryIq&);
+
+ // XEP-0047 In-Band Bytestreams
+ void ibbCloseIqReceived(const QXmppIbbCloseIq&);
+ void ibbDataIqReceived(const QXmppIbbDataIq&);
+ void ibbOpenIqReceived(const QXmppIbbOpenIq&);
+
+ // XEP-0045: Multi-User Chat
+ void mucAdminIqReceived(const QXmppMucAdminIq&);
+ void mucOwnerIqReceived(const QXmppMucOwnerIq&);
+
+ // XEP-0054: vcard-temp
+ void vCardIqReceived(const QXmppVCard&);
+
+ // XEP-0065: SOCKS5 Bytestreams
+ void byteStreamIqReceived(const QXmppByteStreamIq&);
+
+ // XEP-0095: Stream Initiation
+ void streamInitiationIqReceived(const QXmppStreamInitiationIq&);
+
+ // XEP-0136: Message Archiving
+ void archiveChatIqReceived(const QXmppArchiveChatIq&);
+ void archiveListIqReceived(const QXmppArchiveListIq&);
+ void archivePrefIqReceived(const QXmppArchivePrefIq&);
+
+ // XEP-0166: Jingle
+ void jingleIqReceived(const QXmppJingleIq&);
+
+protected:
+ // Overridable methods
+ virtual void handleStart();
+ virtual void handleStanza(const QDomElement &element);
+ virtual void handleStream(const QDomElement &element);
+
+private slots:
+ void socketError(QAbstractSocket::SocketError);
+ void socketSslErrors(const QList<QSslError>&);
+
+ void pingStart();
+ void pingStop();
+ void pingSend();
+ void pingTimeout();
+
+private:
+ QXmppDiscoveryIq capabilities() const;
+ void sendAuthDigestMD5ResponseStep1(const QString& challenge);
+ void sendAuthDigestMD5ResponseStep2();
+ void sendNonSASLAuth(bool plaintext);
+ void sendNonSASLAuthQuery();
+
+ QXmppOutgoingClientPrivate * const d;
+};
+
+#endif // QXMPPOUTGOINGCLIENT_H
diff --git a/src/QXmppRosterManager.cpp b/src/QXmppRosterManager.cpp
index 684e6d1f..98907b64 100644
--- a/src/QXmppRosterManager.cpp
+++ b/src/QXmppRosterManager.cpp
@@ -27,14 +27,14 @@
#include "QXmppUtils.h"
#include "QXmppRosterIq.h"
#include "QXmppPresence.h"
-#include "QXmppStream.h"
+#include "QXmppOutgoingClient.h"
-QXmppRosterManager::QXmppRosterManager(QXmppStream* stream, QObject *parent)
+QXmppRosterManager::QXmppRosterManager(QXmppOutgoingClient* stream, QObject *parent)
: QObject(parent),
m_stream(stream),
m_isRosterReceived(false)
{
- bool check = QObject::connect(m_stream, SIGNAL(xmppConnected()),
+ bool check = QObject::connect(m_stream, SIGNAL(connected()),
this, SLOT(connected()));
Q_ASSERT(check);
diff --git a/src/QXmppRosterManager.h b/src/QXmppRosterManager.h
index bde31752..1e21b4e3 100644
--- a/src/QXmppRosterManager.h
+++ b/src/QXmppRosterManager.h
@@ -34,7 +34,7 @@
class QXmppRosterIq;
class QXmppPresence;
-class QXmppStream;
+class QXmppOutgoingClient;
/// \brief The QXmppRosterManager class provides access to a connected client's roster.
///
@@ -68,7 +68,7 @@ public:
// FIXME : is this class really necessary?
typedef QXmppRosterIq::Item QXmppRosterEntry;
- QXmppRosterManager(QXmppStream* stream, QObject *parent = 0);
+ QXmppRosterManager(QXmppOutgoingClient* stream, QObject *parent = 0);
~QXmppRosterManager();
bool isRosterReceived();
@@ -102,7 +102,7 @@ signals:
private:
//reverse pointer to stream
- QXmppStream* m_stream;
+ QXmppOutgoingClient* m_stream;
//map of bareJid and its rosterEntry
QMap<QString, QXmppRosterIq::Item> m_entries;
// map of resources of the jid and map of resources and presences
diff --git a/src/QXmppStream.cpp b/src/QXmppStream.cpp
index 0ff172d6..fd1f38f9 100644
--- a/src/QXmppStream.cpp
+++ b/src/QXmppStream.cpp
@@ -1,8 +1,9 @@
/*
* Copyright (C) 2008-2010 The QXmpp developers
*
- * Author:
+ * Authors:
* Manjeet Dahiya
+ * Jeremy Lainé
*
* Source:
* http://code.google.com/p/qxmpp
@@ -22,41 +23,20 @@
*/
-#include "QXmppConfiguration.h"
#include "QXmppConstants.h"
-#include "QXmppIq.h"
#include "QXmppLogger.h"
-#include "QXmppMessage.h"
#include "QXmppPacket.h"
-#include "QXmppPresence.h"
#include "QXmppStream.h"
-#include "QXmppStreamFeatures.h"
-#include "QXmppNonSASLAuth.h"
#include "QXmppUtils.h"
-// IQ types
-#include "QXmppArchiveIq.h"
-#include "QXmppBind.h"
-#include "QXmppByteStreamIq.h"
-#include "QXmppDiscoveryIq.h"
-#include "QXmppIbbIq.h"
-#include "QXmppJingleIq.h"
-#include "QXmppMucIq.h"
-#include "QXmppPingIq.h"
-#include "QXmppRpcIq.h"
-#include "QXmppRosterIq.h"
-#include "QXmppSession.h"
-#include "QXmppStreamInitiationIq.h"
-#include "QXmppVCard.h"
-#include "QXmppVersionIq.h"
-
-#include <QCoreApplication>
+#include <QBuffer>
#include <QDomDocument>
-#include <QStringList>
-#include <QRegExp>
#include <QHostAddress>
+#include <QRegExp>
+#include <QSslSocket>
+#include <QStringList>
+#include <QTime>
#include <QXmlStreamWriter>
-#include <QTimer>
static bool randomSeeded = false;
static const QString capabilitiesNode = "http://code.google.com/p/qxmpp";
@@ -67,39 +47,25 @@ class QXmppStreamPrivate
public:
QXmppStreamPrivate();
- // This object provides the configuration
- // required for connecting to the XMPP server.
- QXmppConfiguration config;
QByteArray dataBuffer;
QXmppLogger* logger;
QSslSocket* socket;
- QAbstractSocket::SocketError socketError;
- QXmppStanza::Error::Condition xmppStreamError;
- // State data
- int authStep;
- QString bindId;
- QString sessionId;
- bool sessionAvailable;
- QString streamId;
- QString streamFrom;
+ // stream state
QByteArray streamStart;
- QString streamVersion;
- QString nonSASLAuthId;
-
- // Timers
- QTimer *pingTimer;
- QTimer *timeoutTimer;
};
QXmppStreamPrivate::QXmppStreamPrivate()
: logger(0),
- authStep(0),
- sessionAvailable(false)
+ socket(0)
{
}
-QXmppStream::QXmppStream(QSslSocket *socket, QObject *parent)
+/// Constructs a base XMPP stream.
+///
+/// \param parent
+
+QXmppStream::QXmppStream(QObject *parent)
: QObject(parent),
d(new QXmppStreamPrivate)
{
@@ -109,165 +75,185 @@ QXmppStream::QXmppStream(QSslSocket *socket, QObject *parent)
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
randomSeeded = true;
}
-
- // initialise logger
- setLogger(QXmppLogger::getLogger());
-
- // create socket
- d->socket = socket;
-
- bool check = QObject::connect(d->socket, SIGNAL(hostFound()),
- this, SLOT(socketHostFound()));
- Q_ASSERT(check);
- check = QObject::connect(d->socket, SIGNAL(connected()),
- this, SLOT(socketConnected()));
- Q_ASSERT(check);
- check = QObject::connect(d->socket, SIGNAL(disconnected()),
- this, SLOT(socketDisconnected()));
- Q_ASSERT(check);
- check = QObject::connect(d->socket, SIGNAL(readyRead()),
- this, SLOT(socketReadReady()));
- Q_ASSERT(check);
- check = QObject::connect(d->socket, SIGNAL(encrypted()),
- this, SLOT(socketEncrypted()));
- Q_ASSERT(check);
- check = QObject::connect(d->socket,
- SIGNAL(sslErrors(const QList<QSslError>&)), this,
- SLOT(socketSslErrors(const QList<QSslError>&)));
- Q_ASSERT(check);
- check = QObject::connect(d->socket,
- SIGNAL(error(QAbstractSocket::SocketError)), this,
- SLOT(socketError(QAbstractSocket::SocketError)));
- Q_ASSERT(check);
-
- // XEP-0199: XMPP Ping
- d->pingTimer = new QTimer(this);
- check = QObject::connect(d->pingTimer, SIGNAL(timeout()), this, SLOT(pingSend()));
- Q_ASSERT(check);
-
- d->timeoutTimer = new QTimer(this);
- d->timeoutTimer->setSingleShot(true);
- check = QObject::connect(d->timeoutTimer, SIGNAL(timeout()), this, SLOT(pingTimeout()));
- Q_ASSERT(check);
-
- check = QObject::connect(this, SIGNAL(xmppConnected()), this, SLOT(pingStart()));
- Q_ASSERT(check);
-
- check = QObject::connect(this, SIGNAL(disconnected()), this, SLOT(pingStop()));
- Q_ASSERT(check);
}
+/// Destroys a base XMPP stream.
+
QXmppStream::~QXmppStream()
{
delete d;
}
-QXmppConfiguration& QXmppStream::configuration()
+/// Disconnects from the remote host.
+///
+
+void QXmppStream::disconnectFromHost()
{
- return d->config;
+ sendData(streamRootElementEnd);
+ if (d->socket)
+ {
+ d->socket->flush();
+ d->socket->disconnectFromHost();
+ }
}
-void QXmppStream::connectToHost()
+void QXmppStream::handleStart()
{
- info(QString("Connecting to: %1:%2").arg(configuration().
- host()).arg(configuration().port()));
+}
- // prepare for connection
- d->authStep = 0;
+/// Returns the QXmppLogger associated with the current stream.
- d->socket->setProxy(configuration().networkProxy());
- d->socket->connectToHost(configuration().
- host(), configuration().port());
+QXmppLogger *QXmppStream::logger()
+{
+ return d->logger;
}
-void QXmppStream::socketSslErrors(const QList<QSslError> & error)
+/// Sets the QXmppLogger associated with the current stream.
+
+void QXmppStream::setLogger(QXmppLogger *logger)
{
- warning("SSL errors");
- for(int i = 0; i< error.count(); ++i)
- warning(error.at(i).errorString());
+ if (d->logger)
+ disconnect(this, SIGNAL(logMessage(QXmppLogger::MessageType, QString)),
+ d->logger, SLOT(log(QXmppLogger::MessageType, QString)));
+ d->logger = logger;
+ if (d->logger)
+ connect(this, SIGNAL(logMessage(QXmppLogger::MessageType, QString)),
+ d->logger, SLOT(log(QXmppLogger::MessageType, QString)));
+}
- if (configuration().ignoreSslErrors())
- d->socket->ignoreSslErrors();
+void QXmppStream::debug(const QString &data)
+{
+ emit logMessage(QXmppLogger::DebugMessage, data);
}
-void QXmppStream::socketHostFound()
+void QXmppStream::info(const QString &data)
{
- debug("Host found");
- emit hostFound();
+ emit logMessage(QXmppLogger::InformationMessage, data);
}
-void QXmppStream::socketConnected()
+void QXmppStream::warning(const QString &data)
{
- flushDataBuffer();
- info("Connected");
- emit connected();
- sendStartStream();
+ emit logMessage(QXmppLogger::WarningMessage, data);
}
-void QXmppStream::socketDisconnected()
+/// Returns true if the stream is connected.
+///
+
+bool QXmppStream::isConnected() const
{
- flushDataBuffer();
- info("Disconnected");
- emit disconnected();
+ return d->socket &&
+ d->socket->state() == QAbstractSocket::ConnectedState;
}
-void QXmppStream::socketEncrypted()
+/// Sends raw data to the peer.
+///
+/// \param data
+
+bool QXmppStream::sendData(const QByteArray &data)
{
- debug("Encrypted");
- sendStartStream();
+ emit logMessage(QXmppLogger::SentMessage, QString::fromUtf8(data));
+ if (!d->socket || d->socket->state() != QAbstractSocket::ConnectedState)
+ return false;
+ return d->socket->write(data) == data.size();
}
-void QXmppStream::socketError(QAbstractSocket::SocketError ee)
+/// Sends an XML element to the peer.
+///
+/// \param element
+
+bool QXmppStream::sendElement(const QDomElement &element)
{
- d->socketError = ee;
- emit error(QXmppClient::SocketError);
- warning(QString("Socket error: " + d->socket->errorString()));
+ // prepare packet
+ QBuffer buffer;
+ buffer.open(QIODevice::ReadWrite);
+ QXmlStreamWriter writer(&buffer);
+ const QStringList omitNamespaces = QStringList() << ns_client << ns_server;
+ helperToXmlAddDomElement(&writer, element, omitNamespaces);
+
+ // send packet
+ return sendData(buffer.data());
}
-void QXmppStream::socketReadReady()
+/// Sends an XMPP packet to the peer.
+///
+/// \param packet
+
+bool QXmppStream::sendPacket(const QXmppPacket &packet)
{
- const QByteArray data = d->socket->readAll();
- //debug("SERVER [COULD BE PARTIAL DATA]:" + data.left(20));
- parser(data);
+ // prepare packet
+ QByteArray data;
+ QXmlStreamWriter xmlStream(&data);
+ packet.toXml(&xmlStream);
+
+ // send packet
+ return sendData(data);
}
-/// Returns the QXmppLogger associated with the current QXmppStream.
+/// Returns the QSslSocket used for this stream.
+///
-QXmppLogger *QXmppStream::logger()
+QSslSocket *QXmppStream::socket()
{
- return d->logger;
+ return d->socket;
}
-/// Sets the QXmppLogger associated with the current QXmppStream.
+/// Sets the QSslSocket used for this stream.
+///
-void QXmppStream::setLogger(QXmppLogger *logger)
+void QXmppStream::setSocket(QSslSocket *socket)
{
- if (d->logger)
- QObject::disconnect(this, SIGNAL(logMessage(QXmppLogger::MessageType, QString)),
- d->logger, SLOT(log(QXmppLogger::MessageType, QString)));
- d->logger = logger;
- if (d->logger)
- QObject::connect(this, SIGNAL(logMessage(QXmppLogger::MessageType, QString)),
- d->logger, SLOT(log(QXmppLogger::MessageType, QString)));
+ d->socket = socket;
+
+ // socket events
+ bool check = connect(socket, SIGNAL(connected()),
+ this, SLOT(socketConnected()));
+ Q_ASSERT(check);
+
+ check = connect(socket, SIGNAL(disconnected()),
+ this, SLOT(socketDisconnected()));
+ Q_ASSERT(check);
+
+ check = connect(socket, SIGNAL(encrypted()),
+ this, SLOT(socketEncrypted()));
+ Q_ASSERT(check);
+
+ check = connect(socket, SIGNAL(readyRead()),
+ this, SLOT(socketReadyRead()));
+ Q_ASSERT(check);
+
+ // relay signals
+ check = connect(socket, SIGNAL(disconnected()),
+ this, SIGNAL(disconnected()));
+ Q_ASSERT(check);
}
-void QXmppStream::debug(const QString &data)
+void QXmppStream::socketConnected()
{
- emit logMessage(QXmppLogger::DebugMessage, data);
+ info(QString("Socket connected to %1 %2").arg(
+ socket()->peerAddress().toString(),
+ QString::number(socket()->peerPort())));
+ d->dataBuffer.clear();
+ handleStart();
}
-void QXmppStream::info(const QString &data)
+void QXmppStream::socketDisconnected()
{
- emit logMessage(QXmppLogger::InformationMessage, data);
+ info("Socket disconnected");
+ d->dataBuffer.clear();
}
-void QXmppStream::warning(const QString &data)
+void QXmppStream::socketEncrypted()
{
- emit logMessage(QXmppLogger::WarningMessage, data);
+ debug("Socket encrypted");
+ d->dataBuffer.clear();
+ handleStart();
}
-void QXmppStream::parser(const QByteArray& data)
+void QXmppStream::socketReadyRead()
{
+ const QByteArray data = socket()->readAll();
+ //debug("SERVER [COULD BE PARTIAL DATA]:" + data.left(20));
+
d->dataBuffer.append(data);
// FIXME : maybe these QRegExps could be static?
@@ -298,7 +284,7 @@ void QXmppStream::parser(const QByteArray& data)
// remove data from buffer
emit logMessage(QXmppLogger::ReceivedMessage, strData);
- flushDataBuffer();
+ d->dataBuffer.clear();
// process stream start
QDomElement nodeRecv = doc.documentElement().firstChildElement();
@@ -311,734 +297,9 @@ void QXmppStream::parser(const QByteArray& data)
// process stanzas
while(!nodeRecv.isNull())
{
- // if we receive any kind of data, stop the timeout timer
- d->timeoutTimer->stop();
-
handleStanza(nodeRecv);
nodeRecv = nodeRecv.nextSiblingElement();
}
}
-void QXmppStream::handleStream(const QDomElement &streamElement)
-{
- if(d->streamId.isEmpty())
- d->streamId = streamElement.attribute("id");
- if (d->streamFrom.isEmpty())
- d->streamFrom = streamElement.attribute("from");
- if(d->streamVersion.isEmpty())
- {
- d->streamVersion = streamElement.attribute("version");
-
- // no version specified, signals XMPP Version < 1.0.
- // switch to old auth mechanism
- if(d->streamVersion.isEmpty())
- sendNonSASLAuthQuery();
- }
-}
-
-void QXmppStream::handleStanza(const QDomElement &nodeRecv)
-{
- const QString ns = nodeRecv.namespaceURI();
-
- // give client opportunity to handle stanza
- bool handled = false;
- emit elementReceived(nodeRecv, handled);
- if (handled)
- return;
-
- if(QXmppStreamFeatures::isStreamFeatures(nodeRecv))
- {
- QXmppStreamFeatures features;
- features.parse(nodeRecv);
-
- const bool nonSaslAvailable = features.isNonSaslAuthAvailable();
- const bool saslAvailable = !features.authMechanisms().isEmpty();
- const bool useSasl = configuration().useSASLAuthentication();
-
- if (!d->socket->isEncrypted())
- {
- // determine TLS mode to use
- const QXmppConfiguration::StreamSecurityMode localSecurity = configuration().streamSecurityMode();
- const QXmppConfiguration::StreamSecurityMode remoteSecurity = features.securityMode();
- if (!d->socket->supportsSsl() &&
- (localSecurity == QXmppConfiguration::TLSRequired ||
- remoteSecurity == QXmppConfiguration::TLSRequired))
- {
- warning("Disconnecting as TLS is required, but SSL support is not available");
- disconnectFromHost();
- return;
- }
- if (localSecurity == QXmppConfiguration::TLSRequired &&
- remoteSecurity == QXmppConfiguration::TLSDisabled)
- {
- warning("Disconnecting as TLS is required, but not supported by the server");
- disconnectFromHost();
- return;
- }
-
- if (d->socket->supportsSsl() &&
- remoteSecurity != QXmppConfiguration::TLSDisabled &&
- localSecurity != QXmppConfiguration::TLSDisabled)
- {
- // enable TLS as it is support by both parties
- sendData("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
- return;
- }
- }
-
- // handle authentication
- if((saslAvailable && nonSaslAvailable && !useSasl) ||
- (!saslAvailable && nonSaslAvailable))
- {
- sendNonSASLAuthQuery();
- }
- else if(saslAvailable)
- {
- // determine SASL Authentication mechanism to use
- QList<QXmppConfiguration::SASLAuthMechanism> mechanisms = features.authMechanisms();
- QXmppConfiguration::SASLAuthMechanism mechanism = configuration().sASLAuthMechanism();
- if (mechanisms.isEmpty())
- {
- warning("No supported SASL Authentication mechanism available");
- disconnectFromHost();
- return;
- }
- else if (!mechanisms.contains(mechanism))
- {
- info("Desired SASL Auth mechanism is not available, selecting first available one");
- mechanism = mechanisms.first();
- }
-
- // send SASL Authentication request
- switch(mechanism)
- {
- case QXmppConfiguration::SASLPlain:
- {
- QString userPass('\0' + configuration().user() +
- '\0' + configuration().passwd());
- QByteArray data = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>";
- data += userPass.toUtf8().toBase64();
- data += "</auth>";
- sendData(data);
- }
- break;
- case QXmppConfiguration::SASLDigestMD5:
- sendData("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>");
- break;
- case QXmppConfiguration::SASLAnonymous:
- sendData("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>");
- break;
- }
- }
-
- // check whether bind is available
- if (features.isBindAvailable())
- sendBindIQ();
-
- // check whether session is available
- if (features.isSessionAvailable())
- d->sessionAvailable = true;
- }
- else if(ns == ns_stream && nodeRecv.tagName() == "error")
- {
- if (!nodeRecv.firstChildElement("conflict").isNull())
- d->xmppStreamError = QXmppStanza::Error::Conflict;
- else
- d->xmppStreamError = QXmppStanza::Error::UndefinedCondition;
- emit error(QXmppClient::XmppStreamError);
- }
- else if(ns == ns_tls)
- {
- if(nodeRecv.tagName() == "proceed")
- {
- debug("Starting encryption");
- d->socket->startClientEncryption();
- return;
- }
- }
- else if(ns == ns_sasl)
- {
- if(nodeRecv.tagName() == "success")
- {
- debug("Authenticated");
- sendStartStream();
- }
- else if(nodeRecv.tagName() == "challenge")
- {
- // TODO: Track which mechanism was used for when other SASL protocols which use challenges are supported
- d->authStep++;
- switch (d->authStep)
- {
- case 1 :
- sendAuthDigestMD5ResponseStep1(nodeRecv.text());
- break;
- case 2 :
- sendAuthDigestMD5ResponseStep2();
- break;
- default :
- warning("Too many authentication steps");
- disconnectFromHost();
- break;
- }
- }
- else if(nodeRecv.tagName() == "failure")
- {
- if (!nodeRecv.firstChildElement("not-authorized").isNull())
- d->xmppStreamError = QXmppStanza::Error::NotAuthorized;
- else
- d->xmppStreamError = QXmppStanza::Error::UndefinedCondition;
- emit error(QXmppClient::XmppStreamError);
-
- warning("Authentication failure");
- disconnectFromHost();
- }
- }
- else if(ns == ns_client)
- {
-
- if(nodeRecv.tagName() == "iq")
- {
- QDomElement element = nodeRecv.firstChildElement();
- QString id = nodeRecv.attribute("id");
- QString type = nodeRecv.attribute("type");
- if(type.isEmpty())
- warning("QXmppStream: iq type can't be empty");
-
- if(id == d->sessionId)
- {
- QXmppSession session;
- session.parse(nodeRecv);
-
- // get back add configuration whether to send
- // roster and intial presence in beginning
- // process SessionIq
-
- // xmpp connection made
- emit xmppConnected();
- }
- else if(QXmppBind::isBind(nodeRecv) && id == d->bindId)
- {
- QXmppBind bind;
- bind.parse(nodeRecv);
-
- // bind result
- if (bind.type() == QXmppIq::Result)
- {
- if (!bind.jid().isEmpty())
- {
- QRegExp jidRegex("^([^@/]+)@([^@/]+)/(.+)$");
- if (jidRegex.exactMatch(bind.jid()))
- {
- configuration().setUser(jidRegex.cap(1));
- configuration().setDomain(jidRegex.cap(2));
- configuration().setResource(jidRegex.cap(3));
- } else {
- warning("Bind IQ received with invalid JID: " + bind.jid());
- }
- }
- if (d->sessionAvailable)
- sendSessionIQ();
- }
- }
- else if(QXmppRosterIq::isRosterIq(nodeRecv))
- {
- QXmppRosterIq rosterIq;
- rosterIq.parse(nodeRecv);
- emit rosterIqReceived(rosterIq);
- }
- // extensions
-
- // XEP-0009: Jabber-RPC
- else if(QXmppRpcInvokeIq::isRpcInvokeIq(nodeRecv))
- {
- QXmppRpcInvokeIq rpcIqPacket;
- rpcIqPacket.parse(nodeRecv);
- emit rpcCallInvoke(rpcIqPacket);
- }
- else if(QXmppRpcResponseIq::isRpcResponseIq(nodeRecv))
- {
- QXmppRpcResponseIq rpcResponseIq;
- rpcResponseIq.parse(nodeRecv);
- emit rpcCallResponse(rpcResponseIq);
- }
- else if(QXmppRpcErrorIq::isRpcErrorIq(nodeRecv))
- {
- QXmppRpcErrorIq rpcErrorIq;
- rpcErrorIq.parse(nodeRecv);
- emit rpcCallError(rpcErrorIq);
- }
-
- // XEP-0030: Service Discovery
- else if(QXmppDiscoveryIq::isDiscoveryIq(nodeRecv))
- {
- QXmppDiscoveryIq discoIq;
- discoIq.parse(nodeRecv);
-
- if (discoIq.type() == QXmppIq::Get &&
- discoIq.queryType() == QXmppDiscoveryIq::InfoQuery &&
- (discoIq.queryNode().isEmpty() || discoIq.queryNode().startsWith(capabilitiesNode)))
- {
- // respond to info query
- QXmppDiscoveryIq qxmppFeatures = capabilities();
- qxmppFeatures.setId(discoIq.id());
- qxmppFeatures.setTo(discoIq.from());
- qxmppFeatures.setQueryNode(discoIq.queryNode());
- sendPacket(qxmppFeatures);
- } else {
- emit discoveryIqReceived(discoIq);
- }
- }
- // XEP-0045: Multi-User Chat
- else if (QXmppMucAdminIq::isMucAdminIq(nodeRecv))
- {
- QXmppMucAdminIq mucIq;
- mucIq.parse(nodeRecv);
- emit mucAdminIqReceived(mucIq);
- }
- else if (QXmppMucOwnerIq::isMucOwnerIq(nodeRecv))
- {
- QXmppMucOwnerIq mucIq;
- mucIq.parse(nodeRecv);
- emit mucOwnerIqReceived(mucIq);
- }
- // XEP-0047 In-Band Bytestreams
- else if(QXmppIbbCloseIq::isIbbCloseIq(nodeRecv))
- {
- QXmppIbbCloseIq ibbCloseIq;
- ibbCloseIq.parse(nodeRecv);
- emit ibbCloseIqReceived(ibbCloseIq);
- }
- else if(QXmppIbbDataIq::isIbbDataIq(nodeRecv))
- {
- QXmppIbbDataIq ibbDataIq;
- ibbDataIq.parse(nodeRecv);
- emit ibbDataIqReceived(ibbDataIq);
- }
- else if(QXmppIbbOpenIq::isIbbOpenIq(nodeRecv))
- {
- QXmppIbbOpenIq ibbOpenIq;
- ibbOpenIq.parse(nodeRecv);
- emit ibbOpenIqReceived(ibbOpenIq);
- }
- // XEP-0054: vcard-temp
- else if(nodeRecv.firstChildElement("vCard").
- namespaceURI() == ns_vcard)
- {
- QXmppVCard vcardIq;
- vcardIq.parse(nodeRecv);
- emit vCardIqReceived(vcardIq);
- }
- // XEP-0065: SOCKS5 Bytestreams
- else if(QXmppByteStreamIq::isByteStreamIq(nodeRecv))
- {
- QXmppByteStreamIq byteStreamIq;
- byteStreamIq.parse(nodeRecv);
- emit byteStreamIqReceived(byteStreamIq);
- }
- // XEP-0078: Non-SASL Authentication
- else if(id == d->nonSASLAuthId && type == "result")
- {
- // successful Non-SASL Authentication
- debug("Authenticated (Non-SASL)");
-
- // xmpp connection made
- emit xmppConnected();
- }
- else if(QXmppNonSASLAuthIq::isNonSASLAuthIq(nodeRecv))
- {
- if(type == "result")
- {
- bool digest = !nodeRecv.firstChildElement("query").
- firstChildElement("digest").isNull();
- bool plain = !nodeRecv.firstChildElement("query").
- firstChildElement("password").isNull();
- bool plainText = false;
-
- if(plain && digest)
- {
- if(configuration().nonSASLAuthMechanism() ==
- QXmppConfiguration::NonSASLDigest)
- plainText = false;
- else
- plainText = true;
- }
- else if(plain)
- plainText = true;
- else if(digest)
- plainText = false;
- else
- {
- warning("No supported Non-SASL Authentication mechanism available");
- disconnectFromHost();
- return;
- }
- sendNonSASLAuth(plainText);
- }
- }
- // XEP-0092: Software Version
- else if(QXmppVersionIq::isVersionIq(nodeRecv))
- {
- QXmppVersionIq versionIq;
- versionIq.parse(nodeRecv);
-
- if (versionIq.type() == QXmppIq::Get)
- {
- // respond to query
- QXmppVersionIq responseIq;
- responseIq.setType(QXmppIq::Result);
- responseIq.setId(versionIq.id());
- responseIq.setTo(versionIq.from());
- responseIq.setName(qApp->applicationName());
- responseIq.setVersion(qApp->applicationVersion());
- sendPacket(responseIq);
- } else {
- emit iqReceived(versionIq);
- }
-
- }
- // XEP-0095: Stream Initiation
- else if(QXmppStreamInitiationIq::isStreamInitiationIq(nodeRecv))
- {
- QXmppStreamInitiationIq siIq;
- siIq.parse(nodeRecv);
- emit streamInitiationIqReceived(siIq);
- }
- // XEP-0136: Message Archiving
- else if(QXmppArchiveChatIq::isArchiveChatIq(nodeRecv))
- {
- QXmppArchiveChatIq archiveIq;
- archiveIq.parse(nodeRecv);
- emit archiveChatIqReceived(archiveIq);
- }
- else if(QXmppArchiveListIq::isArchiveListIq(nodeRecv))
- {
- QXmppArchiveListIq archiveIq;
- archiveIq.parse(nodeRecv);
- emit archiveListIqReceived(archiveIq);
- }
- else if(QXmppArchivePrefIq::isArchivePrefIq(nodeRecv))
- {
- QXmppArchivePrefIq archiveIq;
- archiveIq.parse(nodeRecv);
- emit archivePrefIqReceived(archiveIq);
- }
- // XEP-0166: Jingle
- else if(QXmppJingleIq::isJingleIq(nodeRecv))
- {
- QXmppJingleIq jingleIq;
- jingleIq.parse(nodeRecv);
- emit jingleIqReceived(jingleIq);
- }
- // XEP-0199: XMPP Ping
- else if(QXmppPingIq::isPingIq(nodeRecv))
- {
- QXmppPingIq req;
- req.parse(nodeRecv);
-
- QXmppIq iq(QXmppIq::Result);
- iq.setId(req.id());
- iq.setTo(req.from());
- iq.setFrom(req.to());
- sendPacket(iq);
- }
- else
- {
- QXmppIq iqPacket;
- iqPacket.parse(nodeRecv);
-
- // if we didn't understant the iq, reply with error
- // except for "result" and "error" iqs
- if (type != "result" && type != "error")
- {
- QXmppIq iq(QXmppIq::Error);
- iq.setId(iqPacket.id());
- iq.setTo(iqPacket.from());
- iq.setFrom(iqPacket.to());
- QXmppStanza::Error error(QXmppStanza::Error::Cancel,
- QXmppStanza::Error::FeatureNotImplemented);
- iq.setError(error);
- sendPacket(iq);
- } else {
- emit iqReceived(iqPacket);
- }
- }
- }
- else if(nodeRecv.tagName() == "presence")
- {
- QXmppPresence presence;
- presence.parse(nodeRecv);
-
- // emit presence
- emit presenceReceived(presence);
- }
- else if(nodeRecv.tagName() == "message")
- {
- QXmppMessage message;
- message.parse(nodeRecv);
-
- // emit message
- emit messageReceived(message);
- }
- }
-}
-
-bool QXmppStream::sendStartStream()
-{
- QByteArray data = "<?xml version='1.0'?><stream:stream to='";
- data.append(configuration().domain());
- data.append("' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>");
- return sendData(data);
-}
-
-bool QXmppStream::sendData(const QByteArray& packet)
-{
- emit logMessage(QXmppLogger::SentMessage, QString::fromUtf8(packet));
- if (!isConnected())
- return false;
- return d->socket->write( packet ) == packet.size();
-}
-
-void QXmppStream::sendNonSASLAuth(bool plainText)
-{
- QXmppNonSASLAuthIq authQuery;
- authQuery.setType(QXmppIq::Set);
- authQuery.setUsername(configuration().user());
- if (plainText)
- authQuery.setPassword(configuration().passwd());
- else
- authQuery.setDigest(d->streamId, configuration().passwd());
- authQuery.setResource(configuration().resource());
- d->nonSASLAuthId = authQuery.id();
- sendPacket(authQuery);
-}
-
-void QXmppStream::sendNonSASLAuthQuery()
-{
- QXmppNonSASLAuthIq authQuery;
- authQuery.setType(QXmppIq::Get);
- authQuery.setTo(d->streamFrom);
- // FIXME : why are we setting the username, XEP-0078 states we should
- // not attempt to guess the required fields?
- authQuery.setUsername(configuration().user());
- sendPacket(authQuery);
-}
-
-// challenge is BASE64 encoded string
-void QXmppStream::sendAuthDigestMD5ResponseStep1(const QString& challenge)
-{
- QByteArray ba = QByteArray::fromBase64(challenge.toUtf8());
-
- QMap<QByteArray, QByteArray> map = parseDigestMd5(ba);
-
- if (!map.contains("nonce"))
- {
- warning("sendAuthDigestMD5ResponseStep1: Invalid input");
- disconnectFromHost();
- return;
- }
-
- const QByteArray user = configuration().user().toUtf8();
- const QByteArray passwd = configuration().passwd().toUtf8();
- const QByteArray domain = configuration().domain().toUtf8();
- const QByteArray realm = map.value("realm");
-
- QByteArray cnonce(32, 'm');
- for(int n = 0; n < cnonce.size(); ++n)
- cnonce[n] = (char)(256.0*qrand()/(RAND_MAX+1.0));
-
- // The random data can the '=' char is not valid as it is a delimiter,
- // so to be safe, base64 the nonce
- cnonce = cnonce.toBase64();
-
- const QByteArray nc = "00000001";
- const QByteArray digest_uri = "xmpp/" + domain;
- const QByteArray authzid = map.value("authzid");
- const QByteArray nonce = map.value("nonce");
- const QByteArray a1 = user + ':' + realm + ':' + passwd;
-
- // Build response
- QMap<QByteArray, QByteArray> response;
- response["username"] = user;
- if(!realm.isEmpty())
- response["realm"] = realm;
- response["nonce"] = map["nonce"];
- response["cnonce"] = cnonce;
- response["nc"] = nc;
- response["qop"] = "auth";
- response["digest-uri"] = digest_uri;
- response["response"] = calculateDigestMd5(a1, nonce, nc, cnonce, digest_uri, authzid).toHex();
- if(!authzid.isEmpty())
- response["authzid"] = authzid;
- response["charset"] = "utf-8";
-
- const QByteArray data = serializeDigestMd5(response);
- QByteArray packet = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
- + data.toBase64() + "</response>";
- sendData(packet);
-}
-
-void QXmppStream::sendAuthDigestMD5ResponseStep2()
-{
- sendData("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
-}
-
-void QXmppStream::sendBindIQ()
-{
- QXmppBind bind(QXmppIq::Set);
- bind.setResource(configuration().resource());
- d->bindId = bind.id();
- sendPacket(bind);
-}
-
-void QXmppStream::sendSessionIQ()
-{
- QXmppSession session(QXmppIq::Set);
- session.setTo(configuration().domain());
- d->sessionId = session.id();
- sendPacket(session);
-}
-
-void QXmppStream::disconnectFromHost()
-{
- d->authStep = 0;
- sendEndStream();
- d->socket->flush();
- d->socket->disconnectFromHost();
-}
-
-bool QXmppStream::isConnected() const
-{
- return d->socket->state() == QAbstractSocket::ConnectedState;
-}
-
-bool QXmppStream::sendPacket(const QXmppPacket& packet)
-{
- // prepare packet
- QByteArray data;
- QXmlStreamWriter xmlStream(&data);
- packet.toXml(&xmlStream);
-
- // send packet
- return sendData(data);
-}
-
-bool QXmppStream::sendEndStream()
-{
- return sendData(streamRootElementEnd);
-}
-
-void QXmppStream::pingStart()
-{
- const int interval = configuration().keepAliveInterval();
- // start ping timer
- if (interval > 0)
- {
- d->pingTimer->setInterval(interval * 1000);
- d->pingTimer->start();
- }
-}
-
-void QXmppStream::pingStop()
-{
- // stop all timers
- d->pingTimer->stop();
- d->timeoutTimer->stop();
-}
-
-void QXmppStream::pingSend()
-{
- // send ping packet
- QXmppPingIq ping;
- ping.setFrom(configuration().jid());
- ping.setTo(configuration().domain());
- sendPacket(ping);
-
- // start timeout timer
- const int timeout = configuration().keepAliveTimeout();
- if (timeout > 0)
- {
- d->timeoutTimer->setInterval(timeout * 1000);
- d->timeoutTimer->start();
- }
-}
-
-void QXmppStream::pingTimeout()
-{
- warning("Ping timeout");
- disconnectFromHost();
- emit error(QXmppClient::KeepAliveError);
-}
-
-QAbstractSocket::SocketError QXmppStream::socketError()
-{
- return d->socketError;
-}
-
-QXmppStanza::Error::Condition QXmppStream::xmppStreamError()
-{
- return d->xmppStreamError;
-}
-
-void QXmppStream::flushDataBuffer()
-{
- d->dataBuffer.clear();
-}
-
-QXmppDiscoveryIq QXmppStream::capabilities() const
-{
- QXmppDiscoveryIq iq;
- iq.setType(QXmppIq::Result);
- iq.setQueryType(QXmppDiscoveryIq::InfoQuery);
-
- // features
- QStringList features;
- features
- << ns_rpc // XEP-0009: Jabber-RPC
- << ns_disco_info // XEP-0030: Service Discovery
- << ns_ibb // XEP-0047: In-Band Bytestreams
- << ns_vcard // XEP-0054: vcard-temp
- << ns_bytestreams // XEP-0065: SOCKS5 Bytestreams
- << ns_chat_states // XEP-0085: Chat State Notifications
- << ns_version // XEP-0092: Software Version
- << ns_stream_initiation // XEP-0095: Stream Initiation
- << ns_stream_initiation_file_transfer // XEP-0096: SI File Transfer
- << ns_capabilities // XEP-0115 : Entity Capabilities
- << ns_jingle // XEP-0166 : Jingle
- << ns_jingle_rtp // XEP-0167 : Jingle RTP Sessions
- << ns_jingle_rtp_audio
- << ns_jingle_ice_udp // XEP-0176 : Jingle ICE-UDP Transport Method
- << ns_ping; // XEP-0199: XMPP Ping
- iq.setFeatures(features);
-
- // identities
- QList<QXmppDiscoveryIq::Identity> identities;
- QXmppDiscoveryIq::Identity identity;
-
- identity.setCategory("automation");
- identity.setType("rpc");
- identities.append(identity);
-
- identity.setCategory("client");
- identity.setType("pc");
- identity.setName(QString("%1 %2").arg(qApp->applicationName(), qApp->applicationVersion()));
- identities.append(identity);
-
- iq.setIdentities(identities);
- return iq;
-}
-
-QXmppElementList QXmppStream::presenceExtensions() const
-{
- QXmppElementList extensions;
-
- QXmppElement caps;
- caps.setTagName("c");
- caps.setAttribute("xmlns", ns_capabilities);
- caps.setAttribute("hash", "sha-1");
- caps.setAttribute("node", capabilitiesNode);
- caps.setAttribute("ver", capabilities().verificationString().toBase64());
- extensions << caps;
-
- return extensions;
-}
diff --git a/src/QXmppStream.h b/src/QXmppStream.h
index ddfe9d9e..ccc11c93 100644
--- a/src/QXmppStream.h
+++ b/src/QXmppStream.h
@@ -1,8 +1,9 @@
/*
* Copyright (C) 2008-2010 The QXmpp developers
*
- * Author:
+ * Authors:
* Manjeet Dahiya
+ * Jeremy Lainé
*
* Source:
* http://code.google.com/p/qxmpp
@@ -26,146 +27,69 @@
#define QXMPPSTREAM_H
#include <QObject>
-#include <QSslSocket>
-#include "QXmppClient.h"
#include "QXmppLogger.h"
-#include "QXmppStanza.h"
class QDomElement;
-
-class QXmppClient;
-class QXmppConfiguration;
+class QSslSocket;
class QXmppPacket;
-class QXmppPresence;
-class QXmppIq;
-class QXmppBind;
-class QXmppRosterIq;
-class QXmppVCard;
-class QXmppMessage;
-class QXmppRpcResponseIq;
-class QXmppRpcErrorIq;
-class QXmppArchiveChatIq;
-class QXmppArchiveListIq;
-class QXmppArchivePrefIq;
-class QXmppByteStreamIq;
-class QXmppDiscoveryIq;
-class QXmppIbbCloseIq;
-class QXmppIbbDataIq;
-class QXmppIbbOpenIq;
-class QXmppJingleIq;
-class QXmppMucAdminIq;
-class QXmppMucOwnerIq;
-class QXmppStreamInitiationIq;
class QXmppStreamPrivate;
-class QXmppVersionIq;
+
+/// \brief The QXmppStream class is the base class for all XMPP streams.
+///
class QXmppStream : public QObject
{
Q_OBJECT
public:
- QXmppStream(QSslSocket *socket, QObject *parent);
+ QXmppStream(QObject *parent);
~QXmppStream();
- void connectToHost();
- void disconnectFromHost();
- bool isConnected() const;
- bool sendData(const QByteArray&);
- bool sendPacket(const QXmppPacket&);
-
- QAbstractSocket::SocketError socketError();
- QXmppStanza::Error::Condition xmppStreamError();
- QXmppConfiguration& configuration();
+ bool isConnected() const;
+ void disconnectFromHost();
QXmppLogger *logger();
void setLogger(QXmppLogger *logger);
- QXmppElementList presenceExtensions() const;
+ bool sendData(const QByteArray&);
+ bool sendElement(const QDomElement&);
+ bool sendPacket(const QXmppPacket&);
signals:
- // socket host found
- void hostFound();
-
- // socket connected
+ /// This signal is emitted when the stream is connected.
void connected();
- // socket disconnected
+ /// This signal is emitted when the stream is disconnected.
void disconnected();
- // xmpp connected
- void xmppConnected();
+ /// This signal is emitted when an element is received.
+ void elementReceived(const QDomElement &element, bool &handled);
/// This signal is emitted to send logging messages.
void logMessage(QXmppLogger::MessageType type, const QString &msg);
- void error(QXmppClient::Error);
- void elementReceived(const QDomElement &element, bool &handled);
- void presenceReceived(const QXmppPresence&);
- void messageReceived(const QXmppMessage&);
- void iqReceived(const QXmppIq&);
- void rosterIqReceived(const QXmppRosterIq&);
- void vCardIqReceived(const QXmppVCard&);
-
- void rpcCallInvoke(const QXmppRpcInvokeIq &invoke);
- void rpcCallResponse(const QXmppRpcResponseIq& result);
- void rpcCallError(const QXmppRpcErrorIq &err);
-
- void archiveChatIqReceived(const QXmppArchiveChatIq&);
- void archiveListIqReceived(const QXmppArchiveListIq&);
- void archivePrefIqReceived(const QXmppArchivePrefIq&);
-
- void discoveryIqReceived(const QXmppDiscoveryIq&);
-
- void byteStreamIqReceived(const QXmppByteStreamIq&);
- void ibbCloseIqReceived(const QXmppIbbCloseIq&);
- void ibbDataIqReceived(const QXmppIbbDataIq&);
- void ibbOpenIqReceived(const QXmppIbbOpenIq&);
- void streamInitiationIqReceived(const QXmppStreamInitiationIq&);
-
- // XEP-0045: Multi-User Chat
- void mucAdminIqReceived(const QXmppMucAdminIq&);
- void mucOwnerIqReceived(const QXmppMucOwnerIq&);
-
- // XEP-0166: Jingle
- void jingleIqReceived(const QXmppJingleIq&);
-
protected:
// Logging helpers
void debug(const QString&);
void info(const QString&);
void warning(const QString&);
+ // Access to underlying socket
+ QSslSocket *socket();
+ void setSocket(QSslSocket *socket);
+
// Overridable methods
- virtual void handleStanza(const QDomElement &element);
- virtual void handleStream(const QDomElement &element);
- virtual bool sendStartStream();
- virtual bool sendEndStream();
+ virtual void handleStart();
+ virtual void handleStanza(const QDomElement &element) = 0;
+ virtual void handleStream(const QDomElement &element) = 0;
private slots:
- void socketHostFound();
- void socketReadReady();
- void socketEncrypted();
void socketConnected();
void socketDisconnected();
- void socketError(QAbstractSocket::SocketError);
- void socketSslErrors(const QList<QSslError>&);
-
- void pingStart();
- void pingStop();
- void pingSend();
- void pingTimeout();
+ void socketEncrypted();
+ void socketReadyRead();
private:
- QXmppDiscoveryIq capabilities() const;
- void flushDataBuffer();
- void parser(const QByteArray&);
- void sendNonSASLAuth(bool plaintext);
- void sendNonSASLAuthQuery();
- void sendAuthDigestMD5ResponseStep1(const QString& challenge);
- void sendAuthDigestMD5ResponseStep2();
- void sendBindIQ();
- void sendSessionIQ();
-
QXmppStreamPrivate * const d;
};
diff --git a/src/QXmppTransferManager.cpp b/src/QXmppTransferManager.cpp
index 9ee1a76f..a371fb16 100644
--- a/src/QXmppTransferManager.cpp
+++ b/src/QXmppTransferManager.cpp
@@ -32,7 +32,7 @@
#include "QXmppIbbIq.h"
#include "QXmppLogger.h"
#include "QXmppSocks.h"
-#include "QXmppStream.h"
+#include "QXmppOutgoingClient.h"
#include "QXmppStreamInitiationIq.h"
#include "QXmppTransferManager.h"
#include "QXmppUtils.h"
@@ -360,7 +360,7 @@ bool QXmppTransferJob::writeData(const QByteArray &data)
return true;
}
-QXmppTransferManager::QXmppTransferManager(QXmppStream *stream, QObject *parent)
+QXmppTransferManager::QXmppTransferManager(QXmppOutgoingClient *stream, QObject *parent)
: QObject(parent),
m_stream(stream),
m_ibbBlockSize(4096),
diff --git a/src/QXmppTransferManager.h b/src/QXmppTransferManager.h
index 8a8685b6..3386e0aa 100644
--- a/src/QXmppTransferManager.h
+++ b/src/QXmppTransferManager.h
@@ -36,7 +36,7 @@
class QTcpSocket;
class QXmppByteStreamIq;
-class QXmppStream;
+class QXmppOutgoingClient;
class QXmppIbbCloseIq;
class QXmppIbbDataIq;
class QXmppIbbOpenIq;
@@ -213,7 +213,7 @@ class QXmppTransferManager : public QObject
Q_OBJECT
public:
- QXmppTransferManager(QXmppStream *stream, QObject *parent = 0);
+ QXmppTransferManager(QXmppOutgoingClient *stream, QObject *parent = 0);
QXmppTransferJob *sendFile(const QString &jid, const QString &fileName, const QString &sid = QString());
QXmppTransferJob *sendFile(const QString &jid, QIODevice *device, const QXmppTransferFileInfo &fileInfo, const QString &sid = QString());
@@ -266,7 +266,7 @@ private:
void socksServerSendOffer(QXmppTransferJob *job);
// reference to XMPP stream (no ownership)
- QXmppStream* m_stream;
+ QXmppOutgoingClient* m_stream;
int m_ibbBlockSize;
QList<QXmppTransferJob*> m_jobs;
QString m_proxy;
diff --git a/src/QXmppVCardManager.cpp b/src/QXmppVCardManager.cpp
index 7a30f506..8a3cb1dd 100644
--- a/src/QXmppVCardManager.cpp
+++ b/src/QXmppVCardManager.cpp
@@ -23,10 +23,10 @@
#include "QXmppVCardManager.h"
-#include "QXmppStream.h"
+#include "QXmppOutgoingClient.h"
#include "QXmppUtils.h"
-QXmppVCardManager::QXmppVCardManager(QXmppStream* stream, QObject *parent)
+QXmppVCardManager::QXmppVCardManager(QXmppOutgoingClient* stream, QObject *parent)
: QObject(parent),
m_stream(stream),
m_isClientVCardReceived(false)
diff --git a/src/QXmppVCardManager.h b/src/QXmppVCardManager.h
index a7efca48..0216b6eb 100644
--- a/src/QXmppVCardManager.h
+++ b/src/QXmppVCardManager.h
@@ -29,7 +29,7 @@
#include "QXmppVCard.h"
-class QXmppStream;
+class QXmppOutgoingClient;
/// \brief The QXmppVCardManager gets/sets XMPP vCards. It is an
/// implentation of <B>XEP-0054: vcard-temp</B>.
@@ -59,7 +59,7 @@ class QXmppVCardManager : public QObject
Q_OBJECT
public:
- QXmppVCardManager(QXmppStream* stream, QObject *parent = 0);
+ QXmppVCardManager(QXmppOutgoingClient* stream, QObject *parent = 0);
void requestVCard(const QString& bareJid = "");
const QXmppVCard& clientVCard() const;
@@ -81,7 +81,7 @@ private slots:
private:
// reference to the xmpp stream (no ownership)
- QXmppStream* m_stream;
+ QXmppOutgoingClient* m_stream;
QXmppVCard m_clientVCard; ///< Stores the vCard of the connected client
bool m_isClientVCardReceived;
diff --git a/src/src.pro b/src/src.pro
index c850b896..04743abc 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -40,6 +40,7 @@ HEADERS += QXmppUtils.h \
QXmppMucIq.h \
QXmppMucManager.h \
QXmppNonSASLAuth.h \
+ QXmppOutgoingClient.h \
QXmppPacket.h \
QXmppPingIq.h \
QXmppPresence.h \
@@ -84,6 +85,7 @@ SOURCES += QXmppUtils.cpp \
QXmppMucIq.cpp \
QXmppMucManager.cpp \
QXmppNonSASLAuth.cpp \
+ QXmppOutgoingClient.cpp \
QXmppPacket.cpp \
QXmppPingIq.cpp \
QXmppPresence.cpp \