diff options
Diffstat (limited to 'Swiften/Server')
-rw-r--r-- | Swiften/Server/ServerFromClientSession.cpp | 97 | ||||
-rw-r--r-- | Swiften/Server/ServerFromClientSession.h | 52 | ||||
-rw-r--r-- | Swiften/Server/ServerSession.cpp | 8 | ||||
-rw-r--r-- | Swiften/Server/ServerSession.h | 17 | ||||
-rw-r--r-- | Swiften/Server/ServerStanzaRouter.cpp | 67 | ||||
-rw-r--r-- | Swiften/Server/ServerStanzaRouter.h | 24 | ||||
-rw-r--r-- | Swiften/Server/SimpleUserRegistry.cpp | 17 | ||||
-rw-r--r-- | Swiften/Server/SimpleUserRegistry.h | 22 | ||||
-rw-r--r-- | Swiften/Server/UnitTest/ServerStanzaRouterTest.cpp | 144 | ||||
-rw-r--r-- | Swiften/Server/UserRegistry.cpp | 8 | ||||
-rw-r--r-- | Swiften/Server/UserRegistry.h | 13 |
11 files changed, 469 insertions, 0 deletions
diff --git a/Swiften/Server/ServerFromClientSession.cpp b/Swiften/Server/ServerFromClientSession.cpp new file mode 100644 index 0000000..c974e18 --- /dev/null +++ b/Swiften/Server/ServerFromClientSession.cpp @@ -0,0 +1,97 @@ +#include "Swiften/Server/ServerFromClientSession.h" + +#include <boost/bind.hpp> + +#include "Swiften/Elements/ProtocolHeader.h" +#include "Swiften/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 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) { +} + + +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") { + getXMPPLayer()->writeElement(boost::shared_ptr<AuthFailure>(new AuthFailure)); + finishSession(NoSupportedAuthMechanismsError); + } + else { + PLAINMessage plainMessage(authRequest->getMessage()); + 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 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"); + } + else { + features->setHasResourceBind(); + features->setHasSession(); + } + getXMPPLayer()->writeElement(features); +} + +void ServerFromClientSession::setInitialized() { + initialized = true; + onSessionStarted(); +} + + +} diff --git a/Swiften/Server/ServerFromClientSession.h b/Swiften/Server/ServerFromClientSession.h new file mode 100644 index 0000000..33826a4 --- /dev/null +++ b/Swiften/Server/ServerFromClientSession.h @@ -0,0 +1,52 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/signal.hpp> +#include <boost/enable_shared_from_this.hpp> + +#include "Swiften/Base/String.h" +#include "Swiften/Session/Session.h" +#include "Swiften/JID/JID.h" +#include "Swiften/Network/Connection.h" + +namespace Swift { + class ProtocolHeader; + class Element; + class Stanza; + class PayloadParserFactoryCollection; + class PayloadSerializerCollection; + class StreamStack; + class UserRegistry; + class XMPPLayer; + class ConnectionLayer; + class Connection; + class ByteArray; + + class ServerFromClientSession : public Session { + public: + ServerFromClientSession( + const String& id, + boost::shared_ptr<Connection> connection, + PayloadParserFactoryCollection* payloadParserFactories, + PayloadSerializerCollection* payloadSerializers, + UserRegistry* userRegistry); + + boost::signal<void ()> onSessionStarted; + + private: + void handleElement(boost::shared_ptr<Element>); + void handleStreamStart(const ProtocolHeader& header); + + void setInitialized(); + bool isInitialized() const { + return initialized; + } + + private: + String id_; + UserRegistry* userRegistry_; + bool authenticated_; + bool initialized; + String user_; + }; +} diff --git a/Swiften/Server/ServerSession.cpp b/Swiften/Server/ServerSession.cpp new file mode 100644 index 0000000..e62957c --- /dev/null +++ b/Swiften/Server/ServerSession.cpp @@ -0,0 +1,8 @@ +#include "Swiften/Server/ServerSession.h" + +namespace Swift { + +ServerSession::~ServerSession() { +} + +} diff --git a/Swiften/Server/ServerSession.h b/Swiften/Server/ServerSession.h new file mode 100644 index 0000000..1ebf68e --- /dev/null +++ b/Swiften/Server/ServerSession.h @@ -0,0 +1,17 @@ +#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/Swiften/Server/ServerStanzaRouter.cpp b/Swiften/Server/ServerStanzaRouter.cpp new file mode 100644 index 0000000..5661de5 --- /dev/null +++ b/Swiften/Server/ServerStanzaRouter.cpp @@ -0,0 +1,67 @@ +#include "Swiften/Server/ServerStanzaRouter.h" +#include "Swiften/Server/ServerSession.h" + +#include <cassert> +#include <algorithm> + +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) { + clientSessions_.erase(std::remove(clientSessions_.begin(), clientSessions_.end(), clientSession), clientSessions_.end()); +} + +} diff --git a/Swiften/Server/ServerStanzaRouter.h b/Swiften/Server/ServerStanzaRouter.h new file mode 100644 index 0000000..057a2ea --- /dev/null +++ b/Swiften/Server/ServerStanzaRouter.h @@ -0,0 +1,24 @@ +#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/Swiften/Server/SimpleUserRegistry.cpp b/Swiften/Server/SimpleUserRegistry.cpp new file mode 100644 index 0000000..1a6743a --- /dev/null +++ b/Swiften/Server/SimpleUserRegistry.cpp @@ -0,0 +1,17 @@ +#include "Swiften/Server/SimpleUserRegistry.h" + +namespace Swift { + +SimpleUserRegistry::SimpleUserRegistry() { +} + +bool SimpleUserRegistry::isValidUserPassword(const JID& user, const String& password) const { + std::map<JID,String>::const_iterator i = users.find(user); + return i != users.end() ? i->second == password : false; +} + +void SimpleUserRegistry::addUser(const JID& user, const String& password) { + users.insert(std::make_pair(user, password)); +} + +} diff --git a/Swiften/Server/SimpleUserRegistry.h b/Swiften/Server/SimpleUserRegistry.h new file mode 100644 index 0000000..253025d --- /dev/null +++ b/Swiften/Server/SimpleUserRegistry.h @@ -0,0 +1,22 @@ +#pragma once + +#include <map> + +#include "Swiften/JID/JID.h" +#include "Swiften/Base/String.h" +#include "Swiften/Server/UserRegistry.h" + +namespace Swift { + class String; + + class SimpleUserRegistry : public UserRegistry { + public: + SimpleUserRegistry(); + + virtual bool isValidUserPassword(const JID& user, const String& password) const; + void addUser(const JID& user, const String& password); + + private: + std::map<JID, String> users; + }; +} diff --git a/Swiften/Server/UnitTest/ServerStanzaRouterTest.cpp b/Swiften/Server/UnitTest/ServerStanzaRouterTest.cpp new file mode 100644 index 0000000..03a607a --- /dev/null +++ b/Swiften/Server/UnitTest/ServerStanzaRouterTest.cpp @@ -0,0 +1,144 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include "Swiften/Elements/Message.h" +#include "Swiften/Server/ServerStanzaRouter.h" +#include "Swiften/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 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/Swiften/Server/UserRegistry.cpp b/Swiften/Server/UserRegistry.cpp new file mode 100644 index 0000000..d1a509f --- /dev/null +++ b/Swiften/Server/UserRegistry.cpp @@ -0,0 +1,8 @@ +#include "Swiften/Server/UserRegistry.h" + +namespace Swift { + +UserRegistry::~UserRegistry() { +} + +} diff --git a/Swiften/Server/UserRegistry.h b/Swiften/Server/UserRegistry.h new file mode 100644 index 0000000..5ced019 --- /dev/null +++ b/Swiften/Server/UserRegistry.h @@ -0,0 +1,13 @@ +#pragma once + +namespace Swift { + class String; + class JID; + + class UserRegistry { + public: + virtual ~UserRegistry(); + + virtual bool isValidUserPassword(const JID& user, const String& password) const = 0; + }; +} |