From 36bbeb02497f5f79ddae56857893c2f71bf08ee9 Mon Sep 17 00:00:00 2001 From: Jeremy Lainé Date: Wed, 8 Feb 2012 11:53:52 +0000 Subject: move server code to "server" directory --- src/QXmppDialback.cpp | 117 ------ src/QXmppDialback.h | 69 --- src/QXmppIncomingClient.cpp | 445 -------------------- src/QXmppIncomingClient.h | 73 ---- src/QXmppIncomingServer.cpp | 207 --------- src/QXmppIncomingServer.h | 69 --- src/QXmppOutgoingServer.cpp | 331 --------------- src/QXmppOutgoingServer.h | 82 ---- src/QXmppServer.cpp | 807 ------------------------------------ src/QXmppServer.h | 148 ------- src/QXmppServerExtension.cpp | 155 ------- src/QXmppServerExtension.h | 79 ---- src/QXmppServerPlugin.h | 60 --- src/server/QXmppDialback.cpp | 117 ++++++ src/server/QXmppDialback.h | 69 +++ src/server/QXmppIncomingClient.cpp | 445 ++++++++++++++++++++ src/server/QXmppIncomingClient.h | 73 ++++ src/server/QXmppIncomingServer.cpp | 207 +++++++++ src/server/QXmppIncomingServer.h | 69 +++ src/server/QXmppOutgoingServer.cpp | 331 +++++++++++++++ src/server/QXmppOutgoingServer.h | 82 ++++ src/server/QXmppServer.cpp | 807 ++++++++++++++++++++++++++++++++++++ src/server/QXmppServer.h | 148 +++++++ src/server/QXmppServerExtension.cpp | 155 +++++++ src/server/QXmppServerExtension.h | 79 ++++ src/server/QXmppServerPlugin.h | 60 +++ src/src.pro | 30 +- 27 files changed, 2657 insertions(+), 2657 deletions(-) delete mode 100644 src/QXmppDialback.cpp delete mode 100644 src/QXmppDialback.h delete mode 100644 src/QXmppIncomingClient.cpp delete mode 100644 src/QXmppIncomingClient.h delete mode 100644 src/QXmppIncomingServer.cpp delete mode 100644 src/QXmppIncomingServer.h delete mode 100644 src/QXmppOutgoingServer.cpp delete mode 100644 src/QXmppOutgoingServer.h delete mode 100644 src/QXmppServer.cpp delete mode 100644 src/QXmppServer.h delete mode 100644 src/QXmppServerExtension.cpp delete mode 100644 src/QXmppServerExtension.h delete mode 100644 src/QXmppServerPlugin.h create mode 100644 src/server/QXmppDialback.cpp create mode 100644 src/server/QXmppDialback.h create mode 100644 src/server/QXmppIncomingClient.cpp create mode 100644 src/server/QXmppIncomingClient.h create mode 100644 src/server/QXmppIncomingServer.cpp create mode 100644 src/server/QXmppIncomingServer.h create mode 100644 src/server/QXmppOutgoingServer.cpp create mode 100644 src/server/QXmppOutgoingServer.h create mode 100644 src/server/QXmppServer.cpp create mode 100644 src/server/QXmppServer.h create mode 100644 src/server/QXmppServerExtension.cpp create mode 100644 src/server/QXmppServerExtension.h create mode 100644 src/server/QXmppServerPlugin.h (limited to 'src') diff --git a/src/QXmppDialback.cpp b/src/QXmppDialback.cpp deleted file mode 100644 index f9100869..00000000 --- a/src/QXmppDialback.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 - -#include "QXmppConstants.h" -#include "QXmppDialback.h" -#include "QXmppUtils.h" - -/// Constructs a QXmppDialback. - -QXmppDialback::QXmppDialback() - : m_command(Result) -{ -} - -/// Returns the dialback command. - -QXmppDialback::Command QXmppDialback::command() const -{ - return m_command; -} - -/// Sets the dialback command. -/// -/// \param command - -void QXmppDialback::setCommand(QXmppDialback::Command command) -{ - m_command = command; -} - -/// Returns the dialback key. - -QString QXmppDialback::key() const -{ - return m_key; -} - -/// Sets the dialback key. -/// -/// \param key - -void QXmppDialback::setKey(const QString &key) -{ - m_key = key; -} - -/// Returns the dialback type. - -QString QXmppDialback::type() const -{ - return m_type; -} - -/// Sets the dialback type. -/// -/// \param type - -void QXmppDialback::setType(const QString &type) -{ - m_type = type; -} - -bool QXmppDialback::isDialback(const QDomElement &element) -{ - return element.namespaceURI() == ns_server_dialback && - (element.tagName() == QLatin1String("result") || - element.tagName() == QLatin1String("verify")); -} - -void QXmppDialback::parse(const QDomElement &element) -{ - QXmppStanza::parse(element); - if (element.tagName() == QLatin1String("result")) - m_command = Result; - else - m_command = Verify; - m_type = element.attribute("type"); - m_key = element.text(); -} - -void QXmppDialback::toXml(QXmlStreamWriter *xmlWriter) const -{ - if (m_command == Result) - xmlWriter->writeStartElement("db:result"); - else - xmlWriter->writeStartElement("db:verify"); - helperToXmlAddAttribute(xmlWriter, "id", id()); - helperToXmlAddAttribute(xmlWriter, "to", to()); - helperToXmlAddAttribute(xmlWriter, "from", from()); - helperToXmlAddAttribute(xmlWriter, "type", m_type); - if (!m_key.isEmpty()) - xmlWriter->writeCharacters(m_key); - xmlWriter->writeEndElement(); -} - diff --git a/src/QXmppDialback.h b/src/QXmppDialback.h deleted file mode 100644 index 28f01f81..00000000 --- a/src/QXmppDialback.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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. - * - */ - -#ifndef QXMPPDIALBACK_H -#define QXMPPDIALBACK_H - -#include "QXmppStanza.h" - -/// \brief The QXmppDialback class represents a stanza used for the Server -/// Dialback protocol as specified by XEP-0220: Server Dialback. -/// -/// \ingroup Stanzas - -class QXmppDialback : public QXmppStanza -{ -public: - /// This enum is used to describe a dialback command. - enum Command { - Result, ///< A dialback command between the originating server - ///< and the receiving server. - Verify, ///< A dialback command between the receiving server - ///< and the authoritative server. - }; - - QXmppDialback(); - - Command command() const; - void setCommand(Command command); - - QString key() const; - void setKey(const QString &key); - - QString type() const; - void setType(const QString &type); - - /// \cond - void parse(const QDomElement &element); - void toXml(QXmlStreamWriter *writer) const; - - static bool isDialback(const QDomElement &element); - /// \endcond - -private: - Command m_command; - QString m_key; - QString m_type; -}; - -#endif diff --git a/src/QXmppIncomingClient.cpp b/src/QXmppIncomingClient.cpp deleted file mode 100644 index 7338115a..00000000 --- a/src/QXmppIncomingClient.cpp +++ /dev/null @@ -1,445 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include "QXmppBindIq.h" -#include "QXmppConstants.h" -#include "QXmppMessage.h" -#include "QXmppPasswordChecker.h" -#include "QXmppSaslAuth.h" -#include "QXmppSessionIq.h" -#include "QXmppStreamFeatures.h" -#include "QXmppUtils.h" - -#include "QXmppIncomingClient.h" - -class QXmppIncomingClientPrivate -{ -public: - QTimer *idleTimer; - - QString domain; - QString username; - QString resource; - QString jid; - QXmppPasswordChecker *passwordChecker; - QXmppSaslDigestMd5 saslDigest; - int saslDigestStep; - QString saslDigestUsername; -}; - -/// Constructs a new incoming client stream. -/// -/// \param socket The socket for the XMPP stream. -/// \param domain The local domain. -/// \param parent The parent QObject for the stream (optional). -/// - -QXmppIncomingClient::QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent) - : QXmppStream(parent), - d(new QXmppIncomingClientPrivate) -{ - d->passwordChecker = 0; - d->domain = domain; - d->saslDigestStep = 0; - - if (socket) { - info(QString("Incoming client connection from %1 %2").arg( - socket->peerAddress().toString(), - QString::number(socket->peerPort()))); - setSocket(socket); - } - - // create inactivity timer - d->idleTimer = new QTimer(this); - d->idleTimer->setSingleShot(true); - bool check = connect(d->idleTimer, SIGNAL(timeout()), - this, SLOT(onTimeout())); - Q_ASSERT(check); - Q_UNUSED(check); -} - -/// Destroys the current stream. -/// - -QXmppIncomingClient::~QXmppIncomingClient() -{ - delete d; -} - -/// Returns true if the socket is connected, the client is authenticated -/// and a resource is bound. -/// - -bool QXmppIncomingClient::isConnected() const -{ - return QXmppStream::isConnected() && - !d->username.isEmpty() && - !d->resource.isEmpty(); -} - -/// Returns the client's JID. -/// - -QString QXmppIncomingClient::jid() const -{ - return d->jid; -} - -/// Sets the number of seconds after which a client will be disconnected -/// for inactivity. - -void QXmppIncomingClient::setInactivityTimeout(int secs) -{ - d->idleTimer->stop(); - d->idleTimer->setInterval(secs * 1000); - if (d->idleTimer->interval()) - d->idleTimer->start(); -} - -/// Sets the password checker used to verify client credentials. -/// -/// \param checker -/// - -void QXmppIncomingClient::setPasswordChecker(QXmppPasswordChecker *checker) -{ - d->passwordChecker = checker; -} - -void QXmppIncomingClient::handleStream(const QDomElement &streamElement) -{ - if (d->idleTimer->interval()) - d->idleTimer->start(); - d->saslDigestStep = 0; - d->saslDigestUsername.clear(); - - // start stream - const QByteArray sessionId = generateStanzaHash().toAscii(); - QString response = QString("").arg( - ns_client, - ns_stream, - sessionId, - d->domain.toAscii()); - sendData(response.toUtf8()); - - // check requested domain - if (streamElement.attribute("to") != d->domain) - { - QString response = QString("" - "" - "" - "This server does not serve %1" - "" - "").arg(streamElement.attribute("to")); - sendData(response.toUtf8()); - disconnectFromHost(); - return; - } - - // send stream features - QXmppStreamFeatures features; - if (socket() && !socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull()) - features.setTlsMode(QXmppStreamFeatures::Enabled); - if (!d->username.isEmpty()) - { - features.setBindMode(QXmppStreamFeatures::Required); - features.setSessionMode(QXmppStreamFeatures::Enabled); - } - else if (d->passwordChecker) - { - QList mechanisms; - mechanisms << QXmppConfiguration::SASLPlain; - if (d->passwordChecker->hasGetPassword()) - mechanisms << QXmppConfiguration::SASLDigestMD5; - features.setAuthMechanisms(mechanisms); - } - sendPacket(features); -} - -void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv) -{ - const QString ns = nodeRecv.namespaceURI(); - - if (d->idleTimer->interval()) - d->idleTimer->start(); - - if (ns == ns_tls && nodeRecv.tagName() == QLatin1String("starttls")) - { - sendData(""); - socket()->flush(); - socket()->startServerEncryption(); - return; - } - else if (ns == ns_sasl) - { - if (nodeRecv.tagName() == QLatin1String("auth")) - { - const QString mechanism = nodeRecv.attribute("mechanism"); - if (mechanism == QLatin1String("PLAIN")) - { - QList auth = QByteArray::fromBase64(nodeRecv.text().toAscii()).split('\0'); - if (auth.size() != 3) - { - sendData(""); - disconnectFromHost(); - return; - } - - QXmppPasswordRequest request; - request.setDomain(d->domain); - request.setUsername(QString::fromUtf8(auth[1])); - request.setPassword(QString::fromUtf8(auth[2])); - if (!d->passwordChecker) { - // FIXME: what type of failure? - warning(QString("Cannot authenticate '%1', no password checker").arg(request.username())); - sendData(""); - disconnectFromHost(); - return; - } - - QXmppPasswordReply *reply = d->passwordChecker->checkPassword(request); - reply->setParent(this); - reply->setProperty("__sasl_username", request.username()); - connect(reply, SIGNAL(finished()), this, SLOT(onPasswordReply())); - } - else if (mechanism == QLatin1String("DIGEST-MD5")) - { - // generate nonce - d->saslDigest.setNonce(QXmppSaslDigestMd5::generateNonce()); - d->saslDigest.setQop("auth"); - d->saslDigestStep = 1; - - QMap challenge; - challenge["nonce"] = d->saslDigest.nonce(); - challenge["realm"] = d->domain.toUtf8(); - challenge["qop"] = d->saslDigest.qop(); - challenge["charset"] = "utf-8"; - challenge["algorithm"] = "md5-sess"; - - const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64(); - sendData("" + data +""); - } - else - { - // unsupported method - sendData(""); - disconnectFromHost(); - return; - } - } - else if (nodeRecv.tagName() == QLatin1String("response")) - { - if (d->saslDigestStep == 1) - { - const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii()); - QMap saslResponse = QXmppSaslDigestMd5::parseMessage(raw); - - // check credentials - const QString username = QString::fromUtf8(saslResponse.value("username")); - if (!d->passwordChecker) { - // FIXME: what type of failure? - warning(QString("Cannot authenticate '%1', no password checker").arg(username)); - sendData(""); - disconnectFromHost(); - return; - } - - QXmppPasswordRequest request; - request.setUsername(username); - request.setDomain(d->domain); - - QXmppPasswordReply *reply = d->passwordChecker->getDigest(request); - reply->setParent(this); - reply->setProperty("__sasl_raw", raw); - connect(reply, SIGNAL(finished()), this, SLOT(onDigestReply())); - } - else if (d->saslDigestStep == 2) - { - // authentication succeeded - d->saslDigestStep = 3; - d->username = d->saslDigestUsername; - d->jid = QString("%1@%2").arg(d->username, d->domain); - info(QString("Authentication succeeded for '%1'").arg(d->username)); - sendData(""); - handleStart(); - } - } - } - else if (ns == ns_client) - { - if (nodeRecv.tagName() == QLatin1String("iq")) - { - const QString type = nodeRecv.attribute("type"); - if (QXmppBindIq::isBindIq(nodeRecv) && type == QLatin1String("set")) - { - QXmppBindIq bindSet; - bindSet.parse(nodeRecv); - d->resource = bindSet.resource().trimmed(); - if (d->resource.isEmpty()) - d->resource = generateStanzaHash(); - d->jid = QString("%1@%2/%3").arg(d->username, d->domain, d->resource); - - QXmppBindIq bindResult; - bindResult.setType(QXmppIq::Result); - bindResult.setId(bindSet.id()); - bindResult.setJid(d->jid); - sendPacket(bindResult); - - // bound - emit connected(); - return; - } - else if (QXmppSessionIq::isSessionIq(nodeRecv) && type == QLatin1String("set")) - { - QXmppSessionIq sessionSet; - sessionSet.parse(nodeRecv); - - QXmppIq sessionResult; - sessionResult.setType(QXmppIq::Result); - sessionResult.setId(sessionSet.id()); - sessionResult.setTo(d->jid); - sendPacket(sessionResult); - return; - } - } - - // check the sender is legitimate - const QString from = nodeRecv.attribute("from"); - if (!from.isEmpty() && from != d->jid && from != jidToBareJid(d->jid)) - { - warning(QString("Received a stanza from unexpected JID %1").arg(from)); - return; - } - - // process unhandled stanzas - if (nodeRecv.tagName() == QLatin1String("iq") || - nodeRecv.tagName() == QLatin1String("message") || - nodeRecv.tagName() == QLatin1String("presence")) - { - QDomElement nodeFull(nodeRecv); - - // if the sender is empty, set it to the appropriate JID - if (nodeFull.attribute("from").isEmpty()) - { - if (nodeFull.tagName() == QLatin1String("presence") && - (nodeFull.attribute("type") == QLatin1String("subscribe") || - nodeFull.attribute("type") == QLatin1String("subscribed"))) - nodeFull.setAttribute("from", jidToBareJid(d->jid)); - else - nodeFull.setAttribute("from", d->jid); - } - - // if the recipient is empty, set it to the local domain - if (nodeFull.attribute("to").isEmpty()) - nodeFull.setAttribute("to", d->domain); - - // emit stanza for processing by server - emit elementReceived(nodeFull); - } - } -} - -void QXmppIncomingClient::onDigestReply() -{ - QXmppPasswordReply *reply = qobject_cast(sender()); - if (!reply) - return; - reply->deleteLater(); - - const QMap saslResponse = QXmppSaslDigestMd5::parseMessage(reply->property("__sasl_raw").toByteArray()); - const QString username = QString::fromUtf8(saslResponse.value("username")); - if (reply->error() == QXmppPasswordReply::TemporaryError) { - warning(QString("Temporary authentication failure for '%1'").arg(username)); - sendData(""); - disconnectFromHost(); - return; - } - - d->saslDigest.setSecret(reply->digest()); - d->saslDigest.setDigestUri(saslResponse.value("digest-uri")); - d->saslDigest.setNc(saslResponse.value("nc")); - d->saslDigest.setCnonce(saslResponse.value("cnonce")); - if (saslResponse.value("response") != d->saslDigest.calculateDigest( - QByteArray("AUTHENTICATE:") + d->saslDigest.digestUri())) - { - sendData(""); - disconnectFromHost(); - return; - } - - // send new challenge - d->saslDigestUsername = username; - d->saslDigestStep = 2; - QMap challenge; - challenge["rspauth"] = d->saslDigest.calculateDigest( - QByteArray(":") + d->saslDigest.digestUri()); - const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64(); - sendData("" + data +""); -} - -void QXmppIncomingClient::onPasswordReply() -{ - QXmppPasswordReply *reply = qobject_cast(sender()); - if (!reply) - return; - reply->deleteLater(); - - const QString username = reply->property("__sasl_username").toString(); - switch (reply->error()) { - case QXmppPasswordReply::NoError: - d->username = username; - d->jid = QString("%1@%2").arg(d->username, d->domain); - info(QString("Authentication succeeded for '%1'").arg(username)); - sendData(""); - handleStart(); - break; - case QXmppPasswordReply::AuthorizationError: - warning(QString("Authentication failed for '%1'").arg(username)); - sendData(""); - disconnectFromHost(); - break; - case QXmppPasswordReply::TemporaryError: - warning(QString("Temporary authentication failure for '%1'").arg(username)); - sendData(""); - disconnectFromHost(); - break; - } -} - -void QXmppIncomingClient::onTimeout() -{ - warning(QString("Idle timeout for '%1'").arg(d->jid)); - disconnectFromHost(); - - // make sure disconnected() gets emitted no matter what - QTimer::singleShot(30, this, SIGNAL(disconnected())); -} - - diff --git a/src/QXmppIncomingClient.h b/src/QXmppIncomingClient.h deleted file mode 100644 index 793b8141..00000000 --- a/src/QXmppIncomingClient.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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. - * - */ - -#ifndef QXMPPINCOMINGCLIENT_H -#define QXMPPINCOMINGCLIENT_H - -#include "QXmppStream.h" - -class QXmppIncomingClientPrivate; -class QXmppPasswordChecker; - -/// \brief Interface for password checkers. -/// - -/// \brief The QXmppIncomingClient class represents an incoming XMPP stream -/// from an XMPP client. -/// - -class QXmppIncomingClient : public QXmppStream -{ - Q_OBJECT - -public: - QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent = 0); - ~QXmppIncomingClient(); - - bool isConnected() const; - QString jid() const; - - void setInactivityTimeout(int secs); - void setPasswordChecker(QXmppPasswordChecker *checker); - -signals: - /// This signal is emitted when an element is received. - void elementReceived(const QDomElement &element); - -protected: - /// \cond - void handleStream(const QDomElement &element); - void handleStanza(const QDomElement &element); - /// \endcond - -private slots: - void onDigestReply(); - void onPasswordReply(); - void onTimeout(); - -private: - Q_DISABLE_COPY(QXmppIncomingClient) - QXmppIncomingClientPrivate* const d; -}; - -#endif diff --git a/src/QXmppIncomingServer.cpp b/src/QXmppIncomingServer.cpp deleted file mode 100644 index 2c4cfb9f..00000000 --- a/src/QXmppIncomingServer.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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 -#include -#include - -#include "QXmppConstants.h" -#include "QXmppDialback.h" -#include "QXmppIncomingServer.h" -#include "QXmppOutgoingServer.h" -#include "QXmppStreamFeatures.h" -#include "QXmppUtils.h" - -class QXmppIncomingServerPrivate -{ -public: - QSet authenticated; - QString domain; - QString localStreamId; -}; - -/// Constructs a new incoming server stream. -/// -/// \param socket The socket for the XMPP stream. -/// \param domain The local domain. -/// \param parent The parent QObject for the stream (optional). -/// - -QXmppIncomingServer::QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent) - : QXmppStream(parent), - d(new QXmppIncomingServerPrivate) -{ - d->domain = domain; - - if (socket) { - info(QString("Incoming server connection from %1 %2").arg( - socket->peerAddress().toString(), - QString::number(socket->peerPort()))); - setSocket(socket); - } -} - -/// Destroys the current stream. - -QXmppIncomingServer::~QXmppIncomingServer() -{ - delete d; -} - -/// Returns the stream's identifier. -/// - -QString QXmppIncomingServer::localStreamId() const -{ - return d->localStreamId; -} - -void QXmppIncomingServer::handleStream(const QDomElement &streamElement) -{ - const QString from = streamElement.attribute("from"); - if (!from.isEmpty()) - info(QString("Incoming server stream from %1").arg(from)); - - // start stream - d->localStreamId = generateStanzaHash().toAscii(); - QString data = QString("").arg( - ns_server, - ns_server_dialback, - ns_stream, - d->localStreamId); - sendData(data.toUtf8()); - - // send stream features - QXmppStreamFeatures features; - if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull()) - features.setTlsMode(QXmppStreamFeatures::Enabled); - sendPacket(features); -} - -void QXmppIncomingServer::handleStanza(const QDomElement &stanza) -{ - const QString ns = stanza.namespaceURI(); - - if (ns == ns_tls && stanza.tagName() == QLatin1String("starttls")) - { - sendData(""); - socket()->flush(); - socket()->startServerEncryption(); - return; - } - else if (QXmppDialback::isDialback(stanza)) - { - QXmppDialback request; - request.parse(stanza); - // check the request is valid - if (!request.type().isEmpty() || - request.from().isEmpty() || - request.to() != d->domain || - request.key().isEmpty()) - { - warning("Invalid dialback received"); - return; - } - - const QString domain = request.from(); - if (request.command() == QXmppDialback::Result) - { - debug(QString("Received a dialback result from %1").arg(domain)); - - // establish dialback connection - QXmppOutgoingServer *stream = new QXmppOutgoingServer(d->domain, this); - bool check = connect(stream, SIGNAL(dialbackResponseReceived(QXmppDialback)), - this, SLOT(slotDialbackResponseReceived(QXmppDialback))); - Q_ASSERT(check); - Q_UNUSED(check); - stream->setVerify(d->localStreamId, request.key()); - stream->connectToHost(domain); - } - else if (request.command() == QXmppDialback::Verify) - { - debug(QString("Received a dialback verify from %1").arg(domain)); - emit dialbackRequestReceived(request); - } - - } - else if (d->authenticated.contains(jidToDomain(stanza.attribute("from")))) - { - // relay stanza if the remote party is authenticated - emit elementReceived(stanza); - } else { - warning(QString("Received an element from unverified domain %1").arg(jidToDomain(stanza.attribute("from")))); - disconnectFromHost(); - } -} - -/// Returns true if the socket is connected and the remote server is -/// authenticated. -/// - -bool QXmppIncomingServer::isConnected() const -{ - return QXmppStream::isConnected() && !d->authenticated.isEmpty(); -} - -/// Handles a dialback response received from the authority server. -/// -/// \param response -/// - -void QXmppIncomingServer::slotDialbackResponseReceived(const QXmppDialback &dialback) -{ - QXmppOutgoingServer *stream = qobject_cast(sender()); - if (!stream || - dialback.command() != QXmppDialback::Verify || - dialback.id() != d->localStreamId || - dialback.from() != stream->remoteDomain()) - return; - - // relay verify response - QXmppDialback response; - response.setCommand(QXmppDialback::Result); - response.setTo(dialback.from()); - response.setFrom(d->domain); - response.setType(dialback.type()); - sendPacket(response); - - // check for success - if (response.type() == QLatin1String("valid")) - { - info(QString("Verified incoming domain %1").arg(dialback.from())); - const bool wasConnected = !d->authenticated.isEmpty(); - d->authenticated.insert(dialback.from()); - if (!wasConnected) - emit connected(); - } else { - warning(QString("Failed to verify incoming domain %1").arg(dialback.from())); - disconnectFromHost(); - } - - // disconnect dialback - stream->disconnectFromHost(); - stream->deleteLater(); -} - diff --git a/src/QXmppIncomingServer.h b/src/QXmppIncomingServer.h deleted file mode 100644 index a06e983c..00000000 --- a/src/QXmppIncomingServer.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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. - * - */ - -#ifndef QXMPPINCOMINGSERVER_H -#define QXMPPINCOMINGSERVER_H - -#include "QXmppStream.h" - -class QXmppDialback; -class QXmppIncomingServerPrivate; -class QXmppOutgoingServer; - -/// \brief The QXmppIncomingServer class represents an incoming XMPP stream -/// from an XMPP server. -/// - -class QXmppIncomingServer : public QXmppStream -{ - Q_OBJECT - -public: - QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent); - ~QXmppIncomingServer(); - - bool isConnected() const; - QString localStreamId() const; - -signals: - /// This signal is emitted when a dialback verify request is received. - void dialbackRequestReceived(const QXmppDialback &result); - - /// This signal is emitted when an element is received. - void elementReceived(const QDomElement &element); - -protected: - /// \cond - void handleStanza(const QDomElement &stanzaElement); - void handleStream(const QDomElement &streamElement); - /// \endcond - -private slots: - void slotDialbackResponseReceived(const QXmppDialback &dialback); - -private: - Q_DISABLE_COPY(QXmppIncomingServer) - QXmppIncomingServerPrivate* const d; -}; - -#endif diff --git a/src/QXmppOutgoingServer.cpp b/src/QXmppOutgoingServer.cpp deleted file mode 100644 index e980c27e..00000000 --- a/src/QXmppOutgoingServer.cpp +++ /dev/null @@ -1,331 +0,0 @@ -/* - * 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 -#include -#include -#include -#include "qdnslookup.h" - -#include "QXmppConstants.h" -#include "QXmppDialback.h" -#include "QXmppOutgoingServer.h" -#include "QXmppStreamFeatures.h" -#include "QXmppUtils.h" - -class QXmppOutgoingServerPrivate -{ -public: - QList 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)), - this, SLOT(slotSslErrors(QList))); - 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("").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 , - // 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(""); - 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 &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(); -} - diff --git a/src/QXmppOutgoingServer.h b/src/QXmppOutgoingServer.h deleted file mode 100644 index 4e117c0e..00000000 --- a/src/QXmppOutgoingServer.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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. - * - */ - -#ifndef QXMPPOUTGOINGSERVER_H -#define QXMPPOUTGOINGSERVER_H - -#include - -#include "QXmppStream.h" - -class QSslError; -class QXmppDialback; -class QXmppOutgoingServer; -class QXmppOutgoingServerPrivate; - -/// \brief The QXmppOutgoingServer class represents an outgoing XMPP stream -/// to another XMPP server. -/// - -class QXmppOutgoingServer : public QXmppStream -{ - Q_OBJECT - -public: - QXmppOutgoingServer(const QString &domain, QObject *parent); - ~QXmppOutgoingServer(); - - bool isConnected() const; - - QString localStreamKey() const; - void setLocalStreamKey(const QString &key); - void setVerify(const QString &id, const QString &key); - - QString remoteDomain() const; - -signals: - /// This signal is emitted when a dialback verify response is received. - void dialbackResponseReceived(const QXmppDialback &response); - -protected: - /// \cond - void handleStart(); - void handleStream(const QDomElement &streamElement); - void handleStanza(const QDomElement &stanzaElement); - /// \endcond - -public slots: - void connectToHost(const QString &domain); - void queueData(const QByteArray &data); - -private slots: - void _q_dnsLookupFinished(); - void sendDialback(); - void slotSslErrors(const QList &errors); - void socketError(QAbstractSocket::SocketError error); - -private: - Q_DISABLE_COPY(QXmppOutgoingServer) - QXmppOutgoingServerPrivate* const d; -}; - -#endif diff --git a/src/QXmppServer.cpp b/src/QXmppServer.cpp deleted file mode 100644 index 4515914e..00000000 --- a/src/QXmppServer.cpp +++ /dev/null @@ -1,807 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include - -#include "QXmppConstants.h" -#include "QXmppDialback.h" -#include "QXmppIq.h" -#include "QXmppIncomingClient.h" -#include "QXmppIncomingServer.h" -#include "QXmppOutgoingServer.h" -#include "QXmppPresence.h" -#include "QXmppServer.h" -#include "QXmppServerExtension.h" -#include "QXmppServerPlugin.h" -#include "QXmppUtils.h" - -#include "server/mod_presence.h" - -// Core plugins -Q_IMPORT_PLUGIN(mod_disco) -Q_IMPORT_PLUGIN(mod_ping) -Q_IMPORT_PLUGIN(mod_proxy65) -Q_IMPORT_PLUGIN(mod_stats) -Q_IMPORT_PLUGIN(mod_time) -Q_IMPORT_PLUGIN(mod_version) - -class QXmppServerPrivate -{ -public: - QXmppServerPrivate(QXmppServer *qq); - void loadExtensions(QXmppServer *server); - bool routeData(const QString &to, const QByteArray &data); - void startExtensions(); - void stopExtensions(); - - void info(const QString &message); - void warning(const QString &message); - - QString domain; - QList extensions; - QXmppLogger *logger; - QXmppPasswordChecker *passwordChecker; - - // client-to-server - QXmppSslServer *serverForClients; - QSet incomingClients; - QHash incomingClientsByJid; - QHash > incomingClientsByBareJid; - - // server-to-server - QSet incomingServers; - QSet outgoingServers; - QXmppSslServer *serverForServers; - -private: - bool loaded; - bool started; - QXmppServer *q; -}; - -QXmppServerPrivate::QXmppServerPrivate(QXmppServer *qq) - : logger(0), - passwordChecker(0), - loaded(false), - started(false), - q(qq) -{ -} - -/// Routes XMPP data to the given recipient. -/// -/// \param to -/// \param data -/// - -bool QXmppServerPrivate::routeData(const QString &to, const QByteArray &data) -{ - // refuse to route packets to empty destination, own domain or sub-domains - const QString toDomain = jidToDomain(to); - if (to.isEmpty() || to == domain || toDomain.endsWith("." + domain)) - return false; - - if (toDomain == domain) { - - // look for a client connection - QList found; - if (jidToResource(to).isEmpty()) { - foreach (QXmppIncomingClient *conn, incomingClientsByBareJid.value(to)) - found << conn; - } else { - QXmppIncomingClient *conn = incomingClientsByJid.value(to); - if (conn) - found << conn; - } - - // send data - foreach (QXmppStream *conn, found) - QMetaObject::invokeMethod(conn, "sendData", Q_ARG(QByteArray, data)); - return !found.isEmpty(); - - } else if (serverForServers->isListening()) { - - bool check; - Q_UNUSED(check); - - // look for an outgoing S2S connection - foreach (QXmppOutgoingServer *conn, outgoingServers) { - if (conn->remoteDomain() == toDomain) { - // send or queue data - QMetaObject::invokeMethod(conn, "queueData", Q_ARG(QByteArray, data)); - return true; - } - } - - // if we did not find an outgoing server, - // we need to establish the S2S connection - QXmppOutgoingServer *conn = new QXmppOutgoingServer(domain, 0); - conn->setLocalStreamKey(generateStanzaHash().toAscii()); - conn->moveToThread(q->thread()); - conn->setParent(q); - - check = QObject::connect(conn, SIGNAL(disconnected()), - q, SLOT(_q_outgoingServerDisconnected())); - Q_UNUSED(check); - - // add stream - outgoingServers.insert(conn); - - // queue data and connect to remote server - QMetaObject::invokeMethod(conn, "queueData", Q_ARG(QByteArray, data)); - QMetaObject::invokeMethod(conn, "connectToHost", Q_ARG(QString, toDomain)); - return true; - - } else { - - // S2S is disabled, failed to route data - return false; - - } -} - -/// Handles an incoming XML element. -/// -/// \param server -/// \param stream -/// \param element - -static void handleStanza(QXmppServer *server, const QDomElement &element) -{ - // try extensions - foreach (QXmppServerExtension *extension, server->extensions()) - if (extension->handleStanza(element)) - return; - - // default handlers - const QString domain = server->domain(); - const QString to = element.attribute("to"); - if (to == domain) { - if (element.tagName() == QLatin1String("iq")) { - // we do not support the given IQ - QXmppIq request; - request.parse(element); - - if (request.type() != QXmppIq::Error && request.type() != QXmppIq::Result) { - QXmppIq response(QXmppIq::Error); - response.setId(request.id()); - response.setFrom(domain); - response.setTo(request.from()); - QXmppStanza::Error error(QXmppStanza::Error::Cancel, - QXmppStanza::Error::FeatureNotImplemented); - response.setError(error); - server->sendPacket(response); - } - } - - } else { - - // route element or reply on behalf of missing peer - if (!server->sendElement(element) && element.tagName() == QLatin1String("iq")) { - QXmppIq request; - request.parse(element); - - QXmppIq response(QXmppIq::Error); - response.setId(request.id()); - response.setFrom(request.to()); - response.setTo(request.from()); - QXmppStanza::Error error(QXmppStanza::Error::Cancel, - QXmppStanza::Error::ServiceUnavailable); - response.setError(error); - server->sendPacket(response); - } - } -} - -void QXmppServerPrivate::info(const QString &message) -{ - if (logger) - logger->log(QXmppLogger::InformationMessage, message); -} - -void QXmppServerPrivate::warning(const QString &message) -{ - if (logger) - logger->log(QXmppLogger::WarningMessage, message); -} - -/// Load the server's extensions. -/// -/// \param server - -void QXmppServerPrivate::loadExtensions(QXmppServer *server) -{ - if (!loaded) - { - QObjectList plugins = QPluginLoader::staticInstances(); - foreach (QObject *object, plugins) - { - QXmppServerPlugin *plugin = qobject_cast(object); - if (!plugin) - continue; - - foreach (const QString &key, plugin->keys()) - server->addExtension(plugin->create(key)); - } - - // FIXME: until we can handle presence errors, we need to - // keep this extension last. - server->addExtension(new QXmppServerPresence); - loaded = true; - } -} - -/// Start the server's extensions. - -void QXmppServerPrivate::startExtensions() -{ - if (!started) - { - foreach (QXmppServerExtension *extension, extensions) - if (!extension->start()) - warning(QString("Could not start extension %1").arg(extension->extensionName())); - started = true; - } -} - -/// Stop the server's extensions (in reverse order). -/// - -void QXmppServerPrivate::stopExtensions() -{ - if (started) - { - for (int i = extensions.size() - 1; i >= 0; --i) - extensions[i]->stop(); - started = false; - } -} - -/// Constructs a new XMPP server instance. -/// -/// \param parent - -QXmppServer::QXmppServer(QObject *parent) - : QXmppLoggable(parent) -{ - bool check; - Q_UNUSED(check); - - qRegisterMetaType("QDomElement"); - - d = new QXmppServerPrivate(this); - d->serverForClients = new QXmppSslServer(this); - check = connect(d->serverForClients, SIGNAL(newConnection(QSslSocket*)), - this, SLOT(_q_clientConnection(QSslSocket*))); - Q_ASSERT(check); - - d->serverForServers = new QXmppSslServer(this); - check = connect(d->serverForServers, SIGNAL(newConnection(QSslSocket*)), - this, SLOT(_q_serverConnection(QSslSocket*))); - Q_ASSERT(check); -} - -/// Destroys an XMPP server instance. -/// - -QXmppServer::~QXmppServer() -{ - close(); - delete d; -} - -/// Registers a new extension with the server. -/// -/// \param extension - -void QXmppServer::addExtension(QXmppServerExtension *extension) -{ - if (!extension || d->extensions.contains(extension)) - return; - d->info(QString("Added extension %1").arg(extension->extensionName())); - extension->setParent(this); - extension->setServer(this); - d->extensions << extension; -} - -/// Returns the list of loaded extensions. -/// - -QList QXmppServer::extensions() -{ - d->loadExtensions(this); - return d->extensions; -} - -/// Returns the server's domain. -/// - -QString QXmppServer::domain() const -{ - return d->domain; -} - -/// Sets the server's domain. -/// -/// \param domain - -void QXmppServer::setDomain(const QString &domain) -{ - d->domain = domain; -} - -/// Returns the QXmppLogger associated with the server. -/// - -QXmppLogger *QXmppServer::logger() -{ - return d->logger; -} - -/// Sets the QXmppLogger associated with the server. -/// -/// \param logger - -void QXmppServer::setLogger(QXmppLogger *logger) -{ - if (d->logger) - QObject::disconnect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)), - d->logger, SLOT(log(QXmppLogger::MessageType,QString))); - d->logger = logger; - d->logger = logger; - if (d->logger) - connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)), - d->logger, SLOT(log(QXmppLogger::MessageType,QString))); -} - -/// Returns the password checker used to verify client credentials. -/// - -QXmppPasswordChecker *QXmppServer::passwordChecker() -{ - return d->passwordChecker; -} - -/// Sets the password checker used to verify client credentials. -/// -/// \param checker -/// - -void QXmppServer::setPasswordChecker(QXmppPasswordChecker *checker) -{ - d->passwordChecker = checker; -} - -/// Returns the statistics for the server. - -QVariantMap QXmppServer::statistics() const -{ - QVariantMap stats; - stats["version"] = qApp->applicationVersion(); - stats["incoming-clients"] = d->incomingClients.size(); - stats["incoming-servers"] = d->incomingServers.size(); - stats["outgoing-servers"] = d->outgoingServers.size(); - return stats; -} - -/// Sets the path for additional SSL CA certificates. -/// -/// \param path - -void QXmppServer::addCaCertificates(const QString &path) -{ - if (!path.isEmpty() && !QFileInfo(path).isReadable()) - d->warning(QString("SSL CA certificates are not readable %1").arg(path)); - QList certificates = QSslCertificate::fromPath(path); - d->serverForClients->addCaCertificates(certificates); - d->serverForServers->addCaCertificates(certificates); -} - -/// Sets the path for the local SSL certificate. -/// -/// \param path - -void QXmppServer::setLocalCertificate(const QString &path) -{ - QSslCertificate certificate; - QFile file(path); - if (!path.isEmpty() && file.open(QIODevice::ReadOnly | QIODevice::Text)) - certificate = QSslCertificate(file.readAll()); - else - d->warning(QString("SSL certificate is not readable %1").arg(path)); - d->serverForClients->setLocalCertificate(certificate); - d->serverForServers->setLocalCertificate(certificate); -} - -/// Sets the path for the local SSL private key. -/// -/// \param path - -void QXmppServer::setPrivateKey(const QString &path) -{ - QSslKey key; - QFile file(path); - if (!path.isEmpty() && file.open(QIODevice::ReadOnly)) - key = QSslKey(file.readAll(), QSsl::Rsa); - else - d->warning(QString("SSL key is not readable %1").arg(path)); - d->serverForClients->setPrivateKey(key); - d->serverForServers->setPrivateKey(key); -} - -/// Listen for incoming XMPP client connections. -/// -/// \param address -/// \param port - -bool QXmppServer::listenForClients(const QHostAddress &address, quint16 port) -{ - if (!d->serverForClients->listen(address, port)) - { - d->warning(QString("Could not start listening for C2S on port %1").arg(QString::number(port))); - return false; - } - - // start extensions - d->loadExtensions(this); - d->startExtensions(); - return true; -} - -/// Closes the server. -/// - -void QXmppServer::close() -{ - // prevent new connections - d->serverForClients->close(); - d->serverForServers->close(); - - // stop extensions - d->stopExtensions(); - - // close XMPP streams - foreach (QXmppIncomingClient *stream, d->incomingClients) - stream->disconnectFromHost(); - foreach (QXmppIncomingServer *stream, d->incomingServers) - stream->disconnectFromHost(); - foreach (QXmppOutgoingServer *stream, d->outgoingServers) - stream->disconnectFromHost(); -} - -/// Listen for incoming XMPP server connections. -/// -/// \param address -/// \param port - -bool QXmppServer::listenForServers(const QHostAddress &address, quint16 port) -{ - if (!d->serverForServers->listen(address, port)) - { - d->warning(QString("Could not start listening for S2S on port %1").arg(QString::number(port))); - return false; - } - - // start extensions - d->loadExtensions(this); - d->startExtensions(); - return true; -} - -/// Route an XMPP stanza. -/// -/// \param element - -bool QXmppServer::sendElement(const QDomElement &element) -{ - // serialize data - QByteArray data; - QXmlStreamWriter xmlStream(&data); - const QStringList omitNamespaces = QStringList() << ns_client << ns_server; - helperToXmlAddDomElement(&xmlStream, element, omitNamespaces); - - // route data - return d->routeData(element.attribute("to"), data); -} - -/// Route an XMPP packet. -/// -/// \param packet - -bool QXmppServer::sendPacket(const QXmppStanza &packet) -{ - // serialize data - QByteArray data; - QXmlStreamWriter xmlStream(&data); - packet.toXml(&xmlStream); - - // route data - return d->routeData(packet.to(), data); -} - -/// Add a new incoming client stream. -/// -/// \param stream - -void QXmppServer::addIncomingClient(QXmppIncomingClient *stream) -{ - bool check; - Q_UNUSED(check); - - stream->setPasswordChecker(d->passwordChecker); - - check = connect(stream, SIGNAL(connected()), - this, SLOT(_q_clientConnected())); - Q_ASSERT(check); - - check = connect(stream, SIGNAL(disconnected()), - this, SLOT(_q_clientDisconnected())); - Q_ASSERT(check); - - check = connect(stream, SIGNAL(elementReceived(QDomElement)), - this, SLOT(handleElement(QDomElement))); - Q_ASSERT(check); - - // add stream - d->incomingClients.insert(stream); -} - -/// Handle a new incoming TCP connection from a client. -/// -/// \param socket - -void QXmppServer::_q_clientConnection(QSslSocket *socket) -{ - // check the socket didn't die since the signal was emitted - if (socket->state() != QAbstractSocket::ConnectedState) { - delete socket; - return; - } - - QXmppIncomingClient *stream = new QXmppIncomingClient(socket, d->domain, this); - stream->setInactivityTimeout(120); - socket->setParent(stream); - addIncomingClient(stream); -} - -/// Handle a successful stream connection for a client. -/// - -void QXmppServer::_q_clientConnected() -{ - QXmppIncomingClient *client = qobject_cast(sender()); - if (!client) - return; - - // FIXME: at this point the JID must contain a resource, assert it? - const QString jid = client->jid(); - - // check whether the connection conflicts with another one - QXmppIncomingClient *old = d->incomingClientsByJid.value(jid); - if (old && old != client) { - old->sendData("Replaced by new connection"); - old->disconnectFromHost(); - } - d->incomingClientsByJid.insert(jid, client); - d->incomingClientsByBareJid[jidToBareJid(jid)].insert(client); - - // emit signal - emit clientConnected(jid); -} - -/// Handle a stream disconnection for a client. - -void QXmppServer::_q_clientDisconnected() -{ - QXmppIncomingClient *client = qobject_cast(sender()); - if (!client) - return; - - if (d->incomingClients.remove(client)) { - // remove stream from routing tables - const QString jid = client->jid(); - if (!jid.isEmpty()) { - if (d->incomingClientsByJid.value(jid) == client) - d->incomingClientsByJid.remove(jid); - const QString bareJid = jidToBareJid(jid); - if (d->incomingClientsByBareJid.contains(bareJid)) - d->incomingClientsByBareJid[bareJid].remove(client); - } - - // destroy client - client->deleteLater(); - - // emit signal - if (!jid.isEmpty()) - emit clientDisconnected(jid); - } -} - -void QXmppServer::_q_dialbackRequestReceived(const QXmppDialback &dialback) -{ - QXmppIncomingServer *stream = qobject_cast(sender()); - if (!stream) - return; - - if (dialback.command() == QXmppDialback::Verify) - { - // handle a verify request - foreach (QXmppOutgoingServer *out, d->outgoingServers) { - if (out->remoteDomain() != dialback.from()) - continue; - - bool isValid = dialback.key() == out->localStreamKey(); - QXmppDialback verify; - verify.setCommand(QXmppDialback::Verify); - verify.setId(dialback.id()); - verify.setTo(dialback.from()); - verify.setFrom(d->domain); - verify.setType(isValid ? "valid" : "invalid"); - stream->sendPacket(verify); - return; - } - } -} - -/// Handle an incoming XML element. - -void QXmppServer::handleElement(const QDomElement &element) -{ - handleStanza(this, element); -} - -/// Handle a stream disconnection for an outgoing server. - -void QXmppServer::_q_outgoingServerDisconnected() -{ - QXmppOutgoingServer *outgoing = qobject_cast(sender()); - if (!outgoing) - return; - - if (d->outgoingServers.remove(outgoing)) - outgoing->deleteLater(); -} - -/// Handle a new incoming TCP connection from a server. -/// -/// \param socket - -void QXmppServer::_q_serverConnection(QSslSocket *socket) -{ - bool check; - Q_UNUSED(check); - - // check the socket didn't die since the signal was emitted - if (socket->state() != QAbstractSocket::ConnectedState) { - delete socket; - return; - } - - QXmppIncomingServer *stream = new QXmppIncomingServer(socket, d->domain, this); - socket->setParent(stream); - - check = connect(stream, SIGNAL(disconnected()), - this, SLOT(_q_serverDisconnected())); - Q_ASSERT(check); - - check = connect(stream, SIGNAL(dialbackRequestReceived(QXmppDialback)), - this, SLOT(_q_dialbackRequestReceived(QXmppDialback))); - Q_ASSERT(check); - - check = connect(stream, SIGNAL(elementReceived(QDomElement)), - this, SLOT(handleElement(QDomElement))); - Q_ASSERT(check); - - // add stream - d->incomingServers.insert(stream); -} - -/// Handle a stream disconnection for an incoming server. - -void QXmppServer::_q_serverDisconnected() -{ - QXmppIncomingServer *incoming = qobject_cast(sender()); - if (!incoming) - return; - - if (d->incomingServers.remove(incoming)) - incoming->deleteLater(); -} - -class QXmppSslServerPrivate -{ -public: - QList caCertificates; - QSslCertificate localCertificate; - QSslKey privateKey; -}; - -/// Constructs a new SSL server instance. -/// -/// \param parent - -QXmppSslServer::QXmppSslServer(QObject *parent) - : QTcpServer(parent), - d(new QXmppSslServerPrivate) -{ -} - -/// Destroys an SSL server instance. -/// - -QXmppSslServer::~QXmppSslServer() -{ - delete d; -} - -void QXmppSslServer::incomingConnection(int socketDescriptor) -{ - QSslSocket *socket = new QSslSocket; - if (!socket->setSocketDescriptor(socketDescriptor)) { - delete socket; - return; - } - - if (!d->localCertificate.isNull() && !d->privateKey.isNull()) { - socket->setProtocol(QSsl::AnyProtocol); - socket->addCaCertificates(d->caCertificates); - socket->setLocalCertificate(d->localCertificate); - socket->setPrivateKey(d->privateKey); - } - emit newConnection(socket); -} - -/// Adds the given certificates to the CA certificate database to be used -/// for incoming connnections. -/// -/// \param certificates - -void QXmppSslServer::addCaCertificates(const QList &certificates) -{ - d->caCertificates += certificates; -} - -/// Sets the local certificate to be used for incoming connections. -/// -/// \param certificate - -void QXmppSslServer::setLocalCertificate(const QSslCertificate &certificate) -{ - d->localCertificate = certificate; -} - -/// Sets the local private key to be used for incoming connections. -/// -/// \param key - -void QXmppSslServer::setPrivateKey(const QSslKey &key) -{ - d->privateKey = key; -} - diff --git a/src/QXmppServer.h b/src/QXmppServer.h deleted file mode 100644 index cb5fcedd..00000000 --- a/src/QXmppServer.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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. - * - */ - -#ifndef QXMPPSERVER_H -#define QXMPPSERVER_H - -#include -#include - -#include "QXmppLogger.h" - -class QDomElement; -class QSslCertificate; -class QSslKey; -class QSslSocket; - -class QXmppDialback; -class QXmppIncomingClient; -class QXmppOutgoingServer; -class QXmppPasswordChecker; -class QXmppPresence; -class QXmppServerExtension; -class QXmppServerPrivate; -class QXmppSslServer; -class QXmppStanza; -class QXmppStream; - -/// \brief The QXmppServer class represents an XMPP server. -/// -/// It provides support for both client-to-server and server-to-server -/// communications, SSL encryption and logging facilities. -/// -/// QXmppServer comes with a number of modules for service discovery, -/// XMPP ping, statistics and file transfer proxy support. You can write -/// your own extensions for QXmppServer by subclassing QXmppServerExtension. -/// -/// \ingroup Core - -class QXmppServer : public QXmppLoggable -{ - Q_OBJECT - -public: - QXmppServer(QObject *parent = 0); - ~QXmppServer(); - - void addExtension(QXmppServerExtension *extension); - QList extensions(); - - QString domain() const; - void setDomain(const QString &domain); - - QXmppLogger *logger(); - void setLogger(QXmppLogger *logger); - - QXmppPasswordChecker *passwordChecker(); - void setPasswordChecker(QXmppPasswordChecker *checker); - - QVariantMap statistics() const; - - void addCaCertificates(const QString &caCertificates); - void setLocalCertificate(const QString &path); - void setPrivateKey(const QString &path); - - void close(); - bool listenForClients(const QHostAddress &address = QHostAddress::Any, quint16 port = 5222); - bool listenForServers(const QHostAddress &address = QHostAddress::Any, quint16 port = 5269); - - bool sendElement(const QDomElement &element); - bool sendPacket(const QXmppStanza &stanza); - - /// \cond - // FIXME: this method should not be public, but it is needed to - // implement BOSH support as an extension. - void addIncomingClient(QXmppIncomingClient *stream); - /// \endcond - -signals: - /// This signal is emitted when a client has connected. - void clientConnected(const QString &jid); - - /// This signal is emitted when a client has disconnected. - void clientDisconnected(const QString &jid); - -public slots: - void handleElement(const QDomElement &element); - -private slots: - void _q_clientConnection(QSslSocket *socket); - void _q_clientConnected(); - void _q_clientDisconnected(); - void _q_dialbackRequestReceived(const QXmppDialback &dialback); - void _q_outgoingServerDisconnected(); - void _q_serverConnection(QSslSocket *socket); - void _q_serverDisconnected(); - -private: - friend class QXmppServerPrivate; - QXmppServerPrivate *d; -}; - -class QXmppSslServerPrivate; - -/// \brief The QXmppSslServer class represents an SSL-enabled TCP server. -/// - -class QXmppSslServer : public QTcpServer -{ - Q_OBJECT - -public: - QXmppSslServer(QObject *parent = 0); - ~QXmppSslServer(); - - void addCaCertificates(const QList &certificates); - void setLocalCertificate(const QSslCertificate &certificate); - void setPrivateKey(const QSslKey &key); - -signals: - /// This signal is emitted when a new connection is established. - void newConnection(QSslSocket *socket); - -private: - void incomingConnection(int socketDescriptor); - QXmppSslServerPrivate * const d; -}; - -#endif diff --git a/src/QXmppServerExtension.cpp b/src/QXmppServerExtension.cpp deleted file mode 100644 index 7369a4f7..00000000 --- a/src/QXmppServerExtension.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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 -#include -#include - -#include "QXmppLogger.h" -#include "QXmppServer.h" -#include "QXmppServerExtension.h" - -class QXmppServerExtensionPrivate -{ -public: - QXmppServer *server; -}; - -QXmppServerExtension::QXmppServerExtension() - : d(new QXmppServerExtensionPrivate) -{ - d->server = 0; -} - -QXmppServerExtension::~QXmppServerExtension() -{ - delete d; -} - -/// Returns the discovery features to add to the server. -/// - -QStringList QXmppServerExtension::discoveryFeatures() const -{ - return QStringList(); -} - -/// Returns the discovery items to add to the server. -/// - -QStringList QXmppServerExtension::discoveryItems() const -{ - return QStringList(); -} - -/// Returns the extension's name. -/// - -QString QXmppServerExtension::extensionName() const -{ - int index = metaObject()->indexOfClassInfo("ExtensionName"); - if (index < 0) - return QString(); - const char *name = metaObject()->classInfo(index).value(); - return QString::fromLatin1(name); -} - -/// Handles an incoming XMPP stanza. -/// -/// Return true if no further processing should occur, false otherwise. -/// -/// \param stanza The received stanza. - -bool QXmppServerExtension::handleStanza(const QDomElement &stanza) -{ - Q_UNUSED(stanza); - return false; -} - -/// Returns the list of subscribers for the given JID. -/// -/// \param jid - -QSet QXmppServerExtension::presenceSubscribers(const QString &jid) -{ - Q_UNUSED(jid); - return QSet(); -} - -/// Returns the list of subscriptions for the given JID. -/// -/// \param jid - -QSet QXmppServerExtension::presenceSubscriptions(const QString &jid) -{ - Q_UNUSED(jid); - return QSet(); -} - -/// Returns the extension's statistics. -/// - -QVariantMap QXmppServerExtension::statistics() const -{ - return QVariantMap(); -} - -/// Sets the extension's statistics. -/// - -void QXmppServerExtension::setStatistics(const QVariantMap &statistics) -{ - Q_UNUSED(statistics); -} - -/// Starts the extension. -/// -/// Return true if the extension was started, false otherwise. - -bool QXmppServerExtension::start() -{ - return true; -} - -/// Stops the extension. - -void QXmppServerExtension::stop() -{ -} - -/// Returns the server which loaded this extension. - -QXmppServer *QXmppServerExtension::server() -{ - return d->server; -} - -/// Sets the server which loaded this extension. -/// -/// \param server - -void QXmppServerExtension::setServer(QXmppServer *server) -{ - d->server = server; -} - diff --git a/src/QXmppServerExtension.h b/src/QXmppServerExtension.h deleted file mode 100644 index d908e17b..00000000 --- a/src/QXmppServerExtension.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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. - * - */ - -#ifndef QXMPPSERVEREXTENSION_H -#define QXMPPSERVEREXTENSION_H - -#include - -#include "QXmppLogger.h" - -class QDomElement; -class QStringList; - -class QXmppServer; -class QXmppServerExtensionPrivate; -class QXmppStream; - -/// \brief The QXmppServerExtension class is the base class for QXmppServer -/// extensions. -/// -/// If you want to extend QXmppServer, for instance to support an IQ type -/// which is not natively supported, you can subclass QXmppServerExtension -/// and implement handleStanza(). You can then add your extension to the -/// client instance using QXmppServer::addExtension(). -/// -/// \ingroup Core - -class QXmppServerExtension : public QXmppLoggable -{ - Q_OBJECT - -public: - QXmppServerExtension(); - ~QXmppServerExtension(); - QString extensionName() const; - - virtual QStringList discoveryFeatures() const; - virtual QStringList discoveryItems() const; - virtual bool handleStanza(const QDomElement &stanza); - virtual QSet presenceSubscribers(const QString &jid); - virtual QSet presenceSubscriptions(const QString &jid); - - virtual QVariantMap statistics() const; - virtual void setStatistics(const QVariantMap &statistics); - - virtual bool start(); - virtual void stop(); - -protected: - QXmppServer *server(); - -private: - void setServer(QXmppServer *server); - QXmppServerExtensionPrivate * const d; - - friend class QXmppServer; -}; - -#endif diff --git a/src/QXmppServerPlugin.h b/src/QXmppServerPlugin.h deleted file mode 100644 index d247dd9e..00000000 --- a/src/QXmppServerPlugin.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - * - */ - -#ifndef QXMPPSERVERPLUGIN_H -#define QXMPPSERVERPLUGIN_H - -#include - -class QXmppServer; -class QXmppServerExtension; - -class QXmppServerPluginInterface -{ -public: - virtual QXmppServerExtension *create(const QString &key) = 0; - virtual QStringList keys() const = 0; -}; - -Q_DECLARE_INTERFACE(QXmppServerPluginInterface, "com.googlecode.qxmpp.ServerPlugin/1.0") - -/// \brief The QXmppServerPlugin class is the base class for QXmppServer plugins. -/// - -class QXmppServerPlugin : public QObject, public QXmppServerPluginInterface -{ - Q_OBJECT - Q_INTERFACES(QXmppServerPluginInterface) - -public: - /// Creates and returns the specified QXmppServerExtension. - /// - /// \param key The key for the QXmppServerExtension. - virtual QXmppServerExtension *create(const QString &key) = 0; - - /// Returns the list of keys supported by this plugin. - /// - virtual QStringList keys() const = 0; -}; - -#endif diff --git a/src/server/QXmppDialback.cpp b/src/server/QXmppDialback.cpp new file mode 100644 index 00000000..f9100869 --- /dev/null +++ b/src/server/QXmppDialback.cpp @@ -0,0 +1,117 @@ +/* + * 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 + +#include "QXmppConstants.h" +#include "QXmppDialback.h" +#include "QXmppUtils.h" + +/// Constructs a QXmppDialback. + +QXmppDialback::QXmppDialback() + : m_command(Result) +{ +} + +/// Returns the dialback command. + +QXmppDialback::Command QXmppDialback::command() const +{ + return m_command; +} + +/// Sets the dialback command. +/// +/// \param command + +void QXmppDialback::setCommand(QXmppDialback::Command command) +{ + m_command = command; +} + +/// Returns the dialback key. + +QString QXmppDialback::key() const +{ + return m_key; +} + +/// Sets the dialback key. +/// +/// \param key + +void QXmppDialback::setKey(const QString &key) +{ + m_key = key; +} + +/// Returns the dialback type. + +QString QXmppDialback::type() const +{ + return m_type; +} + +/// Sets the dialback type. +/// +/// \param type + +void QXmppDialback::setType(const QString &type) +{ + m_type = type; +} + +bool QXmppDialback::isDialback(const QDomElement &element) +{ + return element.namespaceURI() == ns_server_dialback && + (element.tagName() == QLatin1String("result") || + element.tagName() == QLatin1String("verify")); +} + +void QXmppDialback::parse(const QDomElement &element) +{ + QXmppStanza::parse(element); + if (element.tagName() == QLatin1String("result")) + m_command = Result; + else + m_command = Verify; + m_type = element.attribute("type"); + m_key = element.text(); +} + +void QXmppDialback::toXml(QXmlStreamWriter *xmlWriter) const +{ + if (m_command == Result) + xmlWriter->writeStartElement("db:result"); + else + xmlWriter->writeStartElement("db:verify"); + helperToXmlAddAttribute(xmlWriter, "id", id()); + helperToXmlAddAttribute(xmlWriter, "to", to()); + helperToXmlAddAttribute(xmlWriter, "from", from()); + helperToXmlAddAttribute(xmlWriter, "type", m_type); + if (!m_key.isEmpty()) + xmlWriter->writeCharacters(m_key); + xmlWriter->writeEndElement(); +} + diff --git a/src/server/QXmppDialback.h b/src/server/QXmppDialback.h new file mode 100644 index 00000000..28f01f81 --- /dev/null +++ b/src/server/QXmppDialback.h @@ -0,0 +1,69 @@ +/* + * 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. + * + */ + +#ifndef QXMPPDIALBACK_H +#define QXMPPDIALBACK_H + +#include "QXmppStanza.h" + +/// \brief The QXmppDialback class represents a stanza used for the Server +/// Dialback protocol as specified by XEP-0220: Server Dialback. +/// +/// \ingroup Stanzas + +class QXmppDialback : public QXmppStanza +{ +public: + /// This enum is used to describe a dialback command. + enum Command { + Result, ///< A dialback command between the originating server + ///< and the receiving server. + Verify, ///< A dialback command between the receiving server + ///< and the authoritative server. + }; + + QXmppDialback(); + + Command command() const; + void setCommand(Command command); + + QString key() const; + void setKey(const QString &key); + + QString type() const; + void setType(const QString &type); + + /// \cond + void parse(const QDomElement &element); + void toXml(QXmlStreamWriter *writer) const; + + static bool isDialback(const QDomElement &element); + /// \endcond + +private: + Command m_command; + QString m_key; + QString m_type; +}; + +#endif diff --git a/src/server/QXmppIncomingClient.cpp b/src/server/QXmppIncomingClient.cpp new file mode 100644 index 00000000..7338115a --- /dev/null +++ b/src/server/QXmppIncomingClient.cpp @@ -0,0 +1,445 @@ +/* + * 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 +#include +#include +#include + +#include "QXmppBindIq.h" +#include "QXmppConstants.h" +#include "QXmppMessage.h" +#include "QXmppPasswordChecker.h" +#include "QXmppSaslAuth.h" +#include "QXmppSessionIq.h" +#include "QXmppStreamFeatures.h" +#include "QXmppUtils.h" + +#include "QXmppIncomingClient.h" + +class QXmppIncomingClientPrivate +{ +public: + QTimer *idleTimer; + + QString domain; + QString username; + QString resource; + QString jid; + QXmppPasswordChecker *passwordChecker; + QXmppSaslDigestMd5 saslDigest; + int saslDigestStep; + QString saslDigestUsername; +}; + +/// Constructs a new incoming client stream. +/// +/// \param socket The socket for the XMPP stream. +/// \param domain The local domain. +/// \param parent The parent QObject for the stream (optional). +/// + +QXmppIncomingClient::QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent) + : QXmppStream(parent), + d(new QXmppIncomingClientPrivate) +{ + d->passwordChecker = 0; + d->domain = domain; + d->saslDigestStep = 0; + + if (socket) { + info(QString("Incoming client connection from %1 %2").arg( + socket->peerAddress().toString(), + QString::number(socket->peerPort()))); + setSocket(socket); + } + + // create inactivity timer + d->idleTimer = new QTimer(this); + d->idleTimer->setSingleShot(true); + bool check = connect(d->idleTimer, SIGNAL(timeout()), + this, SLOT(onTimeout())); + Q_ASSERT(check); + Q_UNUSED(check); +} + +/// Destroys the current stream. +/// + +QXmppIncomingClient::~QXmppIncomingClient() +{ + delete d; +} + +/// Returns true if the socket is connected, the client is authenticated +/// and a resource is bound. +/// + +bool QXmppIncomingClient::isConnected() const +{ + return QXmppStream::isConnected() && + !d->username.isEmpty() && + !d->resource.isEmpty(); +} + +/// Returns the client's JID. +/// + +QString QXmppIncomingClient::jid() const +{ + return d->jid; +} + +/// Sets the number of seconds after which a client will be disconnected +/// for inactivity. + +void QXmppIncomingClient::setInactivityTimeout(int secs) +{ + d->idleTimer->stop(); + d->idleTimer->setInterval(secs * 1000); + if (d->idleTimer->interval()) + d->idleTimer->start(); +} + +/// Sets the password checker used to verify client credentials. +/// +/// \param checker +/// + +void QXmppIncomingClient::setPasswordChecker(QXmppPasswordChecker *checker) +{ + d->passwordChecker = checker; +} + +void QXmppIncomingClient::handleStream(const QDomElement &streamElement) +{ + if (d->idleTimer->interval()) + d->idleTimer->start(); + d->saslDigestStep = 0; + d->saslDigestUsername.clear(); + + // start stream + const QByteArray sessionId = generateStanzaHash().toAscii(); + QString response = QString("").arg( + ns_client, + ns_stream, + sessionId, + d->domain.toAscii()); + sendData(response.toUtf8()); + + // check requested domain + if (streamElement.attribute("to") != d->domain) + { + QString response = QString("" + "" + "" + "This server does not serve %1" + "" + "").arg(streamElement.attribute("to")); + sendData(response.toUtf8()); + disconnectFromHost(); + return; + } + + // send stream features + QXmppStreamFeatures features; + if (socket() && !socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull()) + features.setTlsMode(QXmppStreamFeatures::Enabled); + if (!d->username.isEmpty()) + { + features.setBindMode(QXmppStreamFeatures::Required); + features.setSessionMode(QXmppStreamFeatures::Enabled); + } + else if (d->passwordChecker) + { + QList mechanisms; + mechanisms << QXmppConfiguration::SASLPlain; + if (d->passwordChecker->hasGetPassword()) + mechanisms << QXmppConfiguration::SASLDigestMD5; + features.setAuthMechanisms(mechanisms); + } + sendPacket(features); +} + +void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv) +{ + const QString ns = nodeRecv.namespaceURI(); + + if (d->idleTimer->interval()) + d->idleTimer->start(); + + if (ns == ns_tls && nodeRecv.tagName() == QLatin1String("starttls")) + { + sendData(""); + socket()->flush(); + socket()->startServerEncryption(); + return; + } + else if (ns == ns_sasl) + { + if (nodeRecv.tagName() == QLatin1String("auth")) + { + const QString mechanism = nodeRecv.attribute("mechanism"); + if (mechanism == QLatin1String("PLAIN")) + { + QList auth = QByteArray::fromBase64(nodeRecv.text().toAscii()).split('\0'); + if (auth.size() != 3) + { + sendData(""); + disconnectFromHost(); + return; + } + + QXmppPasswordRequest request; + request.setDomain(d->domain); + request.setUsername(QString::fromUtf8(auth[1])); + request.setPassword(QString::fromUtf8(auth[2])); + if (!d->passwordChecker) { + // FIXME: what type of failure? + warning(QString("Cannot authenticate '%1', no password checker").arg(request.username())); + sendData(""); + disconnectFromHost(); + return; + } + + QXmppPasswordReply *reply = d->passwordChecker->checkPassword(request); + reply->setParent(this); + reply->setProperty("__sasl_username", request.username()); + connect(reply, SIGNAL(finished()), this, SLOT(onPasswordReply())); + } + else if (mechanism == QLatin1String("DIGEST-MD5")) + { + // generate nonce + d->saslDigest.setNonce(QXmppSaslDigestMd5::generateNonce()); + d->saslDigest.setQop("auth"); + d->saslDigestStep = 1; + + QMap challenge; + challenge["nonce"] = d->saslDigest.nonce(); + challenge["realm"] = d->domain.toUtf8(); + challenge["qop"] = d->saslDigest.qop(); + challenge["charset"] = "utf-8"; + challenge["algorithm"] = "md5-sess"; + + const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64(); + sendData("" + data +""); + } + else + { + // unsupported method + sendData(""); + disconnectFromHost(); + return; + } + } + else if (nodeRecv.tagName() == QLatin1String("response")) + { + if (d->saslDigestStep == 1) + { + const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii()); + QMap saslResponse = QXmppSaslDigestMd5::parseMessage(raw); + + // check credentials + const QString username = QString::fromUtf8(saslResponse.value("username")); + if (!d->passwordChecker) { + // FIXME: what type of failure? + warning(QString("Cannot authenticate '%1', no password checker").arg(username)); + sendData(""); + disconnectFromHost(); + return; + } + + QXmppPasswordRequest request; + request.setUsername(username); + request.setDomain(d->domain); + + QXmppPasswordReply *reply = d->passwordChecker->getDigest(request); + reply->setParent(this); + reply->setProperty("__sasl_raw", raw); + connect(reply, SIGNAL(finished()), this, SLOT(onDigestReply())); + } + else if (d->saslDigestStep == 2) + { + // authentication succeeded + d->saslDigestStep = 3; + d->username = d->saslDigestUsername; + d->jid = QString("%1@%2").arg(d->username, d->domain); + info(QString("Authentication succeeded for '%1'").arg(d->username)); + sendData(""); + handleStart(); + } + } + } + else if (ns == ns_client) + { + if (nodeRecv.tagName() == QLatin1String("iq")) + { + const QString type = nodeRecv.attribute("type"); + if (QXmppBindIq::isBindIq(nodeRecv) && type == QLatin1String("set")) + { + QXmppBindIq bindSet; + bindSet.parse(nodeRecv); + d->resource = bindSet.resource().trimmed(); + if (d->resource.isEmpty()) + d->resource = generateStanzaHash(); + d->jid = QString("%1@%2/%3").arg(d->username, d->domain, d->resource); + + QXmppBindIq bindResult; + bindResult.setType(QXmppIq::Result); + bindResult.setId(bindSet.id()); + bindResult.setJid(d->jid); + sendPacket(bindResult); + + // bound + emit connected(); + return; + } + else if (QXmppSessionIq::isSessionIq(nodeRecv) && type == QLatin1String("set")) + { + QXmppSessionIq sessionSet; + sessionSet.parse(nodeRecv); + + QXmppIq sessionResult; + sessionResult.setType(QXmppIq::Result); + sessionResult.setId(sessionSet.id()); + sessionResult.setTo(d->jid); + sendPacket(sessionResult); + return; + } + } + + // check the sender is legitimate + const QString from = nodeRecv.attribute("from"); + if (!from.isEmpty() && from != d->jid && from != jidToBareJid(d->jid)) + { + warning(QString("Received a stanza from unexpected JID %1").arg(from)); + return; + } + + // process unhandled stanzas + if (nodeRecv.tagName() == QLatin1String("iq") || + nodeRecv.tagName() == QLatin1String("message") || + nodeRecv.tagName() == QLatin1String("presence")) + { + QDomElement nodeFull(nodeRecv); + + // if the sender is empty, set it to the appropriate JID + if (nodeFull.attribute("from").isEmpty()) + { + if (nodeFull.tagName() == QLatin1String("presence") && + (nodeFull.attribute("type") == QLatin1String("subscribe") || + nodeFull.attribute("type") == QLatin1String("subscribed"))) + nodeFull.setAttribute("from", jidToBareJid(d->jid)); + else + nodeFull.setAttribute("from", d->jid); + } + + // if the recipient is empty, set it to the local domain + if (nodeFull.attribute("to").isEmpty()) + nodeFull.setAttribute("to", d->domain); + + // emit stanza for processing by server + emit elementReceived(nodeFull); + } + } +} + +void QXmppIncomingClient::onDigestReply() +{ + QXmppPasswordReply *reply = qobject_cast(sender()); + if (!reply) + return; + reply->deleteLater(); + + const QMap saslResponse = QXmppSaslDigestMd5::parseMessage(reply->property("__sasl_raw").toByteArray()); + const QString username = QString::fromUtf8(saslResponse.value("username")); + if (reply->error() == QXmppPasswordReply::TemporaryError) { + warning(QString("Temporary authentication failure for '%1'").arg(username)); + sendData(""); + disconnectFromHost(); + return; + } + + d->saslDigest.setSecret(reply->digest()); + d->saslDigest.setDigestUri(saslResponse.value("digest-uri")); + d->saslDigest.setNc(saslResponse.value("nc")); + d->saslDigest.setCnonce(saslResponse.value("cnonce")); + if (saslResponse.value("response") != d->saslDigest.calculateDigest( + QByteArray("AUTHENTICATE:") + d->saslDigest.digestUri())) + { + sendData(""); + disconnectFromHost(); + return; + } + + // send new challenge + d->saslDigestUsername = username; + d->saslDigestStep = 2; + QMap challenge; + challenge["rspauth"] = d->saslDigest.calculateDigest( + QByteArray(":") + d->saslDigest.digestUri()); + const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64(); + sendData("" + data +""); +} + +void QXmppIncomingClient::onPasswordReply() +{ + QXmppPasswordReply *reply = qobject_cast(sender()); + if (!reply) + return; + reply->deleteLater(); + + const QString username = reply->property("__sasl_username").toString(); + switch (reply->error()) { + case QXmppPasswordReply::NoError: + d->username = username; + d->jid = QString("%1@%2").arg(d->username, d->domain); + info(QString("Authentication succeeded for '%1'").arg(username)); + sendData(""); + handleStart(); + break; + case QXmppPasswordReply::AuthorizationError: + warning(QString("Authentication failed for '%1'").arg(username)); + sendData(""); + disconnectFromHost(); + break; + case QXmppPasswordReply::TemporaryError: + warning(QString("Temporary authentication failure for '%1'").arg(username)); + sendData(""); + disconnectFromHost(); + break; + } +} + +void QXmppIncomingClient::onTimeout() +{ + warning(QString("Idle timeout for '%1'").arg(d->jid)); + disconnectFromHost(); + + // make sure disconnected() gets emitted no matter what + QTimer::singleShot(30, this, SIGNAL(disconnected())); +} + + diff --git a/src/server/QXmppIncomingClient.h b/src/server/QXmppIncomingClient.h new file mode 100644 index 00000000..793b8141 --- /dev/null +++ b/src/server/QXmppIncomingClient.h @@ -0,0 +1,73 @@ +/* + * 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. + * + */ + +#ifndef QXMPPINCOMINGCLIENT_H +#define QXMPPINCOMINGCLIENT_H + +#include "QXmppStream.h" + +class QXmppIncomingClientPrivate; +class QXmppPasswordChecker; + +/// \brief Interface for password checkers. +/// + +/// \brief The QXmppIncomingClient class represents an incoming XMPP stream +/// from an XMPP client. +/// + +class QXmppIncomingClient : public QXmppStream +{ + Q_OBJECT + +public: + QXmppIncomingClient(QSslSocket *socket, const QString &domain, QObject *parent = 0); + ~QXmppIncomingClient(); + + bool isConnected() const; + QString jid() const; + + void setInactivityTimeout(int secs); + void setPasswordChecker(QXmppPasswordChecker *checker); + +signals: + /// This signal is emitted when an element is received. + void elementReceived(const QDomElement &element); + +protected: + /// \cond + void handleStream(const QDomElement &element); + void handleStanza(const QDomElement &element); + /// \endcond + +private slots: + void onDigestReply(); + void onPasswordReply(); + void onTimeout(); + +private: + Q_DISABLE_COPY(QXmppIncomingClient) + QXmppIncomingClientPrivate* const d; +}; + +#endif diff --git a/src/server/QXmppIncomingServer.cpp b/src/server/QXmppIncomingServer.cpp new file mode 100644 index 00000000..2c4cfb9f --- /dev/null +++ b/src/server/QXmppIncomingServer.cpp @@ -0,0 +1,207 @@ +/* + * 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 +#include +#include + +#include "QXmppConstants.h" +#include "QXmppDialback.h" +#include "QXmppIncomingServer.h" +#include "QXmppOutgoingServer.h" +#include "QXmppStreamFeatures.h" +#include "QXmppUtils.h" + +class QXmppIncomingServerPrivate +{ +public: + QSet authenticated; + QString domain; + QString localStreamId; +}; + +/// Constructs a new incoming server stream. +/// +/// \param socket The socket for the XMPP stream. +/// \param domain The local domain. +/// \param parent The parent QObject for the stream (optional). +/// + +QXmppIncomingServer::QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent) + : QXmppStream(parent), + d(new QXmppIncomingServerPrivate) +{ + d->domain = domain; + + if (socket) { + info(QString("Incoming server connection from %1 %2").arg( + socket->peerAddress().toString(), + QString::number(socket->peerPort()))); + setSocket(socket); + } +} + +/// Destroys the current stream. + +QXmppIncomingServer::~QXmppIncomingServer() +{ + delete d; +} + +/// Returns the stream's identifier. +/// + +QString QXmppIncomingServer::localStreamId() const +{ + return d->localStreamId; +} + +void QXmppIncomingServer::handleStream(const QDomElement &streamElement) +{ + const QString from = streamElement.attribute("from"); + if (!from.isEmpty()) + info(QString("Incoming server stream from %1").arg(from)); + + // start stream + d->localStreamId = generateStanzaHash().toAscii(); + QString data = QString("").arg( + ns_server, + ns_server_dialback, + ns_stream, + d->localStreamId); + sendData(data.toUtf8()); + + // send stream features + QXmppStreamFeatures features; + if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull()) + features.setTlsMode(QXmppStreamFeatures::Enabled); + sendPacket(features); +} + +void QXmppIncomingServer::handleStanza(const QDomElement &stanza) +{ + const QString ns = stanza.namespaceURI(); + + if (ns == ns_tls && stanza.tagName() == QLatin1String("starttls")) + { + sendData(""); + socket()->flush(); + socket()->startServerEncryption(); + return; + } + else if (QXmppDialback::isDialback(stanza)) + { + QXmppDialback request; + request.parse(stanza); + // check the request is valid + if (!request.type().isEmpty() || + request.from().isEmpty() || + request.to() != d->domain || + request.key().isEmpty()) + { + warning("Invalid dialback received"); + return; + } + + const QString domain = request.from(); + if (request.command() == QXmppDialback::Result) + { + debug(QString("Received a dialback result from %1").arg(domain)); + + // establish dialback connection + QXmppOutgoingServer *stream = new QXmppOutgoingServer(d->domain, this); + bool check = connect(stream, SIGNAL(dialbackResponseReceived(QXmppDialback)), + this, SLOT(slotDialbackResponseReceived(QXmppDialback))); + Q_ASSERT(check); + Q_UNUSED(check); + stream->setVerify(d->localStreamId, request.key()); + stream->connectToHost(domain); + } + else if (request.command() == QXmppDialback::Verify) + { + debug(QString("Received a dialback verify from %1").arg(domain)); + emit dialbackRequestReceived(request); + } + + } + else if (d->authenticated.contains(jidToDomain(stanza.attribute("from")))) + { + // relay stanza if the remote party is authenticated + emit elementReceived(stanza); + } else { + warning(QString("Received an element from unverified domain %1").arg(jidToDomain(stanza.attribute("from")))); + disconnectFromHost(); + } +} + +/// Returns true if the socket is connected and the remote server is +/// authenticated. +/// + +bool QXmppIncomingServer::isConnected() const +{ + return QXmppStream::isConnected() && !d->authenticated.isEmpty(); +} + +/// Handles a dialback response received from the authority server. +/// +/// \param response +/// + +void QXmppIncomingServer::slotDialbackResponseReceived(const QXmppDialback &dialback) +{ + QXmppOutgoingServer *stream = qobject_cast(sender()); + if (!stream || + dialback.command() != QXmppDialback::Verify || + dialback.id() != d->localStreamId || + dialback.from() != stream->remoteDomain()) + return; + + // relay verify response + QXmppDialback response; + response.setCommand(QXmppDialback::Result); + response.setTo(dialback.from()); + response.setFrom(d->domain); + response.setType(dialback.type()); + sendPacket(response); + + // check for success + if (response.type() == QLatin1String("valid")) + { + info(QString("Verified incoming domain %1").arg(dialback.from())); + const bool wasConnected = !d->authenticated.isEmpty(); + d->authenticated.insert(dialback.from()); + if (!wasConnected) + emit connected(); + } else { + warning(QString("Failed to verify incoming domain %1").arg(dialback.from())); + disconnectFromHost(); + } + + // disconnect dialback + stream->disconnectFromHost(); + stream->deleteLater(); +} + diff --git a/src/server/QXmppIncomingServer.h b/src/server/QXmppIncomingServer.h new file mode 100644 index 00000000..a06e983c --- /dev/null +++ b/src/server/QXmppIncomingServer.h @@ -0,0 +1,69 @@ +/* + * 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. + * + */ + +#ifndef QXMPPINCOMINGSERVER_H +#define QXMPPINCOMINGSERVER_H + +#include "QXmppStream.h" + +class QXmppDialback; +class QXmppIncomingServerPrivate; +class QXmppOutgoingServer; + +/// \brief The QXmppIncomingServer class represents an incoming XMPP stream +/// from an XMPP server. +/// + +class QXmppIncomingServer : public QXmppStream +{ + Q_OBJECT + +public: + QXmppIncomingServer(QSslSocket *socket, const QString &domain, QObject *parent); + ~QXmppIncomingServer(); + + bool isConnected() const; + QString localStreamId() const; + +signals: + /// This signal is emitted when a dialback verify request is received. + void dialbackRequestReceived(const QXmppDialback &result); + + /// This signal is emitted when an element is received. + void elementReceived(const QDomElement &element); + +protected: + /// \cond + void handleStanza(const QDomElement &stanzaElement); + void handleStream(const QDomElement &streamElement); + /// \endcond + +private slots: + void slotDialbackResponseReceived(const QXmppDialback &dialback); + +private: + Q_DISABLE_COPY(QXmppIncomingServer) + QXmppIncomingServerPrivate* const d; +}; + +#endif 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 +#include +#include +#include +#include "qdnslookup.h" + +#include "QXmppConstants.h" +#include "QXmppDialback.h" +#include "QXmppOutgoingServer.h" +#include "QXmppStreamFeatures.h" +#include "QXmppUtils.h" + +class QXmppOutgoingServerPrivate +{ +public: + QList 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)), + this, SLOT(slotSslErrors(QList))); + 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("").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 , + // 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(""); + 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 &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(); +} + diff --git a/src/server/QXmppOutgoingServer.h b/src/server/QXmppOutgoingServer.h new file mode 100644 index 00000000..4e117c0e --- /dev/null +++ b/src/server/QXmppOutgoingServer.h @@ -0,0 +1,82 @@ +/* + * 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. + * + */ + +#ifndef QXMPPOUTGOINGSERVER_H +#define QXMPPOUTGOINGSERVER_H + +#include + +#include "QXmppStream.h" + +class QSslError; +class QXmppDialback; +class QXmppOutgoingServer; +class QXmppOutgoingServerPrivate; + +/// \brief The QXmppOutgoingServer class represents an outgoing XMPP stream +/// to another XMPP server. +/// + +class QXmppOutgoingServer : public QXmppStream +{ + Q_OBJECT + +public: + QXmppOutgoingServer(const QString &domain, QObject *parent); + ~QXmppOutgoingServer(); + + bool isConnected() const; + + QString localStreamKey() const; + void setLocalStreamKey(const QString &key); + void setVerify(const QString &id, const QString &key); + + QString remoteDomain() const; + +signals: + /// This signal is emitted when a dialback verify response is received. + void dialbackResponseReceived(const QXmppDialback &response); + +protected: + /// \cond + void handleStart(); + void handleStream(const QDomElement &streamElement); + void handleStanza(const QDomElement &stanzaElement); + /// \endcond + +public slots: + void connectToHost(const QString &domain); + void queueData(const QByteArray &data); + +private slots: + void _q_dnsLookupFinished(); + void sendDialback(); + void slotSslErrors(const QList &errors); + void socketError(QAbstractSocket::SocketError error); + +private: + Q_DISABLE_COPY(QXmppOutgoingServer) + QXmppOutgoingServerPrivate* const d; +}; + +#endif diff --git a/src/server/QXmppServer.cpp b/src/server/QXmppServer.cpp new file mode 100644 index 00000000..4515914e --- /dev/null +++ b/src/server/QXmppServer.cpp @@ -0,0 +1,807 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include "QXmppConstants.h" +#include "QXmppDialback.h" +#include "QXmppIq.h" +#include "QXmppIncomingClient.h" +#include "QXmppIncomingServer.h" +#include "QXmppOutgoingServer.h" +#include "QXmppPresence.h" +#include "QXmppServer.h" +#include "QXmppServerExtension.h" +#include "QXmppServerPlugin.h" +#include "QXmppUtils.h" + +#include "server/mod_presence.h" + +// Core plugins +Q_IMPORT_PLUGIN(mod_disco) +Q_IMPORT_PLUGIN(mod_ping) +Q_IMPORT_PLUGIN(mod_proxy65) +Q_IMPORT_PLUGIN(mod_stats) +Q_IMPORT_PLUGIN(mod_time) +Q_IMPORT_PLUGIN(mod_version) + +class QXmppServerPrivate +{ +public: + QXmppServerPrivate(QXmppServer *qq); + void loadExtensions(QXmppServer *server); + bool routeData(const QString &to, const QByteArray &data); + void startExtensions(); + void stopExtensions(); + + void info(const QString &message); + void warning(const QString &message); + + QString domain; + QList extensions; + QXmppLogger *logger; + QXmppPasswordChecker *passwordChecker; + + // client-to-server + QXmppSslServer *serverForClients; + QSet incomingClients; + QHash incomingClientsByJid; + QHash > incomingClientsByBareJid; + + // server-to-server + QSet incomingServers; + QSet outgoingServers; + QXmppSslServer *serverForServers; + +private: + bool loaded; + bool started; + QXmppServer *q; +}; + +QXmppServerPrivate::QXmppServerPrivate(QXmppServer *qq) + : logger(0), + passwordChecker(0), + loaded(false), + started(false), + q(qq) +{ +} + +/// Routes XMPP data to the given recipient. +/// +/// \param to +/// \param data +/// + +bool QXmppServerPrivate::routeData(const QString &to, const QByteArray &data) +{ + // refuse to route packets to empty destination, own domain or sub-domains + const QString toDomain = jidToDomain(to); + if (to.isEmpty() || to == domain || toDomain.endsWith("." + domain)) + return false; + + if (toDomain == domain) { + + // look for a client connection + QList found; + if (jidToResource(to).isEmpty()) { + foreach (QXmppIncomingClient *conn, incomingClientsByBareJid.value(to)) + found << conn; + } else { + QXmppIncomingClient *conn = incomingClientsByJid.value(to); + if (conn) + found << conn; + } + + // send data + foreach (QXmppStream *conn, found) + QMetaObject::invokeMethod(conn, "sendData", Q_ARG(QByteArray, data)); + return !found.isEmpty(); + + } else if (serverForServers->isListening()) { + + bool check; + Q_UNUSED(check); + + // look for an outgoing S2S connection + foreach (QXmppOutgoingServer *conn, outgoingServers) { + if (conn->remoteDomain() == toDomain) { + // send or queue data + QMetaObject::invokeMethod(conn, "queueData", Q_ARG(QByteArray, data)); + return true; + } + } + + // if we did not find an outgoing server, + // we need to establish the S2S connection + QXmppOutgoingServer *conn = new QXmppOutgoingServer(domain, 0); + conn->setLocalStreamKey(generateStanzaHash().toAscii()); + conn->moveToThread(q->thread()); + conn->setParent(q); + + check = QObject::connect(conn, SIGNAL(disconnected()), + q, SLOT(_q_outgoingServerDisconnected())); + Q_UNUSED(check); + + // add stream + outgoingServers.insert(conn); + + // queue data and connect to remote server + QMetaObject::invokeMethod(conn, "queueData", Q_ARG(QByteArray, data)); + QMetaObject::invokeMethod(conn, "connectToHost", Q_ARG(QString, toDomain)); + return true; + + } else { + + // S2S is disabled, failed to route data + return false; + + } +} + +/// Handles an incoming XML element. +/// +/// \param server +/// \param stream +/// \param element + +static void handleStanza(QXmppServer *server, const QDomElement &element) +{ + // try extensions + foreach (QXmppServerExtension *extension, server->extensions()) + if (extension->handleStanza(element)) + return; + + // default handlers + const QString domain = server->domain(); + const QString to = element.attribute("to"); + if (to == domain) { + if (element.tagName() == QLatin1String("iq")) { + // we do not support the given IQ + QXmppIq request; + request.parse(element); + + if (request.type() != QXmppIq::Error && request.type() != QXmppIq::Result) { + QXmppIq response(QXmppIq::Error); + response.setId(request.id()); + response.setFrom(domain); + response.setTo(request.from()); + QXmppStanza::Error error(QXmppStanza::Error::Cancel, + QXmppStanza::Error::FeatureNotImplemented); + response.setError(error); + server->sendPacket(response); + } + } + + } else { + + // route element or reply on behalf of missing peer + if (!server->sendElement(element) && element.tagName() == QLatin1String("iq")) { + QXmppIq request; + request.parse(element); + + QXmppIq response(QXmppIq::Error); + response.setId(request.id()); + response.setFrom(request.to()); + response.setTo(request.from()); + QXmppStanza::Error error(QXmppStanza::Error::Cancel, + QXmppStanza::Error::ServiceUnavailable); + response.setError(error); + server->sendPacket(response); + } + } +} + +void QXmppServerPrivate::info(const QString &message) +{ + if (logger) + logger->log(QXmppLogger::InformationMessage, message); +} + +void QXmppServerPrivate::warning(const QString &message) +{ + if (logger) + logger->log(QXmppLogger::WarningMessage, message); +} + +/// Load the server's extensions. +/// +/// \param server + +void QXmppServerPrivate::loadExtensions(QXmppServer *server) +{ + if (!loaded) + { + QObjectList plugins = QPluginLoader::staticInstances(); + foreach (QObject *object, plugins) + { + QXmppServerPlugin *plugin = qobject_cast(object); + if (!plugin) + continue; + + foreach (const QString &key, plugin->keys()) + server->addExtension(plugin->create(key)); + } + + // FIXME: until we can handle presence errors, we need to + // keep this extension last. + server->addExtension(new QXmppServerPresence); + loaded = true; + } +} + +/// Start the server's extensions. + +void QXmppServerPrivate::startExtensions() +{ + if (!started) + { + foreach (QXmppServerExtension *extension, extensions) + if (!extension->start()) + warning(QString("Could not start extension %1").arg(extension->extensionName())); + started = true; + } +} + +/// Stop the server's extensions (in reverse order). +/// + +void QXmppServerPrivate::stopExtensions() +{ + if (started) + { + for (int i = extensions.size() - 1; i >= 0; --i) + extensions[i]->stop(); + started = false; + } +} + +/// Constructs a new XMPP server instance. +/// +/// \param parent + +QXmppServer::QXmppServer(QObject *parent) + : QXmppLoggable(parent) +{ + bool check; + Q_UNUSED(check); + + qRegisterMetaType("QDomElement"); + + d = new QXmppServerPrivate(this); + d->serverForClients = new QXmppSslServer(this); + check = connect(d->serverForClients, SIGNAL(newConnection(QSslSocket*)), + this, SLOT(_q_clientConnection(QSslSocket*))); + Q_ASSERT(check); + + d->serverForServers = new QXmppSslServer(this); + check = connect(d->serverForServers, SIGNAL(newConnection(QSslSocket*)), + this, SLOT(_q_serverConnection(QSslSocket*))); + Q_ASSERT(check); +} + +/// Destroys an XMPP server instance. +/// + +QXmppServer::~QXmppServer() +{ + close(); + delete d; +} + +/// Registers a new extension with the server. +/// +/// \param extension + +void QXmppServer::addExtension(QXmppServerExtension *extension) +{ + if (!extension || d->extensions.contains(extension)) + return; + d->info(QString("Added extension %1").arg(extension->extensionName())); + extension->setParent(this); + extension->setServer(this); + d->extensions << extension; +} + +/// Returns the list of loaded extensions. +/// + +QList QXmppServer::extensions() +{ + d->loadExtensions(this); + return d->extensions; +} + +/// Returns the server's domain. +/// + +QString QXmppServer::domain() const +{ + return d->domain; +} + +/// Sets the server's domain. +/// +/// \param domain + +void QXmppServer::setDomain(const QString &domain) +{ + d->domain = domain; +} + +/// Returns the QXmppLogger associated with the server. +/// + +QXmppLogger *QXmppServer::logger() +{ + return d->logger; +} + +/// Sets the QXmppLogger associated with the server. +/// +/// \param logger + +void QXmppServer::setLogger(QXmppLogger *logger) +{ + if (d->logger) + QObject::disconnect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)), + d->logger, SLOT(log(QXmppLogger::MessageType,QString))); + d->logger = logger; + d->logger = logger; + if (d->logger) + connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)), + d->logger, SLOT(log(QXmppLogger::MessageType,QString))); +} + +/// Returns the password checker used to verify client credentials. +/// + +QXmppPasswordChecker *QXmppServer::passwordChecker() +{ + return d->passwordChecker; +} + +/// Sets the password checker used to verify client credentials. +/// +/// \param checker +/// + +void QXmppServer::setPasswordChecker(QXmppPasswordChecker *checker) +{ + d->passwordChecker = checker; +} + +/// Returns the statistics for the server. + +QVariantMap QXmppServer::statistics() const +{ + QVariantMap stats; + stats["version"] = qApp->applicationVersion(); + stats["incoming-clients"] = d->incomingClients.size(); + stats["incoming-servers"] = d->incomingServers.size(); + stats["outgoing-servers"] = d->outgoingServers.size(); + return stats; +} + +/// Sets the path for additional SSL CA certificates. +/// +/// \param path + +void QXmppServer::addCaCertificates(const QString &path) +{ + if (!path.isEmpty() && !QFileInfo(path).isReadable()) + d->warning(QString("SSL CA certificates are not readable %1").arg(path)); + QList certificates = QSslCertificate::fromPath(path); + d->serverForClients->addCaCertificates(certificates); + d->serverForServers->addCaCertificates(certificates); +} + +/// Sets the path for the local SSL certificate. +/// +/// \param path + +void QXmppServer::setLocalCertificate(const QString &path) +{ + QSslCertificate certificate; + QFile file(path); + if (!path.isEmpty() && file.open(QIODevice::ReadOnly | QIODevice::Text)) + certificate = QSslCertificate(file.readAll()); + else + d->warning(QString("SSL certificate is not readable %1").arg(path)); + d->serverForClients->setLocalCertificate(certificate); + d->serverForServers->setLocalCertificate(certificate); +} + +/// Sets the path for the local SSL private key. +/// +/// \param path + +void QXmppServer::setPrivateKey(const QString &path) +{ + QSslKey key; + QFile file(path); + if (!path.isEmpty() && file.open(QIODevice::ReadOnly)) + key = QSslKey(file.readAll(), QSsl::Rsa); + else + d->warning(QString("SSL key is not readable %1").arg(path)); + d->serverForClients->setPrivateKey(key); + d->serverForServers->setPrivateKey(key); +} + +/// Listen for incoming XMPP client connections. +/// +/// \param address +/// \param port + +bool QXmppServer::listenForClients(const QHostAddress &address, quint16 port) +{ + if (!d->serverForClients->listen(address, port)) + { + d->warning(QString("Could not start listening for C2S on port %1").arg(QString::number(port))); + return false; + } + + // start extensions + d->loadExtensions(this); + d->startExtensions(); + return true; +} + +/// Closes the server. +/// + +void QXmppServer::close() +{ + // prevent new connections + d->serverForClients->close(); + d->serverForServers->close(); + + // stop extensions + d->stopExtensions(); + + // close XMPP streams + foreach (QXmppIncomingClient *stream, d->incomingClients) + stream->disconnectFromHost(); + foreach (QXmppIncomingServer *stream, d->incomingServers) + stream->disconnectFromHost(); + foreach (QXmppOutgoingServer *stream, d->outgoingServers) + stream->disconnectFromHost(); +} + +/// Listen for incoming XMPP server connections. +/// +/// \param address +/// \param port + +bool QXmppServer::listenForServers(const QHostAddress &address, quint16 port) +{ + if (!d->serverForServers->listen(address, port)) + { + d->warning(QString("Could not start listening for S2S on port %1").arg(QString::number(port))); + return false; + } + + // start extensions + d->loadExtensions(this); + d->startExtensions(); + return true; +} + +/// Route an XMPP stanza. +/// +/// \param element + +bool QXmppServer::sendElement(const QDomElement &element) +{ + // serialize data + QByteArray data; + QXmlStreamWriter xmlStream(&data); + const QStringList omitNamespaces = QStringList() << ns_client << ns_server; + helperToXmlAddDomElement(&xmlStream, element, omitNamespaces); + + // route data + return d->routeData(element.attribute("to"), data); +} + +/// Route an XMPP packet. +/// +/// \param packet + +bool QXmppServer::sendPacket(const QXmppStanza &packet) +{ + // serialize data + QByteArray data; + QXmlStreamWriter xmlStream(&data); + packet.toXml(&xmlStream); + + // route data + return d->routeData(packet.to(), data); +} + +/// Add a new incoming client stream. +/// +/// \param stream + +void QXmppServer::addIncomingClient(QXmppIncomingClient *stream) +{ + bool check; + Q_UNUSED(check); + + stream->setPasswordChecker(d->passwordChecker); + + check = connect(stream, SIGNAL(connected()), + this, SLOT(_q_clientConnected())); + Q_ASSERT(check); + + check = connect(stream, SIGNAL(disconnected()), + this, SLOT(_q_clientDisconnected())); + Q_ASSERT(check); + + check = connect(stream, SIGNAL(elementReceived(QDomElement)), + this, SLOT(handleElement(QDomElement))); + Q_ASSERT(check); + + // add stream + d->incomingClients.insert(stream); +} + +/// Handle a new incoming TCP connection from a client. +/// +/// \param socket + +void QXmppServer::_q_clientConnection(QSslSocket *socket) +{ + // check the socket didn't die since the signal was emitted + if (socket->state() != QAbstractSocket::ConnectedState) { + delete socket; + return; + } + + QXmppIncomingClient *stream = new QXmppIncomingClient(socket, d->domain, this); + stream->setInactivityTimeout(120); + socket->setParent(stream); + addIncomingClient(stream); +} + +/// Handle a successful stream connection for a client. +/// + +void QXmppServer::_q_clientConnected() +{ + QXmppIncomingClient *client = qobject_cast(sender()); + if (!client) + return; + + // FIXME: at this point the JID must contain a resource, assert it? + const QString jid = client->jid(); + + // check whether the connection conflicts with another one + QXmppIncomingClient *old = d->incomingClientsByJid.value(jid); + if (old && old != client) { + old->sendData("Replaced by new connection"); + old->disconnectFromHost(); + } + d->incomingClientsByJid.insert(jid, client); + d->incomingClientsByBareJid[jidToBareJid(jid)].insert(client); + + // emit signal + emit clientConnected(jid); +} + +/// Handle a stream disconnection for a client. + +void QXmppServer::_q_clientDisconnected() +{ + QXmppIncomingClient *client = qobject_cast(sender()); + if (!client) + return; + + if (d->incomingClients.remove(client)) { + // remove stream from routing tables + const QString jid = client->jid(); + if (!jid.isEmpty()) { + if (d->incomingClientsByJid.value(jid) == client) + d->incomingClientsByJid.remove(jid); + const QString bareJid = jidToBareJid(jid); + if (d->incomingClientsByBareJid.contains(bareJid)) + d->incomingClientsByBareJid[bareJid].remove(client); + } + + // destroy client + client->deleteLater(); + + // emit signal + if (!jid.isEmpty()) + emit clientDisconnected(jid); + } +} + +void QXmppServer::_q_dialbackRequestReceived(const QXmppDialback &dialback) +{ + QXmppIncomingServer *stream = qobject_cast(sender()); + if (!stream) + return; + + if (dialback.command() == QXmppDialback::Verify) + { + // handle a verify request + foreach (QXmppOutgoingServer *out, d->outgoingServers) { + if (out->remoteDomain() != dialback.from()) + continue; + + bool isValid = dialback.key() == out->localStreamKey(); + QXmppDialback verify; + verify.setCommand(QXmppDialback::Verify); + verify.setId(dialback.id()); + verify.setTo(dialback.from()); + verify.setFrom(d->domain); + verify.setType(isValid ? "valid" : "invalid"); + stream->sendPacket(verify); + return; + } + } +} + +/// Handle an incoming XML element. + +void QXmppServer::handleElement(const QDomElement &element) +{ + handleStanza(this, element); +} + +/// Handle a stream disconnection for an outgoing server. + +void QXmppServer::_q_outgoingServerDisconnected() +{ + QXmppOutgoingServer *outgoing = qobject_cast(sender()); + if (!outgoing) + return; + + if (d->outgoingServers.remove(outgoing)) + outgoing->deleteLater(); +} + +/// Handle a new incoming TCP connection from a server. +/// +/// \param socket + +void QXmppServer::_q_serverConnection(QSslSocket *socket) +{ + bool check; + Q_UNUSED(check); + + // check the socket didn't die since the signal was emitted + if (socket->state() != QAbstractSocket::ConnectedState) { + delete socket; + return; + } + + QXmppIncomingServer *stream = new QXmppIncomingServer(socket, d->domain, this); + socket->setParent(stream); + + check = connect(stream, SIGNAL(disconnected()), + this, SLOT(_q_serverDisconnected())); + Q_ASSERT(check); + + check = connect(stream, SIGNAL(dialbackRequestReceived(QXmppDialback)), + this, SLOT(_q_dialbackRequestReceived(QXmppDialback))); + Q_ASSERT(check); + + check = connect(stream, SIGNAL(elementReceived(QDomElement)), + this, SLOT(handleElement(QDomElement))); + Q_ASSERT(check); + + // add stream + d->incomingServers.insert(stream); +} + +/// Handle a stream disconnection for an incoming server. + +void QXmppServer::_q_serverDisconnected() +{ + QXmppIncomingServer *incoming = qobject_cast(sender()); + if (!incoming) + return; + + if (d->incomingServers.remove(incoming)) + incoming->deleteLater(); +} + +class QXmppSslServerPrivate +{ +public: + QList caCertificates; + QSslCertificate localCertificate; + QSslKey privateKey; +}; + +/// Constructs a new SSL server instance. +/// +/// \param parent + +QXmppSslServer::QXmppSslServer(QObject *parent) + : QTcpServer(parent), + d(new QXmppSslServerPrivate) +{ +} + +/// Destroys an SSL server instance. +/// + +QXmppSslServer::~QXmppSslServer() +{ + delete d; +} + +void QXmppSslServer::incomingConnection(int socketDescriptor) +{ + QSslSocket *socket = new QSslSocket; + if (!socket->setSocketDescriptor(socketDescriptor)) { + delete socket; + return; + } + + if (!d->localCertificate.isNull() && !d->privateKey.isNull()) { + socket->setProtocol(QSsl::AnyProtocol); + socket->addCaCertificates(d->caCertificates); + socket->setLocalCertificate(d->localCertificate); + socket->setPrivateKey(d->privateKey); + } + emit newConnection(socket); +} + +/// Adds the given certificates to the CA certificate database to be used +/// for incoming connnections. +/// +/// \param certificates + +void QXmppSslServer::addCaCertificates(const QList &certificates) +{ + d->caCertificates += certificates; +} + +/// Sets the local certificate to be used for incoming connections. +/// +/// \param certificate + +void QXmppSslServer::setLocalCertificate(const QSslCertificate &certificate) +{ + d->localCertificate = certificate; +} + +/// Sets the local private key to be used for incoming connections. +/// +/// \param key + +void QXmppSslServer::setPrivateKey(const QSslKey &key) +{ + d->privateKey = key; +} + diff --git a/src/server/QXmppServer.h b/src/server/QXmppServer.h new file mode 100644 index 00000000..cb5fcedd --- /dev/null +++ b/src/server/QXmppServer.h @@ -0,0 +1,148 @@ +/* + * 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. + * + */ + +#ifndef QXMPPSERVER_H +#define QXMPPSERVER_H + +#include +#include + +#include "QXmppLogger.h" + +class QDomElement; +class QSslCertificate; +class QSslKey; +class QSslSocket; + +class QXmppDialback; +class QXmppIncomingClient; +class QXmppOutgoingServer; +class QXmppPasswordChecker; +class QXmppPresence; +class QXmppServerExtension; +class QXmppServerPrivate; +class QXmppSslServer; +class QXmppStanza; +class QXmppStream; + +/// \brief The QXmppServer class represents an XMPP server. +/// +/// It provides support for both client-to-server and server-to-server +/// communications, SSL encryption and logging facilities. +/// +/// QXmppServer comes with a number of modules for service discovery, +/// XMPP ping, statistics and file transfer proxy support. You can write +/// your own extensions for QXmppServer by subclassing QXmppServerExtension. +/// +/// \ingroup Core + +class QXmppServer : public QXmppLoggable +{ + Q_OBJECT + +public: + QXmppServer(QObject *parent = 0); + ~QXmppServer(); + + void addExtension(QXmppServerExtension *extension); + QList extensions(); + + QString domain() const; + void setDomain(const QString &domain); + + QXmppLogger *logger(); + void setLogger(QXmppLogger *logger); + + QXmppPasswordChecker *passwordChecker(); + void setPasswordChecker(QXmppPasswordChecker *checker); + + QVariantMap statistics() const; + + void addCaCertificates(const QString &caCertificates); + void setLocalCertificate(const QString &path); + void setPrivateKey(const QString &path); + + void close(); + bool listenForClients(const QHostAddress &address = QHostAddress::Any, quint16 port = 5222); + bool listenForServers(const QHostAddress &address = QHostAddress::Any, quint16 port = 5269); + + bool sendElement(const QDomElement &element); + bool sendPacket(const QXmppStanza &stanza); + + /// \cond + // FIXME: this method should not be public, but it is needed to + // implement BOSH support as an extension. + void addIncomingClient(QXmppIncomingClient *stream); + /// \endcond + +signals: + /// This signal is emitted when a client has connected. + void clientConnected(const QString &jid); + + /// This signal is emitted when a client has disconnected. + void clientDisconnected(const QString &jid); + +public slots: + void handleElement(const QDomElement &element); + +private slots: + void _q_clientConnection(QSslSocket *socket); + void _q_clientConnected(); + void _q_clientDisconnected(); + void _q_dialbackRequestReceived(const QXmppDialback &dialback); + void _q_outgoingServerDisconnected(); + void _q_serverConnection(QSslSocket *socket); + void _q_serverDisconnected(); + +private: + friend class QXmppServerPrivate; + QXmppServerPrivate *d; +}; + +class QXmppSslServerPrivate; + +/// \brief The QXmppSslServer class represents an SSL-enabled TCP server. +/// + +class QXmppSslServer : public QTcpServer +{ + Q_OBJECT + +public: + QXmppSslServer(QObject *parent = 0); + ~QXmppSslServer(); + + void addCaCertificates(const QList &certificates); + void setLocalCertificate(const QSslCertificate &certificate); + void setPrivateKey(const QSslKey &key); + +signals: + /// This signal is emitted when a new connection is established. + void newConnection(QSslSocket *socket); + +private: + void incomingConnection(int socketDescriptor); + QXmppSslServerPrivate * const d; +}; + +#endif diff --git a/src/server/QXmppServerExtension.cpp b/src/server/QXmppServerExtension.cpp new file mode 100644 index 00000000..7369a4f7 --- /dev/null +++ b/src/server/QXmppServerExtension.cpp @@ -0,0 +1,155 @@ +/* + * 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 +#include +#include + +#include "QXmppLogger.h" +#include "QXmppServer.h" +#include "QXmppServerExtension.h" + +class QXmppServerExtensionPrivate +{ +public: + QXmppServer *server; +}; + +QXmppServerExtension::QXmppServerExtension() + : d(new QXmppServerExtensionPrivate) +{ + d->server = 0; +} + +QXmppServerExtension::~QXmppServerExtension() +{ + delete d; +} + +/// Returns the discovery features to add to the server. +/// + +QStringList QXmppServerExtension::discoveryFeatures() const +{ + return QStringList(); +} + +/// Returns the discovery items to add to the server. +/// + +QStringList QXmppServerExtension::discoveryItems() const +{ + return QStringList(); +} + +/// Returns the extension's name. +/// + +QString QXmppServerExtension::extensionName() const +{ + int index = metaObject()->indexOfClassInfo("ExtensionName"); + if (index < 0) + return QString(); + const char *name = metaObject()->classInfo(index).value(); + return QString::fromLatin1(name); +} + +/// Handles an incoming XMPP stanza. +/// +/// Return true if no further processing should occur, false otherwise. +/// +/// \param stanza The received stanza. + +bool QXmppServerExtension::handleStanza(const QDomElement &stanza) +{ + Q_UNUSED(stanza); + return false; +} + +/// Returns the list of subscribers for the given JID. +/// +/// \param jid + +QSet QXmppServerExtension::presenceSubscribers(const QString &jid) +{ + Q_UNUSED(jid); + return QSet(); +} + +/// Returns the list of subscriptions for the given JID. +/// +/// \param jid + +QSet QXmppServerExtension::presenceSubscriptions(const QString &jid) +{ + Q_UNUSED(jid); + return QSet(); +} + +/// Returns the extension's statistics. +/// + +QVariantMap QXmppServerExtension::statistics() const +{ + return QVariantMap(); +} + +/// Sets the extension's statistics. +/// + +void QXmppServerExtension::setStatistics(const QVariantMap &statistics) +{ + Q_UNUSED(statistics); +} + +/// Starts the extension. +/// +/// Return true if the extension was started, false otherwise. + +bool QXmppServerExtension::start() +{ + return true; +} + +/// Stops the extension. + +void QXmppServerExtension::stop() +{ +} + +/// Returns the server which loaded this extension. + +QXmppServer *QXmppServerExtension::server() +{ + return d->server; +} + +/// Sets the server which loaded this extension. +/// +/// \param server + +void QXmppServerExtension::setServer(QXmppServer *server) +{ + d->server = server; +} + diff --git a/src/server/QXmppServerExtension.h b/src/server/QXmppServerExtension.h new file mode 100644 index 00000000..d908e17b --- /dev/null +++ b/src/server/QXmppServerExtension.h @@ -0,0 +1,79 @@ +/* + * 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. + * + */ + +#ifndef QXMPPSERVEREXTENSION_H +#define QXMPPSERVEREXTENSION_H + +#include + +#include "QXmppLogger.h" + +class QDomElement; +class QStringList; + +class QXmppServer; +class QXmppServerExtensionPrivate; +class QXmppStream; + +/// \brief The QXmppServerExtension class is the base class for QXmppServer +/// extensions. +/// +/// If you want to extend QXmppServer, for instance to support an IQ type +/// which is not natively supported, you can subclass QXmppServerExtension +/// and implement handleStanza(). You can then add your extension to the +/// client instance using QXmppServer::addExtension(). +/// +/// \ingroup Core + +class QXmppServerExtension : public QXmppLoggable +{ + Q_OBJECT + +public: + QXmppServerExtension(); + ~QXmppServerExtension(); + QString extensionName() const; + + virtual QStringList discoveryFeatures() const; + virtual QStringList discoveryItems() const; + virtual bool handleStanza(const QDomElement &stanza); + virtual QSet presenceSubscribers(const QString &jid); + virtual QSet presenceSubscriptions(const QString &jid); + + virtual QVariantMap statistics() const; + virtual void setStatistics(const QVariantMap &statistics); + + virtual bool start(); + virtual void stop(); + +protected: + QXmppServer *server(); + +private: + void setServer(QXmppServer *server); + QXmppServerExtensionPrivate * const d; + + friend class QXmppServer; +}; + +#endif diff --git a/src/server/QXmppServerPlugin.h b/src/server/QXmppServerPlugin.h new file mode 100644 index 00000000..d247dd9e --- /dev/null +++ b/src/server/QXmppServerPlugin.h @@ -0,0 +1,60 @@ +/* + * 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. + * + */ + +#ifndef QXMPPSERVERPLUGIN_H +#define QXMPPSERVERPLUGIN_H + +#include + +class QXmppServer; +class QXmppServerExtension; + +class QXmppServerPluginInterface +{ +public: + virtual QXmppServerExtension *create(const QString &key) = 0; + virtual QStringList keys() const = 0; +}; + +Q_DECLARE_INTERFACE(QXmppServerPluginInterface, "com.googlecode.qxmpp.ServerPlugin/1.0") + +/// \brief The QXmppServerPlugin class is the base class for QXmppServer plugins. +/// + +class QXmppServerPlugin : public QObject, public QXmppServerPluginInterface +{ + Q_OBJECT + Q_INTERFACES(QXmppServerPluginInterface) + +public: + /// Creates and returns the specified QXmppServerExtension. + /// + /// \param key The key for the QXmppServerExtension. + virtual QXmppServerExtension *create(const QString &key) = 0; + + /// Returns the list of keys supported by this plugin. + /// + virtual QStringList keys() const = 0; +}; + +#endif diff --git a/src/src.pro b/src/src.pro index c6d72d0f..dc4a8e3f 100644 --- a/src/src.pro +++ b/src/src.pro @@ -40,7 +40,6 @@ INSTALL_HEADERS = QXmppUtils.h \ QXmppConfiguration.h \ QXmppConstants.h \ QXmppDataForm.h \ - QXmppDialback.h \ QXmppDiscoveryIq.h \ QXmppDiscoveryManager.h \ QXmppElement.h \ @@ -48,8 +47,6 @@ INSTALL_HEADERS = QXmppUtils.h \ QXmppEntityTimeManager.h \ QXmppGlobal.h \ QXmppIbbIq.h \ - QXmppIncomingClient.h \ - QXmppIncomingServer.h \ QXmppInvokable.h \ QXmppIq.h \ QXmppJingleIq.h \ @@ -60,7 +57,6 @@ INSTALL_HEADERS = QXmppUtils.h \ QXmppMucManager.h \ QXmppNonSASLAuth.h \ QXmppOutgoingClient.h \ - QXmppOutgoingServer.h \ QXmppPacket.h \ QXmppPasswordChecker.h \ QXmppPingIq.h \ @@ -74,9 +70,6 @@ INSTALL_HEADERS = QXmppUtils.h \ QXmppRpcManager.h \ QXmppRtpChannel.h \ QXmppSaslAuth.h \ - QXmppServer.h \ - QXmppServerExtension.h \ - QXmppServerPlugin.h \ QXmppSessionIq.h \ QXmppSocks.h \ QXmppStanza.h \ @@ -88,7 +81,14 @@ INSTALL_HEADERS = QXmppUtils.h \ QXmppVCardManager.h \ QXmppVCardIq.h \ QXmppVersionIq.h \ - QXmppVersionManager.h + QXmppVersionManager.h \ + server/QXmppDialback.h \ + server/QXmppIncomingClient.h \ + server/QXmppIncomingServer.h \ + server/QXmppOutgoingServer.h \ + server/QXmppServer.h \ + server/QXmppServerExtension.h \ + server/QXmppServerPlugin.h HEADERS += $$INSTALL_HEADERS @@ -107,7 +107,6 @@ SOURCES += QXmppUtils.cpp \ QXmppConfiguration.cpp \ QXmppConstants.cpp \ QXmppDataForm.cpp \ - QXmppDialback.cpp \ QXmppDiscoveryIq.cpp \ QXmppDiscoveryManager.cpp \ QXmppElement.cpp \ @@ -115,8 +114,6 @@ SOURCES += QXmppUtils.cpp \ QXmppEntityTimeManager.cpp \ QXmppGlobal.cpp \ QXmppIbbIq.cpp \ - QXmppIncomingClient.cpp \ - QXmppIncomingServer.cpp \ QXmppInvokable.cpp \ QXmppIq.cpp \ QXmppJingleIq.cpp \ @@ -127,7 +124,6 @@ SOURCES += QXmppUtils.cpp \ QXmppMucManager.cpp \ QXmppNonSASLAuth.cpp \ QXmppOutgoingClient.cpp \ - QXmppOutgoingServer.cpp \ QXmppPacket.cpp \ QXmppPasswordChecker.cpp \ QXmppPingIq.cpp \ @@ -141,8 +137,6 @@ SOURCES += QXmppUtils.cpp \ QXmppRpcManager.cpp \ QXmppRtpChannel.cpp \ QXmppSaslAuth.cpp \ - QXmppServer.cpp \ - QXmppServerExtension.cpp \ QXmppSessionIq.cpp \ QXmppSocks.cpp \ QXmppStanza.cpp \ @@ -154,7 +148,13 @@ SOURCES += QXmppUtils.cpp \ QXmppVCardManager.cpp \ QXmppVCardIq.cpp \ QXmppVersionIq.cpp \ - QXmppVersionManager.cpp + QXmppVersionManager.cpp \ + server/QXmppDialback.cpp \ + server/QXmppIncomingClient.cpp \ + server/QXmppIncomingServer.cpp \ + server/QXmppOutgoingServer.cpp \ + server/QXmppServer.cpp \ + server/QXmppServerExtension.cpp # DNS HEADERS += qdnslookup.h qdnslookup_p.h -- cgit v1.2.3