aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2011-09-13 12:08:15 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2011-09-13 12:08:15 +0000
commit8eaf6c3def32f1aa07fe863d5eab69f44672b3a5 (patch)
treee6950f5dbceeb0e34ac98f7639a63049f8cf7c44 /src
parent5d6e2cabfb730fa6b537397db342bed8eec15de1 (diff)
downloadqxmpp-8eaf6c3def32f1aa07fe863d5eab69f44672b3a5.tar.gz
* hide some QXmppServer internals
* improve QXmppServer performance
Diffstat (limited to 'src')
-rw-r--r--src/QXmppIncomingClient.cpp28
-rw-r--r--src/QXmppIncomingServer.cpp4
-rw-r--r--src/QXmppOutgoingServer.cpp24
-rw-r--r--src/QXmppOutgoingServer.h5
-rw-r--r--src/QXmppServer.cpp487
-rw-r--r--src/QXmppServer.h27
-rw-r--r--src/QXmppServerExtension.cpp12
-rw-r--r--src/QXmppServerExtension.h6
-rw-r--r--src/server/mod_disco.cpp4
-rw-r--r--src/server/mod_disco.h2
-rw-r--r--src/server/mod_ping.cpp4
-rw-r--r--src/server/mod_ping.h2
-rw-r--r--src/server/mod_presence.cpp267
-rw-r--r--src/server/mod_presence.h60
-rw-r--r--src/server/mod_proxy65.cpp56
-rw-r--r--src/server/mod_proxy65.h6
-rw-r--r--src/server/mod_stats.cpp57
-rw-r--r--src/server/mod_stats.h6
-rw-r--r--src/server/mod_time.cpp4
-rw-r--r--src/server/mod_time.h2
-rw-r--r--src/server/mod_version.cpp4
-rw-r--r--src/server/mod_version.h2
-rw-r--r--src/src.pro2
23 files changed, 636 insertions, 435 deletions
diff --git a/src/QXmppIncomingClient.cpp b/src/QXmppIncomingClient.cpp
index da8067a8..7338115a 100644
--- a/src/QXmppIncomingClient.cpp
+++ b/src/QXmppIncomingClient.cpp
@@ -190,7 +190,7 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
if (d->idleTimer->interval())
d->idleTimer->start();
- if (ns == ns_tls && nodeRecv.tagName() == "starttls")
+ if (ns == ns_tls && nodeRecv.tagName() == QLatin1String("starttls"))
{
sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
socket()->flush();
@@ -199,10 +199,10 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
}
else if (ns == ns_sasl)
{
- if (nodeRecv.tagName() == "auth")
+ if (nodeRecv.tagName() == QLatin1String("auth"))
{
const QString mechanism = nodeRecv.attribute("mechanism");
- if (mechanism == "PLAIN")
+ if (mechanism == QLatin1String("PLAIN"))
{
QList<QByteArray> auth = QByteArray::fromBase64(nodeRecv.text().toAscii()).split('\0');
if (auth.size() != 3)
@@ -229,7 +229,7 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
reply->setProperty("__sasl_username", request.username());
connect(reply, SIGNAL(finished()), this, SLOT(onPasswordReply()));
}
- else if (mechanism == "DIGEST-MD5")
+ else if (mechanism == QLatin1String("DIGEST-MD5"))
{
// generate nonce
d->saslDigest.setNonce(QXmppSaslDigestMd5::generateNonce());
@@ -254,7 +254,7 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
return;
}
}
- else if (nodeRecv.tagName() == "response")
+ else if (nodeRecv.tagName() == QLatin1String("response"))
{
if (d->saslDigestStep == 1)
{
@@ -294,10 +294,10 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
}
else if (ns == ns_client)
{
- if (nodeRecv.tagName() == "iq")
+ if (nodeRecv.tagName() == QLatin1String("iq"))
{
const QString type = nodeRecv.attribute("type");
- if (QXmppBindIq::isBindIq(nodeRecv) && type == "set")
+ if (QXmppBindIq::isBindIq(nodeRecv) && type == QLatin1String("set"))
{
QXmppBindIq bindSet;
bindSet.parse(nodeRecv);
@@ -316,7 +316,7 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
emit connected();
return;
}
- else if (QXmppSessionIq::isSessionIq(nodeRecv) && type == "set")
+ else if (QXmppSessionIq::isSessionIq(nodeRecv) && type == QLatin1String("set"))
{
QXmppSessionIq sessionSet;
sessionSet.parse(nodeRecv);
@@ -339,18 +339,18 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
}
// process unhandled stanzas
- if (nodeRecv.tagName() == "iq" ||
- nodeRecv.tagName() == "message" ||
- nodeRecv.tagName() == "presence")
+ 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() == "presence" &&
- (nodeFull.attribute("type") == "subscribe" ||
- nodeFull.attribute("type") == "subscribed"))
+ 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);
diff --git a/src/QXmppIncomingServer.cpp b/src/QXmppIncomingServer.cpp
index f6141a57..2c4cfb9f 100644
--- a/src/QXmppIncomingServer.cpp
+++ b/src/QXmppIncomingServer.cpp
@@ -104,7 +104,7 @@ void QXmppIncomingServer::handleStanza(const QDomElement &stanza)
{
const QString ns = stanza.namespaceURI();
- if (ns == ns_tls && stanza.tagName() == "starttls")
+ if (ns == ns_tls && stanza.tagName() == QLatin1String("starttls"))
{
sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
socket()->flush();
@@ -188,7 +188,7 @@ void QXmppIncomingServer::slotDialbackResponseReceived(const QXmppDialback &dial
sendPacket(response);
// check for success
- if (response.type() == "valid")
+ if (response.type() == QLatin1String("valid"))
{
info(QString("Verified incoming domain %1").arg(dialback.from()));
const bool wasConnected = !d->authenticated.isEmpty();
diff --git a/src/QXmppOutgoingServer.cpp b/src/QXmppOutgoingServer.cpp
index adcbd079..7984fd92 100644
--- a/src/QXmppOutgoingServer.cpp
+++ b/src/QXmppOutgoingServer.cpp
@@ -36,6 +36,7 @@
class QXmppOutgoingServerPrivate
{
public:
+ QList<QByteArray> dataQueue;
QString localDomain;
QString localStreamKey;
QString remoteDomain;
@@ -180,7 +181,7 @@ void QXmppOutgoingServer::handleStanza(const QDomElement &stanza)
}
else if (ns == ns_tls)
{
- if (stanza.tagName() == "proceed")
+ if (stanza.tagName() == QLatin1String("proceed"))
{
debug("Starting encryption");
socket()->startClientEncryption();
@@ -202,10 +203,17 @@ void QXmppOutgoingServer::handleStanza(const QDomElement &stanza)
}
if (response.command() == QXmppDialback::Result)
{
- if (response.type() == "valid")
+ 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();
}
}
@@ -252,6 +260,18 @@ void QXmppOutgoingServer::setVerify(const QString &id, const QString &key)
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
diff --git a/src/QXmppOutgoingServer.h b/src/QXmppOutgoingServer.h
index 1ac726bd..a8141a6a 100644
--- a/src/QXmppOutgoingServer.h
+++ b/src/QXmppOutgoingServer.h
@@ -46,7 +46,6 @@ public:
QXmppOutgoingServer(const QString &domain, QObject *parent);
~QXmppOutgoingServer();
- void connectToHost(const QString &domain);
bool isConnected() const;
QString localStreamKey() const;
@@ -66,6 +65,10 @@ protected:
void handleStanza(const QDomElement &stanzaElement);
/// \endcond
+public slots:
+ void connectToHost(const QString &domain);
+ void queueData(const QByteArray &data);
+
private slots:
void connectToHost(const QXmppSrvInfo &serviceInfo);
void sendDialback();
diff --git a/src/QXmppServer.cpp b/src/QXmppServer.cpp
index 2be22877..0ab2e4be 100644
--- a/src/QXmppServer.cpp
+++ b/src/QXmppServer.cpp
@@ -21,12 +21,14 @@
*
*/
+#include <QCoreApplication>
#include <QDomElement>
#include <QFileInfo>
#include <QPluginLoader>
#include <QSslCertificate>
#include <QSslKey>
#include <QSslSocket>
+#include <QtConcurrentRun>
#include "QXmppConstants.h"
#include "QXmppDialback.h"
@@ -40,6 +42,8 @@
#include "QXmppServerPlugin.h"
#include "QXmppUtils.h"
+#include "server/mod_presence.h"
+
// Core plugins
Q_IMPORT_PLUGIN(mod_disco)
Q_IMPORT_PLUGIN(mod_ping)
@@ -52,12 +56,8 @@ class QXmppServerPrivate
{
public:
QXmppServerPrivate(QXmppServer *qq);
- QXmppOutgoingServer *connectToDomain(const QString &domain);
- QList<QXmppStream*> getStreams(const QString &to);
- void handleStanza(QXmppStream *stream, const QDomElement &element);
void loadExtensions(QXmppServer *server);
- QStringList presenceSubscribers(const QString &jid);
- QStringList presenceSubscriptions(const QString &jid);
+ bool routeData(const QString &to, const QByteArray &data);
void startExtensions();
void stopExtensions();
@@ -66,23 +66,19 @@ public:
QString domain;
QList<QXmppServerExtension*> extensions;
- // bare-jid -> full-jid -> presence
- QMap<QString, QMap<QString, QXmppPresence> > presences;
- QMap<QString, QSet<QString> > subscribers;
QXmppLogger *logger;
QXmppPasswordChecker *passwordChecker;
// client-to-server
QXmppSslServer *serverForClients;
- QList<QXmppIncomingClient*> incomingClients;
- QMap<QString, QXmppIncomingClient*> incomingClientsByJid;
- QMap<QString, QSet<QXmppIncomingClient*> > incomingClientsByBareJid;
+ QSet<QXmppIncomingClient*> incomingClients;
+ QHash<QString, QXmppIncomingClient*> incomingClientsByJid;
+ QHash<QString, QSet<QXmppIncomingClient*> > incomingClientsByBareJid;
// server-to-server
- QList<QXmppIncomingServer*> incomingServers;
- QList<QXmppOutgoingServer*> outgoingServers;
+ QSet<QXmppIncomingServer*> incomingServers;
+ QSet<QXmppOutgoingServer*> outgoingServers;
QXmppSslServer *serverForServers;
- QMap<QXmppStream*, QList<QByteArray> > queues;
private:
bool loaded;
@@ -99,48 +95,23 @@ QXmppServerPrivate::QXmppServerPrivate(QXmppServer *qq)
{
}
-/// Returns a new outgoing server-to-server connection to the given domain.
-///
-/// \param toDomain
-
-QXmppOutgoingServer* QXmppServerPrivate::connectToDomain(const QString &toDomain)
-{
- bool check;
-
- // initialise outgoing server-to-server
- QXmppOutgoingServer *stream = new QXmppOutgoingServer(domain, q);
- stream->setLocalStreamKey(generateStanzaHash().toAscii());
-
- check = QObject::connect(stream, SIGNAL(connected()),
- q, SLOT(_q_streamConnected()));
- Q_ASSERT(check);
-
- check = QObject::connect(stream, SIGNAL(disconnected()),
- q, SLOT(_q_streamDisconnected()));
- Q_UNUSED(check);
-
- // add stream
- outgoingServers.append(stream);
- emit q->streamAdded(stream);
-
- // connect to remote server
- stream->connectToHost(toDomain);
- return stream;
-}
-
-/// Returns the XMPP streams for the given recipient.
+/// Routes XMPP data to the given recipient.
///
/// \param to
+/// \param data
///
-QList<QXmppStream*> QXmppServerPrivate::getStreams(const QString &to)
+bool QXmppServerPrivate::routeData(const QString &to, const QByteArray &data)
{
- QList<QXmppStream*> found;
- if (to.isEmpty())
- return found;
+ // 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;
@@ -149,88 +120,71 @@ QList<QXmppStream*> QXmppServerPrivate::getStreams(const QString &to)
if (conn)
found << conn;
}
- } else if (toDomain.endsWith("." + domain)) {
- // refuse to route packets to sub-domains
- return found;
- } else {
+
+ // 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) {
- found << conn;
- break;
+ // 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
- if (found.isEmpty() && serverForServers->isListening())
- found << connectToDomain(toDomain);
+ 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;
+
}
- return found;
}
/// Handles an incoming XML element.
///
+/// \param server
/// \param stream
/// \param element
-void QXmppServerPrivate::handleStanza(QXmppStream *stream, const QDomElement &element)
+static void handleStanza(QXmppServer *server, const QDomElement &element)
{
// try extensions
- foreach (QXmppServerExtension *extension, extensions)
- if (extension->handleStanza(stream, element))
+ 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() == "presence") {
- // presence to the local domain, broadcast it to subscribers
- if (element.attribute("type").isEmpty() || element.attribute("type") == "unavailable") {
- const QString from = element.attribute("from");
- const QString bareFrom = jidToBareJid(from);
- bool isInitial = false;
-
- // record the presence for future use
- QXmppPresence presence;
- presence.parse(element);
- if (presence.type() == QXmppPresence::Available) {
- isInitial = !presences.value(bareFrom).contains(from);
- presences[bareFrom][from] = presence;
- } else {
- presences[bareFrom].remove(from);
- }
-
- // broadcast it to subscribers
- foreach (const QString &subscriber, presenceSubscribers(from)) {
- // avoid loop
- if (subscriber == to)
- continue;
- QDomElement changed(element);
- changed.setAttribute("to", subscriber);
- handleStanza(stream, changed);
- }
-
- // get presences from subscriptions
- if (isInitial) {
- foreach (const QString &subscription, presenceSubscriptions(from)) {
- if (jidToDomain(subscription) != domain) {
- QXmppPresence probe;
- probe.setType(QXmppPresence::Probe);
- probe.setFrom(from);
- probe.setTo(subscription);
- q->sendPacket(probe);
- } else {
- QXmppPresence push;
- foreach (push, presences.value(subscription).values()) {
- push.setTo(from);
- q->sendPacket(push);
- }
- }
- }
- }
- }
- } else if (element.tagName() == "iq") {
+ if (element.tagName() == QLatin1String("iq")) {
// we do not support the given IQ
QXmppIq request;
request.parse(element);
@@ -243,26 +197,14 @@ void QXmppServerPrivate::handleStanza(QXmppStream *stream, const QDomElement &el
QXmppStanza::Error error(QXmppStanza::Error::Cancel,
QXmppStanza::Error::FeatureNotImplemented);
response.setError(error);
- stream->sendPacket(response);
+ server->sendPacket(response);
}
}
} else {
- if (element.tagName() == "presence") {
- // directed presence, update subscribers
- QXmppPresence presence;
- presence.parse(element);
-
- const QString from = presence.from();
- if (presence.type() == QXmppPresence::Available)
- subscribers[from].insert(to);
- else if (presence.type() == QXmppPresence::Unavailable)
- subscribers[from].remove(to);
- }
-
// route element or reply on behalf of missing peer
- if (!q->sendElement(element) && element.tagName() == "iq") {
+ if (!server->sendElement(element) && element.tagName() == QLatin1String("iq")) {
QXmppIq request;
request.parse(element);
@@ -273,7 +215,7 @@ void QXmppServerPrivate::handleStanza(QXmppStream *stream, const QDomElement &el
QXmppStanza::Error error(QXmppStanza::Error::Cancel,
QXmppStanza::Error::ServiceUnavailable);
response.setError(error);
- stream->sendPacket(response);
+ server->sendPacket(response);
}
}
}
@@ -308,40 +250,12 @@ void QXmppServerPrivate::loadExtensions(QXmppServer *server)
foreach (const QString &key, plugin->keys())
server->addExtension(plugin->create(key));
}
- loaded = true;
- }
-}
-
-QStringList QXmppServerPrivate::presenceSubscribers(const QString &jid)
-{
- // start with directed presences
- QSet<QString> recipients = subscribers.value(jid);
-
- // query extensions
- foreach (QXmppServerExtension *extension, extensions)
- {
- const QStringList extras = extension->presenceSubscribers(jid);
- foreach (const QString &extra, extras)
- recipients.insert(extra);
- }
-
- return recipients.toList();
-}
-QStringList QXmppServerPrivate::presenceSubscriptions(const QString &jid)
-{
- // FIXME : start with directed presences?
- QSet<QString> recipients;
-
- // query extensions
- foreach (QXmppServerExtension *extension, extensions)
- {
- const QStringList extras = extension->presenceSubscriptions(jid);
- foreach (const QString &extra, extras)
- recipients.insert(extra);
+ // FIXME: until we can handle presence errors, we need to
+ // keep this extension last.
+ server->addExtension(new QXmppServerPresence);
+ loaded = true;
}
-
- return recipients.toList();
}
/// Start the server's extensions.
@@ -380,6 +294,8 @@ QXmppServer::QXmppServer(QObject *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*)),
@@ -424,15 +340,6 @@ QList<QXmppServerExtension*> QXmppServer::extensions()
return d->extensions;
}
-/// Returns the list of available resources for the given local JID.
-///
-/// \param bareJid
-
-QList<QXmppPresence> QXmppServer::availablePresences(const QString &bareJid)
-{
- return d->presences.value(bareJid).values();
-}
-
/// Returns the server's domain.
///
@@ -492,6 +399,18 @@ 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
@@ -602,22 +521,14 @@ bool QXmppServer::listenForServers(const QHostAddress &address, quint16 port)
bool QXmppServer::sendElement(const QDomElement &element)
{
- bool sent = false;
- const QString to = element.attribute("to");
- foreach (QXmppStream *conn, d->getStreams(to)) {
- if (conn->isConnected() && conn->sendElement(element)) {
- sent = true;
- } else {
- // queue packet
- QByteArray data;
- QXmlStreamWriter xmlStream(&data);
- const QStringList omitNamespaces = QStringList() << ns_client << ns_server;
- helperToXmlAddDomElement(&xmlStream, element, omitNamespaces);
- d->queues[conn] << data;
- sent = true;
- }
- }
- return sent;
+ // 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.
@@ -626,20 +537,13 @@ bool QXmppServer::sendElement(const QDomElement &element)
bool QXmppServer::sendPacket(const QXmppStanza &packet)
{
- bool sent = false;
- foreach (QXmppStream *conn, d->getStreams(packet.to())) {
- if (conn->isConnected() && conn->sendPacket(packet)) {
- sent = true;
- } else {
- // queue packet
- QByteArray data;
- QXmlStreamWriter xmlStream(&data);
- packet.toXml(&xmlStream);
- d->queues[conn] << data;
- sent = true;
- }
- }
- return sent;
+ // serialize data
+ QByteArray data;
+ QXmlStreamWriter xmlStream(&data);
+ packet.toXml(&xmlStream);
+
+ // route data
+ return d->routeData(packet.to(), data);
}
/// Add a new incoming client stream.
@@ -654,20 +558,19 @@ void QXmppServer::addIncomingClient(QXmppIncomingClient *stream)
stream->setPasswordChecker(d->passwordChecker);
check = connect(stream, SIGNAL(connected()),
- this, SLOT(_q_streamConnected()));
+ this, SLOT(_q_clientConnected()));
Q_ASSERT(check);
check = connect(stream, SIGNAL(disconnected()),
- this, SLOT(_q_streamDisconnected()));
+ this, SLOT(_q_clientDisconnected()));
Q_ASSERT(check);
check = connect(stream, SIGNAL(elementReceived(QDomElement)),
- this, SLOT(_q_elementReceived(QDomElement)));
+ this, SLOT(handleElement(QDomElement)));
Q_ASSERT(check);
// add stream
- d->incomingClients.append(stream);
- emit streamAdded(stream);
+ d->incomingClients.insert(stream);
}
/// Handle a new incoming TCP connection from a client.
@@ -688,6 +591,59 @@ void QXmppServer::_q_clientConnection(QSslSocket *socket)
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());
@@ -697,8 +653,7 @@ void QXmppServer::_q_dialbackRequestReceived(const QXmppDialback &dialback)
if (dialback.command() == QXmppDialback::Verify)
{
// handle a verify request
- foreach (QXmppOutgoingServer *out, d->outgoingServers)
- {
+ foreach (QXmppOutgoingServer *out, d->outgoingServers) {
if (out->remoteDomain() != dialback.from())
continue;
@@ -717,12 +672,21 @@ void QXmppServer::_q_dialbackRequestReceived(const QXmppDialback &dialback)
/// Handle an incoming XML element.
-void QXmppServer::_q_elementReceived(const QDomElement &element)
+void QXmppServer::handleElement(const QDomElement &element)
{
- QXmppStream *incoming = qobject_cast<QXmppStream *>(sender());
- if (!incoming)
+ handleStanza(this, element);
+}
+
+/// Handle a stream disconnection for an outgoing server.
+
+void QXmppServer::_q_outgoingServerDisconnected()
+{
+ QXmppOutgoingServer *outgoing = qobject_cast<QXmppOutgoingServer *>(sender());
+ if (!outgoing)
return;
- d->handleStanza(incoming, element);
+
+ if (d->outgoingServers.remove(outgoing))
+ outgoing->deleteLater();
}
/// Handle a new incoming TCP connection from a server.
@@ -743,12 +707,8 @@ void QXmppServer::_q_serverConnection(QSslSocket *socket)
QXmppIncomingServer *stream = new QXmppIncomingServer(socket, d->domain, this);
socket->setParent(stream);
- check = connect(stream, SIGNAL(connected()),
- this, SLOT(_q_streamConnected()));
- Q_ASSERT(check);
-
check = connect(stream, SIGNAL(disconnected()),
- this, SLOT(_q_streamDisconnected()));
+ this, SLOT(_q_serverDisconnected()));
Q_ASSERT(check);
check = connect(stream, SIGNAL(dialbackRequestReceived(QXmppDialback)),
@@ -756,118 +716,23 @@ void QXmppServer::_q_serverConnection(QSslSocket *socket)
Q_ASSERT(check);
check = connect(stream, SIGNAL(elementReceived(QDomElement)),
- this, SLOT(_q_elementReceived(QDomElement)));
+ this, SLOT(handleElement(QDomElement)));
Q_ASSERT(check);
// add stream
- d->incomingServers.append(stream);
- emit streamAdded(stream);
-}
-
-/// Handle a successful stream connection.
-///
-
-void QXmppServer::_q_streamConnected()
-{
- QXmppStream *stream = qobject_cast<QXmppStream*>(sender());
- if (!stream)
- return;
-
- // handle incoming clients
- QXmppIncomingClient *client = qobject_cast<QXmppIncomingClient *>(stream);
- if (client) {
- // 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);
- }
-
- // flush queue
- if (d->queues.contains(stream)) {
- foreach (const QByteArray &data, d->queues[stream])
- stream->sendData(data);
- d->queues.remove(stream);
- }
-
- // emit signal
- emit streamConnected(stream);
+ d->incomingServers.insert(stream);
}
-/// Handle a stream disconnection.
-///
+/// Handle a stream disconnection for an incoming server.
-void QXmppServer::_q_streamDisconnected()
+void QXmppServer::_q_serverDisconnected()
{
- // handle clients
- QXmppIncomingClient *stream = qobject_cast<QXmppIncomingClient *>(sender());
- if (stream && d->incomingClients.contains(stream)) {
- const QString jid = stream->jid();
-
- // check the user exited cleanly
- if (!jid.isEmpty()) {
- QDomDocument doc;
- QDomElement presence = doc.createElement("presence");
- presence.setAttribute("from", jid);
- presence.setAttribute("type", "unavailable");
-
- if (d->presences.value(jidToBareJid(jid)).contains(jid)) {
- // the client had sent an initial available presence but did
- // not sent an unavailable presence, synthesize it
- presence.setAttribute("to", d->domain);
- d->handleStanza(stream, presence);
- } else {
- // synthesize unavailable presence to directed presence receivers
- const QSet<QString> recipients = d->subscribers.value(jid);
- foreach (const QString &recipient, recipients) {
- presence.setAttribute("to", recipient);
- d->handleStanza(stream, presence);
- }
- }
-
- // remove stream from routing tables
- if (d->incomingClientsByJid.value(jid) == stream)
- d->incomingClientsByJid.remove(jid);
- const QString bareJid = jidToBareJid(jid);
- if (d->incomingClientsByBareJid.contains(bareJid))
- d->incomingClientsByBareJid[bareJid].remove(stream);
- }
-
- // remove stream
- d->incomingClients.removeAll(stream);
- d->queues.remove(stream);
- emit streamRemoved(stream);
- stream->deleteLater();
- return;
- }
-
- // handle incoming streams
QXmppIncomingServer *incoming = qobject_cast<QXmppIncomingServer *>(sender());
- if (incoming && d->incomingServers.contains(incoming))
- {
- d->incomingServers.removeAll(incoming);
- d->queues.remove(incoming);
- emit streamRemoved(incoming);
- incoming->deleteLater();
+ if (!incoming)
return;
- }
- // handle outgoing streams
- QXmppOutgoingServer *outgoing = qobject_cast<QXmppOutgoingServer *>(sender());
- if (outgoing && d->outgoingServers.contains(outgoing))
- {
- d->outgoingServers.removeAll(outgoing);
- d->queues.remove(outgoing);
- emit streamRemoved(outgoing);
- outgoing->deleteLater();
- return;
- }
+ if (d->incomingServers.remove(incoming))
+ incoming->deleteLater();
}
class QXmppSslServerPrivate
diff --git a/src/QXmppServer.h b/src/QXmppServer.h
index 7a5bc6da..cb5fcedd 100644
--- a/src/QXmppServer.h
+++ b/src/QXmppServer.h
@@ -25,6 +25,7 @@
#define QXMPPSERVER_H
#include <QTcpServer>
+#include <QVariantMap>
#include "QXmppLogger.h"
@@ -75,6 +76,8 @@ public:
QXmppPasswordChecker *passwordChecker();
void setPasswordChecker(QXmppPasswordChecker *checker);
+ QVariantMap statistics() const;
+
void addCaCertificates(const QString &caCertificates);
void setLocalCertificate(const QString &path);
void setPrivateKey(const QString &path);
@@ -86,26 +89,30 @@ public:
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);
- QList<QXmppPresence> availablePresences(const QString &bareJid);
+ /// \endcond
signals:
- /// This signal is emitted when an XMPP stream is added.
- void streamAdded(QXmppStream *stream);
+ /// This signal is emitted when a client has connected.
+ void clientConnected(const QString &jid);
- /// This signal is emitted when an XMPP stream is connected.
- void streamConnected(QXmppStream *stream);
+ /// This signal is emitted when a client has disconnected.
+ void clientDisconnected(const QString &jid);
- /// This signal is emitted when an XMPP stream is removed.
- void streamRemoved(QXmppStream *stream);
+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_elementReceived(const QDomElement &element);
+ void _q_outgoingServerDisconnected();
void _q_serverConnection(QSslSocket *socket);
- void _q_streamConnected();
- void _q_streamDisconnected();
+ void _q_serverDisconnected();
private:
friend class QXmppServerPrivate;
diff --git a/src/QXmppServerExtension.cpp b/src/QXmppServerExtension.cpp
index 525d1cb5..7369a4f7 100644
--- a/src/QXmppServerExtension.cpp
+++ b/src/QXmppServerExtension.cpp
@@ -78,12 +78,10 @@ QString QXmppServerExtension::extensionName() const
///
/// Return true if no further processing should occur, false otherwise.
///
-/// \param stream The QXmppStream on which the stanza was received.
/// \param stanza The received stanza.
-bool QXmppServerExtension::handleStanza(QXmppStream *stream, const QDomElement &stanza)
+bool QXmppServerExtension::handleStanza(const QDomElement &stanza)
{
- Q_UNUSED(stream);
Q_UNUSED(stanza);
return false;
}
@@ -92,20 +90,20 @@ bool QXmppServerExtension::handleStanza(QXmppStream *stream, const QDomElement &
///
/// \param jid
-QStringList QXmppServerExtension::presenceSubscribers(const QString &jid)
+QSet<QString> QXmppServerExtension::presenceSubscribers(const QString &jid)
{
Q_UNUSED(jid);
- return QStringList();
+ return QSet<QString>();
}
/// Returns the list of subscriptions for the given JID.
///
/// \param jid
-QStringList QXmppServerExtension::presenceSubscriptions(const QString &jid)
+QSet<QString> QXmppServerExtension::presenceSubscriptions(const QString &jid)
{
Q_UNUSED(jid);
- return QStringList();
+ return QSet<QString>();
}
/// Returns the extension's statistics.
diff --git a/src/QXmppServerExtension.h b/src/QXmppServerExtension.h
index 46920e01..d908e17b 100644
--- a/src/QXmppServerExtension.h
+++ b/src/QXmppServerExtension.h
@@ -56,9 +56,9 @@ public:
virtual QStringList discoveryFeatures() const;
virtual QStringList discoveryItems() const;
- virtual bool handleStanza(QXmppStream *stream, const QDomElement &stanza);
- virtual QStringList presenceSubscribers(const QString &jid);
- virtual QStringList presenceSubscriptions(const QString &jid);
+ virtual bool handleStanza(const QDomElement &stanza);
+ virtual QSet<QString> presenceSubscribers(const QString &jid);
+ virtual QSet<QString> presenceSubscriptions(const QString &jid);
virtual QVariantMap statistics() const;
virtual void setStatistics(const QVariantMap &statistics);
diff --git a/src/server/mod_disco.cpp b/src/server/mod_disco.cpp
index 715511c8..61caf5dc 100644
--- a/src/server/mod_disco.cpp
+++ b/src/server/mod_disco.cpp
@@ -52,10 +52,8 @@ QStringList QXmppServerDiscovery::discoveryItems() const
return m_discoveryItems;
}
-bool QXmppServerDiscovery::handleStanza(QXmppStream *stream, const QDomElement &element)
+bool QXmppServerDiscovery::handleStanza(const QDomElement &element)
{
- Q_UNUSED(stream);
-
if (element.attribute("to") != server()->domain())
return false;
diff --git a/src/server/mod_disco.h b/src/server/mod_disco.h
index 1a0e00c6..41791cf7 100644
--- a/src/server/mod_disco.h
+++ b/src/server/mod_disco.h
@@ -44,7 +44,7 @@ public:
/// \cond
QStringList discoveryFeatures() const;
QStringList discoveryItems() const;
- bool handleStanza(QXmppStream *stream, const QDomElement &element);
+ bool handleStanza(const QDomElement &element);
/// \endcond
private:
diff --git a/src/server/mod_ping.cpp b/src/server/mod_ping.cpp
index 96fb55d9..36622b07 100644
--- a/src/server/mod_ping.cpp
+++ b/src/server/mod_ping.cpp
@@ -36,10 +36,8 @@ QStringList QXmppServerPing::discoveryFeatures() const
return QStringList() << ns_ping;
}
-bool QXmppServerPing::handleStanza(QXmppStream *stream, const QDomElement &element)
+bool QXmppServerPing::handleStanza(const QDomElement &element)
{
- Q_UNUSED(stream);
-
if (element.attribute("to") != server()->domain())
return false;
diff --git a/src/server/mod_ping.h b/src/server/mod_ping.h
index 567cc489..960c17f6 100644
--- a/src/server/mod_ping.h
+++ b/src/server/mod_ping.h
@@ -36,7 +36,7 @@ class QXmppServerPing : public QXmppServerExtension
public:
QStringList discoveryFeatures() const;
- bool handleStanza(QXmppStream *stream, const QDomElement &element);
+ bool handleStanza(const QDomElement &element);
};
#endif
diff --git a/src/server/mod_presence.cpp b/src/server/mod_presence.cpp
new file mode 100644
index 00000000..33e7aa26
--- /dev/null
+++ b/src/server/mod_presence.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2008-2011 The QXmpp developers
+ *
+ * Author:
+ * Jeremy Lainé
+ *
+ * Source:
+ * http://code.google.com/p/qxmpp
+ *
+ * This file is a part of QXmpp library.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#include <QDomElement>
+
+#include "QXmppConstants.h"
+#include "QXmppPresence.h"
+#include "QXmppServer.h"
+#include "QXmppServerPlugin.h"
+#include "QXmppStream.h"
+#include "QXmppUtils.h"
+
+#include "mod_presence.h"
+
+class QXmppServerPresencePrivate
+{
+public:
+ QXmppServerPresencePrivate(QXmppServerPresence *qq);
+
+ QSet<QString> collectSubscribers(const QString &jid);
+ QSet<QString> collectSubscriptions(const QString &jid);
+
+ QHash<QString, QHash<QString, QXmppPresence> > presences;
+ QHash<QString, QSet<QString> > subscribers;
+
+private:
+ QXmppServerPresence *q;
+};
+
+QXmppServerPresencePrivate::QXmppServerPresencePrivate(QXmppServerPresence *qq)
+ : q(qq)
+{
+}
+
+/// Collect subscribers from the extensions.
+///
+/// \param jid
+
+QSet<QString> QXmppServerPresencePrivate::collectSubscribers(const QString &jid)
+{
+ QSet<QString> recipients;
+ foreach (QXmppServerExtension *extension, q->server()->extensions())
+ recipients += extension->presenceSubscribers(jid);
+ return recipients;
+}
+
+/// Collect subscriptions from the extensions.
+///
+/// \param jid
+
+QSet<QString> QXmppServerPresencePrivate::collectSubscriptions(const QString &jid)
+{
+ QSet<QString> recipients;
+ foreach (QXmppServerExtension *extension, q->server()->extensions())
+ recipients += extension->presenceSubscriptions(jid);
+ return recipients;
+}
+
+QXmppServerPresence::QXmppServerPresence()
+{
+ d = new QXmppServerPresencePrivate(this);
+}
+
+QXmppServerPresence::~QXmppServerPresence()
+{
+ delete d;
+}
+
+/// Returns the list of available resources for the given local JID.
+///
+/// \param bareJid
+
+QList<QXmppPresence> QXmppServerPresence::availablePresences(const QString &bareJid) const
+{
+ return d->presences.value(bareJid).values();
+}
+
+bool QXmppServerPresence::handleStanza(const QDomElement &element)
+{
+ if (element.tagName() != QLatin1String("presence"))
+ return false;
+
+ const QString domain = server()->domain();
+ const QString from = element.attribute("from");
+ const QString type = element.attribute("type");
+ const QString to = element.attribute("to");
+
+ if (to == domain) {
+ // presence to the local domain
+
+ // we only want available or unavailable presences from local users
+ if ((!type.isEmpty() && type != QLatin1String("unavailable"))
+ || (jidToDomain(from) != domain))
+ return true;
+
+ const QString bareFrom = jidToBareJid(from);
+ bool isInitial = false;
+
+ if (type.isEmpty()) {
+ QXmppPresence presence;
+ presence.parse(element);
+
+ // record the presence for future use
+ isInitial = !d->presences.value(bareFrom).contains(from);
+ d->presences[bareFrom][from] = presence;
+ } else {
+ d->presences[bareFrom].remove(from);
+ if (d->presences[bareFrom].isEmpty())
+ d->presences.remove(bareFrom);
+ }
+
+ // broadcast it to subscribers
+ foreach (const QString &subscriber, d->collectSubscribers(from)) {
+ // avoid loop
+ if (subscriber == to)
+ continue;
+ QDomElement changed = element.cloneNode(true).toElement();
+ changed.setAttribute("to", subscriber);
+ server()->handleElement(changed);
+ }
+
+ // get presences from subscriptions
+ if (isInitial) {
+ foreach (const QString &subscription, d->collectSubscriptions(from)) {
+ if (jidToDomain(subscription) != domain) {
+ QXmppPresence probe;
+ probe.setType(QXmppPresence::Probe);
+ probe.setFrom(from);
+ probe.setTo(subscription);
+ server()->sendPacket(probe);
+ } else {
+ QXmppPresence push;
+ foreach (push, availablePresences(subscription)) {
+ push.setTo(from);
+ server()->sendPacket(push);
+ }
+ }
+ }
+ }
+
+ // the presence was for us, stop here
+ return true;
+ } else {
+ // directed presence
+ if ((type.isEmpty() || type == QLatin1String("unavailable")) && jidToDomain(from) == domain) {
+ // available or unavailable presence from local user
+ if (type.isEmpty())
+ d->subscribers[from].insert(to);
+ else {
+ d->subscribers[from].remove(to);
+ if (d->subscribers[from].isEmpty())
+ d->subscribers.remove(from);
+ }
+ } else if (type == QLatin1String("error") && jidToDomain(to) == domain) {
+ // error presence to a local user
+ d->subscribers[to].remove(from);
+ if (d->subscribers[to].isEmpty())
+ d->subscribers.remove(to);
+ }
+
+ // the presence was not for us
+ return false;
+ }
+}
+
+QSet<QString> QXmppServerPresence::presenceSubscribers(const QString &jid)
+{
+ return d->subscribers.value(jid);
+}
+
+QXmppServerPresence* QXmppServerPresence::instance(QXmppServer *server)
+{
+ foreach (QXmppServerExtension *extension, server->extensions()) {
+ QXmppServerPresence *presenceExtension = qobject_cast<QXmppServerPresence*>(extension);
+ if (presenceExtension)
+ return presenceExtension;
+ }
+ return 0;
+}
+
+bool QXmppServerPresence::start()
+{
+ bool check;
+ Q_UNUSED(check);
+
+ check = connect(server(), SIGNAL(clientDisconnected(QString)),
+ this, SLOT(_q_clientDisconnected(QString)));
+ Q_ASSERT(check);
+
+ return true;
+}
+
+void QXmppServerPresence::stop()
+{
+ disconnect(server(), SIGNAL(clientDisconnected(QString)),
+ this, SLOT(_q_clientDisconnected(QString)));
+}
+
+void QXmppServerPresence::_q_clientDisconnected(const QString &jid)
+{
+ Q_ASSERT(!jid.isEmpty());
+
+ // check the user exited cleanly
+ const bool hadPresence = d->presences.value(jidToBareJid(jid)).contains(jid);
+ if (hadPresence) {
+ // the client had sent an initial available presence but did
+ // not sent an unavailable presence, synthesize it
+ QDomDocument doc;
+ QDomElement presence = doc.createElement("presence");
+ presence.setAttribute("from", jid);
+ presence.setAttribute("type", "unavailable");
+ presence.setAttribute("to", server()->domain());
+ server()->handleElement(presence);
+ } else {
+ // synthesize unavailable presence to directed presence receivers
+ foreach (const QString &recipient, presenceSubscribers(jid)) {
+ QDomDocument doc;
+ QDomElement presence = doc.createElement("presence");
+ presence.setAttribute("from", jid);
+ presence.setAttribute("type", "unavailable");
+ presence.setAttribute("to", recipient);
+ server()->handleElement(presence);
+ }
+ }
+}
+
+// PLUGIN
+
+class QXmppServerPresencePlugin : public QXmppServerPlugin
+{
+public:
+ QXmppServerExtension *create(const QString &key)
+ {
+ if (key == QLatin1String("presence"))
+ return new QXmppServerPresence;
+ else
+ return 0;
+ };
+
+ QStringList keys() const
+ {
+ return QStringList() << QLatin1String("presence");
+ };
+};
+
+Q_EXPORT_STATIC_PLUGIN2(mod_presence, QXmppServerPresencePlugin)
+
diff --git a/src/server/mod_presence.h b/src/server/mod_presence.h
new file mode 100644
index 00000000..4424f17d
--- /dev/null
+++ b/src/server/mod_presence.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 QXMPP_SERVER_PRESENCE_H
+#define QXMPP_SERVER_PRESENCE_H
+
+#include "QXmppServerExtension.h"
+
+class QXmppPresence;
+class QXmppServerPresencePrivate;
+
+/// \brief QXmppServer extension for presence handling.
+///
+
+class QXmppServerPresence : public QXmppServerExtension
+{
+ Q_OBJECT
+ Q_CLASSINFO("ExtensionName", "presence");
+
+public:
+ QXmppServerPresence();
+ ~QXmppServerPresence();
+
+ QList<QXmppPresence> availablePresences(const QString &bareJid) const;
+ bool handleStanza(const QDomElement &element);
+ QSet<QString> presenceSubscribers(const QString &jid);
+ bool start();
+ void stop();
+
+ static QXmppServerPresence* instance(QXmppServer *server);
+
+private slots:
+ void _q_clientDisconnected(const QString &jid);
+
+private:
+ friend class QXmppServerPresencePrivate;
+ QXmppServerPresencePrivate *d;
+};
+
+#endif
diff --git a/src/server/mod_proxy65.cpp b/src/server/mod_proxy65.cpp
index 9562eae4..374adc79 100644
--- a/src/server/mod_proxy65.cpp
+++ b/src/server/mod_proxy65.cpp
@@ -160,14 +160,15 @@ class QXmppServerProxy65Private
{
public:
// configuration
+ QString advertisedName;
+ QHostAddress advertisedAddress;
QStringList allowedDomains;
QString jid;
- QHostAddress hostAddress;
QString hostName;
quint16 port;
// state
- QMap<QString, QTcpSocketPair*> pairs;
+ QHash<QString, QTcpSocketPair*> pairs;
QXmppSocksServer *server;
// statistics
@@ -205,6 +206,26 @@ QXmppServerProxy65::~QXmppServerProxy65()
delete d;
}
+/// Returns the host which is advertised to clients for SOCKS5 connections.
+///
+/// You should only care about this if the host which the clients will see is
+/// different from \a host().
+
+QString QXmppServerProxy65::advertisedHost() const
+{
+ return d->advertisedName;
+}
+
+/// Sets the host which is advertised to clients for SOCKS5 connections.
+///
+/// You should only care about this if the host which the clients will see is
+/// different from \a host().
+
+void QXmppServerProxy65::setAdvertisedHost(const QString &advertisedHost)
+{
+ d->advertisedName = advertisedHost;
+}
+
/// Returns the XMPP domains which are allowed to use the proxy.
///
@@ -284,10 +305,8 @@ QStringList QXmppServerProxy65::discoveryItems() const
return QStringList() << d->jid;
}
-bool QXmppServerProxy65::handleStanza(QXmppStream *stream, const QDomElement &element)
+bool QXmppServerProxy65::handleStanza(const QDomElement &element)
{
- Q_UNUSED(stream);
-
if (element.attribute("to") != d->jid)
return false;
@@ -341,7 +360,7 @@ bool QXmppServerProxy65::handleStanza(QXmppStream *stream, const QDomElement &el
QXmppByteStreamIq::StreamHost streamHost;
streamHost.setJid(d->jid);
- streamHost.setHost(d->hostAddress);
+ streamHost.setHost(d->advertisedAddress);
streamHost.setPort(d->port);
streamHosts.append(streamHost);
@@ -389,10 +408,11 @@ bool QXmppServerProxy65::start()
if (d->jid.isEmpty())
d->jid = "proxy." + server()->domain();
- // determine address
+ // determine bind address
if (d->hostName.isEmpty())
d->hostName = server()->domain();
- if (!d->hostAddress.setAddress(d->hostName))
+ QHostAddress bindAddress;
+ if (!bindAddress.setAddress(d->hostName))
{
QHostInfo hostInfo = QHostInfo::fromName(d->hostName);
if (hostInfo.addresses().isEmpty())
@@ -400,11 +420,27 @@ bool QXmppServerProxy65::start()
warning(QString("Could not lookup host %1").arg(d->hostName));
return false;
}
- d->hostAddress = hostInfo.addresses().first();
+ bindAddress = hostInfo.addresses().first();
+ }
+
+ // determine advertised address
+ if (d->advertisedName.isEmpty()) {
+ d->advertisedName = d->hostName;
+ d->advertisedAddress = bindAddress;
+ }
+ else if (!d->advertisedAddress.setAddress(d->advertisedName))
+ {
+ QHostInfo hostInfo = QHostInfo::fromName(d->advertisedName);
+ if (hostInfo.addresses().isEmpty())
+ {
+ warning(QString("Could not lookup host %1").arg(d->hostName));
+ return false;
+ }
+ d->advertisedAddress = hostInfo.addresses().first();
}
// start listening
- if (!d->server->listen(d->hostAddress, d->port))
+ if (!d->server->listen(bindAddress, d->port))
return false;
// start statistics update
diff --git a/src/server/mod_proxy65.h b/src/server/mod_proxy65.h
index 308725f4..e891963b 100644
--- a/src/server/mod_proxy65.h
+++ b/src/server/mod_proxy65.h
@@ -66,6 +66,7 @@ class QXmppServerProxy65 : public QXmppServerExtension
{
Q_OBJECT
Q_CLASSINFO("ExtensionName", "proxy65");
+ Q_PROPERTY(QString advertisedHost READ advertisedHost WRITE setAdvertisedHost);
Q_PROPERTY(QStringList allowedDomains READ allowedDomains WRITE setAllowedDomains);
Q_PROPERTY(QString jid READ jid WRITE setJid);
Q_PROPERTY(QString host READ host WRITE setHost);
@@ -75,6 +76,9 @@ public:
QXmppServerProxy65();
~QXmppServerProxy65();
+ QString advertisedHost() const;
+ void setAdvertisedHost(const QString &advertisedHost);
+
QStringList allowedDomains() const;
void setAllowedDomains(const QStringList &allowedDomains);
@@ -89,7 +93,7 @@ public:
/// \cond
QStringList discoveryItems() const;
- bool handleStanza(QXmppStream *stream, const QDomElement &element);
+ bool handleStanza(const QDomElement &element);
bool start();
void stop();
QVariantMap statistics() const;
diff --git a/src/server/mod_stats.cpp b/src/server/mod_stats.cpp
index 36cea79d..357634aa 100644
--- a/src/server/mod_stats.cpp
+++ b/src/server/mod_stats.cpp
@@ -28,9 +28,6 @@
#include "QXmppConstants.h"
#include "QXmppDiscoveryIq.h"
-#include "QXmppIncomingClient.h"
-#include "QXmppIncomingServer.h"
-#include "QXmppOutgoingServer.h"
#include "QXmppServer.h"
#include "QXmppServerPlugin.h"
#include "QXmppUtils.h"
@@ -173,20 +170,13 @@ QStringList QXmppServerStats::discoveryItems() const
return QStringList() << d->jid;
}
-QVariantMap QXmppServerStats::statistics() const
+QVariantMap QXmppServerStats::statistics()
{
- QVariantMap stats;
- stats["version"] = qApp->applicationVersion();
- stats["incoming-clients"] = d->incomingClients;
- stats["incoming-servers"] = d->incomingServers;
- stats["outgoing-servers"] = d->outgoingServers;
- return stats;
+ return server()->statistics();
}
-bool QXmppServerStats::handleStanza(QXmppStream *stream, const QDomElement &element)
+bool QXmppServerStats::handleStanza(const QDomElement &element)
{
- Q_UNUSED(stream);
-
if (element.attribute("to") != d->jid)
return false;
@@ -280,21 +270,10 @@ bool QXmppServerStats::handleStanza(QXmppStream *stream, const QDomElement &elem
bool QXmppServerStats::start()
{
- bool check;
- Q_UNUSED(check);
-
// determine jid
if (d->jid.isEmpty())
d->jid = "statistics." + server()->domain();
- check = connect(server(), SIGNAL(streamAdded(QXmppStream*)),
- this, SLOT(streamAdded(QXmppStream*)));
- Q_ASSERT(check);
-
- check = connect(server(), SIGNAL(streamRemoved(QXmppStream*)),
- this, SLOT(streamRemoved(QXmppStream*)));
- Q_ASSERT(check);
-
d->statisticsTimer->start();
return true;
@@ -303,36 +282,6 @@ bool QXmppServerStats::start()
void QXmppServerStats::stop()
{
d->statisticsTimer->stop();
-
- disconnect(server(), SIGNAL(streamAdded(QXmppStream*)),
- this, SLOT(streamAdded(QXmppStream*)));
-
- disconnect(server(), SIGNAL(streamRemoved(QXmppStream*)),
- this, SLOT(streamRemoved(QXmppStream*)));
-}
-
-void QXmppServerStats::streamAdded(QXmppStream *stream)
-{
- if (qobject_cast<QXmppIncomingClient*>(stream))
- d->incomingClients++;
- else if (qobject_cast<QXmppIncomingServer*>(stream))
- d->incomingServers++;
- else if (qobject_cast<QXmppOutgoingServer*>(stream))
- d->outgoingServers++;
- else
- return;
-}
-
-void QXmppServerStats::streamRemoved(QXmppStream *stream)
-{
- if (qobject_cast<QXmppIncomingClient*>(stream))
- d->incomingClients--;
- else if (qobject_cast<QXmppIncomingServer*>(stream))
- d->incomingServers--;
- else if (qobject_cast<QXmppOutgoingServer*>(stream))
- d->outgoingServers--;
- else
- return;
}
// PLUGIN
diff --git a/src/server/mod_stats.h b/src/server/mod_stats.h
index d53d60a5..6eff688f 100644
--- a/src/server/mod_stats.h
+++ b/src/server/mod_stats.h
@@ -51,15 +51,13 @@ public:
/// cond
QStringList discoveryItems() const;
- bool handleStanza(QXmppStream *stream, const QDomElement &element);
- QVariantMap statistics() const;
+ bool handleStanza(const QDomElement &element);
+ QVariantMap statistics();
bool start();
void stop();
/// \endcond
private slots:
- void streamAdded(QXmppStream *stream);
- void streamRemoved(QXmppStream *stream);
void writeStatistics();
private:
diff --git a/src/server/mod_time.cpp b/src/server/mod_time.cpp
index 0523eaf0..ec535662 100644
--- a/src/server/mod_time.cpp
+++ b/src/server/mod_time.cpp
@@ -37,10 +37,8 @@ QStringList QXmppServerTime::discoveryFeatures() const
return QStringList() << ns_entity_time;
}
-bool QXmppServerTime::handleStanza(QXmppStream *stream, const QDomElement &element)
+bool QXmppServerTime::handleStanza(const QDomElement &element)
{
- Q_UNUSED(stream);
-
if (element.attribute("to") != server()->domain())
return false;
diff --git a/src/server/mod_time.h b/src/server/mod_time.h
index ecc94b90..2935ac49 100644
--- a/src/server/mod_time.h
+++ b/src/server/mod_time.h
@@ -36,7 +36,7 @@ class QXmppServerTime : public QXmppServerExtension
public:
QStringList discoveryFeatures() const;
- bool handleStanza(QXmppStream *stream, const QDomElement &element);
+ bool handleStanza(const QDomElement &element);
};
#endif
diff --git a/src/server/mod_version.cpp b/src/server/mod_version.cpp
index ec069e0f..500bd173 100644
--- a/src/server/mod_version.cpp
+++ b/src/server/mod_version.cpp
@@ -37,10 +37,8 @@ QStringList QXmppServerVersion::discoveryFeatures() const
return QStringList() << ns_version;
}
-bool QXmppServerVersion::handleStanza(QXmppStream *stream, const QDomElement &element)
+bool QXmppServerVersion::handleStanza(const QDomElement &element)
{
- Q_UNUSED(stream);
-
if (element.attribute("to") != server()->domain())
return false;
diff --git a/src/server/mod_version.h b/src/server/mod_version.h
index d88cbd5b..4dd1ace7 100644
--- a/src/server/mod_version.h
+++ b/src/server/mod_version.h
@@ -36,7 +36,7 @@ class QXmppServerVersion : public QXmppServerExtension
public:
QStringList discoveryFeatures() const;
- bool handleStanza(QXmppStream *stream, const QDomElement &element);
+ bool handleStanza(const QDomElement &element);
};
#endif
diff --git a/src/src.pro b/src/src.pro
index b6a4ad16..65ba8a83 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -159,6 +159,7 @@ DEFINES += QT_STATICPLUGIN
HEADERS += \
server/mod_disco.h \
server/mod_ping.h \
+ server/mod_presence.h \
server/mod_proxy65.h \
server/mod_stats.h \
server/mod_time.h \
@@ -166,6 +167,7 @@ HEADERS += \
SOURCES += \
server/mod_disco.cpp \
server/mod_ping.cpp \
+ server/mod_presence.cpp \
server/mod_proxy65.cpp \
server/mod_stats.cpp \
server/mod_time.cpp \