summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Client')
-rw-r--r--Swiften/Client/ClientOptions.h28
-rw-r--r--Swiften/Client/ClientSession.cpp2
-rw-r--r--Swiften/Client/ClientXMLTracer.cpp17
-rw-r--r--Swiften/Client/ClientXMLTracer.h3
-rw-r--r--Swiften/Client/CoreClient.cpp82
-rw-r--r--Swiften/Client/CoreClient.h5
-rw-r--r--Swiften/Client/UnitTest/ClientSessionTest.cpp4
7 files changed, 105 insertions, 36 deletions
diff --git a/Swiften/Client/ClientOptions.h b/Swiften/Client/ClientOptions.h
index 3b51a87..06bf947 100644
--- a/Swiften/Client/ClientOptions.h
+++ b/Swiften/Client/ClientOptions.h
@@ -1,26 +1,29 @@
/*
* Copyright (c) 2011 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
+#include <Swiften/Base/URL.h>
+#include <Swiften/Base/SafeString.h>
+
namespace Swift {
struct ClientOptions {
enum UseTLS {
NeverUseTLS,
UseTLSWhenAvailable,
RequireTLS
};
- ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), allowPLAINWithoutTLS(false), useStreamResumption(false), forgetPassword(false), useAcks(true) {
+ ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), allowPLAINWithoutTLS(false), useStreamResumption(false), forgetPassword(false), useAcks(true), boshHTTPConnectProxyAuthID(""), boshHTTPConnectProxyAuthPassword("") {
}
/**
* Whether ZLib stream compression should be used when available.
*
* Default: true
*/
bool useStreamCompression;
@@ -55,11 +58,34 @@ namespace Swift {
* Default: false
*/
bool forgetPassword;
/**
* Use XEP-0198 acks in the stream when available.
* Default: true
*/
bool useAcks;
+
+ /**
+ * If non-empty, use BOSH instead of direct TCP, with the given URL.
+ * The host currently needs to be specified by IP, rather than hostname.
+ * Default: empty (no BOSH)
+ */
+ URL boshURL;
+
+ /**
+ * If non-empty, BOSH connections will try to connect over this HTTP CONNECT
+ * proxy instead of directly.
+ * Must be specified by IP, rather than hostname.
+ * Default: empty (no proxy)
+ */
+ URL boshHTTPConnectProxyURL;
+
+ /**
+ * If this and matching Password are non-empty, BOSH connections over
+ * HTTP CONNECT proxies will use these credentials for proxy access.
+ * Default: empty (no authentication needed by the proxy)
+ */
+ SafeString boshHTTPConnectProxyAuthID;
+ SafeString boshHTTPConnectProxyAuthPassword;
};
}
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp
index 55e0bc2..bfc9313 100644
--- a/Swiften/Client/ClientSession.cpp
+++ b/Swiften/Client/ClientSession.cpp
@@ -175,19 +175,19 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) {
}
if (streamFeatures->hasStartTLS() && stream->supportsTLSEncryption() && useTLS != NeverUseTLS) {
state = WaitingForEncrypt;
stream->writeElement(boost::make_shared<StartTLSRequest>());
}
else if (useTLS == RequireTLS && !stream->isTLSEncrypted()) {
finishSession(Error::NoSupportedAuthMechanismsError);
}
- else if (useStreamCompression && streamFeatures->hasCompressionMethod("zlib")) {
+ else if (useStreamCompression && stream->supportsZLibCompression() && streamFeatures->hasCompressionMethod("zlib")) {
state = Compressing;
stream->writeElement(boost::make_shared<CompressRequest>("zlib"));
}
else if (streamFeatures->hasAuthenticationMechanisms()) {
if (stream->hasTLSCertificate()) {
if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) {
state = Authenticating;
stream->writeElement(boost::make_shared<AuthRequest>("EXTERNAL", createSafeByteArray("")));
}
diff --git a/Swiften/Client/ClientXMLTracer.cpp b/Swiften/Client/ClientXMLTracer.cpp
index c1093eb..405e3d1 100644
--- a/Swiften/Client/ClientXMLTracer.cpp
+++ b/Swiften/Client/ClientXMLTracer.cpp
@@ -5,31 +5,44 @@
*/
#include <Swiften/Client/ClientXMLTracer.h>
#include <iostream>
#include <boost/bind.hpp>
namespace Swift {
-ClientXMLTracer::ClientXMLTracer(CoreClient* client) {
+ClientXMLTracer::ClientXMLTracer(CoreClient* client, bool bosh) : bosh(bosh) {
beautifier = new XMLBeautifier(true, true);
client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, this, '<', _1));
client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, this, '>', _1));
}
ClientXMLTracer::~ClientXMLTracer() {
delete beautifier;
}
void ClientXMLTracer::printData(char direction, const SafeByteArray& data) {
printLine(direction);
- std::cerr << beautifier->beautify(byteArrayToString(ByteArray(data.begin(), data.end()))) << std::endl;
+ if (bosh) {
+ std::string line = byteArrayToString(ByteArray(data.begin(), data.end()));
+ size_t endOfHTTP = line.find("\r\n\r\n");
+ if (false && endOfHTTP != std::string::npos) {
+ /* Disabled because it swallows bits of XML (namespaces, if I recall) */
+ std::cerr << line.substr(0, endOfHTTP) << std::endl << beautifier->beautify(line.substr(endOfHTTP)) << std::endl;
+ }
+ else {
+ std::cerr << line << std::endl;
+ }
+ }
+ else {
+ std::cerr << beautifier->beautify(byteArrayToString(ByteArray(data.begin(), data.end()))) << std::endl;
+ }
}
void ClientXMLTracer::printLine(char c) {
for (unsigned int i = 0; i < 80; ++i) {
std::cerr << c;
}
std::cerr << std::endl;
}
diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h
index 0752faa..67040c4 100644
--- a/Swiften/Client/ClientXMLTracer.h
+++ b/Swiften/Client/ClientXMLTracer.h
@@ -7,19 +7,20 @@
#pragma once
#include <Swiften/Client/CoreClient.h>
#include <Swiften/Client/XMLBeautifier.h>
#include <Swiften/Base/SafeByteArray.h>
namespace Swift {
class ClientXMLTracer {
public:
- ClientXMLTracer(CoreClient* client);
+ ClientXMLTracer(CoreClient* client, bool bosh = false);
~ClientXMLTracer();
private:
void printData(char direction, const SafeByteArray& data);
void printLine(char c);
private:
XMLBeautifier *beautifier;
+ bool bosh;
};
}
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index 08f31a0..cef2b24 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -1,11 +1,11 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010-2011 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swiften/Client/CoreClient.h>
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
@@ -14,18 +14,19 @@
#include <Swiften/Base/foreach.h>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Client/ClientSession.h>
#include <Swiften/TLS/CertificateVerificationError.h>
#include <Swiften/Network/ChainedConnector.h>
#include <Swiften/Network/NetworkFactories.h>
#include <Swiften/Network/ProxyProvider.h>
#include <Swiften/TLS/PKCS12Certificate.h>
#include <Swiften/Session/BasicSessionStream.h>
+#include <Swiften/Session/BOSHSessionStream.h>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Client/ClientSessionStanzaChannel.h>
#include <Swiften/Network/SOCKS5ProxiedConnectionFactory.h>
#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h>
#include <Swiften/Network/BOSHConnectionFactory.h>
namespace Swift {
CoreClient::CoreClient(const JID& jid, const SafeByteArray& password, NetworkFactories* networkFactories) : jid_(jid), password_(password), networkFactories(networkFactories), disconnectRequested_(false), certificateTrustChecker(NULL) {
@@ -64,27 +65,64 @@ void CoreClient::connect(const std::string& host) {
assert(!connector_);
assert(proxyConnectionFactories.empty());
if(networkFactories->getProxyProvider()->getSOCKS5Proxy().isValid()) {
proxyConnectionFactories.push_back(new SOCKS5ProxiedConnectionFactory(networkFactories->getConnectionFactory(), networkFactories->getProxyProvider()->getSOCKS5Proxy()));
}
if(networkFactories->getProxyProvider()->getHTTPConnectProxy().isValid()) {
proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getConnectionFactory(), networkFactories->getProxyProvider()->getHTTPConnectProxy()));
}
std::vector<ConnectionFactory*> connectionFactories(proxyConnectionFactories);
- // connectionFactories.push_back(networkFactories->getConnectionFactory());
- connectionFactories.push_back(new BOSHConnectionFactory(networkFactories->getConnectionFactory()));
+ if (options.boshURL.empty()) {
+ 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();
+ }
+ else {
+ /* Autodiscovery of which proxy works is largely ok with a TCP session, because this is a one-off. With BOSH
+ * it would be quite painful given that potentially every stanza could be sent on a new connection.
+ */
+ //sessionStream_ = boost::make_shared<BOSHSessionStream>(boost::make_shared<BOSHConnectionFactory>(options.boshURL, networkFactories->getConnectionFactory(), networkFactories->getXMLParserFactory(), networkFactories->getTLSContextFactory()), getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword);
+ sessionStream_ = boost::shared_ptr<BOSHSessionStream>(new BOSHSessionStream(boost::make_shared<BOSHConnectionFactory>(options.boshURL, networkFactories->getConnectionFactory(), networkFactories->getXMLParserFactory(), networkFactories->getTLSContextFactory()), getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword));
+ sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1));
+ sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1));
+ bindSessionToStream();
+ }
+
+}
- 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::bindSessionToStream() {
+ 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();
}
+/**
+ * Only called for TCP sessions. BOSH is handled inside the BOSHSessionStream.
+ */
void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connection) {
resetConnector();
if (!connection) {
if (options.forgetPassword) {
purgePassword();
}
onDisconnected(disconnectRequested_ ? boost::optional<ClientError>() : boost::optional<ClientError>(ClientError::ConnectionError));
}
else {
@@ -93,38 +131,19 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio
assert(!sessionStream_);
sessionStream_ = boost::make_shared<BasicSessionStream>(ClientStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), networkFactories->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();
+ bindSessionToStream();
}
}
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();
@@ -333,21 +352,26 @@ void CoreClient::resetConnector() {
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();
+ if (connection_) {
+ connection_->disconnect();
+ }
+ else if (boost::dynamic_pointer_cast<BOSHSessionStream>(sessionStream_)) {
+ sessionStream_->close();
+ }
+ sessionStream_.reset();
connection_.reset();
}
void CoreClient::forceReset() {
if (connector_) {
std::cerr << "Warning: Client not disconnected properly: Connector still active" << std::endl;
resetConnector();
}
if (sessionStream_ || connection_) {
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index 3c089c1..c231fdc 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -23,19 +23,19 @@ namespace Swift {
class Error;
class IQRouter;
class TLSContextFactory;
class ConnectionFactory;
class Connection;
class TimerFactory;
class ClientSession;
class StanzaChannel;
class Stanza;
- class BasicSessionStream;
+ class SessionStream;
class CertificateTrustChecker;
class NetworkFactories;
class ClientSessionStanzaChannel;
/**
* The central class for communicating with an XMPP server.
*
* This class is responsible for setting up the connection with the XMPP
* server, authenticating, and initializing the session.
@@ -201,31 +201,32 @@ namespace Swift {
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 bindSessionToStream();
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;
boost::shared_ptr<Connection> connection_;
- boost::shared_ptr<BasicSessionStream> sessionStream_;
+ boost::shared_ptr<SessionStream> sessionStream_;
boost::shared_ptr<ClientSession> session_;
std::string certificate_;
bool disconnectRequested_;
CertificateTrustChecker* certificateTrustChecker;
};
}
diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp
index e9d1b21..22db8fc 100644
--- a/Swiften/Client/UnitTest/ClientSessionTest.cpp
+++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp
@@ -397,18 +397,22 @@ class ClientSessionTest : public CppUnit::TestFixture {
virtual Certificate::ref getPeerCertificate() const {
return Certificate::ref(new SimpleCertificate());
}
virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const {
return boost::shared_ptr<CertificateVerificationError>();
}
+ virtual bool supportsZLibCompression() {
+ return true;
+ }
+
virtual void addZLibCompression() {
compressed = true;
}
virtual void setWhitespacePingEnabled(bool enabled) {
whitespacePingEnabled = enabled;
}
virtual void resetXMPPParser() {