diff options
Diffstat (limited to 'Swiften')
| -rw-r--r-- | Swiften/Component/ComponentError.h | 29 | ||||
| -rw-r--r-- | Swiften/Component/ComponentSessionStanzaChannel.cpp | 78 | ||||
| -rw-r--r-- | Swiften/Component/ComponentSessionStanzaChannel.h | 50 | ||||
| -rw-r--r-- | Swiften/Component/CoreComponent.cpp | 173 | ||||
| -rw-r--r-- | Swiften/Component/CoreComponent.h | 107 | ||||
| -rw-r--r-- | Swiften/Component/SConscript | 2 | ||||
| -rw-r--r-- | Swiften/StreamStack/NullTLSLayerFactory.h | 22 | 
7 files changed, 461 insertions, 0 deletions
| 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 <boost/bind.hpp> + +namespace Swift { + +void ComponentSessionStanzaChannel::setSession(boost::shared_ptr<ComponentSession> 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> iq) { +	send(iq); +} + +void ComponentSessionStanzaChannel::sendMessage(boost::shared_ptr<Message> message) { +	send(message); +} + +void ComponentSessionStanzaChannel::sendPresence(boost::shared_ptr<Presence> presence) { +	send(presence); +} + +String ComponentSessionStanzaChannel::getNewIQID() { +	return idGenerator.generateID(); +} + +void ComponentSessionStanzaChannel::send(boost::shared_ptr<Stanza> 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<Error>) { +	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> stanza) { +	boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(stanza); +	if (message) { +		onMessageReceived(message); +		return; +	} + +	boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(stanza); +	if (presence) { +		onPresenceReceived(presence); +		return; +	} + +	boost::shared_ptr<IQ> iq = boost::dynamic_pointer_cast<IQ>(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 <boost/shared_ptr.hpp> + +#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<ComponentSession> session); + +			void sendIQ(boost::shared_ptr<IQ> iq); +			void sendMessage(boost::shared_ptr<Message> message); +			void sendPresence(boost::shared_ptr<Presence> presence); + +			bool getStreamManagementEnabled() const { +				return false; +			} + +			bool isAvailable() const { +				return session && session->getState() == ComponentSession::Initialized; +			} + +		private: +			String getNewIQID(); +			void send(boost::shared_ptr<Stanza> stanza); +			void handleSessionFinished(boost::shared_ptr<Error> error); +			void handleStanza(boost::shared_ptr<Stanza> stanza); +			void handleSessionInitialized(); + +		private: +			IDGenerator idGenerator; +			boost::shared_ptr<ComponentSession> 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 <boost/bind.hpp> + +#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> 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<BasicSessionStream>(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> 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<ComponentSession::Error> actualError = boost::dynamic_pointer_cast<ComponentSession::Error>(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<SessionStream::Error> actualError = boost::dynamic_pointer_cast<SessionStream::Error>(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> message) { +	stanzaChannel_->sendMessage(message); +} + +void CoreComponent::sendPresence(boost::shared_ptr<Presence> 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 <boost/shared_ptr.hpp> + +#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<Message>); +			void sendPresence(boost::shared_ptr<Presence>); + +			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<void (const ComponentError&)> onError; +			boost::signal<void ()> onConnected; +			boost::signal<void (const String&)> onDataRead; +			boost::signal<void (const String&)> onDataWritten; + +			boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived; +			boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived; + +		private: +			void handleConnectorFinished(boost::shared_ptr<Connection>); +			void handleStanzaChannelAvailableChanged(bool available); +			void handleSessionFinished(boost::shared_ptr<Error>); +			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> connection_; +			boost::shared_ptr<BasicSessionStream> sessionStream_; +			boost::shared_ptr<ComponentSession> 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<TLSLayer> createTLSLayer() { +				return boost::shared_ptr<TLSLayer>(); +			} +	}; +} | 
 Swift
 Swift