summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swiften/Client/CoreClient.cpp57
-rw-r--r--Swiften/Client/CoreClient.h4
-rw-r--r--Swiften/QA/ClientTest/ClientTest.cpp34
3 files changed, 62 insertions, 33 deletions
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index dbc6de2..22e3612 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -9,189 +9,174 @@
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <Swiften/Base/IDGenerator.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/foreach.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Client/ClientSession.h>
#include <Swiften/TLS/PlatformTLSFactories.h>
#include <Swiften/TLS/CertificateVerificationError.h>
#include <Swiften/Network/ChainedConnector.h>
#include <Swiften/Network/NetworkFactories.h>
#include <Swiften/TLS/PKCS12Certificate.h>
#include <Swiften/Session/BasicSessionStream.h>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Client/ClientSessionStanzaChannel.h>
#include <Swiften/Network/PlatformProxyProvider.h>
#include <Swiften/Network/SOCKS5ProxiedConnectionFactory.h>
#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h>
namespace Swift {
CoreClient::CoreClient(const JID& jid, const SafeByteArray& password, NetworkFactories* networkFactories) : jid_(jid), password_(password), networkFactories(networkFactories), disconnectRequested_(false), certificateTrustChecker(NULL) {
stanzaChannel_ = new ClientSessionStanzaChannel();
stanzaChannel_->onMessageReceived.connect(boost::bind(&CoreClient::handleMessageReceived, this, _1));
stanzaChannel_->onPresenceReceived.connect(boost::bind(&CoreClient::handlePresenceReceived, this, _1));
stanzaChannel_->onStanzaAcked.connect(boost::bind(&CoreClient::handleStanzaAcked, this, _1));
stanzaChannel_->onAvailableChanged.connect(boost::bind(&CoreClient::handleStanzaChannelAvailableChanged, this, _1));
iqRouter_ = new IQRouter(stanzaChannel_);
iqRouter_->setJID(jid);
tlsFactories = new PlatformTLSFactories();
}
CoreClient::~CoreClient() {
- if ((session_ && !session_->isFinished()) || connection_) {
- std::cerr << "Warning: Client not disconnected properly" << std::endl;
- }
+ forceReset();
delete tlsFactories;
delete iqRouter_;
stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&CoreClient::handleStanzaChannelAvailableChanged, this, _1));
stanzaChannel_->onMessageReceived.disconnect(boost::bind(&CoreClient::handleMessageReceived, this, _1));
stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&CoreClient::handlePresenceReceived, this, _1));
stanzaChannel_->onStanzaAcked.disconnect(boost::bind(&CoreClient::handleStanzaAcked, this, _1));
delete stanzaChannel_;
}
void CoreClient::connect(const ClientOptions& o) {
SWIFT_LOG(debug) << "Connecting" << std::endl;
options = o;
connect(jid_.getDomain());
}
void CoreClient::connect(const std::string& host) {
+ forceReset();
+
SWIFT_LOG(debug) << "Connecting to host " << host << std::endl;
disconnectRequested_ = false;
assert(!connector_);
-
assert(proxyConnectionFactories.empty());
PlatformProxyProvider proxyProvider;
if(proxyProvider.getSOCKS5Proxy().isValid()) {
proxyConnectionFactories.push_back(new SOCKS5ProxiedConnectionFactory(networkFactories->getConnectionFactory(), proxyProvider.getSOCKS5Proxy()));
}
if(proxyProvider.getHTTPConnectProxy().isValid()) {
proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getConnectionFactory(), proxyProvider.getHTTPConnectProxy()));
}
std::vector<ConnectionFactory*> connectionFactories(proxyConnectionFactories);
connectionFactories.push_back(networkFactories->getConnectionFactory());
connector_ = boost::make_shared<ChainedConnector>(host, networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory());
connector_->onConnectFinished.connect(boost::bind(&CoreClient::handleConnectorFinished, this, _1));
connector_->setTimeoutMilliseconds(60*1000);
connector_->start();
}
void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connection) {
- connector_->onConnectFinished.disconnect(boost::bind(&CoreClient::handleConnectorFinished, this, _1));
- connector_.reset();
- foreach(ConnectionFactory* f, proxyConnectionFactories) {
- delete f;
- }
- proxyConnectionFactories.clear();
-
+ resetConnector();
if (!connection) {
if (options.forgetPassword) {
purgePassword();
}
onDisconnected(disconnectRequested_ ? boost::optional<ClientError>() : boost::optional<ClientError>(ClientError::ConnectionError));
}
else {
assert(!connection_);
connection_ = connection;
assert(!sessionStream_);
sessionStream_ = boost::make_shared<BasicSessionStream>(ClientStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), tlsFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory());
if (!certificate_.empty()) {
sessionStream_->setTLSCertificate(PKCS12Certificate(certificate_, password_));
}
sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1));
sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1));
session_ = ClientSession::create(jid_, sessionStream_);
session_->setCertificateTrustChecker(certificateTrustChecker);
session_->setUseStreamCompression(options.useStreamCompression);
session_->setAllowPLAINOverNonTLS(options.allowPLAINWithoutTLS);
switch(options.useTLS) {
case ClientOptions::UseTLSWhenAvailable:
session_->setUseTLS(ClientSession::UseTLSWhenAvailable);
break;
case ClientOptions::NeverUseTLS:
session_->setUseTLS(ClientSession::NeverUseTLS);
break;
case ClientOptions::RequireTLS:
session_->setUseTLS(ClientSession::RequireTLS);
break;
}
session_->setUseAcks(options.useAcks);
stanzaChannel_->setSession(session_);
session_->onFinished.connect(boost::bind(&CoreClient::handleSessionFinished, this, _1));
session_->onNeedCredentials.connect(boost::bind(&CoreClient::handleNeedCredentials, this));
session_->start();
}
}
void CoreClient::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_->isFinished()) {
session_->finish();
}
else if (connector_) {
connector_->stop();
}
}
void CoreClient::setCertificate(const std::string& certificate) {
certificate_ = certificate;
}
void CoreClient::handleSessionFinished(boost::shared_ptr<Error> error) {
if (options.forgetPassword) {
purgePassword();
}
- session_->onFinished.disconnect(boost::bind(&CoreClient::handleSessionFinished, this, _1));
- session_->onNeedCredentials.disconnect(boost::bind(&CoreClient::handleNeedCredentials, this));
-
- sessionStream_->onDataRead.disconnect(boost::bind(&CoreClient::handleDataRead, this, _1));
- sessionStream_->onDataWritten.disconnect(boost::bind(&CoreClient::handleDataWritten, this, _1));
- sessionStream_.reset();
-
- connection_->disconnect();
- connection_.reset();
+ resetSession();
boost::optional<ClientError> actualError;
if (error) {
ClientError clientError;
if (boost::shared_ptr<ClientSession::Error> actualError = boost::dynamic_pointer_cast<ClientSession::Error>(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;
case ClientSession::Error::StreamError:
clientError = ClientError(ClientError::StreamError);
@@ -309,36 +294,68 @@ void CoreClient::handlePresenceReceived(Presence::ref presence) {
}
void CoreClient::handleMessageReceived(Message::ref message) {
onMessageReceived(message);
}
void CoreClient::handleStanzaAcked(Stanza::ref stanza) {
onStanzaAcked(stanza);
}
bool CoreClient::isAvailable() const {
return stanzaChannel_->isAvailable();
}
bool CoreClient::getStreamManagementEnabled() const {
return stanzaChannel_->getStreamManagementEnabled();
}
StanzaChannel* CoreClient::getStanzaChannel() const {
return stanzaChannel_;
}
const JID& CoreClient::getJID() const {
if (session_) {
return session_->getLocalJID();
}
else {
return jid_;
}
}
void CoreClient::purgePassword() {
safeClear(password_);
}
+void CoreClient::resetConnector() {
+ connector_->onConnectFinished.disconnect(boost::bind(&CoreClient::handleConnectorFinished, this, _1));
+ connector_.reset();
+ foreach(ConnectionFactory* f, proxyConnectionFactories) {
+ delete f;
+ }
+ proxyConnectionFactories.clear();
+}
+
+void CoreClient::resetSession() {
+ session_->onFinished.disconnect(boost::bind(&CoreClient::handleSessionFinished, this, _1));
+ session_->onNeedCredentials.disconnect(boost::bind(&CoreClient::handleNeedCredentials, this));
+
+ sessionStream_->onDataRead.disconnect(boost::bind(&CoreClient::handleDataRead, this, _1));
+ sessionStream_->onDataWritten.disconnect(boost::bind(&CoreClient::handleDataWritten, this, _1));
+ sessionStream_.reset();
+
+ connection_->disconnect();
+ connection_.reset();
+}
+
+void CoreClient::forceReset() {
+ if (connector_) {
+ std::cerr << "Warning: Client not disconnected properly: Connector still active" << std::endl;
+ resetConnector();
+ }
+ if (sessionStream_ || connection_) {
+ std::cerr << "Warning: Client not disconnected properly: Session still active" << std::endl;
+ resetSession();
+ }
+}
+
}
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index 3472e76..ad31e72 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -177,53 +177,57 @@ namespace Swift {
/**
* Emitted when the server acknowledges receipt of a
* stanza (if acknowledgements are available).
*
* \see getStreamManagementEnabled()
*/
boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked;
protected:
boost::shared_ptr<ClientSession> getSession() const {
return session_;
}
NetworkFactories* getNetworkFactories() const {
return networkFactories;
}
/**
* Called before onConnected signal is emmitted.
*/
virtual void handleConnected() {};
private:
void handleConnectorFinished(boost::shared_ptr<Connection>);
void handleStanzaChannelAvailableChanged(bool available);
void handleSessionFinished(boost::shared_ptr<Error>);
void handleNeedCredentials();
void handleDataRead(const SafeByteArray&);
void handleDataWritten(const SafeByteArray&);
void handlePresenceReceived(boost::shared_ptr<Presence>);
void handleMessageReceived(boost::shared_ptr<Message>);
void handleStanzaAcked(boost::shared_ptr<Stanza>);
void purgePassword();
+ void resetConnector();
+ void resetSession();
+ void forceReset();
+
private:
JID jid_;
SafeByteArray password_;
NetworkFactories* networkFactories;
ClientSessionStanzaChannel* stanzaChannel_;
IQRouter* iqRouter_;
ClientOptions options;
boost::shared_ptr<ChainedConnector> connector_;
std::vector<ConnectionFactory*> proxyConnectionFactories;
PlatformTLSFactories* tlsFactories;
boost::shared_ptr<Connection> connection_;
boost::shared_ptr<BasicSessionStream> sessionStream_;
boost::shared_ptr<ClientSession> session_;
std::string certificate_;
bool disconnectRequested_;
CertificateTrustChecker* certificateTrustChecker;
};
}
diff --git a/Swiften/QA/ClientTest/ClientTest.cpp b/Swiften/QA/ClientTest/ClientTest.cpp
index 584f644..4515893 100644
--- a/Swiften/QA/ClientTest/ClientTest.cpp
+++ b/Swiften/QA/ClientTest/ClientTest.cpp
@@ -1,80 +1,88 @@
/*
* Copyright (c) 2010 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <Swiften/Client/Client.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Network/BoostNetworkFactories.h>
#include <Swiften/EventLoop/EventLoop.h>
#include <Swiften/EventLoop/SimpleEventLoop.h>
#include <Swiften/Roster/GetRosterRequest.h>
#include <Swiften/Client/ClientXMLTracer.h>
using namespace Swift;
SimpleEventLoop eventLoop;
BoostNetworkFactories networkFactories(&eventLoop);
Client* client = 0;
-bool reconnected = false;
bool rosterReceived = false;
+enum TestStage {
+ FirstConnect,
+ Reconnect
+};
+TestStage stage;
-void handleDisconnected(boost::optional<ClientError>) {
- eventLoop.stop();
-}
-
-void handleRosterReceived(boost::shared_ptr<Payload>) {
- if (reconnected) {
- rosterReceived = true;
- client->onDisconnected.connect(boost::bind(&handleDisconnected, _1));
- client->disconnect();
+void handleDisconnected(boost::optional<ClientError> e) {
+ std::cout << "Disconnected: " << e << std::endl;
+ if (stage == FirstConnect) {
+ stage = Reconnect;
+ client->connect();
}
else {
- reconnected = true;
- client->disconnect();
- client->connect();
+ eventLoop.stop();
}
}
+void handleRosterReceived(boost::shared_ptr<Payload>) {
+ rosterReceived = true;
+ std::cout << "Disconnecting" << std::endl;
+ client->disconnect();
+}
+
void handleConnected() {
+ std::cout << "Connected" << std::endl;
+ rosterReceived = false;
GetRosterRequest::ref rosterRequest = GetRosterRequest::create(client->getIQRouter());
rosterRequest->onResponse.connect(boost::bind(&handleRosterReceived, _1));
rosterRequest->send();
}
int main(int, char**) {
char* jid = getenv("SWIFT_CLIENTTEST_JID");
if (!jid) {
std::cerr << "Please set the SWIFT_CLIENTTEST_JID environment variable" << std::endl;
return -1;
}
char* pass = getenv("SWIFT_CLIENTTEST_PASS");
if (!pass) {
std::cerr << "Please set the SWIFT_CLIENTTEST_PASS environment variable" << std::endl;
return -1;
}
client = new Swift::Client(JID(jid), std::string(pass), &networkFactories);
ClientXMLTracer* tracer = new ClientXMLTracer(client);
client->onConnected.connect(&handleConnected);
+ client->onDisconnected.connect(boost::bind(&handleDisconnected, _1));
client->setAlwaysTrustCertificates();
+ stage = FirstConnect;
client->connect();
{
Timer::ref timer = networkFactories.getTimerFactory()->createTimer(60000);
timer->onTick.connect(boost::bind(&SimpleEventLoop::stop, &eventLoop));
timer->start();
eventLoop.run();
}
delete tracer;
delete client;
return !rosterReceived;
}