From 8f73a49aeabc9c3acb64b69e1c428a8341538865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Fri, 15 Oct 2010 22:14:50 +0200 Subject: Added CoreComponent. diff --git a/Swiften/Component/ComponentError.h b/Swiften/Component/ComponentError.h new file mode 100644 index 0000000..928af2a --- /dev/null +++ b/Swiften/Component/ComponentError.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +namespace Swift { + class ComponentError { + public: + enum Type { + UnknownError, + ConnectionError, + ConnectionReadError, + ConnectionWriteError, + XMLError, + AuthenticationFailedError, + UnexpectedElementError, + }; + + ComponentError(Type type = UnknownError) : type_(type) {} + + Type getType() const { return type_; } + + private: + Type type_; + }; +} diff --git a/Swiften/Component/ComponentSessionStanzaChannel.cpp b/Swiften/Component/ComponentSessionStanzaChannel.cpp new file mode 100644 index 0000000..9f4dc2e --- /dev/null +++ b/Swiften/Component/ComponentSessionStanzaChannel.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Component/ComponentSessionStanzaChannel.h" + +#include + +namespace Swift { + +void ComponentSessionStanzaChannel::setSession(boost::shared_ptr session) { + assert(!this->session); + this->session = session; + session->onInitialized.connect(boost::bind(&ComponentSessionStanzaChannel::handleSessionInitialized, this)); + session->onFinished.connect(boost::bind(&ComponentSessionStanzaChannel::handleSessionFinished, this, _1)); + session->onStanzaReceived.connect(boost::bind(&ComponentSessionStanzaChannel::handleStanza, this, _1)); +} + +void ComponentSessionStanzaChannel::sendIQ(boost::shared_ptr iq) { + send(iq); +} + +void ComponentSessionStanzaChannel::sendMessage(boost::shared_ptr message) { + send(message); +} + +void ComponentSessionStanzaChannel::sendPresence(boost::shared_ptr presence) { + send(presence); +} + +String ComponentSessionStanzaChannel::getNewIQID() { + return idGenerator.generateID(); +} + +void ComponentSessionStanzaChannel::send(boost::shared_ptr stanza) { + if (!isAvailable()) { + std::cerr << "Warning: Component: Trying to send a stanza while disconnected." << std::endl; + return; + } + session->sendStanza(stanza); +} + +void ComponentSessionStanzaChannel::handleSessionFinished(boost::shared_ptr) { + session->onFinished.disconnect(boost::bind(&ComponentSessionStanzaChannel::handleSessionFinished, this, _1)); + session->onStanzaReceived.disconnect(boost::bind(&ComponentSessionStanzaChannel::handleStanza, this, _1)); + session->onInitialized.disconnect(boost::bind(&ComponentSessionStanzaChannel::handleSessionInitialized, this)); + session.reset(); + + onAvailableChanged(false); +} + +void ComponentSessionStanzaChannel::handleStanza(boost::shared_ptr stanza) { + boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); + if (message) { + onMessageReceived(message); + return; + } + + boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); + if (presence) { + onPresenceReceived(presence); + return; + } + + boost::shared_ptr iq = boost::dynamic_pointer_cast(stanza); + if (iq) { + onIQReceived(iq); + return; + } +} + +void ComponentSessionStanzaChannel::handleSessionInitialized() { + onAvailableChanged(true); +} + +} diff --git a/Swiften/Component/ComponentSessionStanzaChannel.h b/Swiften/Component/ComponentSessionStanzaChannel.h new file mode 100644 index 0000000..856031f --- /dev/null +++ b/Swiften/Component/ComponentSessionStanzaChannel.h @@ -0,0 +1,50 @@ +/* + * 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 + +#include "Swiften/Base/IDGenerator.h" +#include "Swiften/Component/ComponentSession.h" +#include "Swiften/Client/StanzaChannel.h" +#include "Swiften/Elements/Message.h" +#include "Swiften/Elements/IQ.h" +#include "Swiften/Elements/Presence.h" + +namespace Swift { + /** + * StanzaChannel implementation around a ComponentSession. + */ + class ComponentSessionStanzaChannel : public StanzaChannel { + public: + void setSession(boost::shared_ptr session); + + void sendIQ(boost::shared_ptr iq); + void sendMessage(boost::shared_ptr message); + void sendPresence(boost::shared_ptr presence); + + bool getStreamManagementEnabled() const { + return false; + } + + bool isAvailable() const { + return session && session->getState() == ComponentSession::Initialized; + } + + private: + String getNewIQID(); + void send(boost::shared_ptr stanza); + void handleSessionFinished(boost::shared_ptr error); + void handleStanza(boost::shared_ptr stanza); + void handleSessionInitialized(); + + private: + IDGenerator idGenerator; + boost::shared_ptr session; + }; + +} diff --git a/Swiften/Component/CoreComponent.cpp b/Swiften/Component/CoreComponent.cpp new file mode 100644 index 0000000..2f0752a --- /dev/null +++ b/Swiften/Component/CoreComponent.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Component/CoreComponent.h" + +#include + +#include "Swiften/Network/MainBoostIOServiceThread.h" +#include "Swiften/Network/BoostIOServiceThread.h" +#include "Swiften/Component/ComponentSession.h" +#include "Swiften/StreamStack/NullTLSLayerFactory.h" +#include "Swiften/Network/Connector.h" +#include "Swiften/Network/BoostConnectionFactory.h" +#include "Swiften/Network/BoostTimerFactory.h" +#include "Swiften/TLS/PKCS12Certificate.h" +#include "Swiften/Session/BasicSessionStream.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Base/IDGenerator.h" +#include "Swiften/Component/ComponentSessionStanzaChannel.h" + +namespace Swift { + +CoreComponent::CoreComponent(const JID& jid, const String& secret) : jid_(jid), secret_(secret), disconnectRequested_(false) { + stanzaChannel_ = new ComponentSessionStanzaChannel(); + stanzaChannel_->onMessageReceived.connect(boost::ref(onMessageReceived)); + stanzaChannel_->onPresenceReceived.connect(boost::ref(onPresenceReceived)); + stanzaChannel_->onAvailableChanged.connect(boost::bind(&CoreComponent::handleStanzaChannelAvailableChanged, this, _1)); + + iqRouter_ = new IQRouter(stanzaChannel_); + connectionFactory_ = new BoostConnectionFactory(&MainBoostIOServiceThread::getInstance().getIOService()); + timerFactory_ = new BoostTimerFactory(&MainBoostIOServiceThread::getInstance().getIOService()); + tlsLayerFactory_ = new NullTLSLayerFactory(); +} + +CoreComponent::~CoreComponent() { + if (session_ || connection_) { + std::cerr << "Warning: Component not disconnected properly" << std::endl; + } + delete tlsLayerFactory_; + delete timerFactory_; + delete connectionFactory_; + delete iqRouter_; + + stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&CoreComponent::handleStanzaChannelAvailableChanged, this, _1)); + stanzaChannel_->onMessageReceived.disconnect(boost::ref(onMessageReceived)); + stanzaChannel_->onPresenceReceived.disconnect(boost::ref(onPresenceReceived)); + delete stanzaChannel_; +} + +void CoreComponent::connect(const String& host, int port) { + assert(!connector_); + connector_ = ComponentConnector::create(host, port, &resolver_, connectionFactory_, timerFactory_); + connector_->onConnectFinished.connect(boost::bind(&CoreComponent::handleConnectorFinished, this, _1)); + connector_->setTimeoutMilliseconds(60*1000); + connector_->start(); +} + +void CoreComponent::handleConnectorFinished(boost::shared_ptr connection) { + connector_->onConnectFinished.disconnect(boost::bind(&CoreComponent::handleConnectorFinished, this, _1)); + connector_.reset(); + if (!connection) { + if (!disconnectRequested_) { + onError(ComponentError::ConnectionError); + } + } + else { + assert(!connection_); + connection_ = connection; + + assert(!sessionStream_); + sessionStream_ = boost::shared_ptr(new BasicSessionStream(ComponentStreamType, connection_, &payloadParserFactories_, &payloadSerializers_, tlsLayerFactory_, timerFactory_)); + sessionStream_->onDataRead.connect(boost::bind(&CoreComponent::handleDataRead, this, _1)); + sessionStream_->onDataWritten.connect(boost::bind(&CoreComponent::handleDataWritten, this, _1)); + sessionStream_->initialize(); + + session_ = ComponentSession::create(jid_, secret_, sessionStream_); + stanzaChannel_->setSession(session_); + session_->onFinished.connect(boost::bind(&CoreComponent::handleSessionFinished, this, _1)); + session_->start(); + } +} + +void CoreComponent::disconnect() { + // FIXME: We should be able to do without this boolean. We just have to make sure we can tell the difference between + // connector finishing without a connection due to an error or because of a disconnect. + disconnectRequested_ = true; + if (session_) { + session_->finish(); + } + else if (connector_) { + connector_->stop(); + assert(!session_); + } + assert(!session_); + assert(!sessionStream_); + assert(!connector_); + disconnectRequested_ = false; +} + +void CoreComponent::handleSessionFinished(boost::shared_ptr error) { + session_->onFinished.disconnect(boost::bind(&CoreComponent::handleSessionFinished, this, _1)); + session_.reset(); + + sessionStream_->onDataRead.disconnect(boost::bind(&CoreComponent::handleDataRead, this, _1)); + sessionStream_->onDataWritten.disconnect(boost::bind(&CoreComponent::handleDataWritten, this, _1)); + sessionStream_.reset(); + + connection_->disconnect(); + connection_.reset(); + + if (error) { + ComponentError componentError; + if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { + switch(actualError->type) { + case ComponentSession::Error::AuthenticationFailedError: + componentError = ComponentError(ComponentError::AuthenticationFailedError); + break; + case ComponentSession::Error::UnexpectedElementError: + componentError = ComponentError(ComponentError::UnexpectedElementError); + break; + } + } + else if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { + switch(actualError->type) { + case SessionStream::Error::ParseError: + componentError = ComponentError(ComponentError::XMLError); + break; + case SessionStream::Error::TLSError: + assert(false); + componentError = ComponentError(ComponentError::UnknownError); + break; + case SessionStream::Error::InvalidTLSCertificateError: + assert(false); + componentError = ComponentError(ComponentError::UnknownError); + break; + case SessionStream::Error::ConnectionReadError: + componentError = ComponentError(ComponentError::ConnectionReadError); + break; + case SessionStream::Error::ConnectionWriteError: + componentError = ComponentError(ComponentError::ConnectionWriteError); + break; + } + } + onError(componentError); + } +} + +void CoreComponent::handleDataRead(const String& data) { + onDataRead(data); +} + +void CoreComponent::handleDataWritten(const String& data) { + onDataWritten(data); +} + +void CoreComponent::handleStanzaChannelAvailableChanged(bool available) { + if (available) { + onConnected(); + } +} + +void CoreComponent::sendMessage(boost::shared_ptr message) { + stanzaChannel_->sendMessage(message); +} + +void CoreComponent::sendPresence(boost::shared_ptr presence) { + stanzaChannel_->sendPresence(presence); +} + +} diff --git a/Swiften/Component/CoreComponent.h b/Swiften/Component/CoreComponent.h new file mode 100644 index 0000000..21f339e --- /dev/null +++ b/Swiften/Component/CoreComponent.h @@ -0,0 +1,107 @@ +/* + * 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 + +#include "Swiften/Base/boost_bsignals.h" +#include "Swiften/Base/Error.h" +#include "Swiften/Network/PlatformDomainNameResolver.h" +#include "Swiften/Component/ComponentConnector.h" +#include "Swiften/Component/ComponentSession.h" +#include "Swiften/Component/ComponentError.h" +#include "Swiften/Elements/Presence.h" +#include "Swiften/Elements/Message.h" +#include "Swiften/JID/JID.h" +#include "Swiften/Base/String.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" +#include "Swiften/Base/Shared.h" +#include "Swiften/Component/ComponentSessionStanzaChannel.h" + +namespace Swift { + class IQRouter; + class TLSLayerFactory; + class ConnectionFactory; + class TimerFactory; + class ComponentSession; + class BasicSessionStream; + + /** + * The central class for communicating with an XMPP server as a component. + * + * This class is responsible for setting up the connection with the XMPP + * server and authenticating the component. + * + * This class can be used directly in your application, although the Component + * subclass provides more functionality and interfaces, and is better suited + * for most needs. + */ + class CoreComponent { + public: + CoreComponent(const JID& jid, const String& secret); + ~CoreComponent(); + + void connect(const String& host, int port); + void disconnect(); + + void sendMessage(boost::shared_ptr); + void sendPresence(boost::shared_ptr); + + IQRouter* getIQRouter() const { + return iqRouter_; + } + + StanzaChannel* getStanzaChannel() const { + return stanzaChannel_; + } + + bool isAvailable() const { + return stanzaChannel_->isAvailable(); + } + + /** + * Returns the JID of the component + */ + const JID& getJID() const { + return jid_; + } + + public: + boost::signal onError; + boost::signal onConnected; + boost::signal onDataRead; + boost::signal onDataWritten; + + boost::signal)> onMessageReceived; + boost::signal) > onPresenceReceived; + + private: + void handleConnectorFinished(boost::shared_ptr); + void handleStanzaChannelAvailableChanged(bool available); + void handleSessionFinished(boost::shared_ptr); + void handleDataRead(const String&); + void handleDataWritten(const String&); + + private: + PlatformDomainNameResolver resolver_; + JID jid_; + String secret_; + ComponentSessionStanzaChannel* stanzaChannel_; + IQRouter* iqRouter_; + ComponentConnector::ref connector_; + ConnectionFactory* connectionFactory_; + TimerFactory* timerFactory_; + TLSLayerFactory* tlsLayerFactory_; + FullPayloadParserFactoryCollection payloadParserFactories_; + FullPayloadSerializerCollection payloadSerializers_; + boost::shared_ptr connection_; + boost::shared_ptr sessionStream_; + boost::shared_ptr session_; + bool disconnectRequested_; + }; +} diff --git a/Swiften/Component/SConscript b/Swiften/Component/SConscript index 1f08301..4b3ed92 100644 --- a/Swiften/Component/SConscript +++ b/Swiften/Component/SConscript @@ -4,6 +4,8 @@ sources = [ "ComponentHandshakeGenerator.cpp", "ComponentConnector.cpp", "ComponentSession.cpp", + "ComponentSessionStanzaChannel.cpp", + "CoreComponent.cpp", ] swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.StaticObject(sources)) diff --git a/Swiften/StreamStack/NullTLSLayerFactory.h b/Swiften/StreamStack/NullTLSLayerFactory.h new file mode 100644 index 0000000..5ca6d86 --- /dev/null +++ b/Swiften/StreamStack/NullTLSLayerFactory.h @@ -0,0 +1,22 @@ +/* + * 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 "Swiften/StreamStack/TLSLayerFactory.h" + +namespace Swift { + class NullTLSLayerFactory : public TLSLayerFactory { + public: + bool canCreate() const { + return false; + } + + virtual boost::shared_ptr createTLSLayer() { + return boost::shared_ptr(); + } + }; +} -- cgit v0.10.2-6-g49f6