diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-08-23 05:49:24 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2010-08-23 05:49:24 +0000 |
| commit | 6c462ee694b614f538cfbee894134f723ae58820 (patch) | |
| tree | 53e3398d261222ad0c8209fac35057dc3dd86050 /src/QXmppIncomingClient.cpp | |
| parent | 12418dc2d452f372c19d129a1dacfbf4e7e00817 (diff) | |
| download | qxmpp-6c462ee694b614f538cfbee894134f723ae58820.tar.gz | |
- add support for incoming C2S connections
- add support for incoming S2S connections
- add support for outgoing S2S connections
Diffstat (limited to 'src/QXmppIncomingClient.cpp')
| -rw-r--r-- | src/QXmppIncomingClient.cpp | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/src/QXmppIncomingClient.cpp b/src/QXmppIncomingClient.cpp new file mode 100644 index 00000000..0e51958c --- /dev/null +++ b/src/QXmppIncomingClient.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2008-2010 The QXmpp developers + * + * Author: + * 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 <QDomElement> +#include <QSslKey> +#include <QSslSocket> +#include <QTimer> + +#include "QXmppBind.h" +#include "QXmppConstants.h" +#include "QXmppMessage.h" +#include "QXmppSession.h" +#include "QXmppStreamFeatures.h" +#include "QXmppUtils.h" + +#include "QXmppIncomingClient.h" + +class QXmppIncomingClientPrivate +{ +public: + QTimer *idleTimer; + + QString domain; + QString username; + QString resource; + QXmppPasswordChecker *passwordChecker; + QByteArray saslNonce; +}; + +QXmppIncomingClient::QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent) + : QXmppStream(parent), + d(new QXmppIncomingClientPrivate) +{ + d->passwordChecker = 0; + d->domain = domain; + + setObjectName("C2S"); + setSocket(socket); + + // create inactivity timer + d->idleTimer = new QTimer(this); + d->idleTimer->setInterval(70000); + d->idleTimer->setSingleShot(true); + bool check = connect(d->idleTimer, SIGNAL(timeout()), + this, SLOT(slotTimeout())); + Q_ASSERT(check); +} + +QXmppIncomingClient::~QXmppIncomingClient() +{ + delete d; +} + +bool QXmppIncomingClient::isConnected() const +{ + return !d->username.isEmpty(); +} + +QString QXmppIncomingClient::jid() const +{ + if (d->username.isEmpty()) + return QString(); + QString jid = d->username + "@" + d->domain; + if (!d->resource.isEmpty()) + jid += "/" + d->resource; + return jid; +} + +void QXmppIncomingClient::setPasswordChecker(QXmppPasswordChecker *checker) +{ + d->passwordChecker = checker; +} + +void QXmppIncomingClient::handleStream(const QDomElement &streamElement) +{ + Q_UNUSED(streamElement); + + d->idleTimer->start(); + + // start stream + const QByteArray sessionId = generateStanzaHash().toAscii(); + QString response = QString("<?xml version='1.0'?><stream:stream" + " xmlns=\"%1\" xmlns:stream=\"%2\"" + " id=\"%3\" from=\"%4\" version=\"1.0\" xml:lang=\"en\">").arg( + ns_client, + ns_stream, + sessionId, + d->domain.toAscii()); + sendData(response.toUtf8()); + + // send stream features + QXmppStreamFeatures features; + if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull()) + features.setSecurityMode(QXmppConfiguration::TLSEnabled); + if (!d->username.isEmpty()) + { + features.setBindAvailable(true); + features.setSessionAvailable(true); + } + else + { + QList<QXmppConfiguration::SASLAuthMechanism> mechanisms; + mechanisms << QXmppConfiguration::SASLPlain; + mechanisms << QXmppConfiguration::SASLDigestMD5; + features.setAuthMechanisms(mechanisms); + } + sendPacket(features); +} + +void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv) +{ + const QString ns = nodeRecv.namespaceURI(); + + d->idleTimer->start(); + + if (ns == ns_tls && nodeRecv.tagName() == "starttls") + { + sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"); + socket()->flush(); + socket()->startServerEncryption(); + return; + } + else if (ns == ns_sasl) + { + if (nodeRecv.tagName() == "auth") + { + const QString mechanism = nodeRecv.attribute("mechanism"); + if (mechanism == "PLAIN") + { + QList<QByteArray> auth = QByteArray::fromBase64(nodeRecv.text().toAscii()).split('\0'); + if (auth.size() != 3) + { + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); + disconnectFromHost(); + return; + } + + const QString username = QString::fromUtf8(auth[1]); + const QString password = QString::fromUtf8(auth[2]); + if (d->passwordChecker && d->passwordChecker->check(username, password)) + { + d->username = username; + sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); + } else { + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"); + disconnectFromHost(); + return; + } + } + else if (mechanism == "DIGEST-MD5") + { + // generate nonce + QByteArray nonce(32, 'm'); + for(int n = 0; n < nonce.size(); ++n) + nonce[n] = (char)(256.0*qrand()/(RAND_MAX+1.0)); + d->saslNonce = nonce.toBase64(); + + QMap<QByteArray, QByteArray> challenge; + challenge["nonce"] = d->saslNonce; + challenge["realm"] = d->domain.toUtf8(); + challenge["qop"] = "auth"; + challenge["charset"] = "utf-8"; + challenge["algorithm"] = "md5-sess"; + + const QByteArray data = serializeDigestMd5(challenge).toBase64(); + sendData("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + data +"</challenge>"); + } + else + { + // unsupported method + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></failure>"); + disconnectFromHost(); + return; + } + } + else if (nodeRecv.tagName() == "response") + { + const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii()); + QMap<QByteArray, QByteArray> response = parseDigestMd5(raw); + + // check credentials + const QString username = QString::fromUtf8(response.value("username")); + QString password; + if (!d->passwordChecker || !d->passwordChecker->get(username, password)) + { + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"); + disconnectFromHost(); + return; + } + const QByteArray a1 = username.toUtf8() + ':' + d->domain.toUtf8() + ':' + password.toUtf8(); + const QByteArray remote = QByteArray::fromHex(response["response"]); + if (remote != calculateDigestMd5(a1, + d->saslNonce, + response.value("nc"), + response.value("cnonce"), + response.value("digest-uri"), + QByteArray())) + { + sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>"); + disconnectFromHost(); + return; + } + + // authentication succeeded + d->username = username; + sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"); + } + } + else if (ns == ns_client) + { + if (nodeRecv.tagName() == "iq") + { + const QString type = nodeRecv.attribute("type"); + if (QXmppBind::isBind(nodeRecv) && type == "set") + { + QXmppBind bindSet; + bindSet.parse(nodeRecv); + d->resource = bindSet.resource(); + + QXmppBind bindResult(QXmppIq::Result); + bindResult.setId(bindSet.id()); + bindResult.setJid(jid()); + sendPacket(bindResult); + + // bound + emit connected(); + } + else if (QXmppSession::isSession(nodeRecv) && type == "set") + { + QXmppSession sessionSet; + sessionSet.parse(nodeRecv); + + QXmppSession sessionResult(QXmppIq::Result); + sessionResult.setId(sessionSet.id()); + sendPacket(sessionResult); + } + else + { + QDomElement nodeFull(nodeRecv); + nodeFull.setAttribute("from", jid()); + bool handled = false; + emit elementReceived(nodeFull, handled); + } + } + else if (nodeRecv.tagName() == "message" || nodeRecv.tagName() == "presence") + { + QDomElement nodeFull(nodeRecv); + nodeFull.setAttribute("from", jid()); + bool handled = false; + emit elementReceived(nodeFull, handled); + } + } +} + +void QXmppIncomingClient::slotTimeout() +{ + warning(QString("Idle timeout for %1").arg(jid())); + disconnectFromHost(); +} + + |
