aboutsummaryrefslogtreecommitdiff
path: root/src/QXmppStream.cpp
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/QXmppStream.cpp
parent85a3546164c0816a4b94d6fdb45ca64b1547f541 (diff)
downloadqxmpp-80e0607c97aef44e88f8e5af281132668114534f.tar.gz
* make QXmppStream an abstract class
* move client-specific code to QXmppOutgoingClient
Diffstat (limited to 'src/QXmppStream.cpp')
-rw-r--r--src/QXmppStream.cpp1017
1 files changed, 139 insertions, 878 deletions
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;
-}