From 8f73a49aeabc9c3acb64b69e1c428a8341538865 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
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 <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>();
+			}
+	};
+}
-- 
cgit v0.10.2-6-g49f6