diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-08-23 05:40:15 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-08-23 05:40:15 +0000 |
| commit | 80e0607c97aef44e88f8e5af281132668114534f (patch) | |
| tree | 71594029b2bd5bf28dfe489dc1249d6c72b378be /src/QXmppStream.cpp | |
| parent | 85a3546164c0816a4b94d6fdb45ca64b1547f541 (diff) | |
| download | qxmpp-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.cpp | 1017 |
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; -} |
