/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Slimber/Server.h" #include #include #include "Swiften/Base/String.h" #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 #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, EventLoop* eventLoop) : linkLocalServiceRegistered(false), rosterRequested(false), clientConnectionPort(clientConnectionPort), linkLocalConnectionPort(linkLocalConnectionPort), linkLocalServiceBrowser(linkLocalServiceBrowser), vCardCollection(vCardCollection), eventLoop(eventLoop), presenceManager(NULL), stopping(false) { linkLocalServiceBrowser->onServiceRegistered.connect( boost::bind(&Server::handleServiceRegistered, this, _1)); } Server::~Server() { stop(); } void Server::start() { assert(!serverFromClientConnectionServer); serverFromClientConnectionServer = BoostConnectionServer::create( clientConnectionPort, boostIOServiceThread.getIOService(), eventLoop); 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 = BoostConnectionServer::create( linkLocalConnectionPort, boostIOServiceThread.getIOService(), eventLoop); 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()); } void Server::stop(boost::optional e) { if (stopping) { return; } stopping = true; delete presenceManager; presenceManager = NULL; if (serverFromClientSession) { serverFromClientSession->finishSession(); } serverFromClientSession.reset(); foreach(boost::shared_ptr session, linkLocalSessions) { session->finishSession(); } linkLocalSessions.clear(); foreach(boost::shared_ptr 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) { if (serverFromClientSession) { connection->disconnect(); } serverFromClientSession = boost::shared_ptr( new ServerFromClientSession(idGenerator.generateID(), connection, &payloadParserFactories, &payloadSerializers, &userRegistry)); serverFromClientSession->setAllowSASLEXTERNAL(); 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( // new SessionTracer(serverFromClientSession))); serverFromClientSession->startSession(); } void Server::handleSessionStarted() { onSelfConnected(true); } void Server::handleSessionFinished(boost::shared_ptr) { 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, boost::shared_ptr session) { boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); if (!stanza) { return; } stanza->setFrom(session->getRemoteJID()); if (!stanza->getTo().isValid()) { stanza->setTo(session->getLocalJID()); } if (boost::shared_ptr presence = boost::dynamic_pointer_cast(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 = boost::dynamic_pointer_cast(stanza)) { if (iq->getPayload()) { if (iq->getType() == IQ::Get) { session->sendElement(IQ::createResult(iq->getFrom(), iq->getID(), presenceManager->getRoster())); rosterRequested = true; foreach(const boost::shared_ptr presence, presenceManager->getAllPresence()) { session->sendElement(presence); } } else { session->sendElement(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::Forbidden, ErrorPayload::Cancel)); } } if (boost::shared_ptr vcard = iq->getPayload()) { 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 outgoingSession = getLinkLocalSessionForJID(toJID); if (outgoingSession) { outgoingSession->sendElement(stanza); } else { boost::optional service = presenceManager->getServiceForJID(toJID); if (service) { boost::shared_ptr connector = getLinkLocalConnectorForJID(toJID); if (!connector) { connector = boost::shared_ptr( new LinkLocalConnector( *service, linkLocalServiceBrowser->getQuerier(), BoostConnection::create(boostIOServiceThread.getIOService(), eventLoop))); 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) { boost::shared_ptr session( new IncomingLinkLocalSession( selfJID, connection, &payloadParserFactories, &payloadSerializers)); registerLinkLocalSession(session); } void Server::handleLinkLocalSessionFinished(boost::shared_ptr 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, boost::shared_ptr session) { if (boost::shared_ptr stanza = boost::dynamic_pointer_cast(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 connector, bool error) { if (error) { std::cerr << "Error connecting" << std::endl; // TODO: Send back queued stanzas } else { boost::shared_ptr outgoingSession( new OutgoingLinkLocalSession( selfJID, connector->getService().getJID(), connector->getConnection(), &payloadParserFactories, &payloadSerializers)); foreach(const boost::shared_ptr 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->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(new SessionTracer(session))); session->startSession(); } boost::shared_ptr Server::getLinkLocalSessionForJID(const JID& jid) { foreach(const boost::shared_ptr session, linkLocalSessions) { if (session->getRemoteJID() == jid) { return session; } } return boost::shared_ptr(); } boost::shared_ptr Server::getLinkLocalConnectorForJID(const JID& jid) { foreach(const boost::shared_ptr connector, connectors) { if (connector->getService().getJID() == jid) { return connector; } } return boost::shared_ptr(); } void Server::handleServiceRegistered(const DNSSDServiceID& service) { selfJID = JID(service.getName()); } void Server::handleRosterChanged(boost::shared_ptr roster) { if (rosterRequested) { assert(serverFromClientSession); boost::shared_ptr 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) { if (rosterRequested) { serverFromClientSession->sendElement(presence); } } void Server::handleClientConnectionServerStopped(boost::optional e) { if (e) { if (*e == BoostConnectionServer::Conflict) { stop(ServerError(ServerError::C2SPortConflict)); } else { stop(ServerError(ServerError::C2SError)); } } else { stop(); } } void Server::handleLinkLocalConnectionServerStopped(boost::optional 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) { LinkLocalServiceInfo info; boost::shared_ptr vcard = vCardCollection->getOwnVCard(); if (!vcard->getFamilyName().empty() || !vcard->getGivenName().empty()) { info.setFirstName(vcard->getGivenName()); info.setLastName(vcard->getFamilyName()); } else if (!vcard->getFullName().empty()) { std::pair p = String::getSplittedAtFirst(vcard->getFullName(), ' '); info.setFirstName(p.first); info.setLastName(p.second); } if (!vcard->getNickname().empty()) { info.setNick(vcard->getNickname()); } if (!vcard->getPreferredEMailAddress().address.empty()) { info.setEMail(vcard->getPreferredEMailAddress().address); } 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; } }