diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-08-11 07:31:23 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-08-11 07:31:23 +0000 |
| commit | 40c39853816cfab113d79682c34bc76a2c79c357 (patch) | |
| tree | e4d6a184cf565cb87477339ce738299ff9787bc3 /source/QXmppStream.cpp | |
| parent | 551c284e35280b7b91a939fe7352e496ffea402a (diff) | |
| download | qxmpp-40c39853816cfab113d79682c34bc76a2c79c357.tar.gz | |
rename "source" directory to "src"
Diffstat (limited to 'source/QXmppStream.cpp')
| -rw-r--r-- | source/QXmppStream.cpp | 1120 |
1 files changed, 0 insertions, 1120 deletions
diff --git a/source/QXmppStream.cpp b/source/QXmppStream.cpp deleted file mode 100644 index 95acb236..00000000 --- a/source/QXmppStream.cpp +++ /dev/null @@ -1,1120 +0,0 @@ -/* - * Copyright (C) 2008-2010 The QXmpp developers - * - * Author: - * Manjeet Dahiya - * - * 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 "QXmppUtils.h" -#include "QXmppBind.h" -#include "QXmppIq.h" -#include "QXmppLogger.h" -#include "QXmppMessage.h" -#include "QXmppPacket.h" -#include "QXmppPresence.h" -#include "QXmppSession.h" -#include "QXmppConstants.h" -#include "QXmppStream.h" -#include "QXmppNonSASLAuth.h" - -// IQ types -#include "QXmppArchiveIq.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 "QXmppStreamInitiationIq.h" -#include "QXmppVCard.h" -#include "QXmppVersionIq.h" - -#include <QCoreApplication> -#include <QDomDocument> -#include <QStringList> -#include <QRegExp> -#include <QHostAddress> -#include <QXmlStreamWriter> -#include <QTimer> - -static const QString capabilitiesNode = "http://code.google.com/p/qxmpp"; -static const QByteArray streamRootElementStart = "<?xml version=\"1.0\"?><stream:stream xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\" xmlns=\"jabber:client\" xml:lang=\"en\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">\n"; -static const QByteArray streamRootElementEnd = "</stream:stream>"; - -class QXmppStreamPrivate -{ -public: - QSslSocket socket; - QAbstractSocket::SocketError socketError; -}; - -QXmppStream::QXmppStream(QObject *parent) - : QObject(parent), - m_logger(0), - m_sessionAvailable(false), - m_authStep(0), - d(new QXmppStreamPrivate) -{ - // Make sure the random number generator is seeded - qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); - - // initialise logger - setLogger(QXmppLogger::getLogger()); - - 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 - m_pingTimer = new QTimer(this); - check = QObject::connect(m_pingTimer, SIGNAL(timeout()), this, SLOT(pingSend())); - Q_ASSERT(check); - - m_timeoutTimer = new QTimer(this); - m_timeoutTimer->setSingleShot(true); - check = QObject::connect(m_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); -} - -QXmppStream::~QXmppStream() -{ - delete d; -} - -QXmppConfiguration& QXmppStream::configuration() -{ - return m_config; -} - -void QXmppStream::connect() -{ - info(QString("Connecting to: %1:%2").arg(configuration(). - host()).arg(configuration().port())); - - // prepare for connection - m_authStep = 0; - - d->socket.setProxy(configuration().networkProxy()); - d->socket.connectToHost(configuration(). - host(), configuration().port()); -} - -void QXmppStream::socketSslErrors(const QList<QSslError> & error) -{ - warning("SSL errors"); - for(int i = 0; i< error.count(); ++i) - warning(error.at(i).errorString()); - - if (configuration().ignoreSslErrors()) - d->socket.ignoreSslErrors(); -} - -void QXmppStream::socketHostFound() -{ - debug("Host found"); - emit hostFound(); -} - -void QXmppStream::socketConnected() -{ - flushDataBuffer(); - info("Connected"); - emit connected(); - sendStartStream(); -} - -void QXmppStream::socketDisconnected() -{ - flushDataBuffer(); - info("Disconnected"); - emit disconnected(); -} - -void QXmppStream::socketEncrypted() -{ - debug("Encrypted"); - sendStartStream(); -} - -void QXmppStream::socketError(QAbstractSocket::SocketError ee) -{ - d->socketError = ee; - emit error(QXmppClient::SocketError); - warning(QString("Socket error: " + d->socket.errorString())); -} - -void QXmppStream::socketReadReady() -{ - const QByteArray data = d->socket.readAll(); - //debug("SERVER [COULD BE PARTIAL DATA]:" + data.left(20)); - parser(data); -} - -void QXmppStream::sendNonSASLAuthQuery( const QString &to ) -{ - QXmppNonSASLAuthTypesRequestIq authQuery; - authQuery.setTo(to); - authQuery.setUsername(configuration().user()); - - sendPacket(authQuery); -} - -/// Returns the QXmppLogger associated with the current QXmppStream. - -QXmppLogger *QXmppStream::logger() -{ - return m_logger; -} - -/// Sets the QXmppLogger associated with the current QXmppStream. - -void QXmppStream::setLogger(QXmppLogger *logger) -{ - if (m_logger) - QObject::disconnect(this, SIGNAL(logMessage(QXmppLogger::MessageType, QString)), - m_logger, SLOT(log(QXmppLogger::MessageType, QString))); - m_logger = logger; - if (m_logger) - QObject::connect(this, SIGNAL(logMessage(QXmppLogger::MessageType, QString)), - m_logger, SLOT(log(QXmppLogger::MessageType, QString))); -} - -void QXmppStream::debug(const QString &data) -{ - emit logMessage(QXmppLogger::DebugMessage, data); -} - -void QXmppStream::info(const QString &data) -{ - emit logMessage(QXmppLogger::InformationMessage, data); -} - -void QXmppStream::warning(const QString &data) -{ - emit logMessage(QXmppLogger::WarningMessage, data); -} - -void QXmppStream::parser(const QByteArray& data) -{ - QDomDocument doc; - QByteArray completeXml; - - m_dataBuffer = m_dataBuffer + data; - - if(hasStartStreamElement(m_dataBuffer)) - { - completeXml = m_dataBuffer + streamRootElementEnd; - } - else if(hasEndStreamElement(data)) - { - completeXml = streamRootElementStart + m_dataBuffer; - } - else - { - completeXml = streamRootElementStart + m_dataBuffer + streamRootElementEnd; - } - - if(doc.setContent(completeXml, true)) - { - emit logMessage(QXmppLogger::ReceivedMessage, QString::fromUtf8(m_dataBuffer)); - flushDataBuffer(); - - QDomElement nodeRecv = doc.documentElement().firstChildElement(); - - if(nodeRecv.isNull()) - { - QDomElement streamElement = doc.documentElement(); - if(m_streamId.isEmpty()) - m_streamId = streamElement.attribute("id"); - if(m_XMPPVersion.isEmpty()) - { - m_XMPPVersion = streamElement.attribute("version"); - if(m_XMPPVersion.isEmpty()) - { - // no version specified, signals XMPP Version < 1.0. - // switch to old auth mechanism - sendNonSASLAuthQuery(doc.documentElement().attribute("from")); - } - } - } - else - { - //TODO: Make a login error here. - } - while(!nodeRecv.isNull()) - { - - QString ns = nodeRecv.namespaceURI(); - - // if we receive any kind of data, stop the timeout timer - m_timeoutTimer->stop(); - - bool handled = false; - emit elementReceived(nodeRecv, handled); - - if(handled) - { - // already handled by client, do nothing - } - else if(ns == ns_stream && nodeRecv.tagName() == "features") - { - bool nonSaslAvailable = nodeRecv.firstChildElement("auth"). - namespaceURI() == ns_authFeature; - bool saslAvailable = nodeRecv.firstChildElement("mechanisms"). - namespaceURI() == ns_sasl; - bool useSasl = configuration().useSASLAuthentication(); - - if (!d->socket.isEncrypted()) - { - // parse remote TLS mode - QXmppConfiguration::StreamSecurityMode remoteSecurity; - QDomElement tlsElement = nodeRecv.firstChildElement("starttls"); - if (tlsElement.namespaceURI() == ns_tls) - { - if (tlsElement.firstChildElement().tagName() == "required") - remoteSecurity = QXmppConfiguration::TLSRequired; - else - remoteSecurity = QXmppConfiguration::TLSEnabled; - } else { - remoteSecurity = QXmppConfiguration::TLSDisabled; - } - - // determine TLS mode to use - const QXmppConfiguration::StreamSecurityMode localSecurity = configuration().streamSecurityMode(); - if (!d->socket.supportsSsl() && - (localSecurity == QXmppConfiguration::TLSRequired || - remoteSecurity == QXmppConfiguration::TLSRequired)) - { - warning("Disconnecting as TLS is required, but SSL support is not available"); - disconnect(); - return; - } - if (localSecurity == QXmppConfiguration::TLSRequired && - remoteSecurity == QXmppConfiguration::TLSDisabled) - { - warning("Disconnecting as TLS is required, but not supported by the server"); - disconnect(); - return; - } - - if (d->socket.supportsSsl() && - (remoteSecurity == QXmppConfiguration::TLSRequired || - localSecurity != QXmppConfiguration::TLSDisabled)) - { - // enable TLS as it is required by the server - // or supported by the client - sendToServer("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"); - return; - } - } - - if((saslAvailable && nonSaslAvailable && !useSasl) || - (!saslAvailable && nonSaslAvailable)) - { - sendNonSASLAuthQuery(doc.documentElement().attribute("from")); - } - else if(saslAvailable) - { - // parse advertised SASL Authentication mechanisms - QList<QXmppConfiguration::SASLAuthMechanism> mechanisms; - QDomElement element = nodeRecv.firstChildElement("mechanisms"); - QDomElement subElement = element.firstChildElement("mechanism"); - debug("SASL Authentication mechanisms:"); - while(!subElement.isNull()) - { - debug(subElement.text()); - if (subElement.text() == QLatin1String("PLAIN")) - mechanisms << QXmppConfiguration::SASLPlain; - else if (subElement.text() == QLatin1String("DIGEST-MD5")) - mechanisms << QXmppConfiguration::SASLDigestMD5; - else if (subElement.text() == QLatin1String("ANONYMOUS")) - mechanisms << QXmppConfiguration::SASLAnonymous; - subElement = subElement.nextSiblingElement("mechanism"); - } - - // determine SASL Authentication mechanism to use - QXmppConfiguration::SASLAuthMechanism mechanism = configuration().sASLAuthMechanism(); - if (mechanisms.isEmpty()) - { - warning("No supported SASL Authentication mechanism available"); - disconnect(); - 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>"; - sendToServer(data); - } - break; - case QXmppConfiguration::SASLDigestMD5: - sendToServer("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>"); - break; - case QXmppConfiguration::SASLAnonymous: - sendToServer("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>"); - break; - } - } - - if(nodeRecv.firstChildElement("bind"). - namespaceURI() == ns_bind) - { - sendBindIQ(); - } - - if(nodeRecv.firstChildElement("session"). - namespaceURI() == ns_session) - { - m_sessionAvailable = true; - } - } - else if(ns == ns_stream && nodeRecv.tagName() == "error") - { - if (!nodeRecv.firstChildElement("conflict").isNull()) - m_xmppStreamError = QXmppStanza::Error::Conflict; - else - m_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 - m_authStep++; - switch (m_authStep) - { - case 1 : - sendAuthDigestMD5ResponseStep1(nodeRecv.text()); - break; - case 2 : - sendAuthDigestMD5ResponseStep2(); - break; - default : - warning("Too many authentication steps"); - disconnect(); - break; - } - } - else if(nodeRecv.tagName() == "failure") - { - if (!nodeRecv.firstChildElement("not-authorized").isNull()) - m_xmppStreamError = QXmppStanza::Error::NotAuthorized; - else - m_xmppStreamError = QXmppStanza::Error::UndefinedCondition; - emit error(QXmppClient::XmppStreamError); - - warning("Authentication failure"); - disconnect(); - } - } - 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 == m_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 == m_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 (m_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 == m_nonSASLAuthId && type == "result") - { - // successful Non-SASL Authentication - debug("Authenticated (Non-SASL)"); - - // xmpp connection made - emit xmppConnected(); - } - else if(nodeRecv.firstChildElement("query"). - namespaceURI() == ns_auth) - { - 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 - { - //TODO Login error - 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); - } - } - nodeRecv = nodeRecv.nextSiblingElement(); - } - } - else - { - //wait for complete packet - } -} - - -void 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'>"); - sendToServer(data); -} - -bool QXmppStream::sendToServer(const QByteArray& packet) -{ - emit logMessage(QXmppLogger::SentMessage, QString::fromUtf8(packet)); - if (!isConnected()) - return false; - return d->socket.write( packet ) == packet.size(); -} - -bool QXmppStream::hasStartStreamElement(const QByteArray& data) -{ - QString str(data); - QRegExp regex("^(<\\?xml.*\\?>)?\\s*<stream:stream.*>"); - regex.setMinimal(true); - return str.contains(regex); -} - -bool QXmppStream::hasEndStreamElement(const QByteArray& data) -{ - QString str(data); - QRegExp regex("</stream:stream>$"); - regex.setMinimal(true); - return str.contains(regex); -} - -void QXmppStream::sendNonSASLAuth(bool plainText) -{ - QXmppNonSASLAuthIq authQuery; - authQuery.setUsername(configuration().user()); - authQuery.setPassword(configuration().passwd()); - authQuery.setResource(configuration().resource()); - authQuery.setStreamId(m_streamId); - authQuery.setUsePlainText(plainText); - m_nonSASLAuthId = authQuery.id(); - sendPacket(authQuery); -} - -// challenge is BASE64 encoded string -void QXmppStream::sendAuthDigestMD5ResponseStep1(const QString& challenge) -{ - QByteArray ba = QByteArray::fromBase64(challenge.toUtf8()); - - QMap<QByteArray, QByteArray> map; - - QByteArray key; - QByteArray value; - bool parsingValue = false; - int startindex = 0; - for(int i = 0; i < ba.length(); i++) - { - char next = ba.at(i); - switch (next) { - case '=': - if (!parsingValue) - { - // Trim the key, but do not trim the value as it is in delimiters - key = ba.mid(startindex, i - startindex).trimmed(); - // Skip the equals and delimiter - startindex = i + 2; - } - break; - case '"': - // Ignore the opening delimiter - if (startindex != (i + 1)) - { - value = ba.mid(startindex, i - startindex); - map[key] = value; - debug(key + ":" + value); - // Skip the comma - i += 2; - startindex = i; - parsingValue = false; - } - else { - parsingValue = true; - } - - break; - - default: - break; - } - } - - if (!map.contains("nonce")) - { - warning("sendAuthDigestMD5ResponseStep1: Invalid input"); - disconnect(); - return; - } - - QByteArray user = configuration().user().toUtf8(); - QByteArray passwd = configuration().passwd().toUtf8(); - QByteArray domain = configuration().domain().toUtf8(); - QByteArray realm; - if(map.contains("realm")) - realm = map["realm"]; - - QByteArray response; - - 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(); - - QByteArray nc = "00000001"; - QByteArray digest_uri = "xmpp/" + domain; - - QByteArray a1 = user + ':' + realm + ':' + passwd; - QByteArray ha1 = QCryptographicHash::hash(a1, QCryptographicHash::Md5); - ha1 += ':' + map["nonce"] + ':' + cnonce; - - if(map.contains("authzid")) - ha1 += ':' + map["authzid"]; - - QByteArray A1(ha1); - QByteArray A2 = "AUTHENTICATE:" + digest_uri; - QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex(); - QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex(); - QByteArray KD = HA1 + ':' + map["nonce"] + ':' + nc + ':' + cnonce + ':' - + "auth" + ':' + HA2; - QByteArray Z = QCryptographicHash::hash(KD, QCryptographicHash::Md5).toHex(); - - 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=" + Z + ","; - if(map.contains("authzid")) - response += "authzid=\"" + map["authzid"] + "\","; - response += "charset=utf-8"; - - debug(response); - QByteArray packet = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" - + response.toBase64() + "</response>"; - sendToServer(packet); -} - -void QXmppStream::sendAuthDigestMD5ResponseStep2() -{ - sendToServer("<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); -} - -void QXmppStream::sendBindIQ() -{ - QXmppBind bind(QXmppIq::Set); - bind.setResource(configuration().resource()); - m_bindId = bind.id(); - sendPacket(bind); -} - -void QXmppStream::sendSessionIQ() -{ - QXmppSession session(QXmppIq::Set); - session.setTo(configuration().domain()); - m_sessionId = session.id(); - sendPacket(session); -} - -void QXmppStream::acceptSubscriptionRequest(const QString& from, bool accept) -{ - QXmppPresence presence; - presence.setTo(from); - if(accept) - presence.setType(QXmppPresence::Subscribed); - else - presence.setType(QXmppPresence::Unsubscribed); - - sendPacket(presence); -} - -void QXmppStream::sendSubscriptionRequest(const QString& to) -{ - if(to.isEmpty()) - return; - - QXmppPresence presence; - presence.setTo(to); - presence.setType(QXmppPresence::Subscribe); - sendPacket(presence); -} - -void QXmppStream::disconnect() -{ - m_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 sendToServer(data); -} - -void QXmppStream::sendEndStream() -{ - sendToServer(streamRootElementEnd); -} - -void QXmppStream::pingStart() -{ - const int interval = configuration().keepAliveInterval(); - // start ping timer - if (interval > 0) - { - m_pingTimer->setInterval(interval * 1000); - m_pingTimer->start(); - } -} - -void QXmppStream::pingStop() -{ - // stop all timers - m_pingTimer->stop(); - m_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) - { - m_timeoutTimer->setInterval(timeout * 1000); - m_timeoutTimer->start(); - } -} - -void QXmppStream::pingTimeout() -{ - warning("Ping timeout"); - disconnect(); - emit error(QXmppClient::KeepAliveError); -} - -QAbstractSocket::SocketError QXmppStream::socketError() -{ - return d->socketError; -} - -QXmppStanza::Error::Condition QXmppStream::xmppStreamError() -{ - return m_xmppStreamError; -} - -void QXmppStream::flushDataBuffer() -{ - m_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; -} - |
