summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swiften/Component/ComponentError.h29
-rw-r--r--Swiften/Component/ComponentSessionStanzaChannel.cpp78
-rw-r--r--Swiften/Component/ComponentSessionStanzaChannel.h50
-rw-r--r--Swiften/Component/CoreComponent.cpp173
-rw-r--r--Swiften/Component/CoreComponent.h107
-rw-r--r--Swiften/Component/SConscript2
-rw-r--r--Swiften/StreamStack/NullTLSLayerFactory.h22
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>();
+ }
+ };
+}