summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swift/Controllers/MainController.cpp5
-rw-r--r--Swift/QtUI/SConscript2
-rw-r--r--Swiften/Client/Client.cpp3
-rw-r--r--Swiften/Client/ClientError.h1
-rw-r--r--Swiften/Client/ClientSession.cpp17
-rw-r--r--Swiften/Client/ClientSession.h1
-rw-r--r--Swiften/Client/UnitTest/ClientSessionTest.cpp7
-rw-r--r--Swiften/Session/BasicSessionStream.cpp6
-rw-r--r--Swiften/Session/BasicSessionStream.h4
-rw-r--r--Swiften/Session/SessionStream.h2
10 files changed, 45 insertions, 3 deletions
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 22d71e5..d94cadc 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -217,118 +217,119 @@ void MainController::sendPresence(boost::shared_ptr<Presence> presence) {
if (presence->getType() == Presence::Unavailable) {
logout();
}
}
void MainController::handleInputIdleChanged(bool idle) {
if (!client_ || !client_->isAvailable()) {
return;
}
if (idle) {
preIdlePresence_ = lastSentPresence_;
boost::shared_ptr<Presence> presence(new Presence());
presence->setShow(StatusShow::Away);
presence->setStatus("Auto-away");
sendPresence(presence);
}
else {
if (client_) {
sendPresence(preIdlePresence_);
} else {
queuedPresence_ = preIdlePresence_;
}
}
}
void MainController::handleIncomingPresence(boost::shared_ptr<Presence> presence) {
//FIXME: subscribe, subscribed
rosterController_->handleIncomingPresence(presence);
}
void MainController::handleLoginRequest(const String &username, const String &password, const String& certificateFile, bool remember) {
loginWindow_->setMessage("");
ProfileSettingsProvider* profileSettings = new ProfileSettingsProvider(username, settings_);
profileSettings->storeString("jid", username);
profileSettings->storeString("certificate", certificateFile);
profileSettings->storeString("pass", remember ? password : "");
loginWindow_->addAvailableAccount(profileSettings->getStringSetting("jid"), profileSettings->getStringSetting("pass"), profileSettings->getStringSetting("certificate"));
delete profileSettings;
jid_ = JID(username);
password_ = password;
certificateFile_ = certificateFile;
performLoginFromCachedCredentials();
}
void MainController::performLoginFromCachedCredentials() {
if (!client_) {
client_ = new Swift::Client(jid_, password_);
presenceSender_ = new PresenceSender(client_);
- //client_->onDataRead.connect(&printIncomingData);
- //client_->onDataWritten.connect(&printOutgoingData);
+ client_->onDataRead.connect(&printIncomingData);
+ client_->onDataWritten.connect(&printOutgoingData);
if (!certificateFile_.isEmpty()) {
client_->setCertificate(certificateFile_);
}
client_->onError.connect(boost::bind(&MainController::handleError, this, _1));
client_->onConnected.connect(boost::bind(&MainController::handleConnected, this));
client_->onMessageReceived.connect(boost::bind(&MainController::handleIncomingMessage, this, _1));
}
client_->connect();
}
void MainController::handleError(const ClientError& error) {
String message;
switch(error.getType()) {
case ClientError::UnknownError: message = "Unknown Error"; break;
case ClientError::DomainNameResolveError: message = "Unable to find server"; break;
case ClientError::ConnectionError: message = "Error connecting to server"; break;
case ClientError::ConnectionReadError: message = "Error while receiving server data"; break;
case ClientError::ConnectionWriteError: message = "Error while sending data to the server"; break;
case ClientError::XMLError: message = "Error parsing server data"; break;
case ClientError::AuthenticationFailedError: message = "Login/password invalid"; break;
+ case ClientError::CompressionFailedError: message = "Error while compressing stream"; break;
case ClientError::ServerVerificationFailedError: message = "Server verification failed"; break;
case ClientError::NoSupportedAuthMechanismsError: message = "Authentication mechanisms not supported"; break;
case ClientError::UnexpectedElementError: message = "Unexpected response"; break;
case ClientError::ResourceBindError: message = "Error binding resource"; break;
case ClientError::SessionStartError: message = "Error starting session"; break;
case ClientError::TLSError: message = "Encryption error"; break;
case ClientError::ClientCertificateLoadError: message = "Error loading certificate (Invalid password?)"; break;
case ClientError::ClientCertificateError: message = "Certificate not authorized"; break;
}
if (!rosterController_) { //hasn't been logged in yet
signOut();
loginWindow_->setMessage(message);
}
logout();
if (rosterController_) {
//reconnectAfterError();
}
}
void MainController::handleCancelLoginRequest() {
signOut();
}
void MainController::signOut() {
logout();
loginWindow_->loggedOut();
foreach (JIDChatControllerPair controllerPair, chatControllers_) {
delete controllerPair.second;
}
chatControllers_.clear();
foreach (JIDMUCControllerPair controllerPair, mucControllers_) {
delete controllerPair.second;
}
mucControllers_.clear();
delete rosterController_;
rosterController_ = NULL;
resetClient();
}
void MainController::logout() {
if (client_ && client_->isAvailable()) {
client_->disconnect();
}
if (rosterController_) {
rosterController_->getWindow()->setMyStatusType(StatusShow::None);
rosterController_->getWindow()->setMyStatusText("");
}
setManagersEnabled(false);
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index 800a009..42cf07a 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -1,72 +1,74 @@
import os, shutil, datetime
import Version
def generateDefaultTheme(env, target, source) :
sourceDir = source[0].abspath
output = open(target[0].abspath, "w")
output.write("<RCC version =\"1.0\">")
output.write("<qresource prefix=\"/themes/Default\">")
for (path, dirs, files) in os.walk(sourceDir) :
for file in files :
filePath = os.path.join(path,file)
output.write("<file alias=\"%(alias)s\">%(path)s</file>" % {
"alias": filePath[len(sourceDir)+1:],
"path": filePath
})
output.write("</qresource>")
output.write("</RCC>")
Import("env")
myenv = env.Clone()
myenv.MergeFlags(env["SWIFT_CONTROLLERS_FLAGS"])
myenv.MergeFlags(env["SWIFTOOLS_FLAGS"])
+if myenv["HAVE_XSS"] :
+ myenv.MergeFlags(env["XSS_FLAGS"])
myenv.MergeFlags(env["SWIFTEN_FLAGS"])
myenv.MergeFlags(env["CPPUNIT_FLAGS"])
myenv.MergeFlags(env["LIBIDN_FLAGS"])
myenv.MergeFlags(env["BOOST_FLAGS"])
myenv.MergeFlags(env["SQLITE_FLAGS"])
myenv.MergeFlags(env["ZLIB_FLAGS"])
myenv.MergeFlags(env["OPENSSL_FLAGS"])
myenv.MergeFlags(env.get("LIBXML_FLAGS", ""))
myenv.MergeFlags(env.get("EXPAT_FLAGS", ""))
myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"])
myenv.Tool("nsis", toolpath = ["#/BuildTools/SCons/Tools"])
myenv.EnableQt4Modules(['QtCore', 'QtGui', 'QtWebKit'], debug = False)
myenv.Append(CPPPATH = ["/usr/include/phonon"])
myenv.Append(CPPPATH = ["."])
if env["PLATFORM"] == "win32" :
#myenv["LINKFLAGS"] = ["/SUBSYSTEM:CONSOLE"]
myenv.Append(LINKFLAGS = ["/SUBSYSTEM:WINDOWS"])
myenv.Append(LIBS = "qtmain")
myenv.Command("DefaultTheme.qrc", "../resources/themes/Default", Action(generateDefaultTheme, cmdstr = "$GENCOMSTR"))
sources = [
"main.cpp",
"QtAboutWidget.cpp",
"QtAddContactDialog.cpp",
"QtChatWindow.cpp",
"QtChatWindowFactory.cpp",
"QtJoinMUCDialog.cpp",
"QtLoginWindow.cpp",
"QtLoginWindowFactory.cpp",
"QtMainWindow.cpp",
"QtMainWindowFactory.cpp",
"QtSettingsProvider.cpp",
"QtStatusWidget.cpp",
"QtSwift.cpp",
"QtChatView.cpp",
"QtChatTabs.cpp",
"QtSoundPlayer.cpp",
"QtSystemTray.cpp",
"QtTabbable.cpp",
"QtTextEdit.cpp",
"ChatSnippet.cpp",
"MessageSnippet.cpp",
"SystemMessageSnippet.cpp",
"Roster/RosterModel.cpp",
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
index 3962281..19f7ee5 100644
--- a/Swiften/Client/Client.cpp
+++ b/Swiften/Client/Client.cpp
@@ -100,96 +100,99 @@ void Client::sendIQ(boost::shared_ptr<IQ> iq) {
send(iq);
}
void Client::sendMessage(boost::shared_ptr<Message> message) {
send(message);
}
void Client::sendPresence(boost::shared_ptr<Presence> presence) {
send(presence);
}
String Client::getNewIQID() {
return idGenerator_.generateID();
}
void Client::handleElement(boost::shared_ptr<Element> element) {
boost::shared_ptr<Message> message = boost::dynamic_pointer_cast<Message>(element);
if (message) {
onMessageReceived(message);
return;
}
boost::shared_ptr<Presence> presence = boost::dynamic_pointer_cast<Presence>(element);
if (presence) {
onPresenceReceived(presence);
return;
}
boost::shared_ptr<IQ> iq = boost::dynamic_pointer_cast<IQ>(element);
if (iq) {
onIQReceived(iq);
return;
}
}
void Client::setCertificate(const String& certificate) {
certificate_ = certificate;
}
void Client::handleSessionFinished(boost::shared_ptr<Error> error) {
closeConnection();
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;
}
}
else if (boost::shared_ptr<SessionStream::Error> actualError = boost::dynamic_pointer_cast<SessionStream::Error>(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);
}
session_.reset();
}
void Client::handleNeedCredentials() {
diff --git a/Swiften/Client/ClientError.h b/Swiften/Client/ClientError.h
index 55c57fc..a0557d4 100644
--- a/Swiften/Client/ClientError.h
+++ b/Swiften/Client/ClientError.h
@@ -1,31 +1,32 @@
#pragma once
namespace Swift {
class ClientError {
public:
enum Type {
UnknownError,
DomainNameResolveError,
ConnectionError,
ConnectionReadError,
ConnectionWriteError,
XMLError,
AuthenticationFailedError,
+ CompressionFailedError,
ServerVerificationFailedError,
NoSupportedAuthMechanismsError,
UnexpectedElementError,
ResourceBindError,
SessionStartError,
TLSError,
ClientCertificateLoadError,
ClientCertificateError
};
ClientError(Type type = UnknownError) : type_(type) {}
Type getType() const { return type_; }
private:
Type type_;
};
}
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp
index 61ce8ef..8427d27 100644
--- a/Swiften/Client/ClientSession.cpp
+++ b/Swiften/Client/ClientSession.cpp
@@ -1,170 +1,187 @@
#include "Swiften/Client/ClientSession.h"
#include <boost/bind.hpp>
#include "Swiften/Elements/ProtocolHeader.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/AuthChallenge.h"
#include "Swiften/Elements/AuthResponse.h"
+#include "Swiften/Elements/Compressed.h"
+#include "Swiften/Elements/CompressFailure.h"
+#include "Swiften/Elements/CompressRequest.h"
#include "Swiften/Elements/StartSession.h"
#include "Swiften/Elements/IQ.h"
#include "Swiften/Elements/ResourceBind.h"
#include "Swiften/SASL/PLAINClientAuthenticator.h"
#include "Swiften/SASL/SCRAMSHA1ClientAuthenticator.h"
#include "Swiften/Session/SessionStream.h"
namespace Swift {
ClientSession::ClientSession(
const JID& jid,
boost::shared_ptr<SessionStream> stream) :
localJID(jid),
state(Initial),
stream(stream),
needSessionStart(false),
authenticator(NULL) {
}
void ClientSession::start() {
stream->onStreamStartReceived.connect(boost::bind(&ClientSession::handleStreamStart, shared_from_this(), _1));
stream->onElementReceived.connect(boost::bind(&ClientSession::handleElement, shared_from_this(), _1));
stream->onError.connect(boost::bind(&ClientSession::handleStreamError, shared_from_this(), _1));
stream->onTLSEncrypted.connect(boost::bind(&ClientSession::handleTLSEncrypted, shared_from_this()));
assert(state == Initial);
state = WaitingForStreamStart;
sendStreamHeader();
}
void ClientSession::sendStreamHeader() {
ProtocolHeader header;
header.setTo(getRemoteJID());
stream->writeHeader(header);
}
void ClientSession::sendElement(boost::shared_ptr<Element> element) {
stream->writeElement(element);
}
void ClientSession::handleStreamStart(const ProtocolHeader&) {
checkState(WaitingForStreamStart);
state = Negotiating;
}
void ClientSession::handleElement(boost::shared_ptr<Element> element) {
if (getState() == Initialized) {
onElementReceived(element);
}
else if (StreamFeatures* streamFeatures = dynamic_cast<StreamFeatures*>(element.get())) {
if (!checkState(Negotiating)) {
return;
}
if (streamFeatures->hasStartTLS() && stream->supportsTLSEncryption()) {
state = WaitingForEncrypt;
stream->writeElement(boost::shared_ptr<StartTLSRequest>(new StartTLSRequest()));
}
+ else if (streamFeatures->hasCompressionMethod("zlib")) {
+ state = Compressing;
+ stream->writeElement(boost::shared_ptr<CompressRequest>(new CompressRequest("zlib")));
+ }
else if (streamFeatures->hasAuthenticationMechanisms()) {
if (stream->hasTLSCertificate()) {
if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) {
state = Authenticating;
stream->writeElement(boost::shared_ptr<Element>(new AuthRequest("EXTERNAL", "")));
}
else {
finishSession(Error::TLSClientCertificateError);
}
}
else if (streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1")) {
// FIXME: Use a real nonce
authenticator = new SCRAMSHA1ClientAuthenticator("ClientNonce");
state = WaitingForCredentials;
onNeedCredentials();
}
else if (streamFeatures->hasAuthenticationMechanism("PLAIN")) {
authenticator = new PLAINClientAuthenticator();
state = WaitingForCredentials;
onNeedCredentials();
}
else {
finishSession(Error::NoSupportedAuthMechanismsError);
}
}
else {
// Start the session
stream->setWhitespacePingEnabled(true);
if (streamFeatures->hasSession()) {
needSessionStart = true;
}
if (streamFeatures->hasResourceBind()) {
state = BindingResource;
boost::shared_ptr<ResourceBind> resourceBind(new ResourceBind());
if (!localJID.getResource().isEmpty()) {
resourceBind->setResource(localJID.getResource());
}
stream->writeElement(IQ::createRequest(IQ::Set, JID(), "session-bind", resourceBind));
}
else if (needSessionStart) {
sendSessionStart();
}
else {
state = Initialized;
onInitialized();
}
}
}
+ else if (boost::dynamic_pointer_cast<Compressed>(element)) {
+ checkState(Compressing);
+ state = WaitingForStreamStart;
+ stream->addZLibCompression();
+ stream->resetXMPPParser();
+ sendStreamHeader();
+ }
+ else if (boost::dynamic_pointer_cast<CompressFailure>(element)) {
+ finishSession(Error::CompressionFailedError);
+ }
else if (AuthChallenge* challenge = dynamic_cast<AuthChallenge*>(element.get())) {
checkState(Authenticating);
assert(authenticator);
if (authenticator->setChallenge(challenge->getValue())) {
stream->writeElement(boost::shared_ptr<AuthResponse>(new AuthResponse(authenticator->getResponse())));
}
else {
finishSession(Error::AuthenticationFailedError);
}
}
else if (AuthSuccess* authSuccess = dynamic_cast<AuthSuccess*>(element.get())) {
checkState(Authenticating);
if (authenticator && !authenticator->setChallenge(authSuccess->getValue())) {
finishSession(Error::ServerVerificationFailedError);
}
else {
state = WaitingForStreamStart;
delete authenticator;
authenticator = NULL;
stream->resetXMPPParser();
sendStreamHeader();
}
}
else if (dynamic_cast<AuthFailure*>(element.get())) {
delete authenticator;
authenticator = NULL;
finishSession(Error::AuthenticationFailedError);
}
else if (dynamic_cast<TLSProceed*>(element.get())) {
checkState(WaitingForEncrypt);
state = Encrypting;
stream->addTLSEncryption();
}
else if (dynamic_cast<StartTLSFailure*>(element.get())) {
finishSession(Error::TLSError);
}
else if (IQ* iq = dynamic_cast<IQ*>(element.get())) {
if (state == BindingResource) {
boost::shared_ptr<ResourceBind> resourceBind(iq->getPayload<ResourceBind>());
if (iq->getType() == IQ::Error && iq->getID() == "session-bind") {
finishSession(Error::ResourceBindError);
}
else if (!resourceBind) {
finishSession(Error::UnexpectedElementError);
}
else if (iq->getType() == IQ::Result) {
localJID = resourceBind->getJID();
if (!localJID.isValid()) {
diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h
index 5e5acbc..ef4d747 100644
--- a/Swiften/Client/ClientSession.h
+++ b/Swiften/Client/ClientSession.h
@@ -1,84 +1,85 @@
#pragma once
#include <boost/signal.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "Swiften/Base/Error.h"
#include "Swiften/Session/SessionStream.h"
#include "Swiften/Session/BasicSessionStream.h"
#include "Swiften/Base/String.h"
#include "Swiften/JID/JID.h"
#include "Swiften/Elements/Element.h"
namespace Swift {
class ClientAuthenticator;
class ClientSession : public boost::enable_shared_from_this<ClientSession> {
public:
enum State {
Initial,
WaitingForStreamStart,
Negotiating,
Compressing,
WaitingForEncrypt,
Encrypting,
WaitingForCredentials,
Authenticating,
BindingResource,
StartingSession,
Initialized,
Finished
};
struct Error : public Swift::Error {
enum Type {
AuthenticationFailedError,
+ CompressionFailedError,
ServerVerificationFailedError,
NoSupportedAuthMechanismsError,
UnexpectedElementError,
ResourceBindError,
SessionStartError,
TLSClientCertificateError,
TLSError,
} type;
Error(Type type) : type(type) {}
};
static boost::shared_ptr<ClientSession> create(const JID& jid, boost::shared_ptr<SessionStream> stream) {
return boost::shared_ptr<ClientSession>(new ClientSession(jid, stream));
}
State getState() const {
return state;
}
void start();
void finish();
void sendCredentials(const String& password);
void sendElement(boost::shared_ptr<Element> element);
public:
boost::signal<void ()> onNeedCredentials;
boost::signal<void ()> onInitialized;
boost::signal<void (boost::shared_ptr<Swift::Error>)> onFinished;
boost::signal<void (boost::shared_ptr<Element>)> onElementReceived;
private:
ClientSession(
const JID& jid,
boost::shared_ptr<SessionStream>);
void finishSession(Error::Type error);
void finishSession(boost::shared_ptr<Swift::Error> error);
JID getRemoteJID() const {
return JID("", localJID.getDomain());
}
void sendStreamHeader();
void sendSessionStart();
void handleElement(boost::shared_ptr<Element>);
void handleStreamStart(const ProtocolHeader&);
diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp
index 9fe2a3d..e035ba3 100644
--- a/Swiften/Client/UnitTest/ClientSessionTest.cpp
+++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp
@@ -128,209 +128,214 @@ class ClientSessionTest : public CppUnit::TestFixture {
server->sendAuthFailure();
CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState());
CPPUNIT_ASSERT(sessionFinishedReceived);
CPPUNIT_ASSERT(sessionFinishedError);
}
void testAuthenticate_NoValidAuthMechanisms() {
boost::shared_ptr<ClientSession> session(createSession());
session->start();
server->receiveStreamStart();
server->sendStreamStart();
server->sendStreamFeaturesWithUnknownAuthentication();
CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState());
CPPUNIT_ASSERT(sessionFinishedReceived);
CPPUNIT_ASSERT(sessionFinishedError);
}
private:
boost::shared_ptr<ClientSession> createSession() {
boost::shared_ptr<ClientSession> session = ClientSession::create(JID("me@foo.com"), server);
session->onFinished.connect(boost::bind(&ClientSessionTest::handleSessionFinished, this, _1));
session->onNeedCredentials.connect(boost::bind(&ClientSessionTest::handleSessionNeedCredentials, this));
return session;
}
void handleSessionFinished(boost::shared_ptr<Error> error) {
sessionFinishedReceived = true;
sessionFinishedError = error;
}
void handleSessionNeedCredentials() {
needCredentials = true;
}
class MockSessionStream : public SessionStream {
public:
struct Event {
Event(boost::shared_ptr<Element> element) : element(element), footer(false) {}
Event(const ProtocolHeader& header) : header(header), footer(false) {}
Event() : footer(true) {}
boost::shared_ptr<Element> element;
boost::optional<ProtocolHeader> header;
bool footer;
};
- MockSessionStream() : available(true), canTLSEncrypt(true), tlsEncrypted(false), whitespacePingEnabled(false), resetCount(0) {
+ MockSessionStream() : available(true), canTLSEncrypt(true), tlsEncrypted(false), compressed(false), whitespacePingEnabled(false), resetCount(0) {
}
virtual bool isAvailable() {
return available;
}
virtual void writeHeader(const ProtocolHeader& header) {
receivedEvents.push_back(Event(header));
}
virtual void writeFooter() {
receivedEvents.push_back(Event());
}
virtual void writeElement(boost::shared_ptr<Element> element) {
receivedEvents.push_back(Event(element));
}
virtual bool supportsTLSEncryption() {
return canTLSEncrypt;
}
virtual void addTLSEncryption() {
tlsEncrypted = true;
}
+ virtual void addZLibCompression() {
+ compressed = true;
+ }
+
virtual void setWhitespacePingEnabled(bool enabled) {
whitespacePingEnabled = enabled;
}
virtual void resetXMPPParser() {
resetCount++;
}
void breakConnection() {
onError(boost::shared_ptr<SessionStream::Error>(new SessionStream::Error(SessionStream::Error::ConnectionReadError)));
}
void breakTLS() {
onError(boost::shared_ptr<SessionStream::Error>(new SessionStream::Error(SessionStream::Error::TLSError)));
}
void sendStreamStart() {
ProtocolHeader header;
header.setTo("foo.com");
return onStreamStartReceived(header);
}
void sendStreamFeaturesWithStartTLS() {
boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
streamFeatures->setHasStartTLS();
onElementReceived(streamFeatures);
}
void sendTLSProceed() {
onElementReceived(boost::shared_ptr<TLSProceed>(new TLSProceed()));
}
void sendTLSFailure() {
onElementReceived(boost::shared_ptr<StartTLSFailure>(new StartTLSFailure()));
}
void sendStreamFeaturesWithPLAINAuthentication() {
boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
streamFeatures->addAuthenticationMechanism("PLAIN");
onElementReceived(streamFeatures);
}
void sendStreamFeaturesWithUnknownAuthentication() {
boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures());
streamFeatures->addAuthenticationMechanism("UNKNOWN");
onElementReceived(streamFeatures);
}
void sendAuthSuccess() {
onElementReceived(boost::shared_ptr<AuthSuccess>(new AuthSuccess()));
}
void sendAuthFailure() {
onElementReceived(boost::shared_ptr<AuthFailure>(new AuthFailure()));
}
void receiveStreamStart() {
Event event = popEvent();
CPPUNIT_ASSERT(event.header);
}
void receiveStartTLS() {
Event event = popEvent();
CPPUNIT_ASSERT(event.element);
CPPUNIT_ASSERT(boost::dynamic_pointer_cast<StartTLSRequest>(event.element));
}
void receiveAuthRequest(const String& mech) {
Event event = popEvent();
CPPUNIT_ASSERT(event.element);
boost::shared_ptr<AuthRequest> request(boost::dynamic_pointer_cast<AuthRequest>(event.element));
CPPUNIT_ASSERT(request);
CPPUNIT_ASSERT_EQUAL(mech, request->getMechanism());
}
Event popEvent() {
CPPUNIT_ASSERT(receivedEvents.size() > 0);
Event event = receivedEvents.front();
receivedEvents.pop_front();
return event;
}
bool available;
bool canTLSEncrypt;
bool tlsEncrypted;
+ bool compressed;
bool whitespacePingEnabled;
int resetCount;
std::deque<Event> receivedEvents;
};
boost::shared_ptr<MockSessionStream> server;
bool sessionFinishedReceived;
bool needCredentials;
boost::shared_ptr<Error> sessionFinishedError;
};
CPPUNIT_TEST_SUITE_REGISTRATION(ClientSessionTest);
#if 0
void testAuthenticate() {
boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
session->onNeedCredentials.connect(boost::bind(&ClientSessionTest::setNeedCredentials, this));
getMockServer()->expectStreamStart();
getMockServer()->sendStreamStart();
getMockServer()->sendStreamFeaturesWithAuthentication();
session->startSession();
processEvents();
CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState());
CPPUNIT_ASSERT(needCredentials_);
getMockServer()->expectAuth("me", "mypass");
getMockServer()->sendAuthSuccess();
getMockServer()->expectStreamStart();
getMockServer()->sendStreamStart();
session->sendCredentials("mypass");
CPPUNIT_ASSERT_EQUAL(ClientSession::Authenticating, session->getState());
processEvents();
CPPUNIT_ASSERT_EQUAL(ClientSession::Negotiating, session->getState());
}
void testAuthenticate_Unauthorized() {
boost::shared_ptr<MockSession> session(createSession("me@foo.com/Bar"));
getMockServer()->expectStreamStart();
getMockServer()->sendStreamStart();
getMockServer()->sendStreamFeaturesWithAuthentication();
session->startSession();
processEvents();
getMockServer()->expectAuth("me", "mypass");
getMockServer()->sendAuthFailure();
session->sendCredentials("mypass");
processEvents();
diff --git a/Swiften/Session/BasicSessionStream.cpp b/Swiften/Session/BasicSessionStream.cpp
index 0d0f49f..ed7f1eb 100644
--- a/Swiften/Session/BasicSessionStream.cpp
+++ b/Swiften/Session/BasicSessionStream.cpp
@@ -1,124 +1,130 @@
#include "Swiften/Session/BasicSessionStream.h"
#include <boost/bind.hpp>
#include "Swiften/StreamStack/XMPPLayer.h"
#include "Swiften/StreamStack/StreamStack.h"
#include "Swiften/StreamStack/ConnectionLayer.h"
#include "Swiften/StreamStack/WhitespacePingLayer.h"
+#include "Swiften/StreamStack/CompressionLayer.h"
#include "Swiften/StreamStack/TLSLayer.h"
#include "Swiften/StreamStack/TLSLayerFactory.h"
namespace Swift {
BasicSessionStream::BasicSessionStream(boost::shared_ptr<Connection> connection, PayloadParserFactoryCollection* payloadParserFactories, PayloadSerializerCollection* payloadSerializers, TLSLayerFactory* tlsLayerFactory, TimerFactory* timerFactory) : available(false), connection(connection), payloadParserFactories(payloadParserFactories), payloadSerializers(payloadSerializers), tlsLayerFactory(tlsLayerFactory), timerFactory(timerFactory) {
}
void BasicSessionStream::initialize() {
xmppLayer = boost::shared_ptr<XMPPLayer>(
new XMPPLayer(payloadParserFactories, payloadSerializers));
xmppLayer->onStreamStart.connect(boost::bind(&BasicSessionStream::handleStreamStartReceived, shared_from_this(), _1));
xmppLayer->onElement.connect(boost::bind(&BasicSessionStream::handleElementReceived, shared_from_this(), _1));
xmppLayer->onError.connect(boost::bind(
&BasicSessionStream::handleXMPPError, shared_from_this()));
xmppLayer->onDataRead.connect(boost::bind(&BasicSessionStream::handleDataRead, shared_from_this(), _1));
xmppLayer->onWriteData.connect(boost::bind(&BasicSessionStream::handleDataWritten, shared_from_this(), _1));
connection->onDisconnected.connect(boost::bind(&BasicSessionStream::handleConnectionError, shared_from_this(), _1));
connectionLayer = boost::shared_ptr<ConnectionLayer>(
new ConnectionLayer(connection));
streamStack = new StreamStack(xmppLayer, connectionLayer);
available = true;
}
BasicSessionStream::~BasicSessionStream() {
delete streamStack;
}
void BasicSessionStream::writeHeader(const ProtocolHeader& header) {
assert(available);
xmppLayer->writeHeader(header);
}
void BasicSessionStream::writeElement(boost::shared_ptr<Element> element) {
assert(available);
xmppLayer->writeElement(element);
}
void BasicSessionStream::writeFooter() {
assert(available);
xmppLayer->writeFooter();
}
bool BasicSessionStream::isAvailable() {
return available;
}
bool BasicSessionStream::supportsTLSEncryption() {
return tlsLayerFactory && tlsLayerFactory->canCreate();
}
void BasicSessionStream::addTLSEncryption() {
assert(available);
tlsLayer = tlsLayerFactory->createTLSLayer();
if (hasTLSCertificate() && !tlsLayer->setClientCertificate(getTLSCertificate())) {
onError(boost::shared_ptr<Error>(new Error(Error::InvalidTLSCertificateError)));
}
else {
streamStack->addLayer(tlsLayer);
tlsLayer->onError.connect(boost::bind(&BasicSessionStream::handleTLSError, shared_from_this()));
tlsLayer->onConnected.connect(boost::bind(&BasicSessionStream::handleTLSConnected, shared_from_this()));
tlsLayer->connect();
}
}
+void BasicSessionStream::addZLibCompression() {
+ boost::shared_ptr<CompressionLayer> compressionLayer(new CompressionLayer());
+ streamStack->addLayer(compressionLayer);
+}
+
void BasicSessionStream::setWhitespacePingEnabled(bool enabled) {
if (enabled) {
if (!whitespacePingLayer) {
whitespacePingLayer = boost::shared_ptr<WhitespacePingLayer>(new WhitespacePingLayer(timerFactory));
streamStack->addLayer(whitespacePingLayer);
}
whitespacePingLayer->setActive();
}
else if (whitespacePingLayer) {
whitespacePingLayer->setInactive();
}
}
void BasicSessionStream::resetXMPPParser() {
xmppLayer->resetParser();
}
void BasicSessionStream::handleStreamStartReceived(const ProtocolHeader& header) {
onStreamStartReceived(header);
}
void BasicSessionStream::handleElementReceived(boost::shared_ptr<Element> element) {
onElementReceived(element);
}
void BasicSessionStream::handleXMPPError() {
available = false;
onError(boost::shared_ptr<Error>(new Error(Error::ParseError)));
}
void BasicSessionStream::handleTLSConnected() {
onTLSEncrypted();
}
void BasicSessionStream::handleTLSError() {
available = false;
onError(boost::shared_ptr<Error>(new Error(Error::TLSError)));
}
void BasicSessionStream::handleConnectionError(const boost::optional<Connection::Error>& error) {
available = false;
if (error == Connection::ReadError) {
onError(boost::shared_ptr<Error>(new Error(Error::ConnectionReadError)));
}
else {
onError(boost::shared_ptr<Error>(new Error(Error::ConnectionWriteError)));
}
}
diff --git a/Swiften/Session/BasicSessionStream.h b/Swiften/Session/BasicSessionStream.h
index f36df83..8618458 100644
--- a/Swiften/Session/BasicSessionStream.h
+++ b/Swiften/Session/BasicSessionStream.h
@@ -1,71 +1,75 @@
#pragma once
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "Swiften/Network/Connection.h"
#include "Swiften/Session/SessionStream.h"
namespace Swift {
class TLSLayerFactory;
class TLSLayer;
class TimerFactory;
class WhitespacePingLayer;
class PayloadParserFactoryCollection;
class PayloadSerializerCollection;
class StreamStack;
class XMPPLayer;
class ConnectionLayer;
+ class CompressionLayer;
class BasicSessionStream :
public SessionStream,
public boost::enable_shared_from_this<BasicSessionStream> {
public:
BasicSessionStream(
boost::shared_ptr<Connection> connection,
PayloadParserFactoryCollection* payloadParserFactories,
PayloadSerializerCollection* payloadSerializers,
TLSLayerFactory* tlsLayerFactory,
TimerFactory* whitespacePingLayerFactory
);
~BasicSessionStream();
void initialize();
virtual bool isAvailable();
virtual void writeHeader(const ProtocolHeader& header);
virtual void writeElement(boost::shared_ptr<Element>);
virtual void writeFooter();
+ virtual void addZLibCompression();
+
virtual bool supportsTLSEncryption();
virtual void addTLSEncryption();
virtual void setWhitespacePingEnabled(bool);
virtual void resetXMPPParser();
private:
void handleConnectionError(const boost::optional<Connection::Error>& error);
void handleXMPPError();
void handleTLSConnected();
void handleTLSError();
void handleStreamStartReceived(const ProtocolHeader&);
void handleElementReceived(boost::shared_ptr<Element>);
void handleDataRead(const ByteArray& data);
void handleDataWritten(const ByteArray& data);
private:
bool available;
boost::shared_ptr<Connection> connection;
PayloadParserFactoryCollection* payloadParserFactories;
PayloadSerializerCollection* payloadSerializers;
TLSLayerFactory* tlsLayerFactory;
TimerFactory* timerFactory;
boost::shared_ptr<XMPPLayer> xmppLayer;
boost::shared_ptr<ConnectionLayer> connectionLayer;
StreamStack* streamStack;
+ boost::shared_ptr<CompressionLayer> compressionLayer;
boost::shared_ptr<TLSLayer> tlsLayer;
boost::shared_ptr<WhitespacePingLayer> whitespacePingLayer;
};
}
diff --git a/Swiften/Session/SessionStream.h b/Swiften/Session/SessionStream.h
index 1252c5a..8c64ccf 100644
--- a/Swiften/Session/SessionStream.h
+++ b/Swiften/Session/SessionStream.h
@@ -1,67 +1,69 @@
#pragma once
#include <boost/signal.hpp>
#include <boost/shared_ptr.hpp>
#include "Swiften/Elements/ProtocolHeader.h"
#include "Swiften/Elements/Element.h"
#include "Swiften/Base/Error.h"
#include "Swiften/TLS/PKCS12Certificate.h"
namespace Swift {
class SessionStream {
public:
class Error : public Swift::Error {
public:
enum Type {
ParseError,
TLSError,
InvalidTLSCertificateError,
ConnectionReadError,
ConnectionWriteError
};
Error(Type type) : type(type) {}
Type type;
};
virtual ~SessionStream();
virtual bool isAvailable() = 0;
virtual void writeHeader(const ProtocolHeader& header) = 0;
virtual void writeFooter() = 0;
virtual void writeElement(boost::shared_ptr<Element>) = 0;
+ virtual void addZLibCompression() = 0;
+
virtual bool supportsTLSEncryption() = 0;
virtual void addTLSEncryption() = 0;
virtual void setWhitespacePingEnabled(bool enabled) = 0;
virtual void resetXMPPParser() = 0;
void setTLSCertificate(const PKCS12Certificate& cert) {
certificate = cert;
}
virtual bool hasTLSCertificate() {
return !certificate.isNull();
}
boost::signal<void (const ProtocolHeader&)> onStreamStartReceived;
boost::signal<void (boost::shared_ptr<Element>)> onElementReceived;
boost::signal<void (boost::shared_ptr<Error>)> onError;
boost::signal<void ()> onTLSEncrypted;
boost::signal<void (const String&)> onDataRead;
boost::signal<void (const String&)> onDataWritten;
protected:
const PKCS12Certificate& getTLSCertificate() const {
return certificate;
}
private:
PKCS12Certificate certificate;
};
}