diff options
Diffstat (limited to 'Swiften/Client')
31 files changed, 1025 insertions, 333 deletions
diff --git a/Swiften/Client/BlockList.cpp b/Swiften/Client/BlockList.cpp new file mode 100644 index 0000000..0b2fc12 --- /dev/null +++ b/Swiften/Client/BlockList.cpp @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Client/BlockList.h> + +using namespace Swift; + +BlockList::~BlockList() { + +} diff --git a/Swiften/Client/BlockList.h b/Swiften/Client/BlockList.h new file mode 100644 index 0000000..39a211d --- /dev/null +++ b/Swiften/Client/BlockList.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <set> + +#include <Swiften/JID/JID.h> +#include <Swiften/Base/boost_bsignals.h> + +namespace Swift { + class BlockList { + public: + enum State { + Requesting, + Available, + Error, + }; + virtual ~BlockList(); + + virtual State getState() const = 0; + + virtual const std::set<JID>& getItems() const = 0; + + public: + boost::signal<void ()> onStateChanged; + boost::signal<void (const JID&)> onItemAdded; + boost::signal<void (const JID&)> onItemRemoved; + }; +} diff --git a/Swiften/Client/BlockListImpl.cpp b/Swiften/Client/BlockListImpl.cpp new file mode 100644 index 0000000..dfaaaf1 --- /dev/null +++ b/Swiften/Client/BlockListImpl.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Client/BlockListImpl.h> + +#include <Swiften/Base/foreach.h> + +using namespace Swift; + +BlockListImpl::BlockListImpl() { + +} + +void BlockListImpl::setItems(const std::vector<JID>& items) { + this->items = std::set<JID>(items.begin(), items.end()); +} + +void BlockListImpl::addItem(const JID& item) { + if (items.insert(item).second) { + onItemAdded(item); + } +} + +void BlockListImpl::removeItem(const JID& item) { + if (items.erase(item)) { + onItemRemoved(item); + } +} + +void BlockListImpl::setState(State state) { + if (this->state != state) { + onStateChanged(); + } +} + +void BlockListImpl::addItems(const std::vector<JID>& items) { + foreach (const JID& item, items) { + addItem(item); + } +} + +void BlockListImpl::removeItems(const std::vector<JID>& items) { + foreach (const JID& item, items) { + removeItem(item); + } +} + +void BlockListImpl::removeAllItems() { + foreach (const JID& item, items) { + removeItem(item); + } +} + diff --git a/Swiften/Client/BlockListImpl.h b/Swiften/Client/BlockListImpl.h new file mode 100644 index 0000000..ef08340 --- /dev/null +++ b/Swiften/Client/BlockListImpl.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <Swiften/Client/BlockList.h> + +namespace Swift { + class BlockListImpl : public BlockList { + public: + BlockListImpl(); + + virtual State getState() const { + return state; + } + + void setState(State state); + + virtual const std::set<JID>& getItems() const { + return items; + } + + void setItems(const std::vector<JID>& items); + void addItem(const JID& item); + void removeItem(const JID& item); + void addItems(const std::vector<JID>& items); + void removeItems(const std::vector<JID>& items); + void removeAllItems(); + + private: + State state; + std::set<JID> items; + }; +} diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp index 7918c46..48eddc2 100644 --- a/Swiften/Client/Client.cpp +++ b/Swiften/Client/Client.cpp @@ -4,38 +4,45 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Client/Client.h" - -#include "Swiften/Queries/Responders/SoftwareVersionResponder.h" -#include "Swiften/Roster/XMPPRosterImpl.h" -#include "Swiften/Roster/XMPPRosterController.h" -#include "Swiften/Presence/PresenceOracle.h" -#include "Swiften/Presence/StanzaChannelPresenceSender.h" -#include "Swiften/Presence/DirectedPresenceSender.h" -#include "Swiften/MUC/MUCRegistry.h" -#include "Swiften/MUC/MUCManager.h" -#include "Swiften/Client/MemoryStorages.h" -#include "Swiften/VCards/VCardManager.h" -#include "Swiften/VCards/VCardManager.h" -#include "Swiften/Avatars/AvatarManagerImpl.h" -#include "Swiften/Disco/CapsManager.h" -#include "Swiften/Disco/EntityCapsManager.h" -#include "Swiften/Disco/ClientDiscoManager.h" -#include "Swiften/Client/NickResolver.h" -#include "Swiften/Presence/SubscriptionManager.h" -#include "Swiften/TLS/BlindCertificateTrustChecker.h" +#include <Swiften/Client/Client.h> + +#include <Swiften/Queries/Responders/SoftwareVersionResponder.h> +#include <Swiften/Roster/XMPPRosterImpl.h> +#include <Swiften/Roster/XMPPRosterController.h> +#include <Swiften/Presence/PresenceOracle.h> +#include <Swiften/Presence/StanzaChannelPresenceSender.h> +#include <Swiften/Presence/DirectedPresenceSender.h> +#include <Swiften/MUC/MUCRegistry.h> +#include <Swiften/MUC/MUCManager.h> +#include <Swiften/Client/MemoryStorages.h> +#include <Swiften/VCards/VCardManager.h> +#include <Swiften/VCards/VCardManager.h> +#include <Swiften/Avatars/AvatarManagerImpl.h> +#include <Swiften/Disco/CapsManager.h> +#include <Swiften/Disco/EntityCapsManager.h> +#include <Swiften/Disco/ClientDiscoManager.h> +#include <Swiften/Client/NickResolver.h> +#include <Swiften/Presence/SubscriptionManager.h> +#include <Swiften/TLS/BlindCertificateTrustChecker.h> #include <Swiften/Client/NickManagerImpl.h> +#include <Swiften/Client/ClientSession.h> +#include <Swiften/Jingle/JingleSessionManager.h> +#include <Swiften/Network/NetworkFactories.h> +#include <Swiften/FileTransfer/FileTransferManagerImpl.h> +#ifndef SWIFT_EXPERIMENTAL_FT +#include <Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h> +#endif namespace Swift { -Client::Client(const JID& jid, const std::string& password, NetworkFactories* networkFactories, Storages* storages) : CoreClient(jid, password, networkFactories), storages(storages) { +Client::Client(const JID& jid, const SafeString& password, NetworkFactories* networkFactories, Storages* storages) : CoreClient(jid, password, networkFactories), storages(storages) { memoryStorages = new MemoryStorages(); softwareVersionResponder = new SoftwareVersionResponder(getIQRouter()); softwareVersionResponder->start(); roster = new XMPPRosterImpl(); - rosterController = new XMPPRosterController(getIQRouter(), roster); + rosterController = new XMPPRosterController(getIQRouter(), roster, getStorages()->getRosterStorage()); subscriptionManager = new SubscriptionManager(getStanzaChannel()); @@ -58,9 +65,15 @@ Client::Client(const JID& jid, const std::string& password, NetworkFactories* ne nickResolver = new NickResolver(jid.toBare(), roster, vcardManager, mucRegistry); blindCertificateTrustChecker = new BlindCertificateTrustChecker(); + + jingleSessionManager = new JingleSessionManager(getIQRouter()); + fileTransferManager = NULL; } Client::~Client() { + delete fileTransferManager; + delete jingleSessionManager; + delete blindCertificateTrustChecker; delete nickResolver; @@ -97,7 +110,20 @@ void Client::setSoftwareVersion(const std::string& name, const std::string& vers softwareVersionResponder->setVersion(name, version, os); } +void Client::handleConnected() { +#ifdef SWIFT_EXPERIMENTAL_FT + fileTransferManager = new FileTransferManagerImpl(getJID(), jingleSessionManager, getIQRouter(), getEntityCapsProvider(), presenceOracle, getNetworkFactories()->getConnectionFactory(), getNetworkFactories()->getConnectionServerFactory(), getNetworkFactories()->getTimerFactory(), getNetworkFactories()->getNATTraverser()); +#else + fileTransferManager = new DummyFileTransferManager(); +#endif +} + void Client::requestRoster() { + // FIXME: We should set this once when the session is finished, but there + // is currently no callback for this + if (getSession()) { + rosterController->setUseVersioning(getSession()->getRosterVersioningSupported()); + } rosterController->requestRoster(); } @@ -133,4 +159,8 @@ NickManager* Client::getNickManager() const { return nickManager; } +FileTransferManager* Client::getFileTransferManager() const { + return fileTransferManager; +} + } diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h index 083b8a0..940a526 100644 --- a/Swiften/Client/Client.h +++ b/Swiften/Client/Client.h @@ -6,7 +6,9 @@ #pragma once -#include "Swiften/Client/CoreClient.h" +#include <Swiften/Client/CoreClient.h> + +#include <Swiften/Base/SafeString.h> namespace Swift { class SoftwareVersionResponder; @@ -31,6 +33,9 @@ namespace Swift { class SubscriptionManager; class ClientDiscoManager; class NickManager; + class FileTransferManager; + class JingleSessionManager; + class FileTransferManager; /** * Provides the core functionality for writing XMPP client software. @@ -47,7 +52,7 @@ namespace Swift { * this is NULL, * all data will be stored in memory (and be lost on shutdown) */ - Client(const JID& jid, const std::string& password, NetworkFactories* networkFactories, Storages* storages = NULL); + Client(const JID& jid, const SafeString& password, NetworkFactories* networkFactories, Storages* storages = NULL); ~Client(); @@ -85,12 +90,12 @@ namespace Swift { /** * Returns the last received presence for the given (full) JID. */ - Presence::ref getLastPresence(const JID& jid) const; + boost::shared_ptr<Presence> getLastPresence(const JID& jid) const; /** * Returns the presence with the highest priority received for the given JID. */ - Presence::ref getHighestPriorityPresence(const JID& bareJID) const; + boost::shared_ptr<Presence> getHighestPriorityPresence(const JID& bareJID) const; PresenceOracle* getPresenceOracle() const { return presenceOracle; @@ -129,6 +134,14 @@ namespace Swift { ClientDiscoManager* getDiscoManager() const { return discoManager; } + + /** + * Returns a FileTransferManager for the client. This is only available after the onConnected + * signal has been fired. + * + * WARNING: File transfer will only work if Swiften is built in 'experimental' mode. + */ + FileTransferManager* getFileTransferManager() const; /** * Configures the client to always trust a non-validating @@ -142,11 +155,14 @@ namespace Swift { /** * This signal is emitted when a JID changes presence. */ - boost::signal<void (Presence::ref)> onPresenceChange; + boost::signal<void (boost::shared_ptr<Presence>)> onPresenceChange; private: Storages* getStorages() const; + protected: + void handleConnected(); + private: Storages* storages; MemoryStorages* memoryStorages; @@ -166,6 +182,8 @@ namespace Swift { SubscriptionManager* subscriptionManager; MUCManager* mucManager; ClientDiscoManager* discoManager; + JingleSessionManager* jingleSessionManager; + FileTransferManager* fileTransferManager; BlindCertificateTrustChecker* blindCertificateTrustChecker; }; } diff --git a/Swiften/Client/ClientBlockListManager.cpp b/Swiften/Client/ClientBlockListManager.cpp new file mode 100644 index 0000000..7222cea --- /dev/null +++ b/Swiften/Client/ClientBlockListManager.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Client/ClientBlockListManager.h> + +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <cassert> + +#include <Swiften/Client/BlockListImpl.h> + +using namespace Swift; + +namespace { + class BlockResponder : public SetResponder<BlockPayload> { + public: + BlockResponder(boost::shared_ptr<BlockListImpl> blockList, IQRouter* iqRouter) : SetResponder<BlockPayload>(iqRouter), blockList(blockList) { + } + + virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr<BlockPayload> payload) { + if (getIQRouter()->isAccountJID(from)) { + if (payload) { + blockList->addItems(payload->getItems()); + } + sendResponse(from, id, boost::shared_ptr<BlockPayload>()); + } + else { + sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Cancel); + } + return true; + } + + private: + boost::shared_ptr<BlockListImpl> blockList; + }; + + class UnblockResponder : public SetResponder<UnblockPayload> { + public: + UnblockResponder(boost::shared_ptr<BlockListImpl> blockList, IQRouter* iqRouter) : SetResponder<UnblockPayload>(iqRouter), blockList(blockList) { + } + + virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr<UnblockPayload> payload) { + if (getIQRouter()->isAccountJID(from)) { + if (payload) { + if (payload->getItems().empty()) { + blockList->removeAllItems(); + } + else { + blockList->removeItems(payload->getItems()); + } + } + sendResponse(from, id, boost::shared_ptr<UnblockPayload>()); + } + else { + sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Cancel); + } + return true; + } + + private: + boost::shared_ptr<BlockListImpl> blockList; + }; +} + +ClientBlockListManager::ClientBlockListManager(IQRouter* iqRouter) : iqRouter(iqRouter) { +} + +ClientBlockListManager::~ClientBlockListManager() { + unblockResponder->stop(); + blockResponder->stop(); + if (getRequest) { + getRequest->onResponse.disconnect(boost::bind(&ClientBlockListManager::handleBlockListReceived, this, _1, _2)); + } +} + +boost::shared_ptr<BlockList> ClientBlockListManager::getBlockList() { + 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); + getRequest->onResponse.connect(boost::bind(&ClientBlockListManager::handleBlockListReceived, this, _1, _2)); + getRequest->send(); + } + return blockList; +} + +void ClientBlockListManager::handleBlockListReceived(boost::shared_ptr<BlockListPayload> payload, ErrorPayload::ref error) { + if (error || !payload) { + blockList->setState(BlockList::Error); + } + else { + blockList->setState(BlockList::Available); + blockList->setItems(payload->getItems()); + blockResponder = boost::make_shared<BlockResponder>(blockList, iqRouter); + blockResponder->start(); + unblockResponder = boost::make_shared<UnblockResponder>(blockList, iqRouter); + unblockResponder->start(); + } +} + diff --git a/Swiften/Client/ClientBlockListManager.h b/Swiften/Client/ClientBlockListManager.h new file mode 100644 index 0000000..21d35e3 --- /dev/null +++ b/Swiften/Client/ClientBlockListManager.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> + +#include <Swiften/Base/boost_bsignals.h> +#include <Swiften/Elements/BlockPayload.h> +#include <Swiften/Elements/BlockListPayload.h> +#include <Swiften/Elements/UnblockPayload.h> +#include <Swiften/Queries/SetResponder.h> +#include <Swiften/Queries/GenericRequest.h> +#include <Swiften/Client/BlockList.h> +#include <Swiften/Client/BlockListImpl.h> + +namespace Swift { + class IQRouter; + + class ClientBlockListManager { + public: + ClientBlockListManager(IQRouter *iqRouter); + ~ClientBlockListManager(); + + bool isSupported() const; + + /** + * Returns the blocklist. + */ + boost::shared_ptr<BlockList> getBlockList(); + + private: + void handleBlockListReceived(boost::shared_ptr<BlockListPayload> payload, ErrorPayload::ref); + + private: + IQRouter* iqRouter; + boost::shared_ptr<GenericRequest<BlockListPayload> > getRequest; + boost::shared_ptr<SetResponder<BlockPayload> > blockResponder; + boost::shared_ptr<SetResponder<UnblockPayload> > unblockResponder; + boost::shared_ptr<BlockListImpl> blockList; + }; +} + diff --git a/Swiften/Client/ClientOptions.h b/Swiften/Client/ClientOptions.h new file mode 100644 index 0000000..3b51a87 --- /dev/null +++ b/Swiften/Client/ClientOptions.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +namespace Swift { + struct ClientOptions { + enum UseTLS { + NeverUseTLS, + UseTLSWhenAvailable, + RequireTLS + }; + + ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), allowPLAINWithoutTLS(false), useStreamResumption(false), forgetPassword(false), useAcks(true) { + } + + /** + * Whether ZLib stream compression should be used when available. + * + * Default: true + */ + bool useStreamCompression; + + /** + * Sets whether TLS encryption should be used. + * + * Default: UseTLSWhenAvailable + */ + UseTLS useTLS; + + /** + * Sets whether plaintext authentication is + * allowed over non-TLS-encrypted connections. + * + * Default: false + */ + bool allowPLAINWithoutTLS; + + /** + * Use XEP-196 stream resumption when available. + * + * Default: false + */ + bool useStreamResumption; + + /** + * Forget the password once it's used. + * This makes the Client useless after the first login attempt. + * + * FIXME: This is a temporary workaround. + * + * Default: false + */ + bool forgetPassword; + + /** + * Use XEP-0198 acks in the stream when available. + * Default: true + */ + bool useAcks; + }; +} diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp index e1c1d8e..791ee75 100644 --- a/Swiften/Client/ClientSession.cpp +++ b/Swiften/Client/ClientSession.cpp @@ -4,41 +4,42 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Client/ClientSession.h" +#include <Swiften/Client/ClientSession.h> #include <boost/bind.hpp> #include <boost/uuid/uuid.hpp> #include <boost/uuid/uuid_io.hpp> #include <boost/uuid/uuid_generators.hpp> +#include <boost/smart_ptr/make_shared.hpp> -#include "Swiften/Elements/ProtocolHeader.h" -#include "Swiften/Elements/StreamFeatures.h" -#include "Swiften/Elements/StreamError.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/EnableStreamManagement.h" -#include "Swiften/Elements/StreamManagementEnabled.h" -#include "Swiften/Elements/StreamManagementFailed.h" -#include "Swiften/Elements/StartSession.h" -#include "Swiften/Elements/StanzaAck.h" -#include "Swiften/Elements/StanzaAckRequest.h" -#include "Swiften/Elements/IQ.h" -#include "Swiften/Elements/ResourceBind.h" -#include "Swiften/SASL/PLAINClientAuthenticator.h" -#include "Swiften/SASL/SCRAMSHA1ClientAuthenticator.h" -#include "Swiften/SASL/DIGESTMD5ClientAuthenticator.h" -#include "Swiften/Session/SessionStream.h" -#include "Swiften/TLS/CertificateTrustChecker.h" -#include "Swiften/TLS/ServerIdentityVerifier.h" +#include <Swiften/Elements/ProtocolHeader.h> +#include <Swiften/Elements/StreamFeatures.h> +#include <Swiften/Elements/StreamError.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/EnableStreamManagement.h> +#include <Swiften/Elements/StreamManagementEnabled.h> +#include <Swiften/Elements/StreamManagementFailed.h> +#include <Swiften/Elements/StartSession.h> +#include <Swiften/Elements/StanzaAck.h> +#include <Swiften/Elements/StanzaAckRequest.h> +#include <Swiften/Elements/IQ.h> +#include <Swiften/Elements/ResourceBind.h> +#include <Swiften/SASL/PLAINClientAuthenticator.h> +#include <Swiften/SASL/SCRAMSHA1ClientAuthenticator.h> +#include <Swiften/SASL/DIGESTMD5ClientAuthenticator.h> +#include <Swiften/Session/SessionStream.h> +#include <Swiften/TLS/CertificateTrustChecker.h> +#include <Swiften/TLS/ServerIdentityVerifier.h> namespace Swift { @@ -51,9 +52,11 @@ ClientSession::ClientSession( allowPLAINOverNonTLS(false), useStreamCompression(true), useTLS(UseTLSWhenAvailable), + useAcks(true), needSessionStart(false), needResourceBind(false), needAcking(false), + rosterVersioningSupported(false), authenticator(NULL), certificateTrustChecker(NULL) { } @@ -173,17 +176,20 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { if (streamFeatures->hasStartTLS() && stream->supportsTLSEncryption() && useTLS != NeverUseTLS) { state = WaitingForEncrypt; - stream->writeElement(boost::shared_ptr<StartTLSRequest>(new StartTLSRequest())); + stream->writeElement(boost::make_shared<StartTLSRequest>()); + } + else if (useTLS == RequireTLS && !stream->isTLSEncrypted()) { + finishSession(Error::NoSupportedAuthMechanismsError); } else if (useStreamCompression && streamFeatures->hasCompressionMethod("zlib")) { state = Compressing; - stream->writeElement(boost::shared_ptr<CompressRequest>(new CompressRequest("zlib"))); + stream->writeElement(boost::make_shared<CompressRequest>("zlib")); } else if (streamFeatures->hasAuthenticationMechanisms()) { if (stream->hasTLSCertificate()) { if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { state = Authenticating; - stream->writeElement(boost::shared_ptr<Element>(new AuthRequest("EXTERNAL", ""))); + stream->writeElement(boost::make_shared<AuthRequest>("EXTERNAL", createSafeByteArray(""))); } else { finishSession(Error::TLSClientCertificateError); @@ -191,7 +197,7 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else if (streamFeatures->hasAuthenticationMechanism("EXTERNAL")) { state = Authenticating; - stream->writeElement(boost::shared_ptr<Element>(new AuthRequest("EXTERNAL", ""))); + stream->writeElement(boost::make_shared<AuthRequest>("EXTERNAL", createSafeByteArray(""))); } else if (streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1") || streamFeatures->hasAuthenticationMechanism("SCRAM-SHA-1-PLUS")) { std::ostringstream s; @@ -223,10 +229,11 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { } else { // Start the session + rosterVersioningSupported = streamFeatures->hasRosterVersioning(); stream->setWhitespacePingEnabled(true); needSessionStart = streamFeatures->hasSession(); needResourceBind = streamFeatures->hasResourceBind(); - needAcking = streamFeatures->hasStreamManagement(); + needAcking = streamFeatures->hasStreamManagement() && useAcks; if (!needResourceBind) { // Resource binding is a MUST finishSession(Error::ResourceBindError); @@ -247,10 +254,10 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { finishSession(Error::CompressionFailedError); } else if (boost::dynamic_pointer_cast<StreamManagementEnabled>(element)) { - stanzaAckRequester_ = boost::shared_ptr<StanzaAckRequester>(new StanzaAckRequester()); + stanzaAckRequester_ = boost::make_shared<StanzaAckRequester>(); stanzaAckRequester_->onRequestAck.connect(boost::bind(&ClientSession::requestAck, shared_from_this())); stanzaAckRequester_->onStanzaAcked.connect(boost::bind(&ClientSession::handleStanzaAcked, shared_from_this(), _1)); - stanzaAckResponder_ = boost::shared_ptr<StanzaAckResponder>(new StanzaAckResponder()); + stanzaAckResponder_ = boost::make_shared<StanzaAckResponder>(); stanzaAckResponder_->onAck.connect(boost::bind(&ClientSession::ack, shared_from_this(), _1)); needAcking = false; continueSessionInitialization(); @@ -263,7 +270,7 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { checkState(Authenticating); assert(authenticator); if (authenticator->setChallenge(challenge->getValue())) { - stream->writeElement(boost::shared_ptr<AuthResponse>(new AuthResponse(authenticator->getResponse()))); + stream->writeElement(boost::make_shared<AuthResponse>(authenticator->getResponse())); } else { finishSession(Error::AuthenticationFailedError); @@ -272,6 +279,8 @@ 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; finishSession(Error::ServerVerificationFailedError); } else { @@ -305,7 +314,7 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) { void ClientSession::continueSessionInitialization() { if (needResourceBind) { state = BindingResource; - boost::shared_ptr<ResourceBind> resourceBind(new ResourceBind()); + boost::shared_ptr<ResourceBind> resourceBind(boost::make_shared<ResourceBind>()); if (!localJID.getResource().empty()) { resourceBind->setResource(localJID.getResource()); } @@ -313,11 +322,11 @@ void ClientSession::continueSessionInitialization() { } else if (needAcking) { state = EnablingSessionManagement; - stream->writeElement(boost::shared_ptr<EnableStreamManagement>(new EnableStreamManagement())); + stream->writeElement(boost::make_shared<EnableStreamManagement>()); } else if (needSessionStart) { state = StartingSession; - sendStanza(IQ::createRequest(IQ::Set, JID(), "session-start", boost::shared_ptr<StartSession>(new StartSession()))); + sendStanza(IQ::createRequest(IQ::Set, JID(), "session-start", boost::make_shared<StartSession>())); } else { state = Initialized; @@ -333,11 +342,11 @@ bool ClientSession::checkState(State state) { return true; } -void ClientSession::sendCredentials(const std::string& password) { +void ClientSession::sendCredentials(const SafeByteArray& password) { assert(WaitingForCredentials); state = Authenticating; authenticator->setCredentials(localJID.getNode(), password); - stream->writeElement(boost::shared_ptr<AuthRequest>(new AuthRequest(authenticator->getName(), authenticator->getResponse()))); + stream->writeElement(boost::make_shared<AuthRequest>(authenticator->getName(), authenticator->getResponse())); } void ClientSession::handleTLSEncrypted() { @@ -354,8 +363,7 @@ void ClientSession::handleTLSEncrypted() { continueAfterTLSEncrypted(); } else { - boost::shared_ptr<CertificateVerificationError> identityError(new CertificateVerificationError(CertificateVerificationError::InvalidServerIdentity)); - checkTrustOrFinish(certificate, identityError); + checkTrustOrFinish(certificate, boost::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidServerIdentity)); } } } @@ -407,19 +415,22 @@ void ClientSession::finish() { } void ClientSession::finishSession(Error::Type error) { - finishSession(boost::shared_ptr<Swift::ClientSession::Error>(new Swift::ClientSession::Error(error))); + finishSession(boost::make_shared<Swift::ClientSession::Error>(error)); } void ClientSession::finishSession(boost::shared_ptr<Swift::Error> error) { state = Finishing; error_ = error; assert(stream->isOpen()); + if (stanzaAckResponder_) { + stanzaAckResponder_->handleAckRequestReceived(); + } stream->writeFooter(); stream->close(); } void ClientSession::requestAck() { - stream->writeElement(boost::shared_ptr<StanzaAckRequest>(new StanzaAckRequest())); + stream->writeElement(boost::make_shared<StanzaAckRequest>()); } void ClientSession::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) { @@ -427,7 +438,7 @@ void ClientSession::handleStanzaAcked(boost::shared_ptr<Stanza> stanza) { } void ClientSession::ack(unsigned int handledStanzasCount) { - stream->writeElement(boost::shared_ptr<StanzaAck>(new StanzaAck(handledStanzasCount))); + stream->writeElement(boost::make_shared<StanzaAck>(handledStanzasCount)); } } diff --git a/Swiften/Client/ClientSession.h b/Swiften/Client/ClientSession.h index 25ee694..2205c8d 100644 --- a/Swiften/Client/ClientSession.h +++ b/Swiften/Client/ClientSession.h @@ -6,17 +6,17 @@ #pragma once -#include "Swiften/Base/boost_bsignals.h" +#include <Swiften/Base/boost_bsignals.h> #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> -#include "Swiften/Base/Error.h" -#include "Swiften/Session/SessionStream.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/StreamManagement/StanzaAckRequester.h" -#include "Swiften/StreamManagement/StanzaAckResponder.h" +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/Element.h> +#include <Swiften/StreamManagement/StanzaAckRequester.h> +#include <Swiften/StreamManagement/StanzaAckResponder.h> namespace Swift { class ClientAuthenticator; @@ -59,7 +59,8 @@ namespace Swift { enum UseTLS { NeverUseTLS, - UseTLSWhenAvailable + UseTLSWhenAvailable, + RequireTLS }; ~ClientSession(); @@ -84,11 +85,19 @@ namespace Swift { useTLS = b; } + void setUseAcks(bool b) { + useAcks = b; + } + bool getStreamManagementEnabled() const { return stanzaAckRequester_; } + bool getRosterVersioningSupported() const { + return rosterVersioningSupported; + } + const JID& getLocalJID() const { return localJID; } @@ -100,7 +109,7 @@ namespace Swift { return getState() == Finished; } - void sendCredentials(const std::string& password); + void sendCredentials(const SafeByteArray& password); void sendStanza(boost::shared_ptr<Stanza>); void setCertificateTrustChecker(CertificateTrustChecker* checker) { @@ -150,9 +159,11 @@ namespace Swift { bool allowPLAINOverNonTLS; bool useStreamCompression; UseTLS useTLS; + bool useAcks; bool needSessionStart; bool needResourceBind; bool needAcking; + bool rosterVersioningSupported; ClientAuthenticator* authenticator; boost::shared_ptr<StanzaAckRequester> stanzaAckRequester_; boost::shared_ptr<StanzaAckResponder> stanzaAckResponder_; diff --git a/Swiften/Client/ClientSessionStanzaChannel.cpp b/Swiften/Client/ClientSessionStanzaChannel.cpp index 6b32b3d..5dc0a42 100644 --- a/Swiften/Client/ClientSessionStanzaChannel.cpp +++ b/Swiften/Client/ClientSessionStanzaChannel.cpp @@ -4,9 +4,10 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Client/ClientSessionStanzaChannel.h" +#include <Swiften/Client/ClientSessionStanzaChannel.h> #include <boost/bind.hpp> +#include <iostream> namespace Swift { diff --git a/Swiften/Client/ClientSessionStanzaChannel.h b/Swiften/Client/ClientSessionStanzaChannel.h index 8a56301..47fb50e 100644 --- a/Swiften/Client/ClientSessionStanzaChannel.h +++ b/Swiften/Client/ClientSessionStanzaChannel.h @@ -8,12 +8,12 @@ #include <boost/shared_ptr.hpp> -#include "Swiften/Base/IDGenerator.h" -#include "Swiften/Client/ClientSession.h" -#include "Swiften/Client/StanzaChannel.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/IQ.h" -#include "Swiften/Elements/Presence.h" +#include <Swiften/Base/IDGenerator.h> +#include <Swiften/Client/ClientSession.h> +#include <Swiften/Client/StanzaChannel.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/IQ.h> +#include <Swiften/Elements/Presence.h> namespace Swift { /** diff --git a/Swiften/Client/ClientXMLTracer.cpp b/Swiften/Client/ClientXMLTracer.cpp new file mode 100644 index 0000000..c1093eb --- /dev/null +++ b/Swiften/Client/ClientXMLTracer.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Client/ClientXMLTracer.h> + +#include <iostream> +#include <boost/bind.hpp> + +namespace Swift { + +ClientXMLTracer::ClientXMLTracer(CoreClient* client) { + beautifier = new XMLBeautifier(true, true); + client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, this, '<', _1)); + client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, this, '>', _1)); +} + +ClientXMLTracer::~ClientXMLTracer() { + delete beautifier; +} + +void ClientXMLTracer::printData(char direction, const SafeByteArray& data) { + printLine(direction); + std::cerr << beautifier->beautify(byteArrayToString(ByteArray(data.begin(), data.end()))) << std::endl; +} + +void ClientXMLTracer::printLine(char c) { + for (unsigned int i = 0; i < 80; ++i) { + std::cerr << c; + } + std::cerr << std::endl; +} + +} diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h index bca2a54..0752faa 100644 --- a/Swiften/Client/ClientXMLTracer.h +++ b/Swiften/Client/ClientXMLTracer.h @@ -6,29 +6,20 @@ #pragma once -#include <boost/bind.hpp> - #include <Swiften/Client/CoreClient.h> +#include <Swiften/Client/XMLBeautifier.h> +#include <Swiften/Base/SafeByteArray.h> namespace Swift { class ClientXMLTracer { public: - ClientXMLTracer(CoreClient* client) { - client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, '<', _1)); - client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, '>', _1)); - } - + ClientXMLTracer(CoreClient* client); + ~ClientXMLTracer(); private: - static void printData(char direction, const std::string& data) { - printLine(direction); - std::cerr << data << std::endl; - } + void printData(char direction, const SafeByteArray& data); + void printLine(char c); - static void printLine(char c) { - for (unsigned int i = 0; i < 80; ++i) { - std::cerr << c; - } - std::cerr << std::endl; - } + private: + XMLBeautifier *beautifier; }; } diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp index 8799d4e..a223e3d 100644 --- a/Swiften/Client/CoreClient.cpp +++ b/Swiften/Client/CoreClient.cpp @@ -4,25 +4,31 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Client/CoreClient.h" +#include <Swiften/Client/CoreClient.h> #include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> -#include "Swiften/Client/ClientSession.h" -#include "Swiften/TLS/PlatformTLSFactories.h" -#include "Swiften/TLS/CertificateVerificationError.h" -#include "Swiften/Network/Connector.h" -#include "Swiften/Network/NetworkFactories.h" -#include "Swiften/TLS/PKCS12Certificate.h" -#include "Swiften/Session/BasicSessionStream.h" -#include "Swiften/Queries/IQRouter.h" -#include "Swiften/Base/IDGenerator.h" -#include "Swiften/Client/ClientSessionStanzaChannel.h" +#include <Swiften/Base/IDGenerator.h> #include <Swiften/Base/Log.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/Algorithm.h> +#include <Swiften/Client/ClientSession.h> +#include <Swiften/TLS/PlatformTLSFactories.h> +#include <Swiften/TLS/CertificateVerificationError.h> +#include <Swiften/Network/ChainedConnector.h> +#include <Swiften/Network/NetworkFactories.h> +#include <Swiften/TLS/PKCS12Certificate.h> +#include <Swiften/Session/BasicSessionStream.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Client/ClientSessionStanzaChannel.h> +#include <Swiften/Network/PlatformProxyProvider.h> +#include <Swiften/Network/SOCKS5ProxiedConnectionFactory.h> +#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h> namespace Swift { -CoreClient::CoreClient(const JID& jid, const std::string& password, NetworkFactories* networkFactories) : jid_(jid), password_(password), networkFactories(networkFactories), useStreamCompression(true), useTLS(UseTLSWhenAvailable), disconnectRequested_(false), certificateTrustChecker(NULL) { +CoreClient::CoreClient(const JID& jid, const SafeByteArray& password, NetworkFactories* networkFactories) : jid_(jid), password_(password), networkFactories(networkFactories), disconnectRequested_(false), certificateTrustChecker(NULL) { stanzaChannel_ = new ClientSessionStanzaChannel(); stanzaChannel_->onMessageReceived.connect(boost::bind(&CoreClient::handleMessageReceived, this, _1)); stanzaChannel_->onPresenceReceived.connect(boost::bind(&CoreClient::handlePresenceReceived, this, _1)); @@ -48,8 +54,9 @@ CoreClient::~CoreClient() { delete stanzaChannel_; } -void CoreClient::connect() { +void CoreClient::connect(const ClientOptions& o) { SWIFT_LOG(debug) << "Connecting" << std::endl; + options = o; connect(jid_.getDomain()); } @@ -57,7 +64,19 @@ void CoreClient::connect(const std::string& host) { SWIFT_LOG(debug) << "Connecting to host " << host << std::endl; disconnectRequested_ = false; assert(!connector_); - connector_ = Connector::create(host, networkFactories->getDomainNameResolver(), networkFactories->getConnectionFactory(), networkFactories->getTimerFactory()); + + assert(proxyConnectionFactories.empty()); + PlatformProxyProvider proxyProvider; + if(proxyProvider.getSOCKS5Proxy().isValid()) { + proxyConnectionFactories.push_back(new SOCKS5ProxiedConnectionFactory(networkFactories->getConnectionFactory(), proxyProvider.getSOCKS5Proxy())); + } + if(proxyProvider.getHTTPConnectProxy().isValid()) { + proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getConnectionFactory(), proxyProvider.getHTTPConnectProxy())); + } + std::vector<ConnectionFactory*> connectionFactories(proxyConnectionFactories); + connectionFactories.push_back(networkFactories->getConnectionFactory()); + + connector_ = boost::make_shared<ChainedConnector>(host, networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory()); connector_->onConnectFinished.connect(boost::bind(&CoreClient::handleConnectorFinished, this, _1)); connector_->setTimeoutMilliseconds(60*1000); connector_->start(); @@ -66,7 +85,15 @@ void CoreClient::connect(const std::string& host) { void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connection) { connector_->onConnectFinished.disconnect(boost::bind(&CoreClient::handleConnectorFinished, this, _1)); connector_.reset(); + foreach(ConnectionFactory* f, proxyConnectionFactories) { + delete f; + } + proxyConnectionFactories.clear(); + if (!connection) { + if (options.forgetPassword) { + purgePassword(); + } onDisconnected(disconnectRequested_ ? boost::optional<ClientError>() : boost::optional<ClientError>(ClientError::ConnectionError)); } else { @@ -74,7 +101,7 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio connection_ = connection; assert(!sessionStream_); - sessionStream_ = boost::shared_ptr<BasicSessionStream>(new BasicSessionStream(ClientStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), tlsFactories->getTLSContextFactory(), networkFactories->getTimerFactory())); + sessionStream_ = boost::make_shared<BasicSessionStream>(ClientStreamType, connection_, getPayloadParserFactories(), getPayloadSerializers(), tlsFactories->getTLSContextFactory(), networkFactories->getTimerFactory()); if (!certificate_.empty()) { sessionStream_->setTLSCertificate(PKCS12Certificate(certificate_, password_)); } @@ -83,15 +110,20 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio session_ = ClientSession::create(jid_, sessionStream_); session_->setCertificateTrustChecker(certificateTrustChecker); - session_->setUseStreamCompression(useStreamCompression); - switch(useTLS) { - case UseTLSWhenAvailable: + session_->setUseStreamCompression(options.useStreamCompression); + session_->setAllowPLAINOverNonTLS(options.allowPLAINWithoutTLS); + switch(options.useTLS) { + case ClientOptions::UseTLSWhenAvailable: session_->setUseTLS(ClientSession::UseTLSWhenAvailable); break; - case NeverUseTLS: + case ClientOptions::NeverUseTLS: session_->setUseTLS(ClientSession::NeverUseTLS); break; + case ClientOptions::RequireTLS: + session_->setUseTLS(ClientSession::RequireTLS); + break; } + session_->setUseAcks(options.useAcks); stanzaChannel_->setSession(session_); session_->onFinished.connect(boost::bind(&CoreClient::handleSessionFinished, this, _1)); session_->onNeedCredentials.connect(boost::bind(&CoreClient::handleNeedCredentials, this)); @@ -116,6 +148,9 @@ void CoreClient::setCertificate(const std::string& certificate) { } void CoreClient::handleSessionFinished(boost::shared_ptr<Error> error) { + if (options.forgetPassword) { + purgePassword(); + } session_->onFinished.disconnect(boost::bind(&CoreClient::handleSessionFinished, this, _1)); session_->onNeedCredentials.disconnect(boost::bind(&CoreClient::handleNeedCredentials, this)); @@ -227,18 +262,22 @@ void CoreClient::handleSessionFinished(boost::shared_ptr<Error> error) { void CoreClient::handleNeedCredentials() { assert(session_); session_->sendCredentials(password_); + if (options.forgetPassword) { + purgePassword(); + } } -void CoreClient::handleDataRead(const std::string& data) { +void CoreClient::handleDataRead(const SafeByteArray& data) { onDataRead(data); } -void CoreClient::handleDataWritten(const std::string& data) { +void CoreClient::handleDataWritten(const SafeByteArray& data) { onDataWritten(data); } void CoreClient::handleStanzaChannelAvailableChanged(bool available) { if (available) { + handleConnected(); onConnected(); } } @@ -276,13 +315,29 @@ void CoreClient::handleStanzaAcked(Stanza::ref stanza) { onStanzaAcked(stanza); } -void CoreClient::setUseStreamCompression(bool b) { - useStreamCompression = b; +bool CoreClient::isAvailable() const { + return stanzaChannel_->isAvailable(); +} + +bool CoreClient::getStreamManagementEnabled() const { + return stanzaChannel_->getStreamManagementEnabled(); } -void CoreClient::setUseTLS(UseTLS b) { - useTLS = b; +StanzaChannel* CoreClient::getStanzaChannel() const { + return stanzaChannel_; } +const JID& CoreClient::getJID() const { + if (session_) { + return session_->getLocalJID(); + } + else { + return jid_; + } +} + +void CoreClient::purgePassword() { + safeClear(password_); +} } diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h index eb9c42c..3472e76 100644 --- a/Swiften/Client/CoreClient.h +++ b/Swiften/Client/CoreClient.h @@ -6,35 +6,34 @@ #pragma once -#include "Swiften/Base/boost_bsignals.h" +#include <string> #include <boost/shared_ptr.hpp> +#include <Swiften/Base/boost_bsignals.h> -#include "Swiften/Network/PlatformDomainNameResolver.h" -#include "Swiften/Network/Connector.h" -#include "Swiften/Base/Error.h" -#include "Swiften/Client/ClientSession.h" -#include "Swiften/Client/ClientError.h" -#include "Swiften/Elements/Presence.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/JID/JID.h" -#include <string> -#include "Swiften/Client/StanzaChannel.h" -#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" -#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h" #include <Swiften/Entity/Entity.h> - -#include "Swiften/Client/ClientSessionStanzaChannel.h" +#include <Swiften/JID/JID.h> +#include <Swiften/Client/ClientError.h> +#include <Swiften/Client/ClientOptions.h> +#include <Swiften/Base/SafeByteArray.h> namespace Swift { + class ChainedConnector; + class Message; + class Presence; + class Error; class IQRouter; class TLSContextFactory; class ConnectionFactory; + class Connection; class TimerFactory; class ClientSession; + class StanzaChannel; + class Stanza; class BasicSessionStream; class PlatformTLSFactories; class CertificateTrustChecker; class NetworkFactories; + class ClientSessionStanzaChannel; /** * The central class for communicating with an XMPP server. @@ -48,16 +47,11 @@ namespace Swift { */ class CoreClient : public Entity { public: - enum UseTLS { - NeverUseTLS, - UseTLSWhenAvailable - }; - /** * 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 std::string& password, NetworkFactories* networkFactories); + CoreClient(const JID& jid, const SafeByteArray& password, NetworkFactories* networkFactories); ~CoreClient(); void setCertificate(const std::string& certificate); @@ -68,7 +62,7 @@ namespace Swift { * After the connection is established, the client will set * initialize the stream and authenticate. */ - void connect(); + void connect(const ClientOptions& = ClientOptions()); /** * Disconnects the client from the server. @@ -80,12 +74,12 @@ namespace Swift { /** * Sends a message. */ - void sendMessage(Message::ref); + void sendMessage(boost::shared_ptr<Message>); /** * Sends a presence stanza. */ - void sendPresence(Presence::ref); + void sendPresence(boost::shared_ptr<Presence>); /** * Sends raw, unchecked data. @@ -103,9 +97,7 @@ namespace Swift { * Checks whether the client is connected to the server, * and stanzas can be sent. */ - bool isAvailable() const { - return stanzaChannel_->isAvailable(); - } + bool isAvailable() const; /** * Checks whether the client is active. @@ -118,14 +110,7 @@ namespace Swift { * Returns the JID of the client. * After the session was initialized, this returns the bound JID. */ - const JID& getJID() const { - if (session_) { - return session_->getLocalJID(); - } - else { - return jid_; - } - } + const JID& getJID() const; /** * Checks whether stream management is enabled. @@ -135,13 +120,9 @@ namespace Swift { * * \see onStanzaAcked */ - bool getStreamManagementEnabled() const { - return stanzaChannel_->getStreamManagementEnabled(); - } + bool getStreamManagementEnabled() const; - StanzaChannel* getStanzaChannel() const { - return stanzaChannel_; - } + StanzaChannel* getStanzaChannel() const; /** * Sets the certificate trust checker. @@ -153,16 +134,6 @@ namespace Swift { */ void setCertificateTrustChecker(CertificateTrustChecker*); - /** - * Sets whether ZLib stream compression should be used when available. - */ - void setUseStreamCompression(bool b); - - /** - * Sets whether TLS encryption should be used. - */ - void setUseTLS(UseTLS useTLS); - public: /** * Emitted when the client was disconnected from the network. @@ -184,7 +155,7 @@ namespace Swift { * This signal is emitted before the XML data is parsed, * so this data is unformatted. */ - boost::signal<void (const std::string&)> onDataRead; + boost::signal<void (const SafeByteArray&)> onDataRead; /** * Emitted when the client sends data. @@ -192,17 +163,17 @@ namespace Swift { * This signal is emitted after the XML was serialized, and * is unformatted. */ - boost::signal<void (const std::string&)> onDataWritten; + boost::signal<void (const SafeByteArray&)> onDataWritten; /** * Emitted when a message is received. */ - boost::signal<void (Message::ref)> onMessageReceived; + boost::signal<void (boost::shared_ptr<Message>)> onMessageReceived; /** * Emitted when a presence stanza is received. */ - boost::signal<void (Presence::ref) > onPresenceReceived; + boost::signal<void (boost::shared_ptr<Presence>) > onPresenceReceived; /** * Emitted when the server acknowledges receipt of a @@ -210,28 +181,43 @@ namespace Swift { * * \see getStreamManagementEnabled() */ - boost::signal<void (Stanza::ref)> onStanzaAcked; + boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked; + + protected: + boost::shared_ptr<ClientSession> getSession() const { + return session_; + } + + NetworkFactories* getNetworkFactories() const { + return networkFactories; + } + + /** + * Called before onConnected signal is emmitted. + */ + virtual void handleConnected() {}; private: void handleConnectorFinished(boost::shared_ptr<Connection>); void handleStanzaChannelAvailableChanged(bool available); void handleSessionFinished(boost::shared_ptr<Error>); void handleNeedCredentials(); - void handleDataRead(const std::string&); - void handleDataWritten(const std::string&); - void handlePresenceReceived(Presence::ref); - void handleMessageReceived(Message::ref); - void handleStanzaAcked(Stanza::ref); + void handleDataRead(const SafeByteArray&); + void handleDataWritten(const SafeByteArray&); + void handlePresenceReceived(boost::shared_ptr<Presence>); + void handleMessageReceived(boost::shared_ptr<Message>); + void handleStanzaAcked(boost::shared_ptr<Stanza>); + void purgePassword(); private: JID jid_; - std::string password_; + SafeByteArray password_; NetworkFactories* networkFactories; - bool useStreamCompression; - UseTLS useTLS; ClientSessionStanzaChannel* stanzaChannel_; IQRouter* iqRouter_; - Connector::ref connector_; + ClientOptions options; + boost::shared_ptr<ChainedConnector> connector_; + std::vector<ConnectionFactory*> proxyConnectionFactories; PlatformTLSFactories* tlsFactories; boost::shared_ptr<Connection> connection_; boost::shared_ptr<BasicSessionStream> sessionStream_; diff --git a/Swiften/Client/DummyStanzaChannel.h b/Swiften/Client/DummyStanzaChannel.h index b9f05c3..c2f3919 100644 --- a/Swiften/Client/DummyStanzaChannel.h +++ b/Swiften/Client/DummyStanzaChannel.h @@ -8,7 +8,7 @@ #include <vector> -#include "Swiften/Client/StanzaChannel.h" +#include <Swiften/Client/StanzaChannel.h> namespace Swift { class DummyStanzaChannel : public StanzaChannel { @@ -56,6 +56,22 @@ namespace Swift { return iqStanza && iqStanza->getType() == type && iqStanza->getTo() == jid && iqStanza->getPayload<T>(); } + bool isResultAtIndex(size_t index, const std::string& id) { + if (index >= sentStanzas.size()) { + return false; + } + boost::shared_ptr<IQ> iqStanza = boost::dynamic_pointer_cast<IQ>(sentStanzas[index]); + return iqStanza && iqStanza->getType() == IQ::Result && iqStanza->getID() == id; + } + + bool isErrorAtIndex(size_t index, const std::string& id) { + if (index >= sentStanzas.size()) { + return false; + } + boost::shared_ptr<IQ> iqStanza = boost::dynamic_pointer_cast<IQ>(sentStanzas[index]); + return iqStanza && iqStanza->getType() == IQ::Error && iqStanza->getID() == id; + } + template<typename T> boost::shared_ptr<T> getStanzaAtIndex(size_t index) { if (sentStanzas.size() <= index) { return boost::shared_ptr<T>(); diff --git a/Swiften/Client/FileStorages.cpp b/Swiften/Client/FileStorages.cpp deleted file mode 100644 index 3c76c46..0000000 --- a/Swiften/Client/FileStorages.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#include "Swiften/Client/FileStorages.h" -#include "Swiften/VCards/VCardFileStorage.h" -#include "Swiften/Avatars/AvatarFileStorage.h" -#include "Swiften/Disco/CapsFileStorage.h" - -namespace Swift { - -FileStorages::FileStorages(const boost::filesystem::path& baseDir, const JID& jid) { - std::string profile = jid.toBare(); - vcardStorage = new VCardFileStorage(baseDir / profile / "vcards"); - capsStorage = new CapsFileStorage(baseDir / "caps"); - avatarStorage = new AvatarFileStorage(baseDir / "avatars", baseDir / profile / "avatars"); -} - -FileStorages::~FileStorages() { - delete avatarStorage; - delete capsStorage; - delete vcardStorage; -} - -VCardStorage* FileStorages::getVCardStorage() const { - return vcardStorage; -} - -CapsStorage* FileStorages::getCapsStorage() const { - return capsStorage; -} - -AvatarStorage* FileStorages::getAvatarStorage() const { - return avatarStorage; -} - -} diff --git a/Swiften/Client/FileStorages.h b/Swiften/Client/FileStorages.h deleted file mode 100644 index 451105f..0000000 --- a/Swiften/Client/FileStorages.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. - */ - -#pragma once - -#include <boost/filesystem.hpp> - -#include "Swiften/Client/Storages.h" - -namespace Swift { - class VCardFileStorage; - class AvatarFileStorage; - class CapsFileStorage; - class JID; - - /** - * A storages implementation that stores all controller data on disk. - */ - class FileStorages : public Storages { - public: - /** - * Creates the storages interface. - * - * All data will be stored relative to a base directory, and - * for some controllers, in a subdirectory for the given profile. - * The data is stored in the following places: - * - Avatars: <basedir>/avatars - * - VCards: <basedir>/<profile>/vcards - * - Entity capabilities: <basedir>/caps - * - * \param baseDir the base dir to store data relative to - * \param jid the subdir in which profile-specific data will be stored. - * The bare JID will be used as the subdir name. - */ - FileStorages(const boost::filesystem::path& baseDir, const JID& jid); - ~FileStorages(); - - virtual VCardStorage* getVCardStorage() const; - virtual AvatarStorage* getAvatarStorage() const; - virtual CapsStorage* getCapsStorage() const; - - private: - VCardFileStorage* vcardStorage; - AvatarFileStorage* avatarStorage; - CapsFileStorage* capsStorage; - }; -} diff --git a/Swiften/Client/MemoryStorages.cpp b/Swiften/Client/MemoryStorages.cpp index 5f6371b..fe171f7 100644 --- a/Swiften/Client/MemoryStorages.cpp +++ b/Swiften/Client/MemoryStorages.cpp @@ -4,10 +4,11 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Client/MemoryStorages.h" -#include "Swiften/VCards/VCardMemoryStorage.h" -#include "Swiften/Avatars/AvatarMemoryStorage.h" -#include "Swiften/Disco/CapsMemoryStorage.h" +#include <Swiften/Client/MemoryStorages.h> +#include <Swiften/VCards/VCardMemoryStorage.h> +#include <Swiften/Avatars/AvatarMemoryStorage.h> +#include <Swiften/Disco/CapsMemoryStorage.h> +#include <Swiften/Roster/RosterMemoryStorage.h> namespace Swift { @@ -15,9 +16,11 @@ MemoryStorages::MemoryStorages() { vcardStorage = new VCardMemoryStorage(); capsStorage = new CapsMemoryStorage(); avatarStorage = new AvatarMemoryStorage(); + rosterStorage = new RosterMemoryStorage(); } MemoryStorages::~MemoryStorages() { + delete rosterStorage; delete avatarStorage; delete capsStorage; delete vcardStorage; @@ -35,4 +38,9 @@ AvatarStorage* MemoryStorages::getAvatarStorage() const { return avatarStorage; } +RosterStorage* MemoryStorages::getRosterStorage() const { + return rosterStorage; +} + + } diff --git a/Swiften/Client/MemoryStorages.h b/Swiften/Client/MemoryStorages.h index 67025cd..ca01a7a 100644 --- a/Swiften/Client/MemoryStorages.h +++ b/Swiften/Client/MemoryStorages.h @@ -6,7 +6,7 @@ #pragma once -#include "Swiften/Client/Storages.h" +#include <Swiften/Client/Storages.h> namespace Swift { class VCardMemoryStorage; @@ -23,10 +23,12 @@ namespace Swift { virtual VCardStorage* getVCardStorage() const; virtual AvatarStorage* getAvatarStorage() const; virtual CapsStorage* getCapsStorage() const; + virtual RosterStorage* getRosterStorage() const; private: VCardMemoryStorage* vcardStorage; AvatarStorage* avatarStorage; CapsStorage* capsStorage; + RosterStorage* rosterStorage; }; } diff --git a/Swiften/Client/NickResolver.cpp b/Swiften/Client/NickResolver.cpp index 6d5e742..e6f2e41 100644 --- a/Swiften/Client/NickResolver.cpp +++ b/Swiften/Client/NickResolver.cpp @@ -4,14 +4,14 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Client/NickResolver.h" +#include <Swiften/Client/NickResolver.h> #include <boost/shared_ptr.hpp> #include <boost/bind.hpp> -#include "Swiften/MUC/MUCRegistry.h" -#include "Swiften/Roster/XMPPRoster.h" -#include "Swiften/VCards/VCardManager.h" +#include <Swiften/MUC/MUCRegistry.h> +#include <Swiften/Roster/XMPPRoster.h> +#include <Swiften/VCards/VCardManager.h> // FIXME: The NickResolver currently relies on the vcard being requested by the client on login. // The VCardManager should get an onConnected() signal (which is signalled when the stanzachannel is available(, and each time this is emitted, diff --git a/Swiften/Client/NickResolver.h b/Swiften/Client/NickResolver.h index 881362a..584f2ce 100644 --- a/Swiften/Client/NickResolver.h +++ b/Swiften/Client/NickResolver.h @@ -5,12 +5,12 @@ */ #include <map> -#include <boost/signals.hpp> #include <boost/shared_ptr.hpp> +#include <Swiften/Base/boost_bsignals.h> #include <string> -#include "Swiften/JID/JID.h" -#include "Swiften/Elements/VCard.h" +#include <Swiften/JID/JID.h> +#include <Swiften/Elements/VCard.h> namespace Swift { class XMPPRoster; diff --git a/Swiften/Client/StanzaChannel.h b/Swiften/Client/StanzaChannel.h index 4d6392c..f1d76e0 100644 --- a/Swiften/Client/StanzaChannel.h +++ b/Swiften/Client/StanzaChannel.h @@ -6,12 +6,12 @@ #pragma once -#include "Swiften/Base/boost_bsignals.h" +#include <Swiften/Base/boost_bsignals.h> #include <boost/shared_ptr.hpp> -#include "Swiften/Queries/IQChannel.h" -#include "Swiften/Elements/Message.h" -#include "Swiften/Elements/Presence.h" +#include <Swiften/Queries/IQChannel.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/Presence.h> namespace Swift { class StanzaChannel : public IQChannel { diff --git a/Swiften/Client/Storages.cpp b/Swiften/Client/Storages.cpp new file mode 100644 index 0000000..3c2dbc5 --- /dev/null +++ b/Swiften/Client/Storages.cpp @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Client/Storages.h> + +using namespace Swift; + +Storages::~Storages() { +} diff --git a/Swiften/Client/Storages.h b/Swiften/Client/Storages.h index e62f0a9..1c5bbe9 100644 --- a/Swiften/Client/Storages.h +++ b/Swiften/Client/Storages.h @@ -10,6 +10,7 @@ namespace Swift { class VCardStorage; class AvatarStorage; class CapsStorage; + class RosterStorage; /** * An interface to hold storage classes for different @@ -17,10 +18,11 @@ namespace Swift { */ class Storages { public: - virtual ~Storages() {} + virtual ~Storages(); virtual VCardStorage* getVCardStorage() const = 0; virtual AvatarStorage* getAvatarStorage() const = 0; virtual CapsStorage* getCapsStorage() const = 0; + virtual RosterStorage* getRosterStorage() const = 0; }; } diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp index 756287c..e9d1b21 100644 --- a/Swiften/Client/UnitTest/ClientSessionTest.cpp +++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp @@ -11,23 +11,25 @@ #include <boost/optional.hpp> #include <boost/smart_ptr/make_shared.hpp> -#include "Swiften/Session/SessionStream.h" -#include "Swiften/Client/ClientSession.h" -#include "Swiften/Elements/StartTLSRequest.h" -#include "Swiften/Elements/StreamFeatures.h" -#include "Swiften/Elements/StreamError.h" -#include "Swiften/Elements/TLSProceed.h" -#include "Swiften/Elements/StartTLSFailure.h" -#include "Swiften/Elements/AuthRequest.h" -#include "Swiften/Elements/AuthSuccess.h" -#include "Swiften/Elements/AuthFailure.h" -#include "Swiften/Elements/StreamManagementEnabled.h" -#include "Swiften/Elements/StreamManagementFailed.h" -#include "Swiften/Elements/EnableStreamManagement.h" -#include "Swiften/Elements/IQ.h" -#include "Swiften/Elements/ResourceBind.h" -#include "Swiften/TLS/SimpleCertificate.h" -#include "Swiften/TLS/BlindCertificateTrustChecker.h" +#include <Swiften/Session/SessionStream.h> +#include <Swiften/Client/ClientSession.h> +#include <Swiften/Elements/Message.h> +#include <Swiften/Elements/StartTLSRequest.h> +#include <Swiften/Elements/StreamFeatures.h> +#include <Swiften/Elements/StreamError.h> +#include <Swiften/Elements/TLSProceed.h> +#include <Swiften/Elements/StartTLSFailure.h> +#include <Swiften/Elements/AuthRequest.h> +#include <Swiften/Elements/AuthSuccess.h> +#include <Swiften/Elements/AuthFailure.h> +#include <Swiften/Elements/StreamManagementEnabled.h> +#include <Swiften/Elements/StreamManagementFailed.h> +#include <Swiften/Elements/StanzaAck.h> +#include <Swiften/Elements/EnableStreamManagement.h> +#include <Swiften/Elements/IQ.h> +#include <Swiften/Elements/ResourceBind.h> +#include <Swiften/TLS/SimpleCertificate.h> +#include <Swiften/TLS/BlindCertificateTrustChecker.h> using namespace Swift; @@ -43,8 +45,11 @@ class ClientSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testAuthenticate); CPPUNIT_TEST(testAuthenticate_Unauthorized); CPPUNIT_TEST(testAuthenticate_NoValidAuthMechanisms); + CPPUNIT_TEST(testAuthenticate_PLAINOverNonTLS); + CPPUNIT_TEST(testAuthenticate_RequireTLS); CPPUNIT_TEST(testStreamManagement); CPPUNIT_TEST(testStreamManagement_Failed); + CPPUNIT_TEST(testFinishAcksStanzas); /* CPPUNIT_TEST(testResourceBind); CPPUNIT_TEST(testResourceBind_ChangeResource); @@ -178,7 +183,7 @@ class ClientSessionTest : public CppUnit::TestFixture { server->sendStreamFeaturesWithPLAINAuthentication(); CPPUNIT_ASSERT(needCredentials); CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState()); - session->sendCredentials("mypass"); + session->sendCredentials(createSafeByteArray("mypass")); server->receiveAuthRequest("PLAIN"); server->sendAuthSuccess(); server->receiveStreamStart(); @@ -194,7 +199,7 @@ class ClientSessionTest : public CppUnit::TestFixture { server->sendStreamFeaturesWithPLAINAuthentication(); CPPUNIT_ASSERT(needCredentials); CPPUNIT_ASSERT_EQUAL(ClientSession::WaitingForCredentials, session->getState()); - session->sendCredentials("mypass"); + session->sendCredentials(createSafeByteArray("mypass")); server->receiveAuthRequest("PLAIN"); server->sendAuthFailure(); @@ -216,6 +221,20 @@ class ClientSessionTest : public CppUnit::TestFixture { CPPUNIT_ASSERT(sessionFinishedError); } + void testAuthenticate_RequireTLS() { + boost::shared_ptr<ClientSession> session(createSession()); + session->setUseTLS(ClientSession::RequireTLS); + session->setAllowPLAINOverNonTLS(true); + session->start(); + server->receiveStreamStart(); + server->sendStreamStart(); + server->sendStreamFeaturesWithMultipleAuthentication(); + + CPPUNIT_ASSERT_EQUAL(ClientSession::Finished, session->getState()); + CPPUNIT_ASSERT(sessionFinishedReceived); + CPPUNIT_ASSERT(sessionFinishedError); + } + void testAuthenticate_NoValidAuthMechanisms() { boost::shared_ptr<ClientSession> session(createSession()); session->start(); @@ -234,7 +253,7 @@ class ClientSessionTest : public CppUnit::TestFixture { server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithPLAINAuthentication(); - session->sendCredentials("mypass"); + session->sendCredentials(createSafeByteArray("mypass")); server->receiveAuthRequest("PLAIN"); server->sendAuthSuccess(); server->receiveStreamStart(); @@ -258,7 +277,7 @@ class ClientSessionTest : public CppUnit::TestFixture { server->receiveStreamStart(); server->sendStreamStart(); server->sendStreamFeaturesWithPLAINAuthentication(); - session->sendCredentials("mypass"); + session->sendCredentials(createSafeByteArray("mypass")); server->receiveAuthRequest("PLAIN"); server->sendAuthSuccess(); server->receiveStreamStart(); @@ -275,6 +294,17 @@ class ClientSessionTest : public CppUnit::TestFixture { session->finish(); } + void testFinishAcksStanzas() { + boost::shared_ptr<ClientSession> session(createSession()); + initializeSession(session); + server->sendMessage(); + server->sendMessage(); + server->sendMessage(); + + session->finish(); + + server->receiveAck(3); + } private: boost::shared_ptr<ClientSession> createSession() { @@ -285,6 +315,23 @@ class ClientSessionTest : public CppUnit::TestFixture { return session; } + void initializeSession(boost::shared_ptr<ClientSession> session) { + session->start(); + server->receiveStreamStart(); + server->sendStreamStart(); + server->sendStreamFeaturesWithPLAINAuthentication(); + session->sendCredentials(createSafeByteArray("mypass")); + server->receiveAuthRequest("PLAIN"); + server->sendAuthSuccess(); + server->receiveStreamStart(); + server->sendStreamStart(); + server->sendStreamFeaturesWithBindAndStreamManagement(); + server->receiveBind(); + server->sendBindResult(); + server->receiveStreamManagementEnable(); + server->sendStreamManagementEnabled(); + } + void handleSessionFinished(boost::shared_ptr<Error> error) { sessionFinishedReceived = true; sessionFinishedError = error; @@ -401,6 +448,14 @@ class ClientSessionTest : public CppUnit::TestFixture { onElementReceived(boost::shared_ptr<StartTLSFailure>(new StartTLSFailure())); } + void sendStreamFeaturesWithMultipleAuthentication() { + boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); + streamFeatures->addAuthenticationMechanism("PLAIN"); + streamFeatures->addAuthenticationMechanism("DIGEST-MD5"); + streamFeatures->addAuthenticationMechanism("SCRAM-SHA1"); + onElementReceived(streamFeatures); + } + void sendStreamFeaturesWithPLAINAuthentication() { boost::shared_ptr<StreamFeatures> streamFeatures(new StreamFeatures()); streamFeatures->addAuthenticationMechanism("PLAIN"); @@ -447,6 +502,12 @@ class ClientSessionTest : public CppUnit::TestFixture { onElementReceived(iq); } + void sendMessage() { + boost::shared_ptr<Message> message = boost::make_shared<Message>(); + message->setTo(JID("foo@bar.com/bla")); + onElementReceived(message); + } + void receiveStreamStart() { Event event = popEvent(); CPPUNIT_ASSERT(event.header); @@ -481,8 +542,16 @@ class ClientSessionTest : public CppUnit::TestFixture { bindID = iq->getID(); } + void receiveAck(unsigned int n) { + Event event = popEvent(); + CPPUNIT_ASSERT(event.element); + boost::shared_ptr<StanzaAck> ack = boost::dynamic_pointer_cast<StanzaAck>(event.element); + CPPUNIT_ASSERT(ack); + CPPUNIT_ASSERT_EQUAL(n, ack->getHandledStanzasCount()); + } + Event popEvent() { - CPPUNIT_ASSERT(receivedEvents.size() > 0); + CPPUNIT_ASSERT(!receivedEvents.empty()); Event event = receivedEvents.front(); receivedEvents.pop_front(); return event; diff --git a/Swiften/Client/UnitTest/NickResolverTest.cpp b/Swiften/Client/UnitTest/NickResolverTest.cpp index bd778d4..dfc90fe 100644 --- a/Swiften/Client/UnitTest/NickResolverTest.cpp +++ b/Swiften/Client/UnitTest/NickResolverTest.cpp @@ -7,13 +7,13 @@ #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include "Swiften/Client/NickResolver.h" -#include "Swiften/MUC/MUCRegistry.h" -#include "Swiften/Roster/XMPPRosterImpl.h" -#include "Swiften/VCards/VCardManager.h" -#include "Swiften/VCards/VCardMemoryStorage.h" -#include "Swiften/Queries/IQRouter.h" -#include "Swiften/Client/DummyStanzaChannel.h" +#include <Swiften/Client/NickResolver.h> +#include <Swiften/MUC/MUCRegistry.h> +#include <Swiften/Roster/XMPPRosterImpl.h> +#include <Swiften/VCards/VCardManager.h> +#include <Swiften/VCards/VCardMemoryStorage.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Client/DummyStanzaChannel.h> using namespace Swift; diff --git a/Swiften/Client/XMLBeautifier.cpp b/Swiften/Client/XMLBeautifier.cpp new file mode 100644 index 0000000..b70fc48 --- /dev/null +++ b/Swiften/Client/XMLBeautifier.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <sstream> +#include <stack> + +#include <Swiften/Base/foreach.h> +#include <Swiften/Base/Log.h> +#include <Swiften/Client/XMLBeautifier.h> +#include <Swiften/Parser/PlatformXMLParserFactory.h> + +namespace Swift { + +XMLBeautifier::XMLBeautifier(bool indention, bool coloring) : doIndention(indention), doColoring(coloring), intLevel(0), parser(NULL), lastWasStepDown(false) { + factory = new PlatformXMLParserFactory(); +} + +XMLBeautifier::~XMLBeautifier() { + delete factory; +} + +std::string XMLBeautifier::beautify(const std::string &text) { + parser = factory->createXMLParser(this); + intLevel = 0; + buffer.str(std::string()); + parser->parse(text); + delete parser; + return buffer.str(); +} + +void XMLBeautifier::indent() { + for (int i = 0; i < intLevel; ++i) { + buffer << " "; + } +} + +// 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"; + + + +std::string XMLBeautifier::styleTag(const std::string& text) const { + std::string result; + result += colorYellow; + result += text; + result += colorReset; + return result; +} + +std::string XMLBeautifier::styleNamespace(const std::string& text) const { + std::string result; + result += colorRed; + result += text; + result += colorReset; + return result; +} + +std::string XMLBeautifier::styleAttribute(const std::string& text) const { + std::string result; + result += colorGreen; + result += text; + result += colorReset; + return result; +} +std::string XMLBeautifier::styleValue(const std::string& text) const { + std::string result; + result += colorCyan; + result += text; + result += colorReset; + return result; +} + +void XMLBeautifier::handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes) { + if (doIndention) { + if (intLevel) buffer << std::endl; + } + indent(); + buffer << "<" << (doColoring ? styleTag(element) : element); + if (!ns.empty() && (!parentNSs.empty() && parentNSs.top() != ns)) { + buffer << " "; + buffer << (doColoring ? styleAttribute("xmlns") : "xmlns"); + buffer << "="; + buffer << "\"" << (doColoring ? styleNamespace(ns) : ns) << "\""; + } + if (!attributes.getEntries().empty()) { + foreach(AttributeMap::Entry entry, attributes.getEntries()) { + buffer << " "; + buffer << (doColoring ? styleAttribute(entry.getAttribute().getName()) : entry.getAttribute().getName()); + buffer << "="; + buffer << "\"" << (doColoring ? styleValue(entry.getValue()) : entry.getValue()) << "\""; + } + } + buffer << ">"; + ++intLevel; + lastWasStepDown = false; + parentNSs.push(ns); +} + +void XMLBeautifier::handleEndElement(const std::string& element, const std::string& /* ns */) { + --intLevel; + parentNSs.pop(); + if (/*hadCDATA.top() ||*/ lastWasStepDown) { + if (doIndention) { + buffer << std::endl; + } + indent(); + } + buffer << "</" << (doColoring ? styleTag(element) : element) << ">"; + lastWasStepDown = true; +} + +void XMLBeautifier::handleCharacterData(const std::string& data) { + buffer << data; + lastWasStepDown = false; +} + +} diff --git a/Swiften/Client/XMLBeautifier.h b/Swiften/Client/XMLBeautifier.h new file mode 100644 index 0000000..44dfd20 --- /dev/null +++ b/Swiften/Client/XMLBeautifier.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <string> +#include <stack> + +#include <Swiften/Base/boost_bsignals.h> +#include <Swiften/Parser/XMLParserFactory.h> +#include <Swiften/Parser/XMLParserClient.h> +#include <Swiften/Parser/XMLParser.h> + +namespace Swift { + +class XMLBeautifier : public XMLParserClient { +public: + XMLBeautifier(bool indention, bool coloring); + virtual ~XMLBeautifier(); + + std::string beautify(const std::string&); + +private: + void handleStartElement(const std::string& element, const std::string& ns, const AttributeMap& attributes); + void handleEndElement(const std::string& element, const std::string& ns); + void handleCharacterData(const std::string& data); + +private: + void indent(); + +private: + std::string styleTag(const std::string& text) const; + std::string styleNamespace(const std::string& text) const; + std::string styleAttribute(const std::string& text) const; + std::string styleValue(const std::string& text) const; + +private: + bool doIndention; + bool doColoring; + + int intLevel; + std::string inputBuffer; + std::stringstream buffer; + XMLParserFactory* factory; + XMLParser* parser; + + bool lastWasStepDown; + std::stack<std::string> parentNSs; +}; +} |