diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2012-02-08 11:53:52 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2012-02-08 11:53:52 +0000 |
| commit | 36bbeb02497f5f79ddae56857893c2f71bf08ee9 (patch) | |
| tree | 34191bb93ff6cb044d5d28d2f630dc0476cc5659 /src/server/QXmppOutgoingServer.cpp | |
| parent | aa334abcca63101292c48a72c7b1851b8ecc26c7 (diff) | |
move server code to "server" directory
Diffstat (limited to 'src/server/QXmppOutgoingServer.cpp')
| -rw-r--r-- | src/server/QXmppOutgoingServer.cpp | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/src/server/QXmppOutgoingServer.cpp b/src/server/QXmppOutgoingServer.cpp new file mode 100644 index 00000000..e980c27e --- /dev/null +++ b/src/server/QXmppOutgoingServer.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2008-2011 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 "qdnslookup.h" + +#include "QXmppConstants.h" +#include "QXmppDialback.h" +#include "QXmppOutgoingServer.h" +#include "QXmppStreamFeatures.h" +#include "QXmppUtils.h" + +class QXmppOutgoingServerPrivate +{ +public: + QList<QByteArray> dataQueue; + QDnsLookup dns; + QString localDomain; + QString localStreamKey; + QString remoteDomain; + QString verifyId; + QString verifyKey; + QTimer *dialbackTimer; + bool ready; +}; + +/// Constructs a new outgoing server-to-server stream. +/// +/// \param domain the local domain +/// \param parent the parent object +/// + +QXmppOutgoingServer::QXmppOutgoingServer(const QString &domain, QObject *parent) + : QXmppStream(parent), + d(new QXmppOutgoingServerPrivate) +{ + bool check; + Q_UNUSED(check); + + // socket initialisation + QSslSocket *socket = new QSslSocket(this); + setSocket(socket); + + check = connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(socketError(QAbstractSocket::SocketError))); + Q_ASSERT(check); + + // DNS lookups + check = connect(&d->dns, SIGNAL(finished()), + this, SLOT(_q_dnsLookupFinished())); + Q_ASSERT(check); + + d->dialbackTimer = new QTimer(this); + d->dialbackTimer->setInterval(5000); + d->dialbackTimer->setSingleShot(true); + check = connect(d->dialbackTimer, SIGNAL(timeout()), + this, SLOT(sendDialback())); + Q_ASSERT(check); + + d->localDomain = domain; + d->ready = false; + + check = connect(socket, SIGNAL(sslErrors(QList<QSslError>)), + this, SLOT(slotSslErrors(QList<QSslError>))); + Q_ASSERT(check); +} + +/// Destroys the stream. +/// + +QXmppOutgoingServer::~QXmppOutgoingServer() +{ + delete d; +} + +/// Attempts to connect to an XMPP server for the specified \a domain. +/// +/// \param domain + +void QXmppOutgoingServer::connectToHost(const QString &domain) +{ + d->remoteDomain = domain; + + // lookup server for domain + debug(QString("Looking up server for domain %1").arg(domain)); + d->dns.setName("_xmpp-server._tcp." + domain); + d->dns.setType(QDnsLookup::SRV); + d->dns.lookup(); +} + +void QXmppOutgoingServer::_q_dnsLookupFinished() +{ + QString host; + quint16 port; + + if (d->dns.error() == QDnsLookup::NoError && + !d->dns.serviceRecords().isEmpty()) { + // take the first returned record + host = d->dns.serviceRecords().first().target(); + port = d->dns.serviceRecords().first().port(); + } else { + // as a fallback, use domain as the host name + warning(QString("Lookup for domain %1 failed: %2") + .arg(d->dns.name(), d->dns.errorString())); + host = d->remoteDomain; + port = 5269; + } + + // connect to server + info(QString("Connecting to %1:%2").arg(host, QString::number(port))); + socket()->connectToHost(host, port); +} + +void QXmppOutgoingServer::handleStart() +{ + QXmppStream::handleStart(); + + QString data = QString("<?xml version='1.0'?><stream:stream" + " xmlns='%1' xmlns:db='%2' xmlns:stream='%3' version='1.0'>").arg( + ns_server, + ns_server_dialback, + ns_stream); + sendData(data.toUtf8()); +} + +void QXmppOutgoingServer::handleStream(const QDomElement &streamElement) +{ + Q_UNUSED(streamElement); + + // gmail.com servers are broken: they never send <stream:features>, + // so we schedule sending the dialback in a couple of seconds + d->dialbackTimer->start(); +} + +void QXmppOutgoingServer::handleStanza(const QDomElement &stanza) +{ + const QString ns = stanza.namespaceURI(); + + if(QXmppStreamFeatures::isStreamFeatures(stanza)) + { + QXmppStreamFeatures features; + features.parse(stanza); + + if (!socket()->isEncrypted()) + { + // check we can satisfy TLS constraints + if (!socket()->supportsSsl() && + features.tlsMode() == QXmppStreamFeatures::Required) + { + warning("Disconnecting as TLS is required, but SSL support is not available"); + disconnectFromHost(); + return; + } + + // enable TLS if possible + if (socket()->supportsSsl() && + features.tlsMode() != QXmppStreamFeatures::Disabled) + { + sendData("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"); + return; + } + } + + // send dialback if needed + d->dialbackTimer->stop(); + sendDialback(); + } + else if (ns == ns_tls) + { + if (stanza.tagName() == QLatin1String("proceed")) + { + debug("Starting encryption"); + socket()->startClientEncryption(); + return; + } + } + else if (QXmppDialback::isDialback(stanza)) + { + QXmppDialback response; + response.parse(stanza); + + // check the request is valid + if (response.from().isEmpty() || + response.to() != d->localDomain || + response.type().isEmpty()) + { + warning("Invalid dialback response received"); + return; + } + if (response.command() == QXmppDialback::Result) + { + if (response.type() == QLatin1String("valid")) + { + info(QString("Outgoing server stream to %1 is ready").arg(response.from())); + d->ready = true; + + // send queued data + foreach (const QByteArray &data, d->dataQueue) + sendData(data); + d->dataQueue.clear(); + + // emit signal + emit connected(); + } + } + else if (response.command() == QXmppDialback::Verify) + { + emit dialbackResponseReceived(response); + } + + } +} + +/// Returns true if the socket is connected and authentication succeeded. +/// + +bool QXmppOutgoingServer::isConnected() const +{ + return QXmppStream::isConnected() && d->ready; +} + +/// Returns the stream's local dialback key. + +QString QXmppOutgoingServer::localStreamKey() const +{ + return d->localStreamKey; +} + +/// Sets the stream's local dialback key. +/// +/// \param key + +void QXmppOutgoingServer::setLocalStreamKey(const QString &key) +{ + d->localStreamKey = key; +} + +/// Sets the stream's verification information. +/// +/// \param id +/// \param key + +void QXmppOutgoingServer::setVerify(const QString &id, const QString &key) +{ + d->verifyId = id; + d->verifyKey = key; +} + +/// Sends or queues data until connected. +/// +/// \param data + +void QXmppOutgoingServer::queueData(const QByteArray &data) +{ + if (isConnected()) + sendData(data); + else + d->dataQueue.append(data); +} + +/// Returns the remote server's domain. + +QString QXmppOutgoingServer::remoteDomain() const +{ + return d->remoteDomain; +} + +void QXmppOutgoingServer::sendDialback() +{ + if (!d->localStreamKey.isEmpty()) + { + // send dialback key + debug(QString("Sending dialback result to %1").arg(d->remoteDomain)); + QXmppDialback dialback; + dialback.setCommand(QXmppDialback::Result); + dialback.setFrom(d->localDomain); + dialback.setTo(d->remoteDomain); + dialback.setKey(d->localStreamKey); + sendPacket(dialback); + } + else if (!d->verifyId.isEmpty() && !d->verifyKey.isEmpty()) + { + // send dialback verify + debug(QString("Sending dialback verify to %1").arg(d->remoteDomain)); + QXmppDialback verify; + verify.setCommand(QXmppDialback::Verify); + verify.setId(d->verifyId); + verify.setFrom(d->localDomain); + verify.setTo(d->remoteDomain); + verify.setKey(d->verifyKey); + sendPacket(verify); + } +} + +void QXmppOutgoingServer::slotSslErrors(const QList<QSslError> &errors) +{ + warning("SSL errors"); + for(int i = 0; i < errors.count(); ++i) + warning(errors.at(i).errorString()); + socket()->ignoreSslErrors(); +} + +void QXmppOutgoingServer::socketError(QAbstractSocket::SocketError error) +{ + Q_UNUSED(error); + emit disconnected(); +} + |
