diff options
Diffstat (limited to 'Swiften/Client')
29 files changed, 689 insertions, 133 deletions
diff --git a/Swiften/Client/BlockList.cpp b/Swiften/Client/BlockList.cpp index 0b2fc12..3ee7864 100644 --- a/Swiften/Client/BlockList.cpp +++ b/Swiften/Client/BlockList.cpp @@ -7,4 +7,6 @@ #include <Swiften/Client/BlockList.h> +#include <algorithm> + using namespace Swift; @@ -12,2 +14,9 @@ BlockList::~BlockList() { } + +bool BlockList::isBlocked(const JID& jid) const { + const std::vector<JID>& items = getItems(); + return (std::find(items.begin(), items.end(), jid.toBare()) != items.end()) || + (std::find(items.begin(), items.end(), JID(jid.getDomain())) != items.end()) || + (std::find(items.begin(), items.end(), jid) != items.end()); +} diff --git a/Swiften/Client/BlockList.h b/Swiften/Client/BlockList.h index 39a211d..99c83c1 100644 --- a/Swiften/Client/BlockList.h +++ b/Swiften/Client/BlockList.h @@ -7,5 +7,5 @@ #pragma once -#include <set> +#include <vector> #include <Swiften/JID/JID.h> @@ -16,7 +16,8 @@ namespace Swift { public: enum State { + Init, Requesting, Available, - Error, + Error }; virtual ~BlockList(); @@ -24,5 +25,7 @@ namespace Swift { virtual State getState() const = 0; - virtual const std::set<JID>& getItems() const = 0; + virtual const std::vector<JID>& getItems() const = 0; + + bool isBlocked(const JID& jid) const; public: diff --git a/Swiften/Client/BlockListImpl.cpp b/Swiften/Client/BlockListImpl.cpp index dfaaaf1..5950233 100644 --- a/Swiften/Client/BlockListImpl.cpp +++ b/Swiften/Client/BlockListImpl.cpp @@ -9,16 +9,30 @@ #include <Swiften/Base/foreach.h> +#include <algorithm> + using namespace Swift; -BlockListImpl::BlockListImpl() { +BlockListImpl::BlockListImpl() : state(Init) { } void BlockListImpl::setItems(const std::vector<JID>& items) { - this->items = std::set<JID>(items.begin(), items.end()); + foreach (const JID& jid, this->items) { + if (std::find(items.begin(), items.end(), jid) != items.end()) { + onItemRemoved(jid); + } + } + + foreach (const JID& jid, items) { + if (std::find(this->items.begin(), this->items.end(), jid) != this->items.end()) { + onItemAdded(jid); + } + } + this->items = items; } void BlockListImpl::addItem(const JID& item) { - if (items.insert(item).second) { + if (std::find(items.begin(), items.end(), item) == items.end()) { + items.push_back(item); onItemAdded(item); } @@ -26,5 +40,7 @@ void BlockListImpl::addItem(const JID& item) { void BlockListImpl::removeItem(const JID& item) { - if (items.erase(item)) { + size_t oldSize = items.size(); + items.erase(std::remove(items.begin(), items.end(), item), items.end()); + if (items.size() != oldSize) { onItemRemoved(item); } @@ -33,4 +49,5 @@ void BlockListImpl::removeItem(const JID& item) { void BlockListImpl::setState(State state) { if (this->state != state) { + this->state = state; onStateChanged(); } @@ -44,5 +61,6 @@ void BlockListImpl::addItems(const std::vector<JID>& items) { void BlockListImpl::removeItems(const std::vector<JID>& items) { - foreach (const JID& item, items) { + std::vector<JID> itemsToRemove = items; + foreach (const JID& item, itemsToRemove) { removeItem(item); } @@ -50,7 +68,5 @@ void BlockListImpl::removeItems(const std::vector<JID>& items) { void BlockListImpl::removeAllItems() { - foreach (const JID& item, items) { - removeItem(item); - } + removeItems(items); } diff --git a/Swiften/Client/BlockListImpl.h b/Swiften/Client/BlockListImpl.h index ef08340..2a799ae 100644 --- a/Swiften/Client/BlockListImpl.h +++ b/Swiften/Client/BlockListImpl.h @@ -20,5 +20,5 @@ namespace Swift { void setState(State state); - virtual const std::set<JID>& getItems() const { + virtual const std::vector<JID>& getItems() const { return items; } @@ -33,5 +33,5 @@ namespace Swift { private: State state; - std::set<JID> items; + std::vector<JID> items; }; } diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp index 4d3ee04..f158370 100644 --- a/Swiften/Client/Client.cpp +++ b/Swiften/Client/Client.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -15,4 +15,5 @@ #include <Swiften/MUC/MUCRegistry.h> #include <Swiften/MUC/MUCManager.h> +#include <Swiften/PubSub/PubSubManagerImpl.h> #include <Swiften/Client/MemoryStorages.h> #include <Swiften/VCards/VCardManager.h> @@ -30,4 +31,6 @@ #include <Swiften/Network/NetworkFactories.h> #include <Swiften/FileTransfer/FileTransferManagerImpl.h> +#include <Swiften/Whiteboard/WhiteboardSessionManager.h> +#include <Swiften/Client/ClientBlockListManager.h> #ifndef SWIFT_EXPERIMENTAL_FT #include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h> @@ -37,5 +40,5 @@ namespace Swift { Client::Client(const JID& jid, const SafeString& password, NetworkFactories* networkFactories, Storages* storages) : CoreClient(jid, password, networkFactories), storages(storages) { - memoryStorages = new MemoryStorages(); + memoryStorages = new MemoryStorages(networkFactories->getCryptoProvider()); softwareVersionResponder = new SoftwareVersionResponder(getIQRouter()); @@ -52,5 +55,5 @@ Client::Client(const JID& jid, const SafeString& password, NetworkFactories* net stanzaChannelPresenceSender = new StanzaChannelPresenceSender(getStanzaChannel()); directedPresenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender); - discoManager = new ClientDiscoManager(getIQRouter(), directedPresenceSender); + discoManager = new ClientDiscoManager(getIQRouter(), directedPresenceSender, networkFactories->getCryptoProvider()); mucRegistry = new MUCRegistry(); @@ -58,6 +61,6 @@ Client::Client(const JID& jid, const SafeString& password, NetworkFactories* net vcardManager = new VCardManager(jid, getIQRouter(), getStorages()->getVCardStorage()); - avatarManager = new AvatarManagerImpl(vcardManager, getStanzaChannel(), getStorages()->getAvatarStorage(), mucRegistry); - capsManager = new CapsManager(getStorages()->getCapsStorage(), getStanzaChannel(), getIQRouter()); + avatarManager = new AvatarManagerImpl(vcardManager, getStanzaChannel(), getStorages()->getAvatarStorage(), networkFactories->getCryptoProvider(), mucRegistry); + capsManager = new CapsManager(getStorages()->getCapsStorage(), getStanzaChannel(), getIQRouter(), networkFactories->getCryptoProvider()); entityCapsManager = new EntityCapsManager(capsManager, getStanzaChannel()); @@ -68,8 +71,19 @@ Client::Client(const JID& jid, const SafeString& password, NetworkFactories* net jingleSessionManager = new JingleSessionManager(getIQRouter()); + blockListManager = new ClientBlockListManager(getIQRouter()); fileTransferManager = NULL; + + whiteboardSessionManager = NULL; +#ifdef SWIFT_EXPERIMENTAL_WB + whiteboardSessionManager = new WhiteboardSessionManager(getIQRouter(), getStanzaChannel(), presenceOracle, getEntityCapsProvider()); +#endif + + pubsubManager = new PubSubManagerImpl(getStanzaChannel(), getIQRouter()); } Client::~Client() { + delete pubsubManager; + delete whiteboardSessionManager; + delete fileTransferManager; delete jingleSessionManager; @@ -113,5 +127,16 @@ void Client::setSoftwareVersion(const std::string& name, const std::string& vers void Client::handleConnected() { #ifdef SWIFT_EXPERIMENTAL_FT - fileTransferManager = new FileTransferManagerImpl(getJID(), jingleSessionManager, getIQRouter(), getEntityCapsProvider(), presenceOracle, getNetworkFactories()->getConnectionFactory(), getNetworkFactories()->getConnectionServerFactory(), getNetworkFactories()->getTimerFactory(), getNetworkFactories()->getNATTraverser()); + fileTransferManager = new FileTransferManagerImpl( + getJID(), + jingleSessionManager, + getIQRouter(), + getEntityCapsProvider(), + presenceOracle, + getNetworkFactories()->getConnectionFactory(), + getNetworkFactories()->getConnectionServerFactory(), + getNetworkFactories()->getTimerFactory(), + getNetworkFactories()->getNetworkEnvironment(), + getNetworkFactories()->getNATTraverser(), + getNetworkFactories()->getCryptoProvider()); #else fileTransferManager = new DummyFileTransferManager(); @@ -165,3 +190,7 @@ FileTransferManager* Client::getFileTransferManager() const { } +WhiteboardSessionManager* Client::getWhiteboardSessionManager() const { + return whiteboardSessionManager; +} + } diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h index 940a526..9253074 100644 --- a/Swiften/Client/Client.h +++ b/Swiften/Client/Client.h @@ -7,6 +7,6 @@ #pragma once +#include <Swiften/Base/API.h> #include <Swiften/Client/CoreClient.h> - #include <Swiften/Base/SafeString.h> @@ -37,4 +37,7 @@ namespace Swift { class JingleSessionManager; class FileTransferManager; + class WhiteboardSessionManager; + class ClientBlockListManager; + class PubSubManager; /** @@ -44,5 +47,5 @@ namespace Swift { * performing most tasks on the XMPP network. */ - class Client : public CoreClient { + class SWIFTEN_API Client : public CoreClient { public: /** @@ -136,4 +139,8 @@ namespace Swift { } + ClientBlockListManager* getClientBlockListManager() const { + return blockListManager; + } + /** * Returns a FileTransferManager for the client. This is only available after the onConnected @@ -152,4 +159,11 @@ namespace Swift { void setAlwaysTrustCertificates(); + WhiteboardSessionManager* getWhiteboardSessionManager() const; + + PubSubManager* getPubSubManager() const { + return pubsubManager; + } + + public: /** @@ -186,4 +200,7 @@ namespace Swift { FileTransferManager* fileTransferManager; BlindCertificateTrustChecker* blindCertificateTrustChecker; + WhiteboardSessionManager* whiteboardSessionManager; + ClientBlockListManager* blockListManager; + PubSubManager* pubsubManager; }; } diff --git a/Swiften/Client/ClientBlockListManager.cpp b/Swiften/Client/ClientBlockListManager.cpp index 7222cea..de43631 100644 --- a/Swiften/Client/ClientBlockListManager.cpp +++ b/Swiften/Client/ClientBlockListManager.cpp @@ -67,11 +67,11 @@ namespace { ClientBlockListManager::ClientBlockListManager(IQRouter* iqRouter) : iqRouter(iqRouter) { + } ClientBlockListManager::~ClientBlockListManager() { + if (blockList && blockList->getState() == BlockList::Available) { unblockResponder->stop(); blockResponder->stop(); - if (getRequest) { - getRequest->onResponse.disconnect(boost::bind(&ClientBlockListManager::handleBlockListReceived, this, _1, _2)); } } @@ -80,13 +80,43 @@ boost::shared_ptr<BlockList> ClientBlockListManager::getBlockList() { if (!blockList) { blockList = boost::make_shared<BlockListImpl>(); + blockList->setState(BlockList::Init); + } + return blockList; +} + +boost::shared_ptr<BlockList> ClientBlockListManager::requestBlockList() { + if (!blockList) { + blockList = boost::make_shared<BlockListImpl>(); + } blockList->setState(BlockList::Requesting); - assert(!getRequest); - getRequest = boost::make_shared< GenericRequest<BlockListPayload> >(IQ::Get, JID(), boost::make_shared<BlockListPayload>(), iqRouter); + boost::shared_ptr<GenericRequest<BlockListPayload> > getRequest = boost::make_shared< GenericRequest<BlockListPayload> >(IQ::Get, JID(), boost::make_shared<BlockListPayload>(), iqRouter); getRequest->onResponse.connect(boost::bind(&ClientBlockListManager::handleBlockListReceived, this, _1, _2)); getRequest->send(); - } return blockList; } +GenericRequest<BlockPayload>::ref ClientBlockListManager::createBlockJIDRequest(const JID& jid) { + return createBlockJIDsRequest(std::vector<JID>(1, jid)); +} + +GenericRequest<BlockPayload>::ref ClientBlockListManager::createBlockJIDsRequest(const std::vector<JID>& jids) { + boost::shared_ptr<BlockPayload> payload = boost::make_shared<BlockPayload>(jids); + return boost::make_shared< GenericRequest<BlockPayload> >(IQ::Set, JID(), payload, iqRouter); +} + +GenericRequest<UnblockPayload>::ref ClientBlockListManager::createUnblockJIDRequest(const JID& jid) { + return createUnblockJIDsRequest(std::vector<JID>(1, jid)); +} + +GenericRequest<UnblockPayload>::ref ClientBlockListManager::createUnblockJIDsRequest(const std::vector<JID>& jids) { + boost::shared_ptr<UnblockPayload> payload = boost::make_shared<UnblockPayload>(jids); + return boost::make_shared< GenericRequest<UnblockPayload> >(IQ::Set, JID(), payload, iqRouter); +} + +GenericRequest<UnblockPayload>::ref ClientBlockListManager::createUnblockAllRequest() { + return createUnblockJIDsRequest(std::vector<JID>()); +} + + void ClientBlockListManager::handleBlockListReceived(boost::shared_ptr<BlockListPayload> payload, ErrorPayload::ref error) { if (error || !payload) { @@ -94,11 +124,15 @@ void ClientBlockListManager::handleBlockListReceived(boost::shared_ptr<BlockList } else { - blockList->setState(BlockList::Available); blockList->setItems(payload->getItems()); + blockList->setState(BlockList::Available); + if (!blockResponder) { blockResponder = boost::make_shared<BlockResponder>(blockList, iqRouter); blockResponder->start(); + } + if (!unblockResponder) { unblockResponder = boost::make_shared<UnblockResponder>(blockList, iqRouter); unblockResponder->start(); } } +} diff --git a/Swiften/Client/ClientBlockListManager.h b/Swiften/Client/ClientBlockListManager.h index 21d35e3..e715737 100644 --- a/Swiften/Client/ClientBlockListManager.h +++ b/Swiften/Client/ClientBlockListManager.h @@ -13,4 +13,5 @@ #include <Swiften/Elements/BlockListPayload.h> #include <Swiften/Elements/UnblockPayload.h> +#include <Swiften/Elements/DiscoInfo.h> #include <Swiften/Queries/SetResponder.h> #include <Swiften/Queries/GenericRequest.h> @@ -26,6 +27,4 @@ namespace Swift { ~ClientBlockListManager(); - bool isSupported() const; - /** * Returns the blocklist. @@ -33,4 +32,16 @@ namespace Swift { boost::shared_ptr<BlockList> getBlockList(); + /** + * Get the blocklist from the server. + */ + boost::shared_ptr<BlockList> requestBlockList(); + + GenericRequest<BlockPayload>::ref createBlockJIDRequest(const JID& jid); + GenericRequest<BlockPayload>::ref createBlockJIDsRequest(const std::vector<JID>& jids); + + GenericRequest<UnblockPayload>::ref createUnblockJIDRequest(const JID& jid); + GenericRequest<UnblockPayload>::ref createUnblockJIDsRequest(const std::vector<JID>& jids); + GenericRequest<UnblockPayload>::ref createUnblockAllRequest(); + private: void handleBlockListReceived(boost::shared_ptr<BlockListPayload> payload, ErrorPayload::ref); @@ -38,5 +49,4 @@ namespace Swift { private: IQRouter* iqRouter; - boost::shared_ptr<GenericRequest<BlockListPayload> > getRequest; boost::shared_ptr<SetResponder<BlockPayload> > blockResponder; boost::shared_ptr<SetResponder<UnblockPayload> > unblockResponder; diff --git a/Swiften/Client/ClientOptions.h b/Swiften/Client/ClientOptions.h index fbec272..e5fcda9 100644 --- a/Swiften/Client/ClientOptions.h +++ b/Swiften/Client/ClientOptions.h @@ -18,5 +18,25 @@ namespace Swift { }; - ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), allowPLAINWithoutTLS(false), useStreamResumption(false), forgetPassword(false), useAcks(true), boshHTTPConnectProxyAuthID(""), boshHTTPConnectProxyAuthPassword("") { + enum ProxyType { + NoProxy, + SystemConfiguredProxy, + SOCKS5Proxy, + HTTPConnectProxy + }; + + ClientOptions() : + useStreamCompression(true), + useTLS(UseTLSWhenAvailable), + allowPLAINWithoutTLS(false), + useStreamResumption(false), + forgetPassword(false), + useAcks(true), + manualHostname(""), + manualPort(-1), + proxyType(SystemConfiguredProxy), + manualProxyHostname(""), + manualProxyPort(-1), + boshHTTPConnectProxyAuthID(""), + boshHTTPConnectProxyAuthPassword("") { } @@ -67,4 +87,33 @@ namespace Swift { /** + * The hostname to connect to. + * Leave this empty for standard XMPP connection, based on the JID domain. + */ + std::string manualHostname; + + /** + * The port to connect to. + * Leave this to -1 to use the port discovered by SRV lookups, and 5222 as a + * fallback. + */ + int manualPort; + + /** + * The type of proxy to use for connecting to the XMPP + * server. + */ + ProxyType proxyType; + + /** + * Override the system-configured proxy hostname. + */ + std::string manualProxyHostname; + + /** + * Override the system-configured proxy port. + */ + int manualProxyPort; + + /** * If non-empty, use BOSH instead of direct TCP, with the given URL. * Default: empty (no BOSH) diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp index 70f0398..500299a 100644 --- a/Swiften/Client/ClientSession.cpp +++ b/Swiften/Client/ClientSession.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -38,6 +38,8 @@ #include <Swiften/Elements/ResourceBind.h> #include <Swiften/SASL/PLAINClientAuthenticator.h> +#include <Swiften/SASL/EXTERNALClientAuthenticator.h> #include <Swiften/SASL/SCRAMSHA1ClientAuthenticator.h> #include <Swiften/SASL/DIGESTMD5ClientAuthenticator.h> +#include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/Session/SessionStream.h> #include <Swiften/TLS/CertificateTrustChecker.h> @@ -48,12 +50,19 @@ #endif +#define CHECK_STATE_OR_RETURN(a) \ + if (!checkState(a)) { return; } + namespace Swift { ClientSession::ClientSession( const JID& jid, - boost::shared_ptr<SessionStream> stream) : + boost::shared_ptr<SessionStream> stream, + IDNConverter* idnConverter, + CryptoProvider* crypto) : localJID(jid), state(Initial), stream(stream), + idnConverter(idnConverter), + crypto(crypto), allowPLAINOverNonTLS(false), useStreamCompression(true), @@ -101,9 +110,9 @@ void ClientSession::sendStanza(boost::shared_ptr<Stanza> stanza) { void ClientSession::handleStreamStart(const ProtocolHeader&) { - checkState(WaitingForStreamStart); + CHECK_STATE_OR_RETURN(WaitingForStreamStart); state = Negotiating; } -void ClientSession::handleElement(boost::shared_ptr<Element> element) { +void ClientSession::handleElement(boost::shared_ptr<ToplevelElement> element) { if (boost::shared_ptr<Stanza> stanza = boost::dynamic_pointer_cast<Stanza>(element)) { if (stanzaAckResponder_) { @@ -162,9 +171,9 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else { - std::cerr << "Warning: Got invalid ack from server" << std::endl; + SWIFT_LOG(warning) << "Got invalid ack from server"; } } else { - std::cerr << "Warning: Ignoring ack" << std::endl; + SWIFT_LOG(warning) << "Ignoring ack"; } } @@ -182,7 +191,5 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else if (StreamFeatures* streamFeatures = dynamic_cast<StreamFeatures*>(element.get())) { - if (!checkState(Negotiating)) { - return; - } + CHECK_STATE_OR_RETURN(Negotiating); if (streamFeatures->hasStartTLS() && stream->supportsTLSEncryption() && useTLS != NeverUseTLS) { @@ -200,4 +207,5 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { if (stream->hasTLSCertificate()) { if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { + authenticator = new EXTERNALClientAuthenticator(); state = Authenticating; stream->writeElement(boost::make_shared<AuthRequest>("EXTERNAL", createSafeByteArray(""))); @@ -208,4 +216,5 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { + authenticator = new EXTERNALClientAuthenticator(); state = Authenticating; stream->writeElement(boost::make_shared<AuthRequest>("EXTERNAL", createSafeByteArray(""))); @@ -214,12 +223,12 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { std::ostringstream s; ByteArray finishMessage; - bool plus = stream->isTLSEncrypted() && streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1-PLUS"); - if (plus) { + bool plus = streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1-PLUS"); + if (stream->isTLSEncrypted()) { finishMessage = stream->getTLSFinishMessage(); plus &= !finishMessage.empty(); } s << boost::uuids::random_generator()(); - SCRAMSHA1ClientAuthenticator* scramAuthenticator = new SCRAMSHA1ClientAuthenticator(s.str(), plus); - if (plus) { + SCRAMSHA1ClientAuthenticator* scramAuthenticator = new SCRAMSHA1ClientAuthenticator(s.str(), plus, idnConverter, crypto); + if (!finishMessage.empty()) { scramAuthenticator->setTLSChannelBindingData(finishMessage); } @@ -233,9 +242,9 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { onNeedCredentials(); } - else if (streamFeatures->hasAuthenticationMechanism("DIGEST-MD5") && DIGESTMD5ClientAuthenticator::canBeUsed()) { + else if (streamFeatures->hasAuthenticationMechanism("DIGEST-MD5") && crypto->isMD5AllowedForCrypto()) { std::ostringstream s; s << boost::uuids::random_generator()(); // FIXME: Host should probably be the actual host - authenticator = new DIGESTMD5ClientAuthenticator(localJID.getDomain(), s.str()); + authenticator = new DIGESTMD5ClientAuthenticator(localJID.getDomain(), s.str(), crypto); state = WaitingForCredentials; onNeedCredentials(); @@ -262,5 +271,5 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else if (boost::dynamic_pointer_cast<Compressed>(element)) { - checkState(Compressing); + CHECK_STATE_OR_RETURN(Compressing); state = WaitingForStreamStart; stream->addZLibCompression(); @@ -285,5 +294,5 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else if (AuthChallenge* challenge = dynamic_cast<AuthChallenge*>(element.get())) { - checkState(Authenticating); + CHECK_STATE_OR_RETURN(Authenticating); assert(authenticator); if (authenticator->setChallenge(challenge->getValue())) { @@ -295,8 +304,7 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else if (AuthSuccess* authSuccess = dynamic_cast<AuthSuccess*>(element.get())) { - checkState(Authenticating); - if (authenticator && !authenticator->setChallenge(authSuccess->getValue())) { - delete authenticator; - authenticator = NULL; + CHECK_STATE_OR_RETURN(Authenticating); + assert(authenticator); + if (!authenticator->setChallenge(authSuccess->getValue())) { finishSession(Error::ServerVerificationFailedError); } @@ -310,10 +318,8 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else if (dynamic_cast<AuthFailure*>(element.get())) { - delete authenticator; - authenticator = NULL; finishSession(Error::AuthenticationFailedError); } else if (dynamic_cast<TLSProceed*>(element.get())) { - checkState(WaitingForEncrypt); + CHECK_STATE_OR_RETURN(WaitingForEncrypt); state = Encrypting; stream->addTLSEncryption(); @@ -362,4 +368,5 @@ bool ClientSession::checkState(State state) { void ClientSession::sendCredentials(const SafeByteArray& password) { assert(WaitingForCredentials); + assert(authenticator); state = Authenticating; authenticator->setCredentials(localJID.getNode(), password); @@ -368,24 +375,24 @@ void ClientSession::sendCredentials(const SafeByteArray& password) { void ClientSession::handleTLSEncrypted() { - checkState(Encrypting); + CHECK_STATE_OR_RETURN(Encrypting); - Certificate::ref certificate = stream->getPeerCertificate(); + std::vector<Certificate::ref> certificateChain = stream->getPeerCertificateChain(); boost::shared_ptr<CertificateVerificationError> verificationError = stream->getPeerCertificateVerificationError(); if (verificationError) { - checkTrustOrFinish(certificate, verificationError); + checkTrustOrFinish(certificateChain, verificationError); } else { - ServerIdentityVerifier identityVerifier(localJID); - if (identityVerifier.certificateVerifies(certificate)) { + ServerIdentityVerifier identityVerifier(localJID, idnConverter); + if (!certificateChain.empty() && identityVerifier.certificateVerifies(certificateChain[0])) { continueAfterTLSEncrypted(); } else { - checkTrustOrFinish(certificate, boost::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidServerIdentity)); + checkTrustOrFinish(certificateChain, boost::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidServerIdentity)); } } } -void ClientSession::checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr<CertificateVerificationError> error) { - if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificate)) { +void ClientSession::checkTrustOrFinish(const std::vector<Certificate::ref>& certificateChain, boost::shared_ptr<CertificateVerificationError> error) { + if (certificateTrustChecker && certificateTrustChecker->isCertificateTrusted(certificateChain)) { continueAfterTLSEncrypted(); } @@ -441,8 +448,15 @@ void ClientSession::finishSession(boost::shared_ptr<Swift::Error> error) { error_ = error; } + else { + SWIFT_LOG(warning) << "Session finished twice"; + } assert(stream->isOpen()); if (stanzaAckResponder_) { stanzaAckResponder_->handleAckRequestReceived(); } + if (authenticator) { + delete authenticator; + authenticator = NULL; + } stream->writeFooter(); stream->close(); diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h index 2205c8d..8a3deb4 100644 --- a/Swiften/Client/ClientSession.h +++ b/Swiften/Client/ClientSession.h @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -11,9 +11,10 @@ #include <boost/enable_shared_from_this.hpp> +#include <Swiften/Base/API.h> #include <Swiften/Base/Error.h> #include <Swiften/Session/SessionStream.h> #include <string> #include <Swiften/JID/JID.h> -#include <Swiften/Elements/Element.h> +#include <Swiften/Elements/ToplevelElement.h> #include <Swiften/StreamManagement/StanzaAckRequester.h> #include <Swiften/StreamManagement/StanzaAckResponder.h> @@ -22,6 +23,8 @@ namespace Swift { class ClientAuthenticator; class CertificateTrustChecker; + class IDNConverter; + class CryptoProvider; - class ClientSession : public boost::enable_shared_from_this<ClientSession> { + class SWIFTEN_API ClientSession : public boost::enable_shared_from_this<ClientSession> { public: enum State { @@ -53,5 +56,5 @@ namespace Swift { TLSClientCertificateError, TLSError, - StreamError, + StreamError } type; Error(Type type) : type(type) {} @@ -66,6 +69,6 @@ namespace Swift { ~ClientSession(); - static boost::shared_ptr<ClientSession> create(const JID& jid, boost::shared_ptr<SessionStream> stream) { - return boost::shared_ptr<ClientSession>(new ClientSession(jid, stream)); + static boost::shared_ptr<ClientSession> create(const JID& jid, boost::shared_ptr<SessionStream> stream, IDNConverter* idnConverter, CryptoProvider* crypto) { + return boost::shared_ptr<ClientSession>(new ClientSession(jid, stream, idnConverter, crypto)); } @@ -92,5 +95,7 @@ namespace Swift { bool getStreamManagementEnabled() const { - return stanzaAckRequester_; + // Explicitly convert to bool. In C++11, it would be cleaner to + // compare to nullptr. + return static_cast<bool>(stanzaAckRequester_); } @@ -99,4 +104,8 @@ namespace Swift { } + std::vector<Certificate::ref> getPeerCertificateChain() const { + return stream->getPeerCertificateChain(); + } + const JID& getLocalJID() const { return localJID; @@ -127,5 +136,7 @@ namespace Swift { ClientSession( const JID& jid, - boost::shared_ptr<SessionStream>); + boost::shared_ptr<SessionStream>, + IDNConverter* idnConverter, + CryptoProvider* crypto); void finishSession(Error::Type error); @@ -138,5 +149,5 @@ namespace Swift { void sendStreamHeader(); - void handleElement(boost::shared_ptr<Element>); + void handleElement(boost::shared_ptr<ToplevelElement>); void handleStreamStart(const ProtocolHeader&); void handleStreamClosed(boost::shared_ptr<Swift::Error>); @@ -151,5 +162,5 @@ namespace Swift { void ack(unsigned int handledStanzasCount); void continueAfterTLSEncrypted(); - void checkTrustOrFinish(Certificate::ref certificate, boost::shared_ptr<CertificateVerificationError> error); + void checkTrustOrFinish(const std::vector<Certificate::ref>& certificateChain, boost::shared_ptr<CertificateVerificationError> error); private: @@ -157,4 +168,6 @@ namespace Swift { State state; boost::shared_ptr<SessionStream> stream; + IDNConverter* idnConverter; + CryptoProvider* crypto; bool allowPLAINOverNonTLS; bool useStreamCompression; diff --git a/Swiften/Client/ClientSessionStanzaChannel.cpp b/Swiften/Client/ClientSessionStanzaChannel.cpp index 5dc0a42..8d85953 100644 --- a/Swiften/Client/ClientSessionStanzaChannel.cpp +++ b/Swiften/Client/ClientSessionStanzaChannel.cpp @@ -83,4 +83,11 @@ bool ClientSessionStanzaChannel::getStreamManagementEnabled() const { } +std::vector<Certificate::ref> ClientSessionStanzaChannel::getPeerCertificateChain() const { + if (session) { + return session->getPeerCertificateChain(); + } + return std::vector<Certificate::ref>(); +} + void ClientSessionStanzaChannel::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) { onStanzaAcked(stanza); diff --git a/Swiften/Client/ClientSessionStanzaChannel.h b/Swiften/Client/ClientSessionStanzaChannel.h index 47fb50e..2743a16 100644 --- a/Swiften/Client/ClientSessionStanzaChannel.h +++ b/Swiften/Client/ClientSessionStanzaChannel.h @@ -28,4 +28,5 @@ namespace Swift { void sendPresence(boost::shared_ptr<Presence> presence); bool getStreamManagementEnabled() const; + virtual std::vector<Certificate::ref> getPeerCertificateChain() const; bool isAvailable() const { diff --git a/Swiften/Client/ClientXMLTracer.cpp b/Swiften/Client/ClientXMLTracer.cpp index 405e3d1..9bfa047 100644 --- a/Swiften/Client/ClientXMLTracer.cpp +++ b/Swiften/Client/ClientXMLTracer.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -10,10 +10,16 @@ #include <boost/bind.hpp> +#include <Swiften/Base/Platform.h> + namespace Swift { ClientXMLTracer::ClientXMLTracer(CoreClient* client, bool bosh) : bosh(bosh) { +#ifdef SWIFTEN_PLATFORM_WIN32 + beautifier = new XMLBeautifier(true, false); +#else beautifier = new XMLBeautifier(true, true); - client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, this, '<', _1)); - client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, this, '>', _1)); +#endif + onDataReadConnection = client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, this, '<', _1)); + onDataWrittenConnection = client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, this, '>', _1)); } @@ -26,12 +32,12 @@ void ClientXMLTracer::printData(char direction, const SafeByteArray& data) { 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 { +// Disabled because it swallows bits of XML (namespaces, if I recall) +// size_t endOfHTTP = line.find("\r\n\r\n"); +// if (false && endOfHTTP != std::string::npos) { +// std::cerr << line.substr(0, endOfHTTP) << std::endl << beautifier->beautify(line.substr(endOfHTTP)) << std::endl; +// } +// else { std::cerr << line << std::endl; - } +// } } else { diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h index 67040c4..95e2f6f 100644 --- a/Swiften/Client/ClientXMLTracer.h +++ b/Swiften/Client/ClientXMLTracer.h @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -7,4 +7,5 @@ #pragma once +#include <Swiften/Base/API.h> #include <Swiften/Client/CoreClient.h> #include <Swiften/Client/XMLBeautifier.h> @@ -12,8 +13,9 @@ namespace Swift { - class ClientXMLTracer { + class SWIFTEN_API ClientXMLTracer { public: ClientXMLTracer(CoreClient* client, bool bosh = false); ~ClientXMLTracer(); + private: void printData(char direction, const SafeByteArray& data); @@ -23,4 +25,6 @@ namespace Swift { XMLBeautifier *beautifier; bool bosh; + boost::bsignals::scoped_connection onDataReadConnection; + boost::bsignals::scoped_connection onDataWrittenConnection; }; } diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index 8a922ba..da755b1 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010-2011 Remko Tronçon + * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -8,4 +8,5 @@ #include <boost/bind.hpp> +#include <boost/optional.hpp> #include <boost/smart_ptr/make_shared.hpp> @@ -54,28 +55,68 @@ CoreClient::~CoreClient() { void CoreClient::connect(const ClientOptions& o) { - SWIFT_LOG(debug) << "Connecting" << std::endl; - options = o; - connect(jid_.getDomain()); -} + SWIFT_LOG(debug) << "Connecting "; -void CoreClient::connect(const std::string& host) { forceReset(); - - SWIFT_LOG(debug) << "Connecting to host " << host << std::endl; disconnectRequested_ = false; - assert(!connector_); + + options = o; + + + // Determine connection types to use assert(proxyConnectionFactories.empty()); - if (networkFactories->getProxyProvider()->getSOCKS5Proxy().isValid()) { - proxyConnectionFactories.push_back(new SOCKS5ProxiedConnectionFactory(networkFactories->getConnectionFactory(), networkFactories->getProxyProvider()->getSOCKS5Proxy())); + bool useDirectConnection = true; + HostAddressPort systemSOCKS5Proxy = networkFactories->getProxyProvider()->getSOCKS5Proxy(); + HostAddressPort systemHTTPConnectProxy = networkFactories->getProxyProvider()->getHTTPConnectProxy(); + switch (o.proxyType) { + case ClientOptions::NoProxy: + SWIFT_LOG(debug) << " without a proxy" << std::endl; + break; + case ClientOptions::SystemConfiguredProxy: + SWIFT_LOG(debug) << " with a system configured proxy" << std::endl; + if (systemSOCKS5Proxy.isValid()) { + SWIFT_LOG(debug) << "Found SOCK5 Proxy: " << systemSOCKS5Proxy.getAddress().toString() << ":" << systemSOCKS5Proxy.getPort() << std::endl; + proxyConnectionFactories.push_back(new SOCKS5ProxiedConnectionFactory(networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory(), systemSOCKS5Proxy.getAddress().toString(), systemSOCKS5Proxy.getPort())); + } + if (systemHTTPConnectProxy.isValid()) { + SWIFT_LOG(debug) << "Found HTTPConnect Proxy: " << systemHTTPConnectProxy.getAddress().toString() << ":" << systemHTTPConnectProxy.getPort() << std::endl; + proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory(), systemHTTPConnectProxy.getAddress().toString(), systemHTTPConnectProxy.getPort())); + } + break; + case ClientOptions::SOCKS5Proxy: { + SWIFT_LOG(debug) << " with manual configured SOCKS5 proxy" << std::endl; + std::string proxyHostname = o.manualProxyHostname.empty() ? systemSOCKS5Proxy.getAddress().toString() : o.manualProxyHostname; + int proxyPort = o.manualProxyPort == -1 ? systemSOCKS5Proxy.getPort() : o.manualProxyPort; + SWIFT_LOG(debug) << "Proxy: " << proxyHostname << ":" << proxyPort << std::endl; + proxyConnectionFactories.push_back(new SOCKS5ProxiedConnectionFactory(networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory(), proxyHostname, proxyPort)); + useDirectConnection = false; + break; + } + case ClientOptions::HTTPConnectProxy: { + SWIFT_LOG(debug) << " with manual configured HTTPConnect proxy" << std::endl; + std::string proxyHostname = o.manualProxyHostname.empty() ? systemHTTPConnectProxy.getAddress().toString() : o.manualProxyHostname; + int proxyPort = o.manualProxyPort == -1 ? systemHTTPConnectProxy.getPort() : o.manualProxyPort; + SWIFT_LOG(debug) << "Proxy: " << proxyHostname << ":" << proxyPort << std::endl; + proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory(), proxyHostname, proxyPort)); + useDirectConnection = false; + break; } - if(networkFactories->getProxyProvider()->getHTTPConnectProxy().isValid()) { - proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory(), networkFactories->getEventLoop(), networkFactories->getProxyProvider()->getHTTPConnectProxy().getAddress().toString(), networkFactories->getProxyProvider()->getHTTPConnectProxy().getPort())); } std::vector<ConnectionFactory*> connectionFactories(proxyConnectionFactories); - if (options.boshURL.empty()) { + if (useDirectConnection) { connectionFactories.push_back(networkFactories->getConnectionFactory()); - connector_ = boost::make_shared<ChainedConnector>(host, networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory()); + } + + // Create connector + std::string host = o.manualHostname.empty() ? jid_.getDomain() : o.manualHostname; + int port = o.manualPort; + boost::optional<std::string> serviceLookupPrefix; + if (o.manualHostname.empty()) { + serviceLookupPrefix = "_xmpp-client._tcp."; + } + assert(!connector_); + if (options.boshURL.isEmpty()) { + connector_ = boost::make_shared<ChainedConnector>(host, port, serviceLookupPrefix, networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory()); connector_->onConnectFinished.connect(boost::bind(&CoreClient::handleConnectorFinished, this, _1, _2)); - connector_->setTimeoutMilliseconds(60*1000); + connector_->setTimeoutMilliseconds(2*60*1000); connector_->start(); } @@ -107,5 +148,5 @@ void CoreClient::connect(const std::string& host) { void CoreClient::bindSessionToStream() { - session_ = ClientSession::create(jid_, sessionStream_); + session_ = ClientSession::create(jid_, sessionStream_, networkFactories->getIDNConverter(), networkFactories->getCryptoProvider()); session_->setCertificateTrustChecker(certificateTrustChecker); session_->setUseStreamCompression(options.useStreamCompression); @@ -361,4 +402,8 @@ bool CoreClient::getStreamManagementEnabled() const { } +bool CoreClient::isStreamEncrypted() const { + return sessionStream_->isTLSEncrypted(); +} + StanzaChannel* CoreClient::getStanzaChannel() const { return stanzaChannel_; @@ -406,9 +451,9 @@ void CoreClient::resetSession() { void CoreClient::forceReset() { if (connector_) { - std::cerr << "Warning: Client not disconnected properly: Connector still active" << std::endl; + SWIFT_LOG(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; + SWIFT_LOG(warning) << "Client not disconnected properly: Session still active" << std::endl; resetSession(); } diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h index cafc634..eadfd9d 100644 --- a/Swiften/Client/CoreClient.h +++ b/Swiften/Client/CoreClient.h @@ -9,6 +9,7 @@ #include <string> #include <boost/shared_ptr.hpp> -#include <Swiften/Base/boost_bsignals.h> +#include <Swiften/Base/API.h> +#include <Swiften/Base/boost_bsignals.h> #include <Swiften/Entity/Entity.h> #include <Swiften/JID/JID.h> @@ -46,9 +47,8 @@ namespace Swift { * for most needs. */ - class CoreClient : public Entity { + class SWIFTEN_API CoreClient : public Entity { public: /** * Constructs a client for the given JID with the given password. - * The given eventLoop will be used to post events to. */ CoreClient(const JID& jid, const SafeByteArray& password, NetworkFactories* networkFactories); @@ -75,6 +75,4 @@ namespace Swift { void disconnect(); - void connect(const std::string& host); - /** * Sends a message. @@ -128,4 +126,9 @@ namespace Swift { bool getStreamManagementEnabled() const; + /** + * Checks whether stream encryption (TLS) is currently active. + */ + bool isStreamEncrypted() const; + StanzaChannel* getStanzaChannel() const; @@ -201,5 +204,5 @@ namespace Swift { * Called before onConnected signal is emmitted. */ - virtual void handleConnected() {}; + virtual void handleConnected() {} private: diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h index c2f3919..5cdedba 100644 --- a/Swiften/Client/DummyStanzaChannel.h +++ b/Swiften/Client/DummyStanzaChannel.h @@ -80,4 +80,8 @@ namespace Swift { } + std::vector<Certificate::ref> getPeerCertificateChain() const { + return std::vector<Certificate::ref>(); + } + std::vector<boost::shared_ptr<Stanza> > sentStanzas; bool available_; diff --git a/Swiften/Client/MemoryStorages.cpp b/Swiften/Client/MemoryStorages.cpp index fe171f7..850c1b2 100644 --- a/Swiften/Client/MemoryStorages.cpp +++ b/Swiften/Client/MemoryStorages.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -10,12 +10,18 @@ #include <Swiften/Disco/CapsMemoryStorage.h> #include <Swiften/Roster/RosterMemoryStorage.h> +#include <Swiften/History/SQLiteHistoryStorage.h> namespace Swift { -MemoryStorages::MemoryStorages() { - vcardStorage = new VCardMemoryStorage(); +MemoryStorages::MemoryStorages(CryptoProvider* crypto) { + vcardStorage = new VCardMemoryStorage(crypto); capsStorage = new CapsMemoryStorage(); avatarStorage = new AvatarMemoryStorage(); rosterStorage = new RosterMemoryStorage(); +#ifdef SWIFT_EXPERIMENTAL_HISTORY + historyStorage = new SQLiteHistoryStorage(":memory:"); +#else + historyStorage = NULL; +#endif } @@ -25,4 +31,5 @@ MemoryStorages::~MemoryStorages() { delete capsStorage; delete vcardStorage; + delete historyStorage; } @@ -43,4 +50,12 @@ RosterStorage* MemoryStorages::getRosterStorage() const { } +HistoryStorage* MemoryStorages::getHistoryStorage() const { +#ifdef SWIFT_EXPERIMENTAL_HISTORY + return historyStorage; +#else + return NULL; +#endif +} + } diff --git a/Swiften/Client/MemoryStorages.h b/Swiften/Client/MemoryStorages.h index ca01a7a..68ec285 100644 --- a/Swiften/Client/MemoryStorages.h +++ b/Swiften/Client/MemoryStorages.h @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -11,4 +11,5 @@ namespace Swift { class VCardMemoryStorage; + class CryptoProvider; /** @@ -18,5 +19,5 @@ namespace Swift { class MemoryStorages : public Storages { public: - MemoryStorages(); + MemoryStorages(CryptoProvider*); ~MemoryStorages(); @@ -25,4 +26,5 @@ namespace Swift { virtual CapsStorage* getCapsStorage() const; virtual RosterStorage* getRosterStorage() const; + virtual HistoryStorage* getHistoryStorage() const; private: @@ -31,4 +33,5 @@ namespace Swift { CapsStorage* capsStorage; RosterStorage* rosterStorage; + HistoryStorage* historyStorage; }; } diff --git a/Swiften/Client/NickManager.h b/Swiften/Client/NickManager.h index 288aa7b..c5a452e 100644 --- a/Swiften/Client/NickManager.h +++ b/Swiften/Client/NickManager.h @@ -7,9 +7,10 @@ #pragma once +#include <Swiften/Base/API.h> #include <Swiften/Base/boost_bsignals.h> #include <string> namespace Swift { - class NickManager { + class SWIFTEN_API NickManager { public: virtual ~NickManager(); diff --git a/Swiften/Client/NickResolver.h b/Swiften/Client/NickResolver.h index 584f2ce..306703e 100644 --- a/Swiften/Client/NickResolver.h +++ b/Swiften/Client/NickResolver.h @@ -7,7 +7,8 @@ #include <map> #include <boost/shared_ptr.hpp> +#include <string> +#include <Swiften/Base/API.h> #include <Swiften/Base/boost_bsignals.h> -#include <string> #include <Swiften/JID/JID.h> #include <Swiften/Elements/VCard.h> @@ -17,5 +18,6 @@ namespace Swift { class MUCRegistry; class VCardManager; - class NickResolver { + + class SWIFTEN_API NickResolver { public: NickResolver(const JID& ownJID, XMPPRoster* xmppRoster, VCardManager* vcardManager, MUCRegistry* mucRegistry); diff --git a/Swiften/Client/StanzaChannel.h b/Swiften/Client/StanzaChannel.h index f1d76e0..5e85d3c 100644 --- a/Swiften/Client/StanzaChannel.h +++ b/Swiften/Client/StanzaChannel.h @@ -13,4 +13,5 @@ #include <Swiften/Elements/Message.h> #include <Swiften/Elements/Presence.h> +#include <Swiften/TLS/Certificate.h> namespace Swift { @@ -21,4 +22,5 @@ namespace Swift { virtual bool isAvailable() const = 0; virtual bool getStreamManagementEnabled() const = 0; + virtual std::vector<Certificate::ref> getPeerCertificateChain() const = 0; boost::signal<void (bool /* isAvailable */)> onAvailableChanged; diff --git a/Swiften/Client/Storages.h b/Swiften/Client/Storages.h index 1c5bbe9..76650a6 100644 --- a/Swiften/Client/Storages.h +++ b/Swiften/Client/Storages.h @@ -7,4 +7,6 @@ #pragma once +#include <Swiften/Base/API.h> + namespace Swift { class VCardStorage; @@ -12,4 +14,5 @@ namespace Swift { class CapsStorage; class RosterStorage; + class HistoryStorage; /** @@ -17,5 +20,5 @@ namespace Swift { * controllers. */ - class Storages { + class SWIFTEN_API Storages { public: virtual ~Storages(); @@ -25,4 +28,5 @@ namespace Swift { virtual CapsStorage* getCapsStorage() const = 0; virtual RosterStorage* getRosterStorage() const = 0; + virtual HistoryStorage* getHistoryStorage() const = 0; }; } diff --git a/Swiften/Client/UnitTest/ClientBlockListManagerTest.cpp b/Swiften/Client/UnitTest/ClientBlockListManagerTest.cpp new file mode 100644 index 0000000..dd6c95d --- /dev/null +++ b/Swiften/Client/UnitTest/ClientBlockListManagerTest.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <algorithm> + +#include <Swiften/Base/foreach.h> + +#include <Swiften/Client/StanzaChannel.h> +#include <Swiften/Client/DummyStanzaChannel.h> +#include <Swiften/Client/ClientBlockListManager.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Elements/IQ.h> + +using namespace Swift; + +class ClientBlockListManagerTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(ClientBlockListManagerTest); + CPPUNIT_TEST(testFetchBlockList); + CPPUNIT_TEST(testBlockCommand); + CPPUNIT_TEST(testUnblockCommand); + CPPUNIT_TEST(testUnblockAllCommand); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + ownJID_ = JID("kev@wonderland.lit"); + stanzaChannel_ = new DummyStanzaChannel(); + iqRouter_ = new IQRouter(stanzaChannel_); + iqRouter_->setJID(ownJID_); + clientBlockListManager_ = new ClientBlockListManager(iqRouter_); + } + + void testFetchBlockList() { + std::vector<JID> blockJids; + blockJids.push_back(JID("romeo@montague.net")); + blockJids.push_back(JID("iago@shakespeare.lit")); + helperInitialBlockListFetch(blockJids); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), clientBlockListManager_->getBlockList()->getItems().size()); + } + + void testBlockCommand() { + // start with an already fetched block list + helperInitialBlockListFetch(std::vector<JID>(1, JID("iago@shakespeare.lit"))); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), clientBlockListManager_->getBlockList()->getItems().size()); + CPPUNIT_ASSERT_EQUAL(BlockList::Available, clientBlockListManager_->getBlockList()->getState()); + + GenericRequest<BlockPayload>::ref blockRequest = clientBlockListManager_->createBlockJIDRequest(JID("romeo@montague.net")); + blockRequest->send(); + IQ::ref request = stanzaChannel_->getStanzaAtIndex<IQ>(2); + CPPUNIT_ASSERT(request.get() != NULL); + boost::shared_ptr<BlockPayload> blockPayload = request->getPayload<BlockPayload>(); + CPPUNIT_ASSERT(blockPayload.get() != NULL); + CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.net"), blockPayload->getItems().at(0)); + + IQ::ref blockRequestResponse = IQ::createResult(request->getFrom(), JID(), request->getID()); + stanzaChannel_->sendIQ(blockRequestResponse); + stanzaChannel_->onIQReceived(blockRequestResponse); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), clientBlockListManager_->getBlockList()->getItems().size()); + + // send block push + boost::shared_ptr<BlockPayload> pushPayload = boost::make_shared<BlockPayload>(); + pushPayload->addItem(JID("romeo@montague.net")); + IQ::ref blockPush = IQ::createRequest(IQ::Set, ownJID_, "push1", pushPayload); + stanzaChannel_->sendIQ(blockPush); + stanzaChannel_->onIQReceived(blockPush); + + std::vector<JID> blockedJIDs = clientBlockListManager_->getBlockList()->getItems(); + CPPUNIT_ASSERT(blockedJIDs.end() != std::find(blockedJIDs.begin(), blockedJIDs.end(), JID("romeo@montague.net"))); + } + + void testUnblockCommand() { + // start with an already fetched block list + std::vector<JID> initialBlockList = std::vector<JID>(1, JID("iago@shakespeare.lit")); + initialBlockList.push_back(JID("romeo@montague.net")); + helperInitialBlockListFetch(initialBlockList); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), clientBlockListManager_->getBlockList()->getItems().size()); + CPPUNIT_ASSERT_EQUAL(BlockList::Available, clientBlockListManager_->getBlockList()->getState()); + + GenericRequest<UnblockPayload>::ref unblockRequest = clientBlockListManager_->createUnblockJIDRequest(JID("romeo@montague.net")); + unblockRequest->send(); + IQ::ref request = stanzaChannel_->getStanzaAtIndex<IQ>(2); + CPPUNIT_ASSERT(request.get() != NULL); + boost::shared_ptr<UnblockPayload> unblockPayload = request->getPayload<UnblockPayload>(); + CPPUNIT_ASSERT(unblockPayload.get() != NULL); + CPPUNIT_ASSERT_EQUAL(JID("romeo@montague.net"), unblockPayload->getItems().at(0)); + + IQ::ref unblockRequestResponse = IQ::createResult(request->getFrom(), JID(), request->getID()); + stanzaChannel_->sendIQ(unblockRequestResponse); + stanzaChannel_->onIQReceived(unblockRequestResponse); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), clientBlockListManager_->getBlockList()->getItems().size()); + + // send block push + boost::shared_ptr<UnblockPayload> pushPayload = boost::make_shared<UnblockPayload>(); + pushPayload->addItem(JID("romeo@montague.net")); + IQ::ref unblockPush = IQ::createRequest(IQ::Set, ownJID_, "push1", pushPayload); + stanzaChannel_->sendIQ(unblockPush); + stanzaChannel_->onIQReceived(unblockPush); + + std::vector<JID> blockedJIDs = clientBlockListManager_->getBlockList()->getItems(); + CPPUNIT_ASSERT(blockedJIDs.end() == std::find(blockedJIDs.begin(), blockedJIDs.end(), JID("romeo@montague.net"))); + } + + void testUnblockAllCommand() { + // start with an already fetched block list + std::vector<JID> initialBlockList = std::vector<JID>(1, JID("iago@shakespeare.lit")); + initialBlockList.push_back(JID("romeo@montague.net")); + initialBlockList.push_back(JID("benvolio@montague.net")); + helperInitialBlockListFetch(initialBlockList); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), clientBlockListManager_->getBlockList()->getItems().size()); + CPPUNIT_ASSERT_EQUAL(BlockList::Available, clientBlockListManager_->getBlockList()->getState()); + + GenericRequest<UnblockPayload>::ref unblockRequest = clientBlockListManager_->createUnblockAllRequest(); + unblockRequest->send(); + IQ::ref request = stanzaChannel_->getStanzaAtIndex<IQ>(2); + CPPUNIT_ASSERT(request.get() != NULL); + boost::shared_ptr<UnblockPayload> unblockPayload = request->getPayload<UnblockPayload>(); + CPPUNIT_ASSERT(unblockPayload.get() != NULL); + CPPUNIT_ASSERT_EQUAL(true, unblockPayload->getItems().empty()); + + IQ::ref unblockRequestResponse = IQ::createResult(request->getFrom(), JID(), request->getID()); + stanzaChannel_->sendIQ(unblockRequestResponse); + stanzaChannel_->onIQReceived(unblockRequestResponse); + + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), clientBlockListManager_->getBlockList()->getItems().size()); + + // send block push + boost::shared_ptr<UnblockPayload> pushPayload = boost::make_shared<UnblockPayload>(); + IQ::ref unblockPush = IQ::createRequest(IQ::Set, ownJID_, "push1", pushPayload); + stanzaChannel_->sendIQ(unblockPush); + stanzaChannel_->onIQReceived(unblockPush); + + CPPUNIT_ASSERT_EQUAL(true, clientBlockListManager_->getBlockList()->getItems().empty()); + } + + void tearDown() { + delete clientBlockListManager_; + delete iqRouter_; + delete stanzaChannel_; + } + + private: + void helperInitialBlockListFetch(const std::vector<JID>& blockedJids) { + boost::shared_ptr<BlockList> blockList = clientBlockListManager_->requestBlockList(); + CPPUNIT_ASSERT(blockList); + + // check for IQ request + IQ::ref request = stanzaChannel_->getStanzaAtIndex<IQ>(0); + CPPUNIT_ASSERT(request.get() != NULL); + boost::shared_ptr<BlockListPayload> requestPayload = request->getPayload<BlockListPayload>(); + CPPUNIT_ASSERT(requestPayload.get() != NULL); + + CPPUNIT_ASSERT_EQUAL(BlockList::Requesting, blockList->getState()); + CPPUNIT_ASSERT_EQUAL(BlockList::Requesting, clientBlockListManager_->getBlockList()->getState()); + + // build IQ response + boost::shared_ptr<BlockListPayload> responsePayload = boost::make_shared<BlockListPayload>(); + foreach(const JID& jid, blockedJids) { + responsePayload->addItem(jid); + } + + IQ::ref response = IQ::createResult(ownJID_, JID(), request->getID(), responsePayload); + stanzaChannel_->sendIQ(response); + stanzaChannel_->onIQReceived(response); + + CPPUNIT_ASSERT_EQUAL(BlockList::Available, clientBlockListManager_->getBlockList()->getState()); + CPPUNIT_ASSERT(responsePayload->getItems() == clientBlockListManager_->getBlockList()->getItems()); + } + + + private: + JID ownJID_; + IQRouter* iqRouter_; + DummyStanzaChannel* stanzaChannel_; + ClientBlockListManager* clientBlockListManager_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ClientBlockListManagerTest); + diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp index 6793643..4e1fc31 100644 --- a/Swiften/Client/UnitTest/ClientSessionTest.cpp +++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2014 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -12,7 +12,10 @@ #include <boost/smart_ptr/make_shared.hpp> +#include <Swiften/IDN/IDNConverter.h> +#include <Swiften/IDN/PlatformIDNConverter.h> #include <Swiften/Session/SessionStream.h> #include <Swiften/Client/ClientSession.h> #include <Swiften/Elements/Message.h> +#include <Swiften/Elements/AuthChallenge.h> #include <Swiften/Elements/StartTLSRequest.h> #include <Swiften/Elements/StreamFeatures.h> @@ -31,4 +34,6 @@ #include <Swiften/TLS/SimpleCertificate.h> #include <Swiften/TLS/BlindCertificateTrustChecker.h> +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/Crypto/PlatformCryptoProvider.h> using namespace Swift; @@ -48,6 +53,8 @@ class ClientSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testAuthenticate_PLAINOverNonTLS); CPPUNIT_TEST(testAuthenticate_RequireTLS); + CPPUNIT_TEST(testAuthenticate_EXTERNAL); CPPUNIT_TEST(testStreamManagement); CPPUNIT_TEST(testStreamManagement_Failed); + CPPUNIT_TEST(testUnexpectedChallenge); CPPUNIT_TEST(testFinishAcksStanzas); /* @@ -67,4 +74,6 @@ class ClientSessionTest : public CppUnit::TestFixture { public: void setUp() { + crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); + idnConverter = boost::shared_ptr<IDNConverter>(PlatformIDNConverter::create()); server = boost::make_shared<MockSessionStream>(); sessionFinishedReceived = false; @@ -248,4 +257,32 @@ class ClientSessionTest : public CppUnit::TestFixture { } + void testAuthenticate_EXTERNAL() { + boost::shared_ptr<ClientSession> session(createSession()); + session->start(); + server->receiveStreamStart(); + server->sendStreamStart(); + server->sendStreamFeaturesWithEXTERNALAuthentication(); + server->receiveAuthRequest("EXTERNAL"); + server->sendAuthSuccess(); + server->receiveStreamStart(); + + session->finish(); + } + + void testUnexpectedChallenge() { + boost::shared_ptr<ClientSession> session(createSession()); + session->start(); + server->receiveStreamStart(); + server->sendStreamStart(); + server->sendStreamFeaturesWithEXTERNALAuthentication(); + server->receiveAuthRequest("EXTERNAL"); + server->sendChallenge(); + server->sendChallenge(); + + CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); + CPPUNIT_ASSERT(sessionFinishedReceived); + CPPUNIT_ASSERT(sessionFinishedError); + } + void testStreamManagement() { boost::shared_ptr<ClientSession> session(createSession()); @@ -309,5 +346,5 @@ class ClientSessionTest : public CppUnit::TestFixture { private: boost::shared_ptr<ClientSession> createSession() { - boost::shared_ptr<ClientSession> session = ClientSession::create(JID("me@foo.com"), server); + boost::shared_ptr<ClientSession> session = ClientSession::create(JID("me@foo.com"), server, idnConverter.get(), crypto.get()); session->onFinished.connect(boost::bind(&ClientSessionTest::handleSessionFinished, this, _1)); session->onNeedCredentials.connect(boost::bind(&ClientSessionTest::handleSessionNeedCredentials, this)); @@ -345,9 +382,9 @@ class ClientSessionTest : public CppUnit::TestFixture { public: struct Event { - Event(boost::shared_ptr<Element> element) : element(element), footer(false) {} + Event(boost::shared_ptr<ToplevelElement> element) : element(element), footer(false) {} Event(const ProtocolHeader& header) : header(header), footer(false) {} Event() : footer(true) {} - boost::shared_ptr<Element> element; + boost::shared_ptr<ToplevelElement> element; boost::optional<ProtocolHeader> header; bool footer; @@ -373,5 +410,5 @@ class ClientSessionTest : public CppUnit::TestFixture { } - virtual void writeElement(boost::shared_ptr<Element> element) { + virtual void writeElement(boost::shared_ptr<ToplevelElement> element) { receivedEvents.push_back(Event(element)); } @@ -400,4 +437,8 @@ class ClientSessionTest : public CppUnit::TestFixture { } + virtual std::vector<Certificate::ref> getPeerCertificateChain() const { + return std::vector<Certificate::ref>(); + } + virtual boost::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const { return boost::shared_ptr<CertificateVerificationError>(); @@ -441,4 +482,8 @@ class ClientSessionTest : public CppUnit::TestFixture { } + void sendChallenge() { + onElementReceived(boost::make_shared<AuthChallenge>()); + } + void sendStreamError() { onElementReceived(boost::make_shared<StreamError>()); @@ -467,4 +512,10 @@ class ClientSessionTest : public CppUnit::TestFixture { } + void sendStreamFeaturesWithEXTERNALAuthentication() { + boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); + streamFeatures->addAuthenticationMechanism("EXTERNAL"); + onElementReceived(streamFeatures); + } + void sendStreamFeaturesWithUnknownAuthentication() { boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); @@ -572,4 +623,5 @@ class ClientSessionTest : public CppUnit::TestFixture { }; + boost::shared_ptr<IDNConverter> idnConverter; boost::shared_ptr<MockSessionStream> server; bool sessionFinishedReceived; @@ -577,4 +629,5 @@ class ClientSessionTest : public CppUnit::TestFixture { boost::shared_ptr<Error> sessionFinishedError; BlindCertificateTrustChecker* blindCertificateTrustChecker; + boost::shared_ptr<CryptoProvider> crypto; }; diff --git a/Swiften/Client/UnitTest/NickResolverTest.cpp b/Swiften/Client/UnitTest/NickResolverTest.cpp index dfc90fe..a8b011c 100644 --- a/Swiften/Client/UnitTest/NickResolverTest.cpp +++ b/Swiften/Client/UnitTest/NickResolverTest.cpp @@ -5,4 +5,10 @@ */ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> @@ -15,4 +21,6 @@ #include <Swiften/Queries/IQRouter.h> #include <Swiften/Client/DummyStanzaChannel.h> +#include <Swiften/Crypto/CryptoProvider.h> +#include <Swiften/Crypto/PlatformCryptoProvider.h> using namespace Swift; @@ -35,9 +43,10 @@ class NickResolverTest : public CppUnit::TestFixture { public: void setUp() { + crypto = boost::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()); ownJID_ = JID("kev@wonderland.lit"); xmppRoster_ = new XMPPRosterImpl(); stanzaChannel_ = new DummyStanzaChannel(); iqRouter_ = new IQRouter(stanzaChannel_); - vCardStorage_ = new VCardMemoryStorage(); + vCardStorage_ = new VCardMemoryStorage(crypto.get()); vCardManager_ = new VCardManager(ownJID_, iqRouter_, vCardStorage_); registry_ = new MUCRegistry(); @@ -145,5 +154,5 @@ class NickResolverTest : public CppUnit::TestFixture { NickResolver* resolver_; JID ownJID_; - + boost::shared_ptr<CryptoProvider> crypto; }; diff --git a/Swiften/Client/XMLBeautifier.cpp b/Swiften/Client/XMLBeautifier.cpp index b70fc48..5d083ff 100644 --- a/Swiften/Client/XMLBeautifier.cpp +++ b/Swiften/Client/XMLBeautifier.cpp @@ -5,4 +5,10 @@ */ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + #include <sstream> #include <stack> @@ -39,11 +45,11 @@ void XMLBeautifier::indent() { // all bold but reset -const char colorBlue[] = "\x1b[01;34m"; -const char colorCyan[] = "\x1b[01;36m"; -const char colorGreen[] = "\x1b[01;32m"; -const char colorMagenta[] = "\x1b[01;35m"; -const char colorRed[] = "\x1b[01;31m"; -const char colorReset[] = "\x1b[0m"; -const char colorYellow[] = "\x1b[01;33m"; +// static const char colorBlue[] = "\x1b[01;34m"; +static const char colorCyan[] = "\x1b[01;36m"; +static const char colorGreen[] = "\x1b[01;32m"; +// static const char colorMagenta[] = "\x1b[01;35m"; +static const char colorRed[] = "\x1b[01;31m"; +static const char colorReset[] = "\x1b[0m"; +static const char colorYellow[] = "\x1b[01;33m"; diff --git a/Swiften/Client/XMLBeautifier.h b/Swiften/Client/XMLBeautifier.h index 44dfd20..25ecd18 100644 --- a/Swiften/Client/XMLBeautifier.h +++ b/Swiften/Client/XMLBeautifier.h @@ -5,6 +5,13 @@ */ +/* +* Copyright (c) 2014 Remko Tronçon and Kevin Smith +* Licensed under the GNU General Public License v3. +* See Documentation/Licenses/GPLv3.txt for more information. +*/ + #pragma once +#include <sstream> #include <string> #include <stack> |