summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Limber/Server')
-rw-r--r--Limber/Server/ServerFromClientSession.cpp117
-rw-r--r--Limber/Server/ServerFromClientSession.h60
-rw-r--r--Limber/Server/ServerSession.cpp14
-rw-r--r--Limber/Server/ServerSession.h23
-rw-r--r--Limber/Server/ServerStanzaRouter.cpp74
-rw-r--r--Limber/Server/ServerStanzaRouter.h30
-rw-r--r--Limber/Server/SimpleUserRegistry.cpp23
-rw-r--r--Limber/Server/SimpleUserRegistry.h28
-rw-r--r--Limber/Server/UnitTest/ServerStanzaRouterTest.cpp150
-rw-r--r--Limber/Server/UserRegistry.cpp14
-rw-r--r--Limber/Server/UserRegistry.h21
11 files changed, 554 insertions, 0 deletions
diff --git a/Limber/Server/ServerFromClientSession.cpp b/Limber/Server/ServerFromClientSession.cpp
new file mode 100644
index 0000000..3a37c65
--- /dev/null
+++ b/Limber/Server/ServerFromClientSession.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Limber/Server/ServerFromClientSession.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#include "Limber/Server/UserRegistry.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Elements/StreamFeatures.h"
+#include "Swiften/Elements/ResourceBind.h"
+#include "Swiften/Elements/StartSession.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/AuthSuccess.h"
+#include "Swiften/Elements/AuthFailure.h"
+#include "Swiften/Elements/AuthRequest.h"
+#include "Swiften/SASL/PLAINMessage.h"
+
+namespace Swift {
+
+ServerFromClientSession::ServerFromClientSession(
+ const std::string& id,
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers,
+ UserRegistry* userRegistry) :
+ Session(connection, payloadParserFactories, payloadSerializers),
+ id_(id),
+ userRegistry_(userRegistry),
+ authenticated_(false),
+ initialized(false),
+ allowSASLEXTERNAL(false) {
+}
+
+
+void ServerFromClientSession::handleElement(boost::shared_ptr<Element> element) {
+ if (isInitialized()) {
+ onElementReceived(element);
+ }
+ else {
+ if (AuthRequest* authRequest = dynamic_cast<AuthRequest*>(element.get())) {
+ if (authRequest->getMechanism() == "PLAIN" || (allowSASLEXTERNAL && authRequest->getMechanism() == "EXTERNAL")) {
+ if (authRequest->getMechanism() == "EXTERNAL") {
+ getXMPPLayer()->writeElement(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
+ authenticated_ = true;
+ getXMPPLayer()->resetParser();
+ }
+ else {
+ PLAINMessage plainMessage(authRequest->getMessage() ? *authRequest->getMessage() : createSafeByteArray(""));
+ if (userRegistry_->isValidUserPassword(JID(plainMessage.getAuthenticationID(), getLocalJID().getDomain()), plainMessage.getPassword())) {
+ getXMPPLayer()->writeElement(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
+ user_ = plainMessage.getAuthenticationID();
+ authenticated_ = true;
+ getXMPPLayer()->resetParser();
+ }
+ else {
+ getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure));
+ finishSession(AuthenticationFailedError);
+ }
+ }
+ }
+ else {
+ getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure));
+ finishSession(NoSupportedAuthMechanismsError);
+ }
+ }
+ else if (IQ* iq = dynamic_cast<IQ*>(element.get())) {
+ if (boost::shared_ptr<ResourceBind> resourceBind = iq->getPayload<ResourceBind>()) {
+ setRemoteJID(JID(user_, getLocalJID().getDomain(), resourceBind->getResource()));
+ boost::shared_ptr<ResourceBind> resultResourceBind(new ResourceBind());
+ resultResourceBind->setJID(getRemoteJID());
+ getXMPPLayer()->writeElement(IQ::createResult(JID(), iq->getID(), resultResourceBind));
+ }
+ else if (iq->getPayload<StartSession>()) {
+ getXMPPLayer()->writeElement(IQ::createResult(getRemoteJID(), iq->getID()));
+ setInitialized();
+ }
+ }
+ }
+}
+
+void ServerFromClientSession::handleStreamStart(const ProtocolHeader& incomingHeader) {
+ setLocalJID(JID("", incomingHeader.getTo()));
+ ProtocolHeader header;
+ header.setFrom(incomingHeader.getTo());
+ header.setID(id_);
+ getXMPPLayer()->writeHeader(header);
+
+ boost::shared_ptr<StreamFeatures> features(new StreamFeatures());
+ if (!authenticated_) {
+ features->addAuthenticationMechanism("PLAIN");
+ if (allowSASLEXTERNAL) {
+ features->addAuthenticationMechanism("EXTERNAL");
+ }
+ }
+ else {
+ features->setHasResourceBind();
+ features->setHasSession();
+ }
+ getXMPPLayer()->writeElement(features);
+}
+
+void ServerFromClientSession::setInitialized() {
+ initialized = true;
+ onSessionStarted();
+}
+
+void ServerFromClientSession::setAllowSASLEXTERNAL() {
+ allowSASLEXTERNAL = true;
+}
+
+}
diff --git a/Limber/Server/ServerFromClientSession.h b/Limber/Server/ServerFromClientSession.h
new file mode 100644
index 0000000..1a0e109
--- /dev/null
+++ b/Limber/Server/ServerFromClientSession.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <Swiften/Base/boost_bsignals.h>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <string>
+#include <Swiften/Session/Session.h>
+#include <Swiften/JID/JID.h>
+#include <Swiften/Network/Connection.h>
+#include <Swiften/Base/ByteArray.h>
+
+namespace Swift {
+ class ProtocolHeader;
+ class Element;
+ class Stanza;
+ class PayloadParserFactoryCollection;
+ class PayloadSerializerCollection;
+ class StreamStack;
+ class UserRegistry;
+ class XMPPLayer;
+ class ConnectionLayer;
+ class Connection;
+
+ class ServerFromClientSession : public Session {
+ public:
+ ServerFromClientSession(
+ const std::string& id,
+ boost::shared_ptr<Connection> connection,
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers,
+ UserRegistry* userRegistry);
+
+ boost::signal<void ()> onSessionStarted;
+ void setAllowSASLEXTERNAL();
+
+ private:
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamStart(const ProtocolHeader& header);
+
+ void setInitialized();
+ bool isInitialized() const {
+ return initialized;
+ }
+
+ private:
+ std::string id_;
+ UserRegistry* userRegistry_;
+ bool authenticated_;
+ bool initialized;
+ bool allowSASLEXTERNAL;
+ std::string user_;
+ };
+}
diff --git a/Limber/Server/ServerSession.cpp b/Limber/Server/ServerSession.cpp
new file mode 100644
index 0000000..5b6d66a
--- /dev/null
+++ b/Limber/Server/ServerSession.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Limber/Server/ServerSession.h"
+
+namespace Swift {
+
+ServerSession::~ServerSession() {
+}
+
+}
diff --git a/Limber/Server/ServerSession.h b/Limber/Server/ServerSession.h
new file mode 100644
index 0000000..486ebaa
--- /dev/null
+++ b/Limber/Server/ServerSession.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Elements/Stanza.h>
+
+namespace Swift {
+ class ServerSession {
+ public:
+ virtual ~ServerSession();
+
+ virtual const JID& getJID() const = 0;
+ virtual int getPriority() const = 0;
+
+ virtual void sendStanza(boost::shared_ptr<Stanza>) = 0;
+ };
+}
diff --git a/Limber/Server/ServerStanzaRouter.cpp b/Limber/Server/ServerStanzaRouter.cpp
new file mode 100644
index 0000000..ea695e3
--- /dev/null
+++ b/Limber/Server/ServerStanzaRouter.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Limber/Server/ServerStanzaRouter.h"
+#include "Limber/Server/ServerSession.h"
+
+#include <cassert>
+#include <algorithm>
+#include <Swiften/Base/Algorithm.h>
+
+namespace Swift {
+
+namespace {
+ struct PriorityLessThan {
+ bool operator()(const ServerSession* s1, const ServerSession* s2) const {
+ return s1->getPriority() < s2->getPriority();
+ }
+ };
+
+ struct HasJID {
+ HasJID(const JID& jid) : jid(jid) {}
+ bool operator()(const ServerSession* session) const {
+ return session->getJID().equals(jid, JID::WithResource);
+ }
+ JID jid;
+ };
+}
+
+ServerStanzaRouter::ServerStanzaRouter() {
+}
+
+bool ServerStanzaRouter::routeStanza(boost::shared_ptr<Stanza> stanza) {
+ JID to = stanza->getTo();
+ assert(to.isValid());
+
+ // For a full JID, first try to route to a session with the full JID
+ if (!to.isBare()) {
+ std::vector<ServerSession*>::const_iterator i = std::find_if(clientSessions_.begin(), clientSessions_.end(), HasJID(to));
+ if (i != clientSessions_.end()) {
+ (*i)->sendStanza(stanza);
+ return true;
+ }
+ }
+
+ // Look for candidate sessions
+ to = to.toBare();
+ std::vector<ServerSession*> candidateSessions;
+ for (std::vector<ServerSession*>::const_iterator i = clientSessions_.begin(); i != clientSessions_.end(); ++i) {
+ if ((*i)->getJID().equals(to, JID::WithoutResource) && (*i)->getPriority() >= 0) {
+ candidateSessions.push_back(*i);
+ }
+ }
+ if (candidateSessions.empty()) {
+ return false;
+ }
+
+ // Find the session with the highest priority
+ std::vector<ServerSession*>::const_iterator i = std::max_element(clientSessions_.begin(), clientSessions_.end(), PriorityLessThan());
+ (*i)->sendStanza(stanza);
+ return true;
+}
+
+void ServerStanzaRouter::addClientSession(ServerSession* clientSession) {
+ clientSessions_.push_back(clientSession);
+}
+
+void ServerStanzaRouter::removeClientSession(ServerSession* clientSession) {
+ erase(clientSessions_, clientSession);
+}
+
+}
diff --git a/Limber/Server/ServerStanzaRouter.h b/Limber/Server/ServerStanzaRouter.h
new file mode 100644
index 0000000..4a9493b
--- /dev/null
+++ b/Limber/Server/ServerStanzaRouter.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+
+#include <Swiften/JID/JID.h>
+#include <Swiften/Elements/Stanza.h>
+
+namespace Swift {
+ class ServerSession;
+
+ class ServerStanzaRouter {
+ public:
+ ServerStanzaRouter();
+
+ bool routeStanza(boost::shared_ptr<Stanza>);
+
+ void addClientSession(ServerSession*);
+ void removeClientSession(ServerSession*);
+
+ private:
+ std::vector<ServerSession*> clientSessions_;
+ };
+}
diff --git a/Limber/Server/SimpleUserRegistry.cpp b/Limber/Server/SimpleUserRegistry.cpp
new file mode 100644
index 0000000..9ae6fd5
--- /dev/null
+++ b/Limber/Server/SimpleUserRegistry.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Limber/Server/SimpleUserRegistry.h"
+
+namespace Swift {
+
+SimpleUserRegistry::SimpleUserRegistry() {
+}
+
+bool SimpleUserRegistry::isValidUserPassword(const JID& user, const SafeByteArray& password) const {
+ std::map<JID,SafeByteArray>::const_iterator i = users.find(user);
+ return i != users.end() ? i->second == password : false;
+}
+
+void SimpleUserRegistry::addUser(const JID& user, const std::string& password) {
+ users.insert(std::make_pair(user, createSafeByteArray(password)));
+}
+
+}
diff --git a/Limber/Server/SimpleUserRegistry.h b/Limber/Server/SimpleUserRegistry.h
new file mode 100644
index 0000000..280bfce
--- /dev/null
+++ b/Limber/Server/SimpleUserRegistry.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <map>
+
+#include <Swiften/JID/JID.h>
+#include <string>
+#include "Limber/Server/UserRegistry.h"
+
+namespace Swift {
+
+
+ class SimpleUserRegistry : public UserRegistry {
+ public:
+ SimpleUserRegistry();
+
+ virtual bool isValidUserPassword(const JID& user, const SafeByteArray& password) const;
+ void addUser(const JID& user, const std::string& password);
+
+ private:
+ std::map<JID, SafeByteArray> users;
+ };
+}
diff --git a/Limber/Server/UnitTest/ServerStanzaRouterTest.cpp b/Limber/Server/UnitTest/ServerStanzaRouterTest.cpp
new file mode 100644
index 0000000..87c713e
--- /dev/null
+++ b/Limber/Server/UnitTest/ServerStanzaRouterTest.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Elements/Message.h"
+#include "Limber/Server/ServerStanzaRouter.h"
+#include "Limber/Server/ServerSession.h"
+
+using namespace Swift;
+
+class ServerStanzaRouterTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(ServerStanzaRouterTest);
+ CPPUNIT_TEST(testRouteStanza_FullJID);
+ CPPUNIT_TEST(testRouteStanza_FullJIDWithNegativePriority);
+ CPPUNIT_TEST(testRouteStanza_FullJIDWithOnlyBareJIDMatchingSession);
+ CPPUNIT_TEST(testRouteStanza_BareJIDWithoutMatchingSession);
+ CPPUNIT_TEST(testRouteStanza_BareJIDWithMultipleSessions);
+ CPPUNIT_TEST(testRouteStanza_BareJIDWithOnlyNegativePriorities);
+ CPPUNIT_TEST(testRouteStanza_BareJIDWithChangingPresence);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ ServerStanzaRouterTest() {}
+
+ void setUp() {
+ }
+
+ void tearDown() {
+ }
+
+ void testRouteStanza_FullJID() {
+ ServerStanzaRouter testling;
+ MockServerSession session1(JID("foo@bar.com/Bla"), 0);
+ testling.addClientSession(&session1);
+ MockServerSession session2(JID("foo@bar.com/Baz"), 0);
+ testling.addClientSession(&session2);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com/Baz"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session1.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session2.sentStanzas.size()));
+ }
+
+ void testRouteStanza_FullJIDWithNegativePriority() {
+ ServerStanzaRouter testling;
+ MockServerSession session1(JID("foo@bar.com/Bla"), -1);
+ testling.addClientSession(&session1);
+ MockServerSession session2(JID("foo@bar.com/Baz"), 0);
+ testling.addClientSession(&session2);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com/Bla"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session1.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session2.sentStanzas.size()));
+ }
+
+ void testRouteStanza_FullJIDWithOnlyBareJIDMatchingSession() {
+ ServerStanzaRouter testling;
+ MockServerSession session(JID("foo@bar.com/Bla"), 0);
+ testling.addClientSession(&session);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com/Baz"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session.sentStanzas.size()));
+ }
+
+ void testRouteStanza_BareJIDWithoutMatchingSession() {
+ ServerStanzaRouter testling;
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testRouteStanza_BareJIDWithMultipleSessions() {
+ ServerStanzaRouter testling;
+ MockServerSession session1(JID("foo@bar.com/Bla"), 1);
+ testling.addClientSession(&session1);
+ MockServerSession session2(JID("foo@bar.com/Baz"), 8);
+ testling.addClientSession(&session2);
+ MockServerSession session3(JID("foo@bar.com/Bar"), 5);
+ testling.addClientSession(&session3);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session1.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session2.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session3.sentStanzas.size()));
+ }
+
+ void testRouteStanza_BareJIDWithOnlyNegativePriorities() {
+ ServerStanzaRouter testling;
+ MockServerSession session(JID("foo@bar.com/Bla"), -1);
+ testling.addClientSession(&session);
+
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com"));
+
+ CPPUNIT_ASSERT(!result);
+ }
+
+ void testRouteStanza_BareJIDWithChangingPresence() {
+ ServerStanzaRouter testling;
+ MockServerSession session1(JID("foo@bar.com/Baz"), 8);
+ testling.addClientSession(&session1);
+ MockServerSession session2(JID("foo@bar.com/Bar"), 5);
+ testling.addClientSession(&session2);
+
+ session1.priority = 3;
+ session2.priority = 4;
+ bool result = testling.routeStanza(createMessageTo("foo@bar.com"));
+
+ CPPUNIT_ASSERT(result);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(session1.sentStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(session2.sentStanzas.size()));
+ }
+
+ private:
+ boost::shared_ptr<Message> createMessageTo(const std::string& recipient) {
+ boost::shared_ptr<Message> message(new Message());
+ message->setTo(JID(recipient));
+ return message;
+ }
+
+ class MockServerSession : public ServerSession {
+ public:
+ MockServerSession(const JID& jid, int priority) : jid(jid), priority(priority) {}
+
+ virtual const JID& getJID() const { return jid; }
+ virtual int getPriority() const { return priority; }
+
+ virtual void sendStanza(boost::shared_ptr<Stanza> stanza) {
+ sentStanzas.push_back(stanza);
+ }
+
+ JID jid;
+ int priority;
+ std::vector< boost::shared_ptr<Stanza> > sentStanzas;
+ };
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ServerStanzaRouterTest);
diff --git a/Limber/Server/UserRegistry.cpp b/Limber/Server/UserRegistry.cpp
new file mode 100644
index 0000000..5ac462a
--- /dev/null
+++ b/Limber/Server/UserRegistry.cpp
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Limber/Server/UserRegistry.h"
+
+namespace Swift {
+
+UserRegistry::~UserRegistry() {
+}
+
+}
diff --git a/Limber/Server/UserRegistry.h b/Limber/Server/UserRegistry.h
new file mode 100644
index 0000000..9584a7e
--- /dev/null
+++ b/Limber/Server/UserRegistry.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <string>
+#include <Swiften/Base/SafeByteArray.h>
+
+namespace Swift {
+ class JID;
+
+ class UserRegistry {
+ public:
+ virtual ~UserRegistry();
+
+ virtual bool isValidUserPassword(const JID& user, const SafeByteArray& password) const = 0;
+ };
+}