diff options
author | Remko Tronçon <git@el-tramo.be> | 2009-06-01 08:48:42 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2009-06-01 09:24:28 (GMT) |
commit | 2812bddd81f8a1b804c7460f4e14cd0aa393d129 (patch) | |
tree | d46294f35150c4f0f43deaf2d31fceaf945ae715 /Swiften/Client/Session.cpp | |
download | swift-2812bddd81f8a1b804c7460f4e14cd0aa393d129.zip swift-2812bddd81f8a1b804c7460f4e14cd0aa393d129.tar.bz2 |
Import.
Diffstat (limited to 'Swiften/Client/Session.cpp')
-rw-r--r-- | Swiften/Client/Session.cpp | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/Swiften/Client/Session.cpp b/Swiften/Client/Session.cpp new file mode 100644 index 0000000..aa3cc62 --- /dev/null +++ b/Swiften/Client/Session.cpp @@ -0,0 +1,292 @@ +#include "Swiften/Client/Session.h" + +#include <boost/bind.hpp> + +#include "Swiften/Network/ConnectionFactory.h" +#include "Swiften/StreamStack/StreamStack.h" +#include "Swiften/StreamStack/ConnectionLayer.h" +#include "Swiften/StreamStack/XMPPLayer.h" +#include "Swiften/StreamStack/TLSLayer.h" +#include "Swiften/StreamStack/TLSLayerFactory.h" +#include "Swiften/Elements/StreamFeatures.h" +#include "Swiften/Elements/StartTLSRequest.h" +#include "Swiften/Elements/StartTLSFailure.h" +#include "Swiften/Elements/TLSProceed.h" +#include "Swiften/Elements/AuthRequest.h" +#include "Swiften/Elements/AuthSuccess.h" +#include "Swiften/Elements/AuthFailure.h" +#include "Swiften/Elements/StartSession.h" +#include "Swiften/Elements/IQ.h" +#include "Swiften/Elements/ResourceBind.h" +#include "Swiften/SASL/PLAINMessage.h" +#include "Swiften/StreamStack/WhitespacePingLayer.h" + +namespace Swift { + +Session::Session(const JID& jid, ConnectionFactory* connectionFactory, TLSLayerFactory* tlsLayerFactory, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers) : + jid_(jid), + connectionFactory_(connectionFactory), + tlsLayerFactory_(tlsLayerFactory), + payloadParserFactories_(payloadParserFactories), + payloadSerializers_(payloadSerializers), + state_(Initial), + error_(NoError), + connection_(0), + xmppLayer_(0), + tlsLayer_(0), + connectionLayer_(0), + whitespacePingLayer_(0), + streamStack_(0), + needSessionStart_(false) { +} + +Session::~Session() { + delete streamStack_; + delete whitespacePingLayer_; + delete connectionLayer_; + delete tlsLayer_; + delete xmppLayer_; + delete connection_; +} + +void Session::start() { + assert(state_ == Initial); + state_ = Connecting; + connection_ = connectionFactory_->createConnection(jid_.getDomain()); + connection_->onConnected.connect(boost::bind(&Session::handleConnected, this)); + connection_->onError.connect(boost::bind(&Session::handleConnectionError, this, _1)); + connection_->connect(); +} + +void Session::stop() { + // TODO: Send end stream header if applicable + connection_->disconnect(); +} + +void Session::handleConnected() { + assert(state_ == Connecting); + initializeStreamStack(); + state_ = WaitingForStreamStart; + sendStreamHeader(); +} + +void Session::sendStreamHeader() { + xmppLayer_->writeHeader(jid_.getDomain()); +} + +void Session::initializeStreamStack() { + xmppLayer_ = new XMPPLayer(payloadParserFactories_, payloadSerializers_); + xmppLayer_->onStreamStart.connect(boost::bind(&Session::handleStreamStart, this)); + xmppLayer_->onElement.connect(boost::bind(&Session::handleElement, this, _1)); + xmppLayer_->onError.connect(boost::bind(&Session::setError, this, XMLError)); + xmppLayer_->onDataRead.connect(boost::bind(boost::ref(onDataRead), _1)); + xmppLayer_->onWriteData.connect(boost::bind(boost::ref(onDataWritten), _1)); + connectionLayer_ = new ConnectionLayer(connection_); + streamStack_ = new StreamStack(xmppLayer_, connectionLayer_); +} + +void Session::handleConnectionError(Connection::Error error) { + switch (error) { + case Connection::DomainNameResolveError: + setError(DomainNameResolveError); + break; + case Connection::ReadError: + setError(ConnectionReadError); + break; + case Connection::ConnectionError: + setError(ConnectionError); + break; + } +} + +void Session::setCertificate(const PKCS12Certificate& certificate) { + certificate_ = certificate; +} + +void Session::handleStreamStart() { + checkState(WaitingForStreamStart); + state_ = Negotiating; +} + +void Session::handleElement(boost::shared_ptr<Element> element) { + if (getState() == SessionStarted) { + onElementReceived(element); + } + else { + StreamFeatures* streamFeatures = dynamic_cast<StreamFeatures*>(element.get()); + if (streamFeatures) { + if (!checkState(Negotiating)) { + return; + } + + if (streamFeatures->hasStartTLS() && tlsLayerFactory_->canCreate()) { + state_ = Encrypting; + xmppLayer_->writeElement(boost::shared_ptr<StartTLSRequest>(new StartTLSRequest())); + } + else if (streamFeatures->hasAuthenticationMechanisms()) { + if (!certificate_.isNull()) { + if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { + state_ = Authenticating; + xmppLayer_->writeElement(boost::shared_ptr<Element>(new AuthRequest("EXTERNAL", ""))); + } + else { + setError(ClientCertificateError); + } + } + else if (streamFeatures->hasAuthenticationMechanism("PLAIN")) { + state_ = WaitingForCredentials; + onNeedCredentials(); + } + else { + setError(NoSupportedAuthMechanismsError); + } + } + else { + // Start the session + + // Add a whitespace ping layer + whitespacePingLayer_ = new WhitespacePingLayer(); + streamStack_->addLayer(whitespacePingLayer_); + + if (streamFeatures->hasSession()) { + needSessionStart_ = true; + } + + if (streamFeatures->hasResourceBind()) { + state_ = BindingResource; + boost::shared_ptr<ResourceBind> resourceBind(new ResourceBind()); + if (!jid_.getResource().isEmpty()) { + resourceBind->setResource(jid_.getResource()); + } + xmppLayer_->writeElement(IQ::createRequest(IQ::Set, JID(), "session-bind", resourceBind)); + } + else if (needSessionStart_) { + sendSessionStart(); + } + else { + state_ = SessionStarted; + onSessionStarted(); + } + } + } + else { + AuthSuccess* authSuccess = dynamic_cast<AuthSuccess*>(element.get()); + if (authSuccess) { + checkState(Authenticating); + state_ = WaitingForStreamStart; + xmppLayer_->resetParser(); + sendStreamHeader(); + } + else if (dynamic_cast<AuthFailure*>(element.get())) { + setError(AuthenticationFailedError); + } + else if (dynamic_cast<TLSProceed*>(element.get())) { + tlsLayer_ = tlsLayerFactory_->createTLSLayer(); + streamStack_->addLayer(tlsLayer_); + if (!certificate_.isNull() && !tlsLayer_->setClientCertificate(certificate_)) { + setError(ClientCertificateLoadError); + } + else { + tlsLayer_->onConnected.connect(boost::bind(&Session::handleTLSConnected, this)); + tlsLayer_->onError.connect(boost::bind(&Session::handleTLSError, this)); + tlsLayer_->connect(); + } + } + else if (dynamic_cast<StartTLSFailure*>(element.get())) { + setError(TLSError); + } + else { + IQ* iq = dynamic_cast<IQ*>(element.get()); + if (iq) { + if (state_ == BindingResource) { + boost::shared_ptr<ResourceBind> resourceBind(iq->getPayload<ResourceBind>()); + if (iq->getType() == IQ::Error && iq->getID() == "session-bind") { + setError(ResourceBindError); + } + else if (!resourceBind) { + setError(UnexpectedElementError); + } + else if (iq->getType() == IQ::Result) { + jid_ = resourceBind->getJID(); + if (!jid_.isValid()) { + setError(ResourceBindError); + } + if (needSessionStart_) { + sendSessionStart(); + } + else { + state_ = SessionStarted; + } + } + else { + setError(UnexpectedElementError); + } + } + else if (state_ == StartingSession) { + if (iq->getType() == IQ::Result) { + state_ = SessionStarted; + onSessionStarted(); + } + else if (iq->getType() == IQ::Error) { + setError(SessionStartError); + } + else { + setError(UnexpectedElementError); + } + } + else { + setError(UnexpectedElementError); + } + } + else { + // FIXME Not correct? + state_ = SessionStarted; + onSessionStarted(); + } + } + } + } +} + +void Session::sendSessionStart() { + state_ = StartingSession; + xmppLayer_->writeElement(IQ::createRequest(IQ::Set, JID(), "session-start", boost::shared_ptr<StartSession>(new StartSession()))); +} + +void Session::setError(SessionError error) { + assert(error != NoError); + state_ = Error; + error_ = error; + onError(error); +} + +bool Session::checkState(State state) { + if (state_ != state) { + setError(UnexpectedElementError); + return false; + } + return true; +} + +void Session::sendCredentials(const String& password) { + assert(WaitingForCredentials); + state_ = Authenticating; + xmppLayer_->writeElement(boost::shared_ptr<Element>(new AuthRequest("PLAIN", PLAINMessage(jid_.getNode(), password).getValue()))); +} + +void Session::sendElement(boost::shared_ptr<Element> element) { + assert(SessionStarted); + xmppLayer_->writeElement(element); +} + +void Session::handleTLSConnected() { + state_ = WaitingForStreamStart; + xmppLayer_->resetParser(); + sendStreamHeader(); +} + +void Session::handleTLSError() { + setError(TLSError); +} + +} |