aboutsummaryrefslogtreecommitdiff
path: root/src/QXmppServer.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2012-02-08 11:53:52 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2012-02-08 11:53:52 +0000
commit36bbeb02497f5f79ddae56857893c2f71bf08ee9 (patch)
tree34191bb93ff6cb044d5d28d2f630dc0476cc5659 /src/QXmppServer.cpp
parentaa334abcca63101292c48a72c7b1851b8ecc26c7 (diff)
downloadqxmpp-36bbeb02497f5f79ddae56857893c2f71bf08ee9.tar.gz
move server code to "server" directory
Diffstat (limited to 'src/QXmppServer.cpp')
-rw-r--r--src/QXmppServer.cpp807
1 files changed, 0 insertions, 807 deletions
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 <QCoreApplication>
-#include <QDomElement>
-#include <QFileInfo>
-#include <QPluginLoader>
-#include <QSslCertificate>
-#include <QSslKey>
-#include <QSslSocket>
-
-#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<QXmppServerExtension*> extensions;
- QXmppLogger *logger;
- QXmppPasswordChecker *passwordChecker;
-
- // client-to-server
- QXmppSslServer *serverForClients;
- QSet<QXmppIncomingClient*> incomingClients;
- QHash<QString, QXmppIncomingClient*> incomingClientsByJid;
- QHash<QString, QSet<QXmppIncomingClient*> > incomingClientsByBareJid;
-
- // server-to-server
- QSet<QXmppIncomingServer*> incomingServers;
- QSet<QXmppOutgoingServer*> 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<QXmppIncomingClient*> 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<QXmppServerPlugin*>(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>("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<QXmppServerExtension*> 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<QSslCertificate> 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<QXmppIncomingClient*>(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("<stream:error><conflict xmlns='urn:ietf:params:xml:ns:xmpp-streams'/><text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>Replaced by new connection</text></stream:error>");
- 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<QXmppIncomingClient *>(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<QXmppIncomingServer *>(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<QXmppOutgoingServer *>(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<QXmppIncomingServer *>(sender());
- if (!incoming)
- return;
-
- if (d->incomingServers.remove(incoming))
- incoming->deleteLater();
-}
-
-class QXmppSslServerPrivate
-{
-public:
- QList<QSslCertificate> 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<QSslCertificate> &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;
-}
-