summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Slimber/Server.cpp')
-rw-r--r--Slimber/Server.cpp427
1 files changed, 427 insertions, 0 deletions
diff --git a/Slimber/Server.cpp b/Slimber/Server.cpp
new file mode 100644
index 0000000..278a572
--- /dev/null
+++ b/Slimber/Server.cpp
@@ -0,0 +1,427 @@
+#include "Slimber/Server.h"
+
+#include <string>
+#include <boost/bind.hpp>
+
+#include "Swiften/LinkLocal/LinkLocalConnector.h"
+#include "Swiften/Network/Connection.h"
+#include "Swiften/Session/SessionTracer.h"
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/RosterPayload.h"
+#include "Swiften/Network/BoostConnection.h"
+#include "Swiften/Network/BoostConnectionServer.h"
+#include "Swiften/Session/SessionTracer.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/VCard.h"
+#include "Swiften/Server/UserRegistry.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Swiften/LinkLocal/OutgoingLinkLocalSession.h"
+#include "Swiften/LinkLocal/IncomingLinkLocalSession.h"
+#include "Swiften/LinkLocal/LinkLocalServiceBrowser.h"
+#include "Swiften/Network/ConnectionServer.h"
+#include "Slimber/VCardCollection.h"
+#include "Slimber/LinkLocalPresenceManager.h"
+#include "Swiften/Server/ServerFromClientSession.h"
+
+namespace Swift {
+
+Server::Server(
+ int clientConnectionPort,
+ int linkLocalConnectionPort,
+ LinkLocalServiceBrowser* linkLocalServiceBrowser,
+ VCardCollection* vCardCollection) :
+ linkLocalServiceRegistered(false),
+ rosterRequested(false),
+ clientConnectionPort(clientConnectionPort),
+ linkLocalConnectionPort(linkLocalConnectionPort),
+ linkLocalServiceBrowser(linkLocalServiceBrowser),
+ vCardCollection(vCardCollection),
+ presenceManager(NULL),
+ stopping(false) {
+ linkLocalServiceBrowser->onServiceRegistered.connect(
+ boost::bind(&Server::handleServiceRegistered, this, _1));
+}
+
+Server::~Server() {
+ stop();
+}
+
+void Server::start() {
+ assert(!serverFromClientConnectionServer);
+ serverFromClientConnectionServer =
+ boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer(
+ clientConnectionPort, &boostIOServiceThread.getIOService()));
+ serverFromClientConnectionServerSignalConnections.push_back(
+ serverFromClientConnectionServer->onNewConnection.connect(
+ boost::bind(&Server::handleNewClientConnection, this, _1)));
+ serverFromClientConnectionServerSignalConnections.push_back(
+ serverFromClientConnectionServer->onStopped.connect(
+ boost::bind(&Server::handleClientConnectionServerStopped, this, _1)));
+
+ assert(!serverFromNetworkConnectionServer);
+ serverFromNetworkConnectionServer =
+ boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer(
+ linkLocalConnectionPort, &boostIOServiceThread.getIOService()));
+ serverFromNetworkConnectionServerSignalConnections.push_back(
+ serverFromNetworkConnectionServer->onNewConnection.connect(
+ boost::bind(&Server::handleNewLinkLocalConnection, this, _1)));
+ serverFromNetworkConnectionServerSignalConnections.push_back(
+ serverFromNetworkConnectionServer->onStopped.connect(
+ boost::bind(&Server::handleLinkLocalConnectionServerStopped, this, _1)));
+
+ assert(!presenceManager);
+ presenceManager = new LinkLocalPresenceManager(linkLocalServiceBrowser);
+ presenceManager->onRosterChanged.connect(
+ boost::bind(&Server::handleRosterChanged, this, _1));
+ presenceManager->onPresenceChanged.connect(
+ boost::bind(&Server::handlePresenceChanged, this, _1));
+
+ serverFromClientConnectionServer->start();
+ serverFromNetworkConnectionServer->start();
+}
+
+void Server::stop() {
+ stop(boost::optional<ServerError>());
+}
+
+void Server::stop(boost::optional<ServerError> e) {
+ if (stopping) {
+ return;
+ }
+
+ stopping = true;
+
+ delete presenceManager;
+ presenceManager = NULL;
+
+ if (serverFromClientSession) {
+ serverFromClientSession->finishSession();
+ }
+ serverFromClientSession.reset();
+ foreach(boost::shared_ptr<Session> session, linkLocalSessions) {
+ session->finishSession();
+ }
+ linkLocalSessions.clear();
+ foreach(boost::shared_ptr<LinkLocalConnector> connector, connectors) {
+ connector->cancel();
+ }
+ connectors.clear();
+ tracers.clear();
+
+ if (serverFromNetworkConnectionServer) {
+ serverFromNetworkConnectionServer->stop();
+ foreach(boost::bsignals::connection& connection, serverFromNetworkConnectionServerSignalConnections) {
+ connection.disconnect();
+ }
+ serverFromNetworkConnectionServerSignalConnections.clear();
+ serverFromNetworkConnectionServer.reset();
+ }
+ if (serverFromClientConnectionServer) {
+ serverFromClientConnectionServer->stop();
+ foreach(boost::bsignals::connection& connection, serverFromClientConnectionServerSignalConnections) {
+ connection.disconnect();
+ }
+ serverFromClientConnectionServerSignalConnections.clear();
+ serverFromClientConnectionServer.reset();
+ }
+
+ stopping = false;
+ onStopped(e);
+}
+
+void Server::handleNewClientConnection(boost::shared_ptr<Connection> connection) {
+ if (serverFromClientSession) {
+ connection->disconnect();
+ }
+ serverFromClientSession = boost::shared_ptr<ServerFromClientSession>(
+ new ServerFromClientSession(idGenerator.generateID(), connection,
+ &payloadParserFactories, &payloadSerializers, &userRegistry));
+ serverFromClientSession->onSessionStarted.connect(
+ boost::bind(&Server::handleSessionStarted, this));
+ serverFromClientSession->onElementReceived.connect(
+ boost::bind(&Server::handleElementReceived, this, _1,
+ serverFromClientSession));
+ serverFromClientSession->onSessionFinished.connect(
+ boost::bind(&Server::handleSessionFinished, this,
+ serverFromClientSession));
+ //tracers.push_back(boost::shared_ptr<SessionTracer>(
+ // new SessionTracer(serverFromClientSession)));
+ serverFromClientSession->startSession();
+}
+
+void Server::handleSessionStarted() {
+ onSelfConnected(true);
+}
+
+void Server::handleSessionFinished(boost::shared_ptr<ServerFromClientSession>) {
+ serverFromClientSession.reset();
+ unregisterService();
+ selfJID = JID();
+ rosterRequested = false;
+ onSelfConnected(false);
+ lastPresence.reset();
+}
+
+void Server::unregisterService() {
+ if (linkLocalServiceRegistered) {
+ linkLocalServiceRegistered = false;
+ linkLocalServiceBrowser->unregisterService();
+ }
+}
+
+void Server::handleElementReceived(boost::shared_ptr<Element> element, boost::shared_ptr<ServerFromClientSession> session) {
+ boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element);
+ if (!stanza) {
+ return;
+ }
+
+ stanza->setFrom(session->getRemoteJID());
+ if (!stanza->getTo().isValid()) {
+ stanza->setTo(session->getLocalJID());
+ }
+
+ if (boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(stanza)) {
+ if (presence->getType() == Presence::Available) {
+ if (!linkLocalServiceRegistered) {
+ linkLocalServiceRegistered = true;
+ linkLocalServiceBrowser->registerService(
+ session->getRemoteJID().toBare().toString(),
+ linkLocalConnectionPort, getLinkLocalServiceInfo(presence));
+ }
+ else {
+ linkLocalServiceBrowser->updateService(
+ getLinkLocalServiceInfo(presence));
+ }
+ lastPresence = presence;
+ }
+ else {
+ unregisterService();
+ }
+ }
+ else if (!stanza->getTo().isValid() || stanza->getTo() == session->getLocalJID() || stanza->getTo() == session->getRemoteJID().toBare()) {
+ if (boost::shared_ptr<IQ> iq = boost::dynamic_pointer_cast<IQ>(stanza)) {
+ if (iq->getPayload<RosterPayload>()) {
+ if (iq->getType() == IQ::Get) {
+ session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), presenceManager->getRoster()));
+ rosterRequested = true;
+ foreach(const boost::shared_ptr<Presence> presence, presenceManager->getAllPresence()) {
+ session->sendElement(presence);
+ }
+ }
+ else {
+ session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::Forbidden, ErrorPayload::Cancel));
+ }
+ }
+ if (boost::shared_ptr<VCard> vcard = iq->getPayload<VCard>()) {
+ if (iq->getType() == IQ::Get) {
+ session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), vCardCollection->getOwnVCard()));
+ }
+ else {
+ vCardCollection->setOwnVCard(vcard);
+ session->sendElement(IQ::createResult(iq->getFrom(), iq->getID()));
+ if (lastPresence) {
+ linkLocalServiceBrowser->updateService(getLinkLocalServiceInfo(lastPresence));
+ }
+ }
+ }
+ else {
+ session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::FeatureNotImplemented, ErrorPayload::Cancel));
+ }
+ }
+ }
+ else {
+ JID toJID = stanza->getTo();
+ boost::shared_ptr<Session> outgoingSession =
+ getLinkLocalSessionForJID(toJID);
+ if (outgoingSession) {
+ outgoingSession->sendElement(stanza);
+ }
+ else {
+ boost::optional<LinkLocalService> service =
+ presenceManager->getServiceForJID(toJID);
+ if (service) {
+ boost::shared_ptr<LinkLocalConnector> connector =
+ getLinkLocalConnectorForJID(toJID);
+ if (!connector) {
+ connector = boost::shared_ptr<LinkLocalConnector>(
+ new LinkLocalConnector(
+ *service,
+ linkLocalServiceBrowser->getQuerier(),
+ boost::shared_ptr<BoostConnection>(new BoostConnection(&boostIOServiceThread.getIOService()))));
+ connector->onConnectFinished.connect(
+ boost::bind(&Server::handleConnectFinished, this, connector, _1));
+ connectors.push_back(connector);
+ connector->connect();
+ }
+ connector->queueElement(element);
+ }
+ else {
+ session->sendElement(IQ::createError(
+ stanza->getFrom(), stanza->getID(),
+ ErrorPayload::RecipientUnavailable, ErrorPayload::Wait));
+ }
+ }
+ }
+}
+
+void Server::handleNewLinkLocalConnection(boost::shared_ptr<Connection> connection) {
+ boost::shared_ptr<IncomingLinkLocalSession> session(
+ new IncomingLinkLocalSession(
+ selfJID, connection,
+ &payloadParserFactories, &payloadSerializers));
+ registerLinkLocalSession(session);
+}
+
+void Server::handleLinkLocalSessionFinished(boost::shared_ptr<Session> session) {
+ //std::cout << "Link local session from " << session->getRemoteJID() << " ended" << std::endl;
+ linkLocalSessions.erase(
+ std::remove(linkLocalSessions.begin(), linkLocalSessions.end(), session),
+ linkLocalSessions.end());
+}
+
+void Server::handleLinkLocalElementReceived(boost::shared_ptr<Element> element, boost::shared_ptr<Session> session) {
+ if (boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element)) {
+ JID fromJID = session->getRemoteJID();
+ if (!presenceManager->getServiceForJID(fromJID.toBare())) {
+ return; // TODO: Send error back
+ }
+ stanza->setFrom(fromJID);
+ serverFromClientSession->sendElement(stanza);
+ }
+}
+
+void Server::handleConnectFinished(boost::shared_ptr<LinkLocalConnector> connector, bool error) {
+ if (error) {
+ std::cerr << "Error connecting" << std::endl;
+ // TODO: Send back queued stanzas
+ }
+ else {
+ boost::shared_ptr<OutgoingLinkLocalSession> outgoingSession(
+ new OutgoingLinkLocalSession(
+ selfJID, connector->getService().getJID(), connector->getConnection(),
+ &payloadParserFactories, &payloadSerializers));
+ foreach(const boost::shared_ptr<Element> element, connector->getQueuedElements()) {
+ outgoingSession->queueElement(element);
+ }
+ registerLinkLocalSession(outgoingSession);
+ }
+ connectors.erase(std::remove(connectors.begin(), connectors.end(), connector), connectors.end());
+}
+
+void Server::registerLinkLocalSession(boost::shared_ptr<Session> session) {
+ session->onSessionFinished.connect(
+ boost::bind(&Server::handleLinkLocalSessionFinished, this, session));
+ session->onElementReceived.connect(
+ boost::bind(&Server::handleLinkLocalElementReceived, this, _1, session));
+ linkLocalSessions.push_back(session);
+ //tracers.push_back(boost::shared_ptr<SessionTracer>(new SessionTracer(session)));
+ session->startSession();
+}
+
+boost::shared_ptr<Session> Server::getLinkLocalSessionForJID(const JID& jid) {
+ foreach(const boost::shared_ptr<Session> session, linkLocalSessions) {
+ if (session->getRemoteJID() == jid) {
+ return session;
+ }
+ }
+ return boost::shared_ptr<Session>();
+}
+
+boost::shared_ptr<LinkLocalConnector> Server::getLinkLocalConnectorForJID(const JID& jid) {
+ foreach(const boost::shared_ptr<LinkLocalConnector> connector, connectors) {
+ if (connector->getService().getJID() == jid) {
+ return connector;
+ }
+ }
+ return boost::shared_ptr<LinkLocalConnector>();
+}
+
+void Server::handleServiceRegistered(const DNSSDServiceID& service) {
+ selfJID = JID(service.getName());
+}
+
+void Server::handleRosterChanged(boost::shared_ptr<RosterPayload> roster) {
+ if (rosterRequested) {
+ assert(serverFromClientSession);
+ boost::shared_ptr<IQ> iq = IQ::createRequest(
+ IQ::Set, serverFromClientSession->getRemoteJID(),
+ idGenerator.generateID(), roster);
+ iq->setFrom(serverFromClientSession->getRemoteJID().toBare());
+ serverFromClientSession->sendElement(iq);
+ }
+}
+
+void Server::handlePresenceChanged(boost::shared_ptr<Presence> presence) {
+ if (rosterRequested) {
+ serverFromClientSession->sendElement(presence);
+ }
+}
+
+void Server::handleClientConnectionServerStopped(boost::optional<BoostConnectionServer::Error> e) {
+ if (e) {
+ if (*e == BoostConnectionServer::Conflict) {
+ stop(ServerError(ServerError::C2SPortConflict));
+ }
+ else {
+ stop(ServerError(ServerError::C2SError));
+ }
+ }
+ else {
+ stop();
+ }
+}
+
+void Server::handleLinkLocalConnectionServerStopped(boost::optional<BoostConnectionServer::Error> e) {
+ if (e) {
+ if (*e == BoostConnectionServer::Conflict) {
+ stop(ServerError(ServerError::LinkLocalPortConflict));
+ }
+ else {
+ stop(ServerError(ServerError::LinkLocalError));
+ }
+ }
+ else {
+ stop();
+ }
+}
+
+LinkLocalServiceInfo Server::getLinkLocalServiceInfo(boost::shared_ptr<Presence> presence) {
+ LinkLocalServiceInfo info;
+ boost::shared_ptr<VCard> vcard = vCardCollection->getOwnVCard();
+ if (!vcard->getFamilyName().isEmpty() || !vcard->getGivenName().isEmpty()) {
+ info.setFirstName(vcard->getGivenName());
+ info.setLastName(vcard->getFamilyName());
+ }
+ else if (!vcard->getFullName().isEmpty()) {
+ std::pair<String,String> p = vcard->getFullName().getSplittedAtFirst(' ');
+ info.setFirstName(p.first);
+ info.setLastName(p.second);
+ }
+ if (!vcard->getNickname().isEmpty()) {
+ info.setNick(vcard->getNickname());
+ }
+ if (!vcard->getEMail().isEmpty()) {
+ info.setEMail(vcard->getEMail());
+ }
+ info.setMessage(presence->getStatus());
+ switch (presence->getShow()) {
+ case StatusShow::Online:
+ case StatusShow::None:
+ case StatusShow::FFC:
+ info.setStatus(LinkLocalServiceInfo::Available);
+ break;
+ case StatusShow::Away:
+ case StatusShow::XA:
+ info.setStatus(LinkLocalServiceInfo::Away);
+ break;
+ case StatusShow::DND:
+ info.setStatus(LinkLocalServiceInfo::DND);
+ break;
+ }
+ info.setPort(linkLocalConnectionPort);
+ return info;
+}
+
+}