/* * Copyright (c) 2010 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "Swiften/Client/Client.h" #include #include "Swiften/Network/MainBoostIOServiceThread.h" #include "Swiften/Network/BoostIOServiceThread.h" #include "Swiften/Client/ClientSession.h" #include "Swiften/StreamStack/PlatformTLSLayerFactory.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/Client/ClientSessionStanzaChannel.h" namespace Swift { Client::Client(const JID& jid, const String& password) : jid_(jid), password_(password), disconnectRequested_(false) { stanzaChannel_ = new ClientSessionStanzaChannel(); stanzaChannel_->onMessageReceived.connect(boost::ref(onMessageReceived)); stanzaChannel_->onPresenceReceived.connect(boost::ref(onPresenceReceived)); stanzaChannel_->onStanzaAcked.connect(boost::ref(onStanzaAcked)); stanzaChannel_->onAvailableChanged.connect(boost::bind(&Client::handleStanzaChannelAvailableChanged, this, _1)); iqRouter_ = new IQRouter(stanzaChannel_); connectionFactory_ = new BoostConnectionFactory(&MainBoostIOServiceThread::getInstance().getIOService()); timerFactory_ = new BoostTimerFactory(&MainBoostIOServiceThread::getInstance().getIOService()); tlsLayerFactory_ = new PlatformTLSLayerFactory(); } Client::~Client() { if (session_ || connection_) { std::cerr << "Warning: Client not disconnected properly" << std::endl; } delete tlsLayerFactory_; delete timerFactory_; delete connectionFactory_; delete iqRouter_; stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&Client::handleStanzaChannelAvailableChanged, this, _1)); stanzaChannel_->onMessageReceived.disconnect(boost::ref(onMessageReceived)); stanzaChannel_->onPresenceReceived.disconnect(boost::ref(onPresenceReceived)); stanzaChannel_->onStanzaAcked.disconnect(boost::ref(onStanzaAcked)); delete stanzaChannel_; } void Client::connect() { connect(jid_.getDomain()); } void Client::connect(const JID& jid) { jid_ = jid; connect(); } void Client::connect(const String& host) { assert(!connector_); connector_ = Connector::create(host, &resolver_, connectionFactory_, timerFactory_); connector_->onConnectFinished.connect(boost::bind(&Client::handleConnectorFinished, this, _1)); connector_->setTimeoutMilliseconds(60*1000); connector_->start(); } void Client::handleConnectorFinished(boost::shared_ptr connection) { connector_->onConnectFinished.disconnect(boost::bind(&Client::handleConnectorFinished, this, _1)); connector_.reset(); if (!connection) { if (!disconnectRequested_) { onError(ClientError::ConnectionError); } } else { assert(!connection_); connection_ = connection; assert(!sessionStream_); sessionStream_ = boost::shared_ptr(new BasicSessionStream(connection_, &payloadParserFactories_, &payloadSerializers_, tlsLayerFactory_, timerFactory_)); if (!certificate_.isEmpty()) { sessionStream_->setTLSCertificate(PKCS12Certificate(certificate_, password_)); } sessionStream_->onDataRead.connect(boost::bind(&Client::handleDataRead, this, _1)); sessionStream_->onDataWritten.connect(boost::bind(&Client::handleDataWritten, this, _1)); sessionStream_->initialize(); session_ = ClientSession::create(jid_, sessionStream_); stanzaChannel_->setSession(session_); session_->onFinished.connect(boost::bind(&Client::handleSessionFinished, this, _1)); session_->onNeedCredentials.connect(boost::bind(&Client::handleNeedCredentials, this)); session_->start(); } } void Client::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 Client::setCertificate(const String& certificate) { certificate_ = certificate; } void Client::handleSessionFinished(boost::shared_ptr error) { session_->onFinished.disconnect(boost::bind(&Client::handleSessionFinished, this, _1)); session_->onNeedCredentials.disconnect(boost::bind(&Client::handleNeedCredentials, this)); session_.reset(); sessionStream_->onDataRead.disconnect(boost::bind(&Client::handleDataRead, this, _1)); sessionStream_->onDataWritten.disconnect(boost::bind(&Client::handleDataWritten, this, _1)); sessionStream_.reset(); connection_->disconnect(); connection_.reset(); if (error) { ClientError clientError; if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { switch(actualError->type) { case ClientSession::Error::AuthenticationFailedError: clientError = ClientError(ClientError::AuthenticationFailedError); break; case ClientSession::Error::CompressionFailedError: clientError = ClientError(ClientError::CompressionFailedError); break; case ClientSession::Error::ServerVerificationFailedError: clientError = ClientError(ClientError::ServerVerificationFailedError); break; case ClientSession::Error::NoSupportedAuthMechanismsError: clientError = ClientError(ClientError::NoSupportedAuthMechanismsError); break; case ClientSession::Error::UnexpectedElementError: clientError = ClientError(ClientError::UnexpectedElementError); break; case ClientSession::Error::ResourceBindError: clientError = ClientError(ClientError::ResourceBindError); break; case ClientSession::Error::SessionStartError: clientError = ClientError(ClientError::SessionStartError); break; case ClientSession::Error::TLSError: clientError = ClientError(ClientError::TLSError); break; case ClientSession::Error::TLSClientCertificateError: clientError = ClientError(ClientError::ClientCertificateError); break; } } else if (boost::shared_ptr actualError = boost::dynamic_pointer_cast(error)) { switch(actualError->type) { case SessionStream::Error::ParseError: clientError = ClientError(ClientError::XMLError); break; case SessionStream::Error::TLSError: clientError = ClientError(ClientError::TLSError); break; case SessionStream::Error::InvalidTLSCertificateError: clientError = ClientError(ClientError::ClientCertificateLoadError); break; case SessionStream::Error::ConnectionReadError: clientError = ClientError(ClientError::ConnectionReadError); break; case SessionStream::Error::ConnectionWriteError: clientError = ClientError(ClientError::ConnectionWriteError); break; } } onError(clientError); } } void Client::handleNeedCredentials() { assert(session_); session_->sendCredentials(password_); } void Client::handleDataRead(const String& data) { onDataRead(data); } void Client::handleDataWritten(const String& data) { onDataWritten(data); } void Client::handleStanzaChannelAvailableChanged(bool available) { if (available) { onConnected(); } } void Client::sendMessage(boost::shared_ptr message) { stanzaChannel_->sendMessage(message); } void Client::sendPresence(boost::shared_ptr presence) { stanzaChannel_->sendPresence(presence); } }